diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller/FeatureInfo.cs | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/FeatureInfo.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/FeatureInfo.cs new file mode 100644 index 00000000..9a1a859a --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/FeatureInfo.cs | |||
@@ -0,0 +1,497 @@ | |||
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; | ||
8 | using System.Collections.Generic; | ||
9 | |||
10 | /// <summary> | ||
11 | /// Accessor for information about features within the context of an installation session. | ||
12 | /// </summary> | ||
13 | public sealed class FeatureInfoCollection : ICollection<FeatureInfo> | ||
14 | { | ||
15 | private Session session; | ||
16 | |||
17 | internal FeatureInfoCollection(Session session) | ||
18 | { | ||
19 | this.session = session; | ||
20 | } | ||
21 | |||
22 | /// <summary> | ||
23 | /// Gets information about a feature within the context of an installation session. | ||
24 | /// </summary> | ||
25 | /// <param name="feature">name of the feature</param> | ||
26 | /// <returns>feature object</returns> | ||
27 | public FeatureInfo this[string feature] | ||
28 | { | ||
29 | get | ||
30 | { | ||
31 | return new FeatureInfo(this.session, feature); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | void ICollection<FeatureInfo>.Add(FeatureInfo item) | ||
36 | { | ||
37 | throw new InvalidOperationException(); | ||
38 | } | ||
39 | |||
40 | void ICollection<FeatureInfo>.Clear() | ||
41 | { | ||
42 | throw new InvalidOperationException(); | ||
43 | } | ||
44 | |||
45 | /// <summary> | ||
46 | /// Checks if the collection contains a feature. | ||
47 | /// </summary> | ||
48 | /// <param name="feature">name of the feature</param> | ||
49 | /// <returns>true if the feature is in the collection, else false</returns> | ||
50 | public bool Contains(string feature) | ||
51 | { | ||
52 | return this.session.Database.CountRows( | ||
53 | "Feature", "`Feature` = '" + feature + "'") == 1; | ||
54 | } | ||
55 | |||
56 | bool ICollection<FeatureInfo>.Contains(FeatureInfo item) | ||
57 | { | ||
58 | return item != null && this.Contains(item.Name); | ||
59 | } | ||
60 | |||
61 | /// <summary> | ||
62 | /// Copies the features into an array. | ||
63 | /// </summary> | ||
64 | /// <param name="array">array that receives the features</param> | ||
65 | /// <param name="arrayIndex">offset into the array</param> | ||
66 | public void CopyTo(FeatureInfo[] array, int arrayIndex) | ||
67 | { | ||
68 | foreach (FeatureInfo feature in this) | ||
69 | { | ||
70 | array[arrayIndex++] = feature; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /// <summary> | ||
75 | /// Gets the number of features defined for the product. | ||
76 | /// </summary> | ||
77 | public int Count | ||
78 | { | ||
79 | get | ||
80 | { | ||
81 | return this.session.Database.CountRows("Feature"); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | bool ICollection<FeatureInfo>.IsReadOnly | ||
86 | { | ||
87 | get | ||
88 | { | ||
89 | return true; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | bool ICollection<FeatureInfo>.Remove(FeatureInfo item) | ||
94 | { | ||
95 | throw new InvalidOperationException(); | ||
96 | } | ||
97 | |||
98 | /// <summary> | ||
99 | /// Enumerates the features in the collection. | ||
100 | /// </summary> | ||
101 | /// <returns>an enumerator over all features in the collection</returns> | ||
102 | public IEnumerator<FeatureInfo> GetEnumerator() | ||
103 | { | ||
104 | using (View featureView = this.session.Database.OpenView( | ||
105 | "SELECT `Feature` FROM `Feature`")) | ||
106 | { | ||
107 | featureView.Execute(); | ||
108 | |||
109 | foreach (Record featureRec in featureView) using (featureRec) | ||
110 | { | ||
111 | string feature = featureRec.GetString(1); | ||
112 | yield return new FeatureInfo(this.session, feature); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | IEnumerator IEnumerable.GetEnumerator() | ||
118 | { | ||
119 | return this.GetEnumerator(); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /// <summary> | ||
124 | /// Provides access to information about a feature within the context of an installation session. | ||
125 | /// </summary> | ||
126 | public class FeatureInfo | ||
127 | { | ||
128 | private Session session; | ||
129 | private string name; | ||
130 | |||
131 | internal FeatureInfo(Session session, string name) | ||
132 | { | ||
133 | this.session = session; | ||
134 | this.name = name; | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Gets the name of the feature (primary key in the Feature table). | ||
139 | /// </summary> | ||
140 | public string Name | ||
141 | { | ||
142 | get | ||
143 | { | ||
144 | return this.name; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /// <summary> | ||
149 | /// Gets the current install state of the feature. | ||
150 | /// </summary> | ||
151 | /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> | ||
152 | /// <exception cref="ArgumentException">an unknown feature was requested</exception> | ||
153 | /// <remarks><p> | ||
154 | /// Win32 MSI API: | ||
155 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeaturestate.asp">MsiGetFeatureState</a> | ||
156 | /// </p></remarks> | ||
157 | public InstallState CurrentState | ||
158 | { | ||
159 | get | ||
160 | { | ||
161 | int installState, actionState; | ||
162 | uint ret = RemotableNativeMethods.MsiGetFeatureState((int) this.session.Handle, this.name, out installState, out actionState); | ||
163 | if (ret != 0) | ||
164 | { | ||
165 | if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) | ||
166 | { | ||
167 | throw InstallerException.ExceptionFromReturnCode(ret, this.name); | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | if (installState == (int) InstallState.Advertised) | ||
176 | { | ||
177 | return InstallState.Advertised; | ||
178 | } | ||
179 | return (InstallState) installState; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | /// <summary> | ||
184 | /// Gets or sets the action state of the feature. | ||
185 | /// </summary> | ||
186 | /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> | ||
187 | /// <exception cref="ArgumentException">an unknown feature was requested</exception> | ||
188 | /// <remarks><p> | ||
189 | /// When changing the feature action, the action state of all the Components linked to the changed | ||
190 | /// Feature records are also updated appropriately, based on the new feature Select state. | ||
191 | /// All Features can be configured at once by specifying the keyword ALL instead of a specific feature name. | ||
192 | /// </p><p> | ||
193 | /// Win32 MSI APIs: | ||
194 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeaturestate.asp">MsiGetFeatureState</a>, | ||
195 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetfeaturestate.asp">MsiSetFeatureState</a> | ||
196 | /// </p></remarks> | ||
197 | public InstallState RequestState | ||
198 | { | ||
199 | get | ||
200 | { | ||
201 | int installState, actionState; | ||
202 | uint ret = RemotableNativeMethods.MsiGetFeatureState((int) this.session.Handle, this.name, out installState, out actionState); | ||
203 | if (ret != 0) | ||
204 | { | ||
205 | if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) | ||
206 | { | ||
207 | throw InstallerException.ExceptionFromReturnCode(ret, this.name); | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
212 | } | ||
213 | } | ||
214 | return (InstallState) actionState; | ||
215 | } | ||
216 | |||
217 | set | ||
218 | { | ||
219 | uint ret = RemotableNativeMethods.MsiSetFeatureState((int) this.session.Handle, this.name, (int) value); | ||
220 | if (ret != 0) | ||
221 | { | ||
222 | if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) | ||
223 | { | ||
224 | throw InstallerException.ExceptionFromReturnCode(ret, this.name); | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | |||
234 | /// <summary> | ||
235 | /// Gets a list of valid installation states for the feature. | ||
236 | /// </summary> | ||
237 | /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> | ||
238 | /// <exception cref="ArgumentException">an unknown feature was requested</exception> | ||
239 | /// <remarks><p> | ||
240 | /// Win32 MSI API: | ||
241 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeaturevalidstates.asp">MsiGetFeatureValidStates</a> | ||
242 | /// </p></remarks> | ||
243 | public ICollection<InstallState> ValidStates | ||
244 | { | ||
245 | get | ||
246 | { | ||
247 | List<InstallState> states = new List<InstallState>(); | ||
248 | uint installState; | ||
249 | uint ret = RemotableNativeMethods.MsiGetFeatureValidStates((int) this.session.Handle, this.name, out installState); | ||
250 | if (ret != 0) | ||
251 | { | ||
252 | if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) | ||
253 | { | ||
254 | throw InstallerException.ExceptionFromReturnCode(ret, this.name); | ||
255 | } | ||
256 | else | ||
257 | { | ||
258 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | for (int i = 1; i <= (int) InstallState.Default; i++) | ||
263 | { | ||
264 | if (((int) installState & (1 << i)) != 0) | ||
265 | { | ||
266 | states.Add((InstallState) i); | ||
267 | } | ||
268 | } | ||
269 | return states.AsReadOnly(); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | /// <summary> | ||
274 | /// Gets or sets the attributes of the feature. | ||
275 | /// </summary> | ||
276 | /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> | ||
277 | /// <exception cref="ArgumentException">an unknown feature was requested</exception> | ||
278 | /// <remarks><p> | ||
279 | /// Win32 MSI APIs: | ||
280 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeatureinfo.asp">MsiGetFeatureInfo</a>, | ||
281 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetfeatureattributes.asp">MsiSetFeatureAttributes</a> | ||
282 | /// </p><p> | ||
283 | /// Since the lpAttributes paramter of | ||
284 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeatureinfo.asp">MsiGetFeatureInfo</a> | ||
285 | /// does not contain an equivalent flag for <see cref="FeatureAttributes.UIDisallowAbsent"/>, this flag will | ||
286 | /// not be retrieved. | ||
287 | /// </p><p> | ||
288 | /// Since the dwAttributes parameter of | ||
289 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetfeatureattributes.asp">MsiSetFeatureAttributes</a> | ||
290 | /// does not contain an equivalent flag for <see cref="FeatureAttributes.UIDisallowAbsent"/>, the presence | ||
291 | /// of this flag will be ignored. | ||
292 | /// </p></remarks> | ||
293 | public FeatureAttributes Attributes | ||
294 | { | ||
295 | get | ||
296 | { | ||
297 | FeatureAttributes attributes; | ||
298 | uint titleBufSize = 0; | ||
299 | uint descBufSize = 0; | ||
300 | uint attr; | ||
301 | uint ret = NativeMethods.MsiGetFeatureInfo( | ||
302 | (int) this.session.Handle, | ||
303 | this.name, | ||
304 | out attr, | ||
305 | null, | ||
306 | ref titleBufSize, | ||
307 | null, | ||
308 | ref descBufSize); | ||
309 | |||
310 | if (ret != 0) | ||
311 | { | ||
312 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
313 | } | ||
314 | |||
315 | // Values for attributes that MsiGetFeatureInfo returns are | ||
316 | // double the values in the Attributes column of the Feature Table. | ||
317 | attributes = (FeatureAttributes) (attr >> 1); | ||
318 | |||
319 | // MsiGetFeatureInfo MSDN documentation indicates | ||
320 | // NOUNSUPPORTEDADVERTISE is 32. Conversion above changes this to 16 | ||
321 | // which is UIDisallowAbsent. MsiGetFeatureInfo isn't documented to | ||
322 | // return an attribute for 'UIDisallowAbsent', so if UIDisallowAbsent | ||
323 | // is set, change it to NoUnsupportedAdvertise which then maps correctly | ||
324 | // to NOUNSUPPORTEDADVERTISE. | ||
325 | if ((attributes & FeatureAttributes.UIDisallowAbsent) == FeatureAttributes.UIDisallowAbsent) | ||
326 | { | ||
327 | attributes &= ~FeatureAttributes.UIDisallowAbsent; | ||
328 | attributes |= FeatureAttributes.NoUnsupportedAdvertise; | ||
329 | } | ||
330 | |||
331 | return attributes; | ||
332 | } | ||
333 | |||
334 | set | ||
335 | { | ||
336 | // MsiSetFeatureAttributes doesn't indicate UIDisallowAbsent is valid | ||
337 | // so remove it. | ||
338 | FeatureAttributes attributes = value; | ||
339 | attributes &= ~FeatureAttributes.UIDisallowAbsent; | ||
340 | |||
341 | // Values for attributes that MsiSetFeatureAttributes uses are | ||
342 | // double the values in the Attributes column of the Feature Table. | ||
343 | uint attr = ((uint) attributes) << 1; | ||
344 | |||
345 | // MsiSetFeatureAttributes MSDN documentation indicates | ||
346 | // NOUNSUPPORTEDADVERTISE is 32. Conversion above changes this to 64 | ||
347 | // which is undefined. Change this back to 32. | ||
348 | uint noUnsupportedAdvertiseDbl = ((uint)FeatureAttributes.NoUnsupportedAdvertise) << 1; | ||
349 | if ((attr & noUnsupportedAdvertiseDbl) == noUnsupportedAdvertiseDbl) | ||
350 | { | ||
351 | attr &= ~noUnsupportedAdvertiseDbl; | ||
352 | attr |= (uint) FeatureAttributes.NoUnsupportedAdvertise; | ||
353 | } | ||
354 | |||
355 | uint ret = RemotableNativeMethods.MsiSetFeatureAttributes((int) this.session.Handle, this.name, attr); | ||
356 | |||
357 | if (ret != (uint)NativeMethods.Error.SUCCESS) | ||
358 | { | ||
359 | if (ret == (uint)NativeMethods.Error.UNKNOWN_FEATURE) | ||
360 | { | ||
361 | throw InstallerException.ExceptionFromReturnCode(ret, this.name); | ||
362 | } | ||
363 | else | ||
364 | { | ||
365 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | } | ||
370 | |||
371 | /// <summary> | ||
372 | /// Gets the title of the feature. | ||
373 | /// </summary> | ||
374 | /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> | ||
375 | /// <exception cref="ArgumentException">an unknown feature was requested</exception> | ||
376 | /// <remarks><p> | ||
377 | /// Win32 MSI API: | ||
378 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeatureinfo.asp">MsiGetFeatureInfo</a> | ||
379 | /// </p></remarks> | ||
380 | public string Title | ||
381 | { | ||
382 | get | ||
383 | { | ||
384 | StringBuilder titleBuf = new StringBuilder(80); | ||
385 | uint titleBufSize = (uint) titleBuf.Capacity; | ||
386 | uint descBufSize = 0; | ||
387 | uint attr; | ||
388 | uint ret = NativeMethods.MsiGetFeatureInfo( | ||
389 | (int) this.session.Handle, | ||
390 | this.name, | ||
391 | out attr, | ||
392 | titleBuf, | ||
393 | ref titleBufSize, | ||
394 | null, | ||
395 | ref descBufSize); | ||
396 | |||
397 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
398 | { | ||
399 | titleBuf.Capacity = (int) ++titleBufSize; | ||
400 | ret = NativeMethods.MsiGetFeatureInfo( | ||
401 | (int) this.session.Handle, | ||
402 | this.name, | ||
403 | out attr, | ||
404 | titleBuf, | ||
405 | ref titleBufSize, | ||
406 | null, | ||
407 | ref descBufSize); | ||
408 | } | ||
409 | |||
410 | if (ret != 0) | ||
411 | { | ||
412 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
413 | } | ||
414 | |||
415 | return titleBuf.ToString(); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | /// <summary> | ||
420 | /// Gets the description of the feature. | ||
421 | /// </summary> | ||
422 | /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> | ||
423 | /// <exception cref="ArgumentException">an unknown feature was requested</exception> | ||
424 | /// <remarks><p> | ||
425 | /// Win32 MSI API: | ||
426 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeatureinfo.asp">MsiGetFeatureInfo</a> | ||
427 | /// </p></remarks> | ||
428 | public string Description | ||
429 | { | ||
430 | get | ||
431 | { | ||
432 | StringBuilder descBuf = new StringBuilder(256); | ||
433 | uint titleBufSize = 0; | ||
434 | uint descBufSize = (uint) descBuf.Capacity; | ||
435 | uint attr; | ||
436 | uint ret = NativeMethods.MsiGetFeatureInfo( | ||
437 | (int) this.session.Handle, | ||
438 | this.name, | ||
439 | out attr, | ||
440 | null, | ||
441 | ref titleBufSize, | ||
442 | descBuf, | ||
443 | ref descBufSize); | ||
444 | |||
445 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
446 | { | ||
447 | descBuf.Capacity = (int) ++descBufSize; | ||
448 | ret = NativeMethods.MsiGetFeatureInfo( | ||
449 | (int) this.session.Handle, | ||
450 | this.name, | ||
451 | out attr, | ||
452 | null, | ||
453 | ref titleBufSize, | ||
454 | descBuf, | ||
455 | ref descBufSize); | ||
456 | } | ||
457 | |||
458 | if (ret != 0) | ||
459 | { | ||
460 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
461 | } | ||
462 | |||
463 | return descBuf.ToString(); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | /// <summary> | ||
468 | /// Calculates the disk space required by the feature and its selected children and parent features. | ||
469 | /// </summary> | ||
470 | /// <param name="includeParents">If true, the parent features are included in the cost.</param> | ||
471 | /// <param name="includeChildren">If true, the child features are included in the cost.</param> | ||
472 | /// <param name="installState">Specifies the installation state.</param> | ||
473 | /// <returns>The disk space requirement in bytes.</returns> | ||
474 | /// <remarks><p> | ||
475 | /// Win32 MSI API: | ||
476 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetfeaturecost.asp">MsiGetFeatureCost</a> | ||
477 | /// </p></remarks> | ||
478 | public long GetCost(bool includeParents, bool includeChildren, InstallState installState) | ||
479 | { | ||
480 | const int MSICOSTTREE_CHILDREN = 1; | ||
481 | const int MSICOSTTREE_PARENTS = 2; | ||
482 | |||
483 | int cost; | ||
484 | uint ret = RemotableNativeMethods.MsiGetFeatureCost( | ||
485 | (int) this.session.Handle, | ||
486 | this.name, | ||
487 | (includeParents ? MSICOSTTREE_PARENTS : 0) | (includeChildren ? MSICOSTTREE_CHILDREN : 0), | ||
488 | (int) installState, | ||
489 | out cost); | ||
490 | if (ret != 0) | ||
491 | { | ||
492 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
493 | } | ||
494 | return cost * 512L; | ||
495 | } | ||
496 | } | ||
497 | } | ||