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