diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs new file mode 100644 index 00000000..6d368899 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs | |||
@@ -0,0 +1,382 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Dtf.WindowsInstaller | ||
4 | { | ||
5 | using System; | ||
6 | using System.Text; | ||
7 | using System.Collections.Generic; | ||
8 | using System.Diagnostics.CodeAnalysis; | ||
9 | |||
10 | /// <summary> | ||
11 | /// Represents an instance of a registered component. | ||
12 | /// </summary> | ||
13 | public class ComponentInstallation : InstallationPart | ||
14 | { | ||
15 | /// <summary> | ||
16 | /// Gets the set of installed components for all products. | ||
17 | /// </summary> | ||
18 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
19 | /// <remarks><p> | ||
20 | /// Win32 MSI API: | ||
21 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumcomponents.asp">MsiEnumComponents</a> | ||
22 | /// </p></remarks> | ||
23 | public static IEnumerable<ComponentInstallation> AllComponents | ||
24 | { | ||
25 | get | ||
26 | { | ||
27 | StringBuilder buf = new StringBuilder(40); | ||
28 | for (uint i = 0; true; i++) | ||
29 | { | ||
30 | uint ret = NativeMethods.MsiEnumComponents(i, buf); | ||
31 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; | ||
32 | if (ret != 0) | ||
33 | { | ||
34 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
35 | } | ||
36 | yield return new ComponentInstallation(buf.ToString()); | ||
37 | } | ||
38 | } | ||
39 | } | ||
40 | |||
41 | /// <summary> | ||
42 | /// Gets the set of installed components for products in the indicated context. | ||
43 | /// </summary> | ||
44 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
45 | /// <remarks><p> | ||
46 | /// Win32 MSI API: | ||
47 | /// <a href="http://msdn.microsoft.com/library/dd407947.aspx">MsiEnumComponentsEx</a> | ||
48 | /// </p></remarks> | ||
49 | public static IEnumerable<ComponentInstallation> Components(string szUserSid, UserContexts dwContext) | ||
50 | { | ||
51 | uint pcchSid = 32; | ||
52 | StringBuilder szSid = new StringBuilder((int)pcchSid); | ||
53 | StringBuilder buf = new StringBuilder(40); | ||
54 | UserContexts installedContext; | ||
55 | for (uint i = 0; true; i++) | ||
56 | { | ||
57 | uint ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); | ||
58 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
59 | { | ||
60 | szSid.EnsureCapacity((int) ++pcchSid); | ||
61 | ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); | ||
62 | } | ||
63 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; | ||
64 | if (ret != 0) | ||
65 | { | ||
66 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
67 | } | ||
68 | yield return new ComponentInstallation(buf.ToString(), szSid.ToString(), installedContext); | ||
69 | } | ||
70 | } | ||
71 | private static string GetProductCode(string component) | ||
72 | { | ||
73 | StringBuilder buf = new StringBuilder(40); | ||
74 | uint ret = NativeMethods.MsiGetProductCode(component, buf); | ||
75 | if (ret != 0) | ||
76 | { | ||
77 | return null; | ||
78 | } | ||
79 | |||
80 | return buf.ToString(); | ||
81 | } | ||
82 | |||
83 | private static string GetProductCode(string component, string szUserSid, UserContexts dwContext) | ||
84 | { | ||
85 | // TODO: We really need what would be MsiGetProductCodeEx, or at least a reasonable facimile thereof (something that restricts the search to the context defined by szUserSid & dwContext) | ||
86 | return GetProductCode(component); | ||
87 | } | ||
88 | |||
89 | /// <summary> | ||
90 | /// Creates a new ComponentInstallation, automatically detecting the | ||
91 | /// product that the component is a part of. | ||
92 | /// </summary> | ||
93 | /// <param name="componentCode">component GUID</param> | ||
94 | /// <remarks><p> | ||
95 | /// Win32 MSI API: | ||
96 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetproductcode.asp">MsiGetProductCode</a> | ||
97 | /// </p></remarks> | ||
98 | public ComponentInstallation(string componentCode) | ||
99 | : this(componentCode, ComponentInstallation.GetProductCode(componentCode)) | ||
100 | { | ||
101 | } | ||
102 | |||
103 | /// <summary> | ||
104 | /// Creates a new ComponentInstallation, automatically detecting the | ||
105 | /// product that the component is a part of. | ||
106 | /// </summary> | ||
107 | /// <param name="componentCode">component GUID</param> | ||
108 | /// <param name="szUserSid">context user SID</param> | ||
109 | /// <param name="dwContext">user contexts</param> | ||
110 | public ComponentInstallation(string componentCode, string szUserSid, UserContexts dwContext) | ||
111 | : this(componentCode, ComponentInstallation.GetProductCode(componentCode, szUserSid, dwContext), szUserSid, dwContext) | ||
112 | { | ||
113 | } | ||
114 | |||
115 | /// <summary> | ||
116 | /// Creates a new ComponentInstallation for a component installed by a | ||
117 | /// specific product. | ||
118 | /// </summary> | ||
119 | /// <param name="componentCode">component GUID</param> | ||
120 | /// <param name="productCode">ProductCode GUID</param> | ||
121 | public ComponentInstallation(string componentCode, string productCode) | ||
122 | : this(componentCode, productCode, null, UserContexts.None) | ||
123 | { | ||
124 | } | ||
125 | |||
126 | /// <summary> | ||
127 | /// Creates a new ComponentInstallation for a component installed by a | ||
128 | /// specific product. | ||
129 | /// </summary> | ||
130 | /// <param name="componentCode">component GUID</param> | ||
131 | /// <param name="productCode">ProductCode GUID</param> | ||
132 | /// <param name="szUserSid">context user SID</param> | ||
133 | /// <param name="dwContext">user contexts</param> | ||
134 | public ComponentInstallation(string componentCode, string productCode, string szUserSid, UserContexts dwContext) | ||
135 | : base(componentCode, productCode, szUserSid, dwContext) | ||
136 | { | ||
137 | if (string.IsNullOrEmpty(componentCode)) | ||
138 | { | ||
139 | throw new ArgumentNullException("componentCode"); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | /// <summary> | ||
144 | /// Gets the component code (GUID) of the component. | ||
145 | /// </summary> | ||
146 | public string ComponentCode | ||
147 | { | ||
148 | get | ||
149 | { | ||
150 | return this.Id; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /// <summary> | ||
155 | /// Gets all client products of a specified component. | ||
156 | /// </summary> | ||
157 | /// <returns>enumeration over all client products of the component</returns> | ||
158 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
159 | /// <remarks><p> | ||
160 | /// Because clients are not ordered, any new component has an arbitrary index. | ||
161 | /// This means that the property may return clients in any order. | ||
162 | /// </p><p> | ||
163 | /// Win32 MSI API: | ||
164 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumclients.asp">MsiEnumClients</a>, | ||
165 | /// <a href="http://msdn.microsoft.com/library/dd407946.aspx">MsiEnumClientsEx</a> | ||
166 | /// </p></remarks> | ||
167 | public IEnumerable<ProductInstallation> ClientProducts | ||
168 | { | ||
169 | get | ||
170 | { | ||
171 | StringBuilder buf = new StringBuilder(40); | ||
172 | for (uint i = 0; true; i++) | ||
173 | { | ||
174 | uint chSid = 0; | ||
175 | UserContexts installedContext; | ||
176 | uint ret = this.Context == UserContexts.None ? | ||
177 | NativeMethods.MsiEnumClients(this.ComponentCode, i, buf) : | ||
178 | NativeMethods.MsiEnumClientsEx(this.ComponentCode, this.UserSid, this.Context, i, buf, out installedContext, null, ref chSid); | ||
179 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; | ||
180 | else if (ret == (uint) NativeMethods.Error.UNKNOWN_COMPONENT) break; | ||
181 | if (ret != 0) | ||
182 | { | ||
183 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
184 | } | ||
185 | yield return new ProductInstallation(buf.ToString()); | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /// <summary> | ||
191 | /// Gets the installed state of a component. | ||
192 | /// </summary> | ||
193 | /// <returns>the installed state of the component, or InstallState.Unknown | ||
194 | /// if this component is not part of a product</returns> | ||
195 | /// <remarks><p> | ||
196 | /// Win32 MSI API: | ||
197 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetcomponentpath.asp">MsiGetComponentPath</a>, | ||
198 | /// <a href="http://msdn.microsoft.com/library/dd408006.aspx">MsiGetComponentPathEx</a> | ||
199 | /// </p></remarks> | ||
200 | public override InstallState State | ||
201 | { | ||
202 | get | ||
203 | { | ||
204 | if (this.ProductCode != null) | ||
205 | { | ||
206 | uint bufSize = 0; | ||
207 | int installState = this.Context == UserContexts.None ? | ||
208 | NativeMethods.MsiGetComponentPath( | ||
209 | this.ProductCode, this.ComponentCode, null, ref bufSize) : | ||
210 | NativeMethods.MsiGetComponentPathEx( | ||
211 | this.ProductCode, this.ComponentCode, this.UserSid, this.Context, null, ref bufSize); | ||
212 | return (InstallState) installState; | ||
213 | } | ||
214 | else | ||
215 | { | ||
216 | return InstallState.Unknown; | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | /// <summary> | ||
222 | /// Gets the full path to an installed component. If the key path for the component is a | ||
223 | /// registry key then the registry key is returned. | ||
224 | /// </summary> | ||
225 | /// <returns>The file or registry keypath to the component, or null if the component is not available.</returns> | ||
226 | /// <exception cref="ArgumentException">An unknown product or component was specified</exception> | ||
227 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
228 | /// <remarks><p> | ||
229 | /// If the component is a registry key, the registry roots are represented numerically. | ||
230 | /// For example, a registry path of "HKEY_CURRENT_USER\SOFTWARE\Microsoft" would be returned | ||
231 | /// as "01:\SOFTWARE\Microsoft". The registry roots returned are defined as follows: | ||
232 | /// HKEY_CLASSES_ROOT=00, HKEY_CURRENT_USER=01, HKEY_LOCAL_MACHINE=02, HKEY_USERS=03, | ||
233 | /// HKEY_PERFORMANCE_DATA=04 | ||
234 | /// </p><p> | ||
235 | /// Win32 MSI APIs: | ||
236 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetcomponentpath.asp">MsiGetComponentPath</a>, | ||
237 | /// <a href="http://msdn.microsoft.com/library/dd408006.aspx">MsiGetComponentPathEx</a>, | ||
238 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msilocatecomponent.asp">MsiLocateComponent</a> | ||
239 | /// </p></remarks> | ||
240 | public string Path | ||
241 | { | ||
242 | get | ||
243 | { | ||
244 | StringBuilder buf = new StringBuilder(256); | ||
245 | uint bufSize = (uint) buf.Capacity; | ||
246 | InstallState installState; | ||
247 | |||
248 | if (this.ProductCode != null) | ||
249 | { | ||
250 | installState = (this.Context == UserContexts.None) ? | ||
251 | (InstallState) NativeMethods.MsiGetComponentPath( | ||
252 | this.ProductCode, this.ComponentCode, buf, ref bufSize) : | ||
253 | (InstallState) NativeMethods.MsiGetComponentPathEx( | ||
254 | this.ProductCode, this.ComponentCode, this.UserSid, this.Context, buf, ref bufSize); | ||
255 | if (installState == InstallState.MoreData) | ||
256 | { | ||
257 | buf.Capacity = (int) ++bufSize; | ||
258 | installState = (this.Context == UserContexts.None) ? | ||
259 | (InstallState) NativeMethods.MsiGetComponentPath( | ||
260 | this.ProductCode, this.ComponentCode, buf, ref bufSize) : | ||
261 | (InstallState) NativeMethods.MsiGetComponentPathEx( | ||
262 | this.ProductCode, this.ComponentCode, this.UserSid, this.Context, buf, ref bufSize); | ||
263 | } | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | installState = (InstallState) NativeMethods.MsiLocateComponent( | ||
268 | this.ComponentCode, buf, ref bufSize); | ||
269 | if (installState == InstallState.MoreData) | ||
270 | { | ||
271 | buf.Capacity = (int) ++bufSize; | ||
272 | installState = (InstallState) NativeMethods.MsiLocateComponent( | ||
273 | this.ComponentCode, buf, ref bufSize); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | switch (installState) | ||
278 | { | ||
279 | case InstallState.Local: | ||
280 | case InstallState.Source: | ||
281 | case InstallState.SourceAbsent: | ||
282 | return buf.ToString(); | ||
283 | |||
284 | default: | ||
285 | return null; | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | |||
290 | /// <summary> | ||
291 | /// Gets the set of registered qualifiers for the component. | ||
292 | /// </summary> | ||
293 | /// <returns>Enumeration of the qulifiers for the component.</returns> | ||
294 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
295 | /// <remarks><p> | ||
296 | /// Because qualifiers are not ordered, any new qualifier has an arbitrary index, | ||
297 | /// meaning the function can return qualifiers in any order. | ||
298 | /// </p><p> | ||
299 | /// Win32 MSI API: | ||
300 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumcomponentqualifiers.asp">MsiEnumComponentQualifiers</a> | ||
301 | /// </p></remarks> | ||
302 | public IEnumerable<ComponentInstallation.Qualifier> Qualifiers | ||
303 | { | ||
304 | get | ||
305 | { | ||
306 | StringBuilder qualBuf = new StringBuilder(255); | ||
307 | StringBuilder dataBuf = new StringBuilder(255); | ||
308 | for (uint i = 0; ; i++) | ||
309 | { | ||
310 | uint qualBufSize = (uint) qualBuf.Capacity; | ||
311 | uint dataBufSize = (uint) dataBuf.Capacity; | ||
312 | uint ret = NativeMethods.MsiEnumComponentQualifiers( | ||
313 | this.ComponentCode, i, qualBuf, ref qualBufSize, dataBuf, ref dataBufSize); | ||
314 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
315 | { | ||
316 | qualBuf.Capacity = (int) ++qualBufSize; | ||
317 | dataBuf.Capacity = (int) ++dataBufSize; | ||
318 | ret = NativeMethods.MsiEnumComponentQualifiers( | ||
319 | this.ComponentCode, i, qualBuf, ref qualBufSize, dataBuf, ref dataBufSize); | ||
320 | } | ||
321 | |||
322 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS || | ||
323 | ret == (uint) NativeMethods.Error.UNKNOWN_COMPONENT) | ||
324 | { | ||
325 | break; | ||
326 | } | ||
327 | |||
328 | if (ret != 0) | ||
329 | { | ||
330 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
331 | } | ||
332 | |||
333 | yield return new ComponentInstallation.Qualifier( | ||
334 | qualBuf.ToString(), dataBuf.ToString()); | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /// <summary> | ||
340 | /// Holds data about a component qualifier. | ||
341 | /// </summary> | ||
342 | /// <remarks><p> | ||
343 | /// Win32 MSI API: | ||
344 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumcomponentqualifiers.asp">MsiEnumComponentQualifiers</a> | ||
345 | /// </p></remarks> | ||
346 | [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] | ||
347 | [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] | ||
348 | public struct Qualifier | ||
349 | { | ||
350 | private string qualifierCode; | ||
351 | private string data; | ||
352 | |||
353 | internal Qualifier(string qualifierCode, string data) | ||
354 | { | ||
355 | this.qualifierCode = qualifierCode; | ||
356 | this.data = data; | ||
357 | } | ||
358 | |||
359 | /// <summary> | ||
360 | /// Gets the qualifier code. | ||
361 | /// </summary> | ||
362 | public string QualifierCode | ||
363 | { | ||
364 | get | ||
365 | { | ||
366 | return this.qualifierCode; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /// <summary> | ||
371 | /// Gets the qualifier data. | ||
372 | /// </summary> | ||
373 | public string Data | ||
374 | { | ||
375 | get | ||
376 | { | ||
377 | return this.data; | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | } | ||
382 | } | ||