aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs382
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
3namespace 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}