Chromium Code Reviews| Index: chrome/browser/conflicts/msi_util_win.cc |
| diff --git a/chrome/browser/conflicts/msi_util_win.cc b/chrome/browser/conflicts/msi_util_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fcf4d563a4a39be2cf8ab973aef28cd77cccc5b9 |
| --- /dev/null |
| +++ b/chrome/browser/conflicts/msi_util_win.cc |
| @@ -0,0 +1,133 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/conflicts/msi_util_win.h" |
| + |
| +#include <windows.h> |
| + |
| +#include <msi.h> |
| +#include <msiquery.h> |
| + |
| +#include <utility> |
| + |
| +#include "base/strings/string_util.h" |
| +#include "base/threading/thread_restrictions.h" |
| + |
| +namespace { |
| + |
| +// Retrieves the file path to the product's installer. |
| +bool GetMsiPath(const base::string16& product_guid, base::string16* result) { |
| + DWORD buffer_size = 0; |
| + if (::MsiGetProductInfo(product_guid.c_str(), INSTALLPROPERTY_LOCALPACKAGE, |
|
chrisha1
2017/05/05 20:01:40
Document that you're querying first for the requir
Patrick Monette
2017/05/06 00:54:37
Done. Let me know if you have any idea to make the
|
| + L"", &buffer_size) != ERROR_MORE_DATA) |
| + return false; |
| + |
| + // Must account for the null terminator. |
| + buffer_size++; |
| + |
| + return ::MsiGetProductInfo(product_guid.c_str(), INSTALLPROPERTY_LOCALPACKAGE, |
| + base::WriteInto(result, buffer_size), |
| + &buffer_size) == ERROR_SUCCESS; |
| +} |
| + |
| +// Returns the string value at position |index| in the given |record_handle|. |
| +// Note that columns are 1-indexed. |
| +bool GetRecordString(MSIHANDLE record_handle, |
| + size_t index, |
| + base::string16* result) { |
| + DWORD buffer_size = 0; |
| + if (::MsiRecordGetString(record_handle, index, L"", &buffer_size) != |
| + ERROR_MORE_DATA) |
| + return false; |
| + |
| + // Must account for the null terminator. |
| + buffer_size++; |
|
chrisha1
2017/05/05 20:01:40
Ditto for this read. If you have reason to believe
Patrick Monette
2017/05/06 00:54:37
Done.
|
| + |
| + return ::MsiRecordGetString(record_handle, index, |
| + base::WriteInto(result, buffer_size), |
| + &buffer_size) == ERROR_SUCCESS; |
| +} |
| + |
| +// Inspects the installer file and extracts the component guids. Each .msi file |
| +// is actually an SQL database. |
| +bool GetMsiComponentGuids(const base::string16& msi_database_path, |
| + std::vector<base::string16>* component_guids) { |
| + PMSIHANDLE msi_database_handle; |
| + if (::MsiOpenDatabase(msi_database_path.c_str(), MSIDBOPEN_READONLY, |
| + &msi_database_handle) != ERROR_SUCCESS) { |
| + return false; |
| + } |
| + |
| + PMSIHANDLE components_view_handle; |
| + if (::MsiDatabaseOpenView(msi_database_handle, |
| + L"SELECT ComponentId FROM Component", |
| + &components_view_handle) != ERROR_SUCCESS) { |
| + return false; |
| + } |
| + |
| + if (::MsiViewExecute(components_view_handle, 0) != ERROR_SUCCESS) |
| + return false; |
| + |
| + PMSIHANDLE record_handle; |
| + while (::MsiViewFetch(components_view_handle, &record_handle) == |
| + ERROR_SUCCESS) { |
| + // The record only have the ComponentId column, and its index is 1. |
| + base::string16 component_guid; |
| + if (GetRecordString(record_handle, 1, &component_guid)) |
| + component_guids->push_back(std::move(component_guid)); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +// Retrieves the |path| to the given component. |
| +bool GetMsiComponentPath(const base::string16& product_guid, |
| + const base::string16& component_guid, |
| + base::string16* path) { |
| + DWORD buffer_size = 0; |
| + if (::MsiGetComponentPath(product_guid.c_str(), component_guid.c_str(), L"", |
|
chrisha1
2017/05/05 20:01:40
Ditto.
Patrick Monette
2017/05/06 00:54:37
Done.
|
| + &buffer_size) != INSTALLSTATE_MOREDATA) { |
| + return false; |
| + } |
| + |
| + // Must account for the null terminator. |
| + buffer_size++; |
| + |
| + return ::MsiGetComponentPath(product_guid.c_str(), component_guid.c_str(), |
| + base::WriteInto(path, buffer_size), |
| + &buffer_size) == INSTALLSTATE_LOCAL; |
| +} |
| + |
| +} // namespace |
| + |
| +// The most efficient way to get the list of components associated to an |
| +// installed product is to inspect the installer file. A copy of the installer |
| +// exists somewhere on the file system because Windows needs it to uninstall the |
| +// product. |
| +// |
| +// So this function retrieves the path to the installer, extract the component |
|
chrisha1
2017/05/05 20:01:40
extracts*
Patrick Monette
2017/05/06 00:54:37
Done.
|
| +// GUIDS from it, and uses those to find the path of each component. |
| +bool MsiUtil::GetMsiComponentPaths( |
| + const base::string16& product_guid, |
| + std::vector<base::string16>* component_paths) { |
| + base::ThreadRestrictions::AssertIOAllowed(); |
| + |
| + base::string16 msi_path; |
| + if (!GetMsiPath(product_guid, &msi_path)) |
| + return false; |
| + |
| + std::vector<base::string16> component_guids; |
| + if (!GetMsiComponentGuids(msi_path, &component_guids)) |
| + return false; |
| + |
| + for (const auto& component_guid : component_guids) { |
| + base::string16 component_path; |
| + if (!GetMsiComponentPath(product_guid, component_guid, &component_path)) |
| + continue; |
| + |
| + component_paths->push_back(std::move(component_path)); |
| + } |
| + |
| + return true; |
| +} |