aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller/PatchInstallation.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller/PatchInstallation.cs413
1 files changed, 413 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/PatchInstallation.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/PatchInstallation.cs
new file mode 100644
index 00000000..defbf64a
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/PatchInstallation.cs
@@ -0,0 +1,413 @@
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.Globalization;
9 using System.Diagnostics.CodeAnalysis;
10
11 /// <summary>
12 /// The Patch object represents a unique instance of a patch that has been
13 /// registered or applied.
14 /// </summary>
15 public class PatchInstallation : Installation
16 {
17 /// <summary>
18 /// Enumerates all patch installations on the system.
19 /// </summary>
20 /// <returns>Enumeration of patch objects.</returns>
21 /// <remarks><p>
22 /// Win32 MSI API:
23 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumpatches.asp">MsiEnumPatches</a>
24 /// </p></remarks>
25 public static IEnumerable<PatchInstallation> AllPatches
26 {
27 get
28 {
29 return PatchInstallation.GetPatches(null, null, null, UserContexts.All, PatchStates.All);
30 }
31 }
32
33 /// <summary>
34 /// Enumerates patch installations based on certain criteria.
35 /// </summary>
36 /// <param name="patchCode">PatchCode (GUID) of the patch to be enumerated. Only
37 /// instances of patches within the scope of the context specified by the
38 /// <paramref name="userSid"/> and <paramref name="context"/> parameters will be
39 /// enumerated. This parameter may be set to null to enumerate all patches in the specified
40 /// context.</param>
41 /// <param name="targetProductCode">ProductCode (GUID) product whose patches are to be
42 /// enumerated. If non-null, patch enumeration is restricted to instances of this product
43 /// within the specified context. If null, the patches for all products under the specified
44 /// context are enumerated.</param>
45 /// <param name="userSid">Specifies a security identifier (SID) that restricts the context
46 /// of enumeration. A SID value other than s-1-1-0 is considered a user SID and restricts
47 /// enumeration to the current user or any user in the system. The special SID string
48 /// s-1-1-0 (Everyone) specifies enumeration across all users in the system. This parameter
49 /// can be set to null to restrict the enumeration scope to the current user. When
50 /// <paramref name="userSid"/> must be null.</param>
51 /// <param name="context">Specifies the user context.</param>
52 /// <param name="states">The <see cref="PatchStates"/> of patches to return.</param>
53 /// <remarks><p>
54 /// Win32 MSI APIs:
55 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumpatchesex.asp">MsiEnumPatchesEx</a>
56 /// </p></remarks>
57 public static IEnumerable<PatchInstallation> GetPatches(
58 string patchCode,
59 string targetProductCode,
60 string userSid,
61 UserContexts context,
62 PatchStates states)
63 {
64 StringBuilder buf = new StringBuilder(40);
65 StringBuilder targetProductBuf = new StringBuilder(40);
66 UserContexts targetContext;
67 StringBuilder targetSidBuf = new StringBuilder(40);
68 for (uint i = 0; ; i++)
69 {
70 uint targetSidBufSize = (uint) targetSidBuf.Capacity;
71 uint ret = NativeMethods.MsiEnumPatchesEx(
72 targetProductCode,
73 userSid,
74 context,
75 (uint) states,
76 i,
77 buf,
78 targetProductBuf,
79 out targetContext,
80 targetSidBuf,
81 ref targetSidBufSize);
82 if (ret == (uint) NativeMethods.Error.MORE_DATA)
83 {
84 targetSidBuf.Capacity = (int) ++targetSidBufSize;
85 ret = NativeMethods.MsiEnumPatchesEx(
86 targetProductCode,
87 userSid,
88 context,
89 (uint) states,
90 i,
91 buf,
92 targetProductBuf,
93 out targetContext,
94 targetSidBuf,
95 ref targetSidBufSize);
96 }
97
98 if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS)
99 {
100 break;
101 }
102
103 if (ret != 0)
104 {
105 throw InstallerException.ExceptionFromReturnCode(ret);
106 }
107
108 string thisPatchCode = buf.ToString();
109 if (patchCode == null || patchCode == thisPatchCode)
110 {
111 yield return new PatchInstallation(
112 buf.ToString(),
113 targetProductBuf.ToString(),
114 targetSidBuf.ToString(),
115 targetContext);
116 }
117 }
118 }
119
120 private string productCode;
121
122 /// <summary>
123 /// Creates a new object for accessing information about a patch installation on the current system.
124 /// </summary>
125 /// <param name="patchCode">Patch code (GUID) of the patch.</param>
126 /// <param name="productCode">ProductCode (GUID) the patch has been applied to.
127 /// This parameter may be null for patches that are registered only and not yet
128 /// applied to any product.</param>
129 /// <remarks><p>
130 /// All available user contexts will be queried.
131 /// </p></remarks>
132 public PatchInstallation(string patchCode, string productCode)
133 : this(patchCode, productCode, null, UserContexts.All)
134 {
135 }
136
137 /// <summary>
138 /// Creates a new object for accessing information about a patch installation on the current system.
139 /// </summary>
140 /// <param name="patchCode">Registered patch code (GUID) of the patch.</param>
141 /// <param name="productCode">ProductCode (GUID) the patch has been applied to.
142 /// This parameter may be null for patches that are registered only and not yet
143 /// applied to any product.</param>
144 /// <param name="userSid">The specific user, when working in a user context. This
145 /// parameter may be null to indicate the current user. The parameter must be null
146 /// when working in a machine context.</param>
147 /// <param name="context">The user context. The calling process must have administrative
148 /// privileges to get information for a product installed for a user other than the
149 /// current user.</param>
150 /// <remarks><p>
151 /// If the <paramref name="productCode"/> is null, the Patch object may
152 /// only be used to read and update the patch's SourceList information.
153 /// </p></remarks>
154 public PatchInstallation(string patchCode, string productCode, string userSid, UserContexts context)
155 : base(patchCode, userSid, context)
156 {
157 if (String.IsNullOrEmpty(patchCode))
158 {
159 throw new ArgumentNullException("patchCode");
160 }
161
162 this.productCode = productCode;
163 }
164
165 /// <summary>
166 /// Gets the patch code (GUID) of the patch.
167 /// </summary>
168 public string PatchCode
169 {
170 get
171 {
172 return this.InstallationCode;
173 }
174 }
175
176 /// <summary>
177 /// Gets the ProductCode (GUID) of the product.
178 /// </summary>
179 public string ProductCode
180 {
181 get
182 {
183 return this.productCode;
184 }
185 }
186
187 /// <summary>
188 /// Gets a value indicating whether this patch is currently installed.
189 /// </summary>
190 public override bool IsInstalled
191 {
192 get
193 {
194 return (this.State & PatchStates.Applied) != 0;
195 }
196 }
197
198 /// <summary>
199 /// Gets a value indicating whether this patch is marked as obsolte.
200 /// </summary>
201 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Obsoleted")]
202 public bool IsObsoleted
203 {
204 get
205 {
206 return (this.State & PatchStates.Obsoleted) != 0;
207 }
208 }
209
210 /// <summary>
211 /// Gets a value indicating whether this patch is present but has been
212 /// superseded by a more recent installed patch.
213 /// </summary>
214 public bool IsSuperseded
215 {
216 get
217 {
218 return (this.State & PatchStates.Superseded) != 0;
219 }
220 }
221
222 internal override int InstallationType
223 {
224 get
225 {
226 const int MSICODE_PATCH = 0x40000000;
227 return MSICODE_PATCH;
228 }
229 }
230
231 /// <summary>
232 /// Gets the installation state of this instance of the patch.
233 /// </summary>
234 /// <exception cref="ArgumentException">An unknown patch was requested</exception>
235 /// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
236 public PatchStates State
237 {
238 get
239 {
240 string stateString = this["State"];
241 return (PatchStates) Int32.Parse(stateString, CultureInfo.InvariantCulture.NumberFormat);
242 }
243 }
244
245 /// <summary>
246 /// Gets the cached patch file that the product uses.
247 /// </summary>
248 public string LocalPackage
249 {
250 get
251 {
252 return this["LocalPackage"];
253 }
254 }
255
256 /// <summary>
257 /// Gets the set of patch transforms that the last patch
258 /// installation applied to the product.
259 /// </summary>
260 /// <remarks><p>
261 /// This value may not be available for per-user, non-managed applications
262 /// if the user is not logged on.
263 /// </p></remarks>
264 public string Transforms
265 {
266 get
267 {
268 // TODO: convert to IList<string>?
269 return this["Transforms"];
270 }
271 }
272
273 /// <summary>
274 /// Gets the date and time when the patch is applied to the product.
275 /// </summary>
276 public DateTime InstallDate
277 {
278 get
279 {
280 try
281 {
282 return DateTime.ParseExact(
283 this["InstallDate"], "yyyyMMdd", CultureInfo.InvariantCulture);
284 }
285 catch (FormatException)
286 {
287 return DateTime.MinValue;
288 }
289 }
290 }
291
292 /// <summary>
293 /// True patch is marked as possible to uninstall from the product.
294 /// </summary>
295 /// <remarks><p>
296 /// Even if this property is true, the installer can still block the
297 /// uninstallation if this patch is required by another patch that
298 /// cannot be uninstalled.
299 /// </p></remarks>
300 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Uninstallable")]
301 public bool Uninstallable
302 {
303 get
304 {
305 return this["Uninstallable"] == "1";
306 }
307 }
308
309 /// <summary>
310 /// Get the registered display name for the patch.
311 /// </summary>
312 public string DisplayName
313 {
314 get
315 {
316 return this["DisplayName"];
317 }
318 }
319
320 /// <summary>
321 /// Gets the registered support information URL for the patch.
322 /// </summary>
323 public Uri MoreInfoUrl
324 {
325 get
326 {
327 string value = this["MoreInfoURL"];
328 if (!String.IsNullOrEmpty(value))
329 {
330 try
331 {
332 return new Uri(value);
333 }
334 catch (UriFormatException) { }
335 }
336
337 return null;
338 }
339 }
340
341 /// <summary>
342 /// Gets information about a specific patch installation.
343 /// </summary>
344 /// <param name="propertyName">The property being retrieved; see remarks for valid properties.</param>
345 /// <returns>The property value, or an empty string if the property is not set for the patch.</returns>
346 /// <exception cref="ArgumentOutOfRangeException">An unknown patch or property was requested</exception>
347 /// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
348 /// <remarks><p>
349 /// Win32 MSI APIs:
350 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetpatchinfo.asp">MsiGetPatchInfo</a>,
351 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetpatchinfoex.asp">MsiGetPatchInfoEx</a>
352 /// </p></remarks>
353 public override string this[string propertyName]
354 {
355 get
356 {
357 StringBuilder buf = new StringBuilder("");
358 uint bufSize = 0;
359 uint ret;
360
361 if (this.Context == UserContexts.UserManaged ||
362 this.Context == UserContexts.UserUnmanaged ||
363 this.Context == UserContexts.Machine)
364 {
365 ret = NativeMethods.MsiGetPatchInfoEx(
366 this.PatchCode,
367 this.ProductCode,
368 this.UserSid,
369 this.Context,
370 propertyName,
371 buf,
372 ref bufSize);
373 if (ret == (uint) NativeMethods.Error.MORE_DATA)
374 {
375 buf.Capacity = (int) ++bufSize;
376 ret = NativeMethods.MsiGetPatchInfoEx(
377 this.PatchCode,
378 this.ProductCode,
379 this.UserSid,
380 this.Context,
381 propertyName,
382 buf,
383 ref bufSize);
384 }
385 }
386 else
387 {
388 ret = NativeMethods.MsiGetPatchInfo(
389 this.PatchCode,
390 propertyName,
391 buf,
392 ref bufSize);
393 if (ret == (uint) NativeMethods.Error.MORE_DATA)
394 {
395 buf.Capacity = (int) ++bufSize;
396 ret = NativeMethods.MsiGetPatchInfo(
397 this.PatchCode,
398 propertyName,
399 buf,
400 ref bufSize);
401 }
402 }
403
404 if (ret != 0)
405 {
406 return null;
407 }
408
409 return buf.ToString();
410 }
411 }
412 }
413}