Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: chrome/browser/conflicts/msi_util_win.cc

Issue 2854143005: Add a utility class to retrieve the components of an MSI product (Closed)
Patch Set: Rebase Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/conflicts/msi_util_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/conflicts/msi_util_win.h"
6
7 #include <windows.h>
8
9 #include <msi.h>
10 #include <msiquery.h>
11
12 #include <utility>
13
14 #include "base/strings/string_util.h"
15 #include "base/threading/thread_restrictions.h"
16
17 namespace {
18
19 // Most strings returned by the MSI API are smaller than this value, so only
20 // 1 call to the API is needed in the common case.
21 constexpr DWORD kBufferInitialSize = 256;
22
23 // Retrieves the file path to the product's installer.
24 bool GetMsiPath(const base::string16& product_guid, base::string16* result) {
25 DWORD buffer_size = kBufferInitialSize;
26 UINT ret =
27 ::MsiGetProductInfo(product_guid.c_str(), INSTALLPROPERTY_LOCALPACKAGE,
28 base::WriteInto(result, buffer_size), &buffer_size);
29 if (ret == ERROR_MORE_DATA) {
30 // Must account for the null terminator.
31 buffer_size++;
32
33 ret =
34 ::MsiGetProductInfo(product_guid.c_str(), INSTALLPROPERTY_LOCALPACKAGE,
35 base::WriteInto(result, buffer_size), &buffer_size);
36 }
37
38 if (ret == ERROR_SUCCESS) {
39 result->resize(buffer_size);
40 return true;
41 }
42 return false;
43 }
44
45 // Returns the string value at position |index| in the given |record_handle|.
46 // Note that columns are 1-indexed.
47 bool GetRecordString(MSIHANDLE record_handle,
48 size_t index,
49 base::string16* result) {
50 DWORD buffer_size = kBufferInitialSize;
51 UINT ret = ::MsiRecordGetString(
52 record_handle, index, base::WriteInto(result, buffer_size), &buffer_size);
53 if (ret == ERROR_MORE_DATA) {
54 // Must account for the null terminator.
55 buffer_size++;
56
57 ret = ::MsiRecordGetString(record_handle, index,
58 base::WriteInto(result, buffer_size),
59 &buffer_size);
60 }
61
62 if (ret == ERROR_SUCCESS) {
63 result->resize(buffer_size);
64 return true;
65 }
66 return false;
67 }
68
69 // Inspects the installer file and extracts the component guids. Each .msi file
70 // is actually an SQL database.
71 bool GetMsiComponentGuids(const base::string16& msi_database_path,
72 std::vector<base::string16>* component_guids) {
73 PMSIHANDLE msi_database_handle;
74 if (::MsiOpenDatabase(msi_database_path.c_str(), MSIDBOPEN_READONLY,
75 &msi_database_handle) != ERROR_SUCCESS) {
76 return false;
77 }
78
79 PMSIHANDLE components_view_handle;
80 if (::MsiDatabaseOpenView(msi_database_handle,
81 L"SELECT ComponentId FROM Component",
82 &components_view_handle) != ERROR_SUCCESS) {
83 return false;
84 }
85
86 if (::MsiViewExecute(components_view_handle, 0) != ERROR_SUCCESS)
87 return false;
88
89 PMSIHANDLE record_handle;
90 while (::MsiViewFetch(components_view_handle, &record_handle) ==
91 ERROR_SUCCESS) {
92 // The record only have the ComponentId column, and its index is 1.
93 base::string16 component_guid;
94 if (GetRecordString(record_handle, 1, &component_guid))
95 component_guids->push_back(std::move(component_guid));
96 }
97
98 return true;
99 }
100
101 // Retrieves the |path| to the given component.
102 bool GetMsiComponentPath(const base::string16& product_guid,
103 const base::string16& component_guid,
104 base::string16* path) {
105 DWORD buffer_size = kBufferInitialSize;
106 INSTALLSTATE ret =
107 ::MsiGetComponentPath(product_guid.c_str(), component_guid.c_str(),
108 base::WriteInto(path, buffer_size), &buffer_size);
109 if (ret == INSTALLSTATE_MOREDATA) {
110 // Must account for the null terminator.
111 buffer_size++;
112
113 ret =
114 ::MsiGetComponentPath(product_guid.c_str(), component_guid.c_str(),
115 base::WriteInto(path, buffer_size), &buffer_size);
116 }
117
118 if (ret == INSTALLSTATE_LOCAL) {
119 path->resize(buffer_size);
120 return true;
121 }
122 return false;
123 }
124
125 } // namespace
126
127 // The most efficient way to get the list of components associated to an
128 // installed product is to inspect the installer file. A copy of the installer
129 // exists somewhere on the file system because Windows needs it to uninstall the
130 // product.
131 //
132 // So this function retrieves the path to the installer, extracts the component
133 // GUIDS from it, and uses those to find the path of each component.
134 bool MsiUtil::GetMsiComponentPaths(
135 const base::string16& product_guid,
136 std::vector<base::string16>* component_paths) {
137 base::ThreadRestrictions::AssertIOAllowed();
138
139 base::string16 msi_path;
140 if (!GetMsiPath(product_guid, &msi_path))
141 return false;
142
143 std::vector<base::string16> component_guids;
144 if (!GetMsiComponentGuids(msi_path, &component_guids))
145 return false;
146
147 for (const auto& component_guid : component_guids) {
148 base::string16 component_path;
149 if (!GetMsiComponentPath(product_guid, component_guid, &component_path))
150 continue;
151
152 component_paths->push_back(std::move(component_path));
153 }
154
155 return true;
156 }
OLDNEW
« no previous file with comments | « chrome/browser/conflicts/msi_util_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698