| 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..38c064a2352db842ce924b9722a1d5b45326a6e1
|
| --- /dev/null
|
| +++ b/chrome/browser/conflicts/msi_util_win.cc
|
| @@ -0,0 +1,149 @@
|
| +// 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 {
|
| +
|
| +GetMsiComponentPathsOverride* g_get_msi_component_paths = nullptr;
|
| +
|
| +// 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,
|
| + 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++;
|
| +
|
| + 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"",
|
| + &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
|
| +// GUIDS from it, and uses those to find the path of each component.
|
| +bool GetMsiComponentPaths(const base::string16& product_guid,
|
| + std::vector<base::string16>* component_paths) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + if (g_get_msi_component_paths)
|
| + return g_get_msi_component_paths->Run(product_guid, component_paths);
|
| +
|
| + 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;
|
| +}
|
| +
|
| +ScopedGetMsiComponentPathsOverride::ScopedGetMsiComponentPathsOverride(
|
| + const GetMsiComponentPathsOverride& override)
|
| + : override_(override) {
|
| + DCHECK(!g_get_msi_component_paths);
|
| + g_get_msi_component_paths = &override_;
|
| +}
|
| +
|
| +ScopedGetMsiComponentPathsOverride::~ScopedGetMsiComponentPathsOverride() {
|
| + DCHECK(g_get_msi_component_paths);
|
| + g_get_msi_component_paths = nullptr;
|
| +}
|
|
|