diff options
Diffstat (limited to 'src/dtf/WixToolset.Dtf.WindowsInstaller/ProductInstallation.cs')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller/ProductInstallation.cs | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/ProductInstallation.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/ProductInstallation.cs new file mode 100644 index 00000000..27739e17 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/ProductInstallation.cs | |||
@@ -0,0 +1,801 @@ | |||
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.Globalization; | ||
9 | using System.Diagnostics.CodeAnalysis; | ||
10 | |||
11 | /// <summary> | ||
12 | /// Represents a unique instance of a product that | ||
13 | /// is either advertised, installed or unknown. | ||
14 | /// </summary> | ||
15 | public class ProductInstallation : Installation | ||
16 | { | ||
17 | /// <summary> | ||
18 | /// Gets the set of all products with a specified upgrade code. This method lists the | ||
19 | /// currently installed and advertised products that have the specified UpgradeCode | ||
20 | /// property in their Property table. | ||
21 | /// </summary> | ||
22 | /// <param name="upgradeCode">Upgrade code of related products</param> | ||
23 | /// <returns>Enumeration of product codes</returns> | ||
24 | /// <remarks><p> | ||
25 | /// Win32 MSI API: | ||
26 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumrelatedproducts.asp">MsiEnumRelatedProducts</a> | ||
27 | /// </p></remarks> | ||
28 | public static IEnumerable<ProductInstallation> GetRelatedProducts(string upgradeCode) | ||
29 | { | ||
30 | StringBuilder buf = new StringBuilder(40); | ||
31 | for (uint i = 0; true; i++) | ||
32 | { | ||
33 | uint ret = NativeMethods.MsiEnumRelatedProducts(upgradeCode, 0, i, buf); | ||
34 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; | ||
35 | if (ret != 0) | ||
36 | { | ||
37 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
38 | } | ||
39 | yield return new ProductInstallation(buf.ToString()); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /// <summary> | ||
44 | /// Enumerates all product installations on the system. | ||
45 | /// </summary> | ||
46 | /// <returns>An enumeration of product objects.</returns> | ||
47 | /// <remarks><p> | ||
48 | /// Win32 MSI API: | ||
49 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumproducts.asp">MsiEnumProducts</a>, | ||
50 | /// </p></remarks> | ||
51 | public static IEnumerable<ProductInstallation> AllProducts | ||
52 | { | ||
53 | get | ||
54 | { | ||
55 | return GetProducts(null, null, UserContexts.All); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /// <summary> | ||
60 | /// Enumerates product installations based on certain criteria. | ||
61 | /// </summary> | ||
62 | /// <param name="productCode">ProductCode (GUID) of the product instances to be enumerated. Only | ||
63 | /// instances of products within the scope of the context specified by the | ||
64 | /// <paramref name="userSid"/> and <paramref name="context"/> parameters will be | ||
65 | /// enumerated. This parameter may be set to null to enumerate all products in the specified | ||
66 | /// context.</param> | ||
67 | /// <param name="userSid">Specifies a security identifier (SID) that restricts the context | ||
68 | /// of enumeration. A SID value other than s-1-1-0 is considered a user SID and restricts | ||
69 | /// enumeration to the current user or any user in the system. The special SID string | ||
70 | /// s-1-1-0 (Everyone) specifies enumeration across all users in the system. This parameter | ||
71 | /// can be set to null to restrict the enumeration scope to the current user. When | ||
72 | /// <paramref name="context"/> is set to the machine context only, | ||
73 | /// <paramref name="userSid"/> must be null.</param> | ||
74 | /// <param name="context">Specifies the user context.</param> | ||
75 | /// <returns>An enumeration of product objects for enumerated product instances.</returns> | ||
76 | /// <remarks><p> | ||
77 | /// Win32 MSI API: | ||
78 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumproductsex.asp">MsiEnumProductsEx</a> | ||
79 | /// </p></remarks> | ||
80 | public static IEnumerable<ProductInstallation> GetProducts( | ||
81 | string productCode, string userSid, UserContexts context) | ||
82 | { | ||
83 | StringBuilder buf = new StringBuilder(40); | ||
84 | UserContexts targetContext; | ||
85 | StringBuilder targetSidBuf = new StringBuilder(40); | ||
86 | for (uint i = 0; ; i++) | ||
87 | { | ||
88 | uint targetSidBufSize = (uint) targetSidBuf.Capacity; | ||
89 | uint ret = NativeMethods.MsiEnumProductsEx( | ||
90 | productCode, | ||
91 | userSid, | ||
92 | context, | ||
93 | i, | ||
94 | buf, | ||
95 | out targetContext, | ||
96 | targetSidBuf, | ||
97 | ref targetSidBufSize); | ||
98 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
99 | { | ||
100 | targetSidBuf.Capacity = (int) ++targetSidBufSize; | ||
101 | ret = NativeMethods.MsiEnumProductsEx( | ||
102 | productCode, | ||
103 | userSid, | ||
104 | context, | ||
105 | i, | ||
106 | buf, | ||
107 | out targetContext, | ||
108 | targetSidBuf, | ||
109 | ref targetSidBufSize); | ||
110 | } | ||
111 | |||
112 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) | ||
113 | { | ||
114 | break; | ||
115 | } | ||
116 | |||
117 | if (ret != 0) | ||
118 | { | ||
119 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
120 | } | ||
121 | |||
122 | yield return new ProductInstallation( | ||
123 | buf.ToString(), | ||
124 | targetSidBuf.ToString(), | ||
125 | targetContext); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | private IDictionary<string, string> properties; | ||
130 | |||
131 | /// <summary> | ||
132 | /// Creates a new object for accessing information about a product installation on the current system. | ||
133 | /// </summary> | ||
134 | /// <param name="productCode">ProductCode (GUID) of the product.</param> | ||
135 | /// <remarks><p> | ||
136 | /// All available user contexts will be queried. | ||
137 | /// </p></remarks> | ||
138 | public ProductInstallation(string productCode) | ||
139 | : this(productCode, null, UserContexts.All) | ||
140 | { | ||
141 | } | ||
142 | |||
143 | /// <summary> | ||
144 | /// Creates a new object for accessing information about a product installation on the current system. | ||
145 | /// </summary> | ||
146 | /// <param name="productCode">ProductCode (GUID) of the product.</param> | ||
147 | /// <param name="userSid">The specific user, when working in a user context. This | ||
148 | /// parameter may be null to indicate the current user. The parameter must be null | ||
149 | /// when working in a machine context.</param> | ||
150 | /// <param name="context">The user context. The calling process must have administrative | ||
151 | /// privileges to get information for a product installed for a user other than the | ||
152 | /// current user.</param> | ||
153 | public ProductInstallation(string productCode, string userSid, UserContexts context) | ||
154 | : base(productCode, userSid, context) | ||
155 | { | ||
156 | if (String.IsNullOrEmpty(productCode)) | ||
157 | { | ||
158 | throw new ArgumentNullException("productCode"); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | internal ProductInstallation(IDictionary<string, string> properties) | ||
163 | : base(properties["ProductCode"], null, UserContexts.None) | ||
164 | { | ||
165 | this.properties = properties; | ||
166 | } | ||
167 | |||
168 | /// <summary> | ||
169 | /// Gets the set of published features for the product. | ||
170 | /// </summary> | ||
171 | /// <returns>Enumeration of published features for the product.</returns> | ||
172 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
173 | /// <remarks><p> | ||
174 | /// Because features are not ordered, any new feature has an arbitrary index, meaning | ||
175 | /// this property can return features in any order. | ||
176 | /// </p><p> | ||
177 | /// Win32 MSI API: | ||
178 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumfeatures.asp">MsiEnumFeatures</a> | ||
179 | /// </p></remarks> | ||
180 | public IEnumerable<FeatureInstallation> Features | ||
181 | { | ||
182 | get | ||
183 | { | ||
184 | StringBuilder buf = new StringBuilder(256); | ||
185 | for (uint i = 0; ; i++) | ||
186 | { | ||
187 | uint ret = NativeMethods.MsiEnumFeatures(this.ProductCode, i, buf, null); | ||
188 | |||
189 | if (ret != 0) | ||
190 | { | ||
191 | break; | ||
192 | } | ||
193 | |||
194 | yield return new FeatureInstallation(buf.ToString(), this.ProductCode); | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /// <summary> | ||
200 | /// Gets the ProductCode (GUID) of the product. | ||
201 | /// </summary> | ||
202 | public string ProductCode | ||
203 | { | ||
204 | get { return this.InstallationCode; } | ||
205 | } | ||
206 | |||
207 | /// <summary> | ||
208 | /// Gets a value indicating whether this product is installed on the current system. | ||
209 | /// </summary> | ||
210 | public override bool IsInstalled | ||
211 | { | ||
212 | get | ||
213 | { | ||
214 | return (this.State == InstallState.Default); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | /// <summary> | ||
219 | /// Gets a value indicating whether this product is advertised on the current system. | ||
220 | /// </summary> | ||
221 | public bool IsAdvertised | ||
222 | { | ||
223 | get | ||
224 | { | ||
225 | return (this.State == InstallState.Advertised); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | /// <summary> | ||
230 | /// Checks whether the product is installed with elevated privileges. An application is called | ||
231 | /// a "managed application" if elevated (system) privileges are used to install the application. | ||
232 | /// </summary> | ||
233 | /// <returns>True if the product is elevated; false otherwise</returns> | ||
234 | /// <remarks><p> | ||
235 | /// Note that this property does not take into account policies such as AlwaysInstallElevated, | ||
236 | /// but verifies that the local system owns the product's registry data. | ||
237 | /// </p></remarks> | ||
238 | public bool IsElevated | ||
239 | { | ||
240 | get | ||
241 | { | ||
242 | bool isElevated; | ||
243 | uint ret = NativeMethods.MsiIsProductElevated(this.ProductCode, out isElevated); | ||
244 | if (ret != 0) | ||
245 | { | ||
246 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
247 | } | ||
248 | return isElevated; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | /// <summary> | ||
253 | /// Gets the source list of this product installation. | ||
254 | /// </summary> | ||
255 | public override SourceList SourceList | ||
256 | { | ||
257 | get | ||
258 | { | ||
259 | return this.properties == null ? base.SourceList : null; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | internal InstallState State | ||
264 | { | ||
265 | get | ||
266 | { | ||
267 | if (this.properties != null) | ||
268 | { | ||
269 | return InstallState.Unknown; | ||
270 | } | ||
271 | else | ||
272 | { | ||
273 | int installState = NativeMethods.MsiQueryProductState(this.ProductCode); | ||
274 | return (InstallState) installState; | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | |||
279 | internal override int InstallationType | ||
280 | { | ||
281 | get | ||
282 | { | ||
283 | const int MSICODE_PRODUCT = 0x00000000; | ||
284 | return MSICODE_PRODUCT; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | /// <summary> | ||
289 | /// The support link. | ||
290 | /// </summary> | ||
291 | public string HelpLink | ||
292 | { | ||
293 | get | ||
294 | { | ||
295 | return this["HelpLink"]; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /// <summary> | ||
300 | /// The support telephone. | ||
301 | /// </summary> | ||
302 | public string HelpTelephone | ||
303 | { | ||
304 | get | ||
305 | { | ||
306 | return this["HelpTelephone"]; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | /// <summary> | ||
311 | /// Date and time the product was installed. | ||
312 | /// </summary> | ||
313 | public DateTime InstallDate | ||
314 | { | ||
315 | get | ||
316 | { | ||
317 | try | ||
318 | { | ||
319 | return DateTime.ParseExact( | ||
320 | this["InstallDate"], "yyyyMMdd", CultureInfo.InvariantCulture); | ||
321 | } | ||
322 | catch (FormatException) | ||
323 | { | ||
324 | return DateTime.MinValue; | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | |||
329 | /// <summary> | ||
330 | /// The installed product name. | ||
331 | /// </summary> | ||
332 | public string ProductName | ||
333 | { | ||
334 | get | ||
335 | { | ||
336 | return this["InstalledProductName"]; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | /// <summary> | ||
341 | /// The installation location. | ||
342 | /// </summary> | ||
343 | public string InstallLocation | ||
344 | { | ||
345 | get | ||
346 | { | ||
347 | return this["InstallLocation"]; | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /// <summary> | ||
352 | /// The installation source. | ||
353 | /// </summary> | ||
354 | public string InstallSource | ||
355 | { | ||
356 | get | ||
357 | { | ||
358 | return this["InstallSource"]; | ||
359 | } | ||
360 | } | ||
361 | |||
362 | /// <summary> | ||
363 | /// The local cached package. | ||
364 | /// </summary> | ||
365 | public string LocalPackage | ||
366 | { | ||
367 | get | ||
368 | { | ||
369 | return this["LocalPackage"]; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /// <summary> | ||
374 | /// The publisher. | ||
375 | /// </summary> | ||
376 | public string Publisher | ||
377 | { | ||
378 | get | ||
379 | { | ||
380 | return this["Publisher"]; | ||
381 | } | ||
382 | } | ||
383 | |||
384 | /// <summary> | ||
385 | /// URL about information. | ||
386 | /// </summary> | ||
387 | public Uri UrlInfoAbout | ||
388 | { | ||
389 | get | ||
390 | { | ||
391 | string value = this["URLInfoAbout"]; | ||
392 | if (!String.IsNullOrEmpty(value)) | ||
393 | { | ||
394 | try | ||
395 | { | ||
396 | return new Uri(value); | ||
397 | } | ||
398 | catch (UriFormatException) { } | ||
399 | } | ||
400 | |||
401 | return null; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | /// <summary> | ||
406 | /// The URL update information. | ||
407 | /// </summary> | ||
408 | public Uri UrlUpdateInfo | ||
409 | { | ||
410 | get | ||
411 | { | ||
412 | string value = this["URLUpdateInfo"]; | ||
413 | if (!String.IsNullOrEmpty(value)) | ||
414 | { | ||
415 | try | ||
416 | { | ||
417 | return new Uri(value); | ||
418 | } | ||
419 | catch (UriFormatException) { } | ||
420 | } | ||
421 | |||
422 | return null; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | /// <summary> | ||
427 | /// The product version. | ||
428 | /// </summary> | ||
429 | public Version ProductVersion | ||
430 | { | ||
431 | get | ||
432 | { | ||
433 | string ver = this["VersionString"]; | ||
434 | return ProductInstallation.ParseVersion(ver); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | /// <summary> | ||
439 | /// The product identifier. | ||
440 | /// </summary> | ||
441 | /// <remarks><p> | ||
442 | /// For more information, see | ||
443 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/productid.asp">ProductID</a> | ||
444 | /// </p></remarks> | ||
445 | public string ProductId | ||
446 | { | ||
447 | get | ||
448 | { | ||
449 | return this["ProductID"]; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | /// <summary> | ||
454 | /// The company that is registered to use the product. | ||
455 | /// </summary> | ||
456 | public string RegCompany | ||
457 | { | ||
458 | get | ||
459 | { | ||
460 | return this["RegCompany"]; | ||
461 | } | ||
462 | } | ||
463 | |||
464 | /// <summary> | ||
465 | /// The owner who is registered to use the product. | ||
466 | /// </summary> | ||
467 | public string RegOwner | ||
468 | { | ||
469 | get | ||
470 | { | ||
471 | return this["RegOwner"]; | ||
472 | } | ||
473 | } | ||
474 | |||
475 | /// <summary> | ||
476 | /// Transforms. | ||
477 | /// </summary> | ||
478 | public string AdvertisedTransforms | ||
479 | { | ||
480 | get | ||
481 | { | ||
482 | return this["Transforms"]; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | /// <summary> | ||
487 | /// Product language. | ||
488 | /// </summary> | ||
489 | public string AdvertisedLanguage | ||
490 | { | ||
491 | get | ||
492 | { | ||
493 | return this["Language"]; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | /// <summary> | ||
498 | /// Human readable product name. | ||
499 | /// </summary> | ||
500 | public string AdvertisedProductName | ||
501 | { | ||
502 | get | ||
503 | { | ||
504 | return this["ProductName"]; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | /// <summary> | ||
509 | /// True if the product is advertised per-machine; | ||
510 | /// false if it is per-user or not advertised. | ||
511 | /// </summary> | ||
512 | public bool AdvertisedPerMachine | ||
513 | { | ||
514 | get | ||
515 | { | ||
516 | return this["AssignmentType"] == "1"; | ||
517 | } | ||
518 | } | ||
519 | |||
520 | /// <summary> | ||
521 | /// Identifier of the package that a product is installed from. | ||
522 | /// </summary> | ||
523 | public string AdvertisedPackageCode | ||
524 | { | ||
525 | get | ||
526 | { | ||
527 | return this["PackageCode"]; | ||
528 | } | ||
529 | } | ||
530 | |||
531 | /// <summary> | ||
532 | /// Version of the advertised product. | ||
533 | /// </summary> | ||
534 | public Version AdvertisedVersion | ||
535 | { | ||
536 | get | ||
537 | { | ||
538 | string ver = this["Version"]; | ||
539 | return ProductInstallation.ParseVersion(ver); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | /// <summary> | ||
544 | /// Primary icon for the package. | ||
545 | /// </summary> | ||
546 | public string AdvertisedProductIcon | ||
547 | { | ||
548 | get | ||
549 | { | ||
550 | return this["ProductIcon"]; | ||
551 | } | ||
552 | } | ||
553 | |||
554 | /// <summary> | ||
555 | /// Name of the installation package for the advertised product. | ||
556 | /// </summary> | ||
557 | public string AdvertisedPackageName | ||
558 | { | ||
559 | get | ||
560 | { | ||
561 | return this["PackageName"]; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | /// <summary> | ||
566 | /// True if the advertised product can be serviced by | ||
567 | /// non-administrators without elevation. | ||
568 | /// </summary> | ||
569 | public bool PrivilegedPatchingAuthorized | ||
570 | { | ||
571 | get | ||
572 | { | ||
573 | return this["AuthorizedLUAApp"] == "1"; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | /// <summary> | ||
578 | /// Gets information about an installation of a product. | ||
579 | /// </summary> | ||
580 | /// <param name="propertyName">Name of the property being retrieved.</param> | ||
581 | /// <exception cref="ArgumentOutOfRangeException">An unknown product or property was requested</exception> | ||
582 | /// <exception cref="InstallerException">The installer configuration data is corrupt</exception> | ||
583 | /// <remarks><p> | ||
584 | /// Win32 MSI APIs: | ||
585 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetproductinfo.asp">MsiGetProductInfo</a>, | ||
586 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetproductinfoex.asp">MsiGetProductInfoEx</a> | ||
587 | /// </p></remarks> | ||
588 | public override string this[string propertyName] | ||
589 | { | ||
590 | get | ||
591 | { | ||
592 | if (this.properties != null) | ||
593 | { | ||
594 | string value = null; | ||
595 | this.properties.TryGetValue(propertyName, out value); | ||
596 | return value; | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | StringBuilder buf = new StringBuilder(40); | ||
601 | uint bufSize = (uint) buf.Capacity; | ||
602 | uint ret; | ||
603 | |||
604 | if (this.Context == UserContexts.UserManaged || | ||
605 | this.Context == UserContexts.UserUnmanaged || | ||
606 | this.Context == UserContexts.Machine) | ||
607 | { | ||
608 | ret = NativeMethods.MsiGetProductInfoEx( | ||
609 | this.ProductCode, | ||
610 | this.UserSid, | ||
611 | this.Context, | ||
612 | propertyName, | ||
613 | buf, | ||
614 | ref bufSize); | ||
615 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
616 | { | ||
617 | buf.Capacity = (int) ++bufSize; | ||
618 | ret = NativeMethods.MsiGetProductInfoEx( | ||
619 | this.ProductCode, | ||
620 | this.UserSid, | ||
621 | this.Context, | ||
622 | propertyName, | ||
623 | buf, | ||
624 | ref bufSize); | ||
625 | } | ||
626 | } | ||
627 | else | ||
628 | { | ||
629 | ret = NativeMethods.MsiGetProductInfo( | ||
630 | this.ProductCode, | ||
631 | propertyName, | ||
632 | buf, | ||
633 | ref bufSize); | ||
634 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
635 | { | ||
636 | buf.Capacity = (int) ++bufSize; | ||
637 | ret = NativeMethods.MsiGetProductInfo( | ||
638 | this.ProductCode, | ||
639 | propertyName, | ||
640 | buf, | ||
641 | ref bufSize); | ||
642 | } | ||
643 | } | ||
644 | |||
645 | if (ret != 0) | ||
646 | { | ||
647 | return null; | ||
648 | } | ||
649 | |||
650 | return buf.ToString(); | ||
651 | } | ||
652 | } | ||
653 | } | ||
654 | |||
655 | /// <summary> | ||
656 | /// Gets the installed state for a product feature. | ||
657 | /// </summary> | ||
658 | /// <param name="feature">The feature being queried; identifier from the | ||
659 | /// Feature table</param> | ||
660 | /// <returns>Installation state of the feature for the product instance: either | ||
661 | /// <see cref="InstallState.Local"/>, <see cref="InstallState.Source"/>, | ||
662 | /// or <see cref="InstallState.Advertised"/>.</returns> | ||
663 | /// <remarks><p> | ||
664 | /// Win32 MSI APIs: | ||
665 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiqueryfeaturestate.asp">MsiQueryFeatureState</a>, | ||
666 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiqueryfeaturestateex.asp">MsiQueryFeatureStateEx</a> | ||
667 | /// </p></remarks> | ||
668 | public InstallState GetFeatureState(string feature) | ||
669 | { | ||
670 | if (this.properties != null) | ||
671 | { | ||
672 | return InstallState.Unknown; | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | int installState; | ||
677 | uint ret = NativeMethods.MsiQueryFeatureStateEx( | ||
678 | this.ProductCode, | ||
679 | this.UserSid, | ||
680 | this.Context, | ||
681 | feature, | ||
682 | out installState); | ||
683 | if (ret != 0) | ||
684 | { | ||
685 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
686 | } | ||
687 | return (InstallState) installState; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | /// <summary> | ||
692 | /// Gets the installed state for a product component. | ||
693 | /// </summary> | ||
694 | /// <param name="component">The component being queried; GUID of the component | ||
695 | /// as found in the ComponentId column of the Component table.</param> | ||
696 | /// <returns>Installation state of the component for the product instance: either | ||
697 | /// <see cref="InstallState.Local"/> or <see cref="InstallState.Source"/>.</returns> | ||
698 | /// <remarks><p> | ||
699 | /// Win32 MSI API: | ||
700 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiquerycomponnetstate.asp">MsiQueryComponentState</a> | ||
701 | /// </p></remarks> | ||
702 | public InstallState GetComponentState(string component) | ||
703 | { | ||
704 | if (this.properties != null) | ||
705 | { | ||
706 | return InstallState.Unknown; | ||
707 | } | ||
708 | else | ||
709 | { | ||
710 | int installState; | ||
711 | uint ret = NativeMethods.MsiQueryComponentState( | ||
712 | this.ProductCode, | ||
713 | this.UserSid, | ||
714 | this.Context, | ||
715 | component, | ||
716 | out installState); | ||
717 | if (ret != 0) | ||
718 | { | ||
719 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
720 | } | ||
721 | return (InstallState) installState; | ||
722 | } | ||
723 | } | ||
724 | |||
725 | /// <summary> | ||
726 | /// Obtains and stores the user information and product ID from an installation wizard. | ||
727 | /// </summary> | ||
728 | /// <remarks><p> | ||
729 | /// This method is typically called by an application during the first run of the application. The application | ||
730 | /// first gets the <see cref="ProductInstallation.ProductId"/> or <see cref="ProductInstallation.RegOwner"/>. | ||
731 | /// If those properties are missing, the application calls CollectUserInfo. | ||
732 | /// CollectUserInfo opens the product's installation package and invokes a wizard sequence that collects | ||
733 | /// user information. Upon completion of the sequence, user information is registered. Since this API requires | ||
734 | /// an authored user interface, the user interface level should be set to full by calling | ||
735 | /// <see cref="Installer.SetInternalUI(InstallUIOptions)"/> as <see cref="InstallUIOptions.Full"/>. | ||
736 | /// </p><p> | ||
737 | /// The CollectUserInfo method invokes a FirstRun dialog from the product installation database. | ||
738 | /// </p><p> | ||
739 | /// Win32 MSI API: | ||
740 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msicollectuserinfo.asp">MsiCollectUserInfo</a> | ||
741 | /// </p></remarks> | ||
742 | public void CollectUserInfo() | ||
743 | { | ||
744 | if (this.properties == null) | ||
745 | { | ||
746 | uint ret = NativeMethods.MsiCollectUserInfo(this.InstallationCode); | ||
747 | if (ret != 0) | ||
748 | { | ||
749 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
750 | } | ||
751 | } | ||
752 | } | ||
753 | |||
754 | /// <summary> | ||
755 | /// Some products might write some invalid/nonstandard version strings to the registry. | ||
756 | /// This method tries to get the best data it can. | ||
757 | /// </summary> | ||
758 | /// <param name="ver">Version string retrieved from the registry.</param> | ||
759 | /// <returns>Version object, or null if the version string is completely invalid.</returns> | ||
760 | private static Version ParseVersion(string ver) | ||
761 | { | ||
762 | if (ver != null) | ||
763 | { | ||
764 | int dotCount = 0; | ||
765 | for (int i = 0; i < ver.Length; i++) | ||
766 | { | ||
767 | char c = ver[i]; | ||
768 | if (c == '.') dotCount++; | ||
769 | else if (!Char.IsDigit(c)) | ||
770 | { | ||
771 | ver = ver.Substring(0, i); | ||
772 | break; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | if (ver.Length > 0) | ||
777 | { | ||
778 | if (dotCount == 0) | ||
779 | { | ||
780 | ver = ver + ".0"; | ||
781 | } | ||
782 | else if (dotCount > 3) | ||
783 | { | ||
784 | string[] verSplit = ver.Split('.'); | ||
785 | ver = String.Join(".", verSplit, 0, 4); | ||
786 | } | ||
787 | |||
788 | try | ||
789 | { | ||
790 | return new Version(ver); | ||
791 | } | ||
792 | catch (ArgumentException) | ||
793 | { | ||
794 | } | ||
795 | } | ||
796 | } | ||
797 | |||
798 | return null; | ||
799 | } | ||
800 | } | ||
801 | } | ||