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; |
+} |