Commit 50cb83d9 authored by pmonette's avatar pmonette Committed by Commit bot

Add a utility class to retrieve the components of an MSI product

This will be used to map dlls to a particular program.

BUG=717696

Review-Url: https://codereview.chromium.org/2854143005
Cr-Commit-Position: refs/heads/master@{#471193}
parent 6f29a51e
......@@ -45,6 +45,7 @@ if (is_win) {
"netapi32.lib",
"ndfapi.lib", # Used by browser/net/net_error_diagnostics_dialog_win.h
"pdh.lib", # Used by browser/private_working_set_snapshot.h
"msi.lib", # Used by browser/conflicts/msi_util_win.h
]
ldflags = [
"/DELAYLOAD:ndfapi.dll",
......@@ -273,6 +274,8 @@ split_static_library("browser") {
"conflicts/module_info_win.h",
"conflicts/module_inspector_win.cc",
"conflicts/module_inspector_win.h",
"conflicts/msi_util_win.cc",
"conflicts/msi_util_win.h",
"content_settings/chrome_content_settings_utils.cc",
"content_settings/chrome_content_settings_utils.h",
"content_settings/cookie_settings_factory.cc",
......
// 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 {
// Most strings returned by the MSI API are smaller than this value, so only
// 1 call to the API is needed in the common case.
constexpr DWORD kBufferInitialSize = 256;
// Retrieves the file path to the product's installer.
bool GetMsiPath(const base::string16& product_guid, base::string16* result) {
DWORD buffer_size = kBufferInitialSize;
UINT ret =
::MsiGetProductInfo(product_guid.c_str(), INSTALLPROPERTY_LOCALPACKAGE,
base::WriteInto(result, buffer_size), &buffer_size);
if (ret == ERROR_MORE_DATA) {
// Must account for the null terminator.
buffer_size++;
ret =
::MsiGetProductInfo(product_guid.c_str(), INSTALLPROPERTY_LOCALPACKAGE,
base::WriteInto(result, buffer_size), &buffer_size);
}
if (ret == ERROR_SUCCESS) {
result->resize(buffer_size);
return true;
}
return false;
}
// 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 = kBufferInitialSize;
UINT ret = ::MsiRecordGetString(
record_handle, index, base::WriteInto(result, buffer_size), &buffer_size);
if (ret == ERROR_MORE_DATA) {
// Must account for the null terminator.
buffer_size++;
ret = ::MsiRecordGetString(record_handle, index,
base::WriteInto(result, buffer_size),
&buffer_size);
}
if (ret == ERROR_SUCCESS) {
result->resize(buffer_size);
return true;
}
return false;
}
// 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 = kBufferInitialSize;
INSTALLSTATE ret =
::MsiGetComponentPath(product_guid.c_str(), component_guid.c_str(),
base::WriteInto(path, buffer_size), &buffer_size);
if (ret == INSTALLSTATE_MOREDATA) {
// Must account for the null terminator.
buffer_size++;
ret =
::MsiGetComponentPath(product_guid.c_str(), component_guid.c_str(),
base::WriteInto(path, buffer_size), &buffer_size);
}
if (ret == INSTALLSTATE_LOCAL) {
path->resize(buffer_size);
return true;
}
return false;
}
} // 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, extracts the component
// 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;
}
// 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.
#ifndef CHROME_BROWSER_CONFLICTS_MSI_UTIL_WIN_H_
#define CHROME_BROWSER_CONFLICTS_MSI_UTIL_WIN_H_
#include <vector>
#include "base/callback.h"
#include "base/strings/string16.h"
class MsiUtil {
public:
// Using the Microsoft Installer API, retrieves the path of all the components
// for a given product. This function should be called on a thread that allows
// access to the file system. Returns false if any error occured, including if
// the |product_guid| passed is not a GUID.
//
// Note: Marked virtual to allow mocking.
virtual bool GetMsiComponentPaths(
const base::string16& product_guid,
std::vector<base::string16>* component_paths);
};
#endif // CHROME_BROWSER_CONFLICTS_MSI_UTIL_WIN_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment