diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller/SourceList.cs | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/SourceList.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/SourceList.cs new file mode 100644 index 00000000..16ec22e8 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/SourceList.cs | |||
@@ -0,0 +1,525 @@ | |||
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 | /// A list of sources for an installed product or patch. | ||
13 | /// </summary> | ||
14 | [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] | ||
15 | public class SourceList : ICollection<string> | ||
16 | { | ||
17 | private Installation installation; | ||
18 | private SourceMediaList mediaList; | ||
19 | |||
20 | internal SourceList(Installation installation) | ||
21 | { | ||
22 | this.installation = installation; | ||
23 | } | ||
24 | |||
25 | /// <summary> | ||
26 | /// Gets the list of disks registered for the media source of | ||
27 | /// the patch or product installation. | ||
28 | /// </summary> | ||
29 | public SourceMediaList MediaList | ||
30 | { | ||
31 | get | ||
32 | { | ||
33 | if (this.mediaList == null) | ||
34 | { | ||
35 | this.mediaList = new SourceMediaList(this.installation); | ||
36 | } | ||
37 | return this.mediaList; | ||
38 | } | ||
39 | } | ||
40 | |||
41 | /// <summary> | ||
42 | /// Gets the number of network and URL sources in the list. | ||
43 | /// </summary> | ||
44 | public int Count | ||
45 | { | ||
46 | get | ||
47 | { | ||
48 | int count = 0; | ||
49 | IEnumerator<string> e = this.GetEnumerator(); | ||
50 | while (e.MoveNext()) | ||
51 | { | ||
52 | count++; | ||
53 | } | ||
54 | return count; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /// <summary> | ||
59 | /// Gets a boolean value indicating whether the list is read-only. | ||
60 | /// A SourceList is never read-only. | ||
61 | /// </summary> | ||
62 | /// <value>read-only status of the list</value> | ||
63 | public bool IsReadOnly | ||
64 | { | ||
65 | get { return false; } | ||
66 | } | ||
67 | |||
68 | /// <summary> | ||
69 | /// Adds a network or URL source to the source list of the installed product. | ||
70 | /// </summary> | ||
71 | /// <param name="item">Path to the source to be added. This parameter is | ||
72 | /// expected to contain only the path without the filename.</param> | ||
73 | /// <remarks><p> | ||
74 | /// If this method is called with a new source, the installer adds the source | ||
75 | /// to the end of the source list. | ||
76 | /// </p><p> | ||
77 | /// If this method is called with a source already existing in the source | ||
78 | /// list, it has no effect. | ||
79 | /// </p><p> | ||
80 | /// Win32 MSI APIs: | ||
81 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistaddsource.asp">MsiSourceListAddSource</a>, | ||
82 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistaddsourceex.asp">MsiSourceListAddSourceEx</a> | ||
83 | /// </p></remarks> | ||
84 | /// <seealso cref="Insert"/> | ||
85 | public void Add(string item) | ||
86 | { | ||
87 | if (!this.Contains(item)) | ||
88 | { | ||
89 | this.Insert(item, 0); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | /// <summary> | ||
94 | /// Adds or reorders a network or URL source for the product or patch. | ||
95 | /// </summary> | ||
96 | /// <param name="item">Path to the source to be added. This parameter is | ||
97 | /// expected to contain only the path without the filename.</param> | ||
98 | /// <param name="index">Specifies the priority order in which the source | ||
99 | /// will be inserted</param> | ||
100 | /// <remarks><p> | ||
101 | /// If this method is called with a new source and <paramref name="index"/> | ||
102 | /// is set to 0, the installer adds the source to the end of the source list. | ||
103 | /// </p><p> | ||
104 | /// If this method is called with a source already existing in the source | ||
105 | /// list and <paramref name="index"/> is set to 0, the installer retains the | ||
106 | /// source's existing index. | ||
107 | /// </p><p> | ||
108 | /// If the method is called with an existing source in the source list | ||
109 | /// and <paramref name="index"/> is set to a non-zero value, the source is | ||
110 | /// removed from its current location in the list and inserted at the position | ||
111 | /// specified by Index, before any source that already exists at that position. | ||
112 | /// </p><p> | ||
113 | /// If the method is called with a new source and Index is set to a | ||
114 | /// non-zero value, the source is inserted at the position specified by | ||
115 | /// <paramref name="index"/>, before any source that already exists at | ||
116 | /// that position. The index value for all sources in the list after the | ||
117 | /// index specified by Index are updated to ensure unique index values and | ||
118 | /// the pre-existing order is guaranteed to remain unchanged. | ||
119 | /// </p><p> | ||
120 | /// If <paramref name="index"/> is greater than the number of sources | ||
121 | /// in the list, the source is placed at the end of the list with an index | ||
122 | /// value one larger than any existing source. | ||
123 | /// </p><p> | ||
124 | /// Win32 MSI API: | ||
125 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistaddsourceex.asp">MsiSourceListAddSourceEx</a> | ||
126 | /// </p></remarks> | ||
127 | public void Insert(string item, int index) | ||
128 | { | ||
129 | if (item == null) | ||
130 | { | ||
131 | throw new ArgumentNullException("item"); | ||
132 | } | ||
133 | |||
134 | NativeMethods.SourceType type = item.Contains("://") ? NativeMethods.SourceType.Url : NativeMethods.SourceType.Network; | ||
135 | |||
136 | uint ret = NativeMethods.MsiSourceListAddSourceEx( | ||
137 | this.installation.InstallationCode, | ||
138 | this.installation.UserSid, | ||
139 | this.installation.Context, | ||
140 | (uint) type | (uint) this.installation.InstallationType, | ||
141 | item, | ||
142 | (uint) index); | ||
143 | if (ret != 0) | ||
144 | { | ||
145 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | /// <summary> | ||
150 | /// Clears sources of all types: network, url, and media. | ||
151 | /// </summary> | ||
152 | /// <remarks><p> | ||
153 | /// Win32 MSI API: | ||
154 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearall.asp">MsiSourceListClearAll</a> | ||
155 | /// </p></remarks> | ||
156 | public void Clear() | ||
157 | { | ||
158 | this.ClearSourceType(NativeMethods.SourceType.Url); | ||
159 | this.ClearSourceType(NativeMethods.SourceType.Network); | ||
160 | this.MediaList.Clear(); | ||
161 | } | ||
162 | |||
163 | /// <summary> | ||
164 | /// Removes all network sources from the list. URL sources are not affected. | ||
165 | /// </summary> | ||
166 | /// <remarks><p> | ||
167 | /// Win32 MSI API: | ||
168 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearallex.asp">MsiSourceListClearAllEx</a> | ||
169 | /// </p></remarks> | ||
170 | public void ClearNetworkSources() | ||
171 | { | ||
172 | this.ClearSourceType(NativeMethods.SourceType.Network); | ||
173 | } | ||
174 | |||
175 | /// <summary> | ||
176 | /// Removes all URL sources from the list. Network sources are not affected. | ||
177 | /// </summary> | ||
178 | /// <remarks><p> | ||
179 | /// Win32 MSI API: | ||
180 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearallex.asp">MsiSourceListClearAllEx</a> | ||
181 | /// </p></remarks> | ||
182 | public void ClearUrlSources() | ||
183 | { | ||
184 | this.ClearSourceType(NativeMethods.SourceType.Url); | ||
185 | } | ||
186 | |||
187 | /// <summary> | ||
188 | /// Checks if the specified source exists in the list. | ||
189 | /// </summary> | ||
190 | /// <param name="item">case-insensitive source to look for</param> | ||
191 | /// <returns>true if the source exists in the list, false otherwise</returns> | ||
192 | public bool Contains(string item) | ||
193 | { | ||
194 | if (String.IsNullOrEmpty(item)) | ||
195 | { | ||
196 | throw new ArgumentNullException("item"); | ||
197 | } | ||
198 | |||
199 | foreach (string s in this) | ||
200 | { | ||
201 | if (s.Equals(item, StringComparison.OrdinalIgnoreCase)) | ||
202 | { | ||
203 | return true; | ||
204 | } | ||
205 | } | ||
206 | return false; | ||
207 | } | ||
208 | |||
209 | /// <summary> | ||
210 | /// Copies the network and URL sources from this list into an array. | ||
211 | /// </summary> | ||
212 | /// <param name="array">destination array to be filed</param> | ||
213 | /// <param name="arrayIndex">offset into the destination array where copying begins</param> | ||
214 | public void CopyTo(string[] array, int arrayIndex) | ||
215 | { | ||
216 | foreach (string source in this) | ||
217 | { | ||
218 | array[arrayIndex++] = source; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | /// <summary> | ||
223 | /// Removes a network or URL source. | ||
224 | /// </summary> | ||
225 | /// <remarks><p> | ||
226 | /// Win32 MSI API: | ||
227 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearsource.asp">MsiSourceListClearSource</a> | ||
228 | /// </p></remarks> | ||
229 | public bool Remove(string item) | ||
230 | { | ||
231 | if (String.IsNullOrEmpty(item)) | ||
232 | { | ||
233 | throw new ArgumentNullException("item"); | ||
234 | } | ||
235 | |||
236 | NativeMethods.SourceType type = item.Contains("://") ? NativeMethods.SourceType.Url : NativeMethods.SourceType.Network; | ||
237 | |||
238 | uint ret = NativeMethods.MsiSourceListClearSource( | ||
239 | this.installation.InstallationCode, | ||
240 | this.installation.UserSid, | ||
241 | this.installation.Context, | ||
242 | (uint) type | (uint) this.installation.InstallationType, | ||
243 | item); | ||
244 | if (ret != 0) | ||
245 | { | ||
246 | // TODO: Figure out when to return false. | ||
247 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
248 | } | ||
249 | return true; | ||
250 | } | ||
251 | |||
252 | /// <summary> | ||
253 | /// Enumerates the network and URL sources in the source list of the patch or product installation. | ||
254 | /// </summary> | ||
255 | /// <remarks><p> | ||
256 | /// Win32 MSI API: | ||
257 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistenumsources.asp">MsiSourceListEnumSources</a> | ||
258 | /// </p></remarks> | ||
259 | public IEnumerator<string> GetEnumerator() | ||
260 | { | ||
261 | StringBuilder sourceBuf = new StringBuilder(256); | ||
262 | uint sourceBufSize = (uint) sourceBuf.Capacity; | ||
263 | for (uint i = 0; true; i++) | ||
264 | { | ||
265 | uint ret = this.EnumSources(sourceBuf, i, NativeMethods.SourceType.Network); | ||
266 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) | ||
267 | { | ||
268 | break; | ||
269 | } | ||
270 | else if (ret != 0) | ||
271 | { | ||
272 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
273 | } | ||
274 | else | ||
275 | { | ||
276 | yield return sourceBuf.ToString(); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | for (uint i = 0; true; i++) | ||
281 | { | ||
282 | uint ret = this.EnumSources(sourceBuf, i, NativeMethods.SourceType.Url); | ||
283 | if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) | ||
284 | { | ||
285 | break; | ||
286 | } | ||
287 | else if (ret != 0) | ||
288 | { | ||
289 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | yield return sourceBuf.ToString(); | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | ||
299 | { | ||
300 | return this.GetEnumerator(); | ||
301 | } | ||
302 | |||
303 | /// <summary> | ||
304 | /// Forces the installer to search the source list for a valid | ||
305 | /// source the next time a source is required. For example, when the | ||
306 | /// installer performs an installation or reinstallation, or when it | ||
307 | /// requires the path for a component that is set to run from source. | ||
308 | /// </summary> | ||
309 | /// <remarks><p> | ||
310 | /// Win32 MSI APIs: | ||
311 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistforceresolution.asp">MsiSourceListForceResolution</a>, | ||
312 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistforceresolutionex.asp">MsiSourceListForceResolutionEx</a> | ||
313 | /// </p></remarks> | ||
314 | public void ForceResolution() | ||
315 | { | ||
316 | uint ret = NativeMethods.MsiSourceListForceResolutionEx( | ||
317 | this.installation.InstallationCode, | ||
318 | this.installation.UserSid, | ||
319 | this.installation.Context, | ||
320 | (uint) this.installation.InstallationType); | ||
321 | if (ret != 0) | ||
322 | { | ||
323 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | /// <summary> | ||
328 | /// Gets or sets the path relative to the root of the installation media. | ||
329 | /// </summary> | ||
330 | public string MediaPackagePath | ||
331 | { | ||
332 | get | ||
333 | { | ||
334 | return this["MediaPackagePath"]; | ||
335 | } | ||
336 | set | ||
337 | { | ||
338 | this["MediaPackagePath"] = value; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /// <summary> | ||
343 | /// Gets or sets the prompt template that is used when prompting the user | ||
344 | /// for installation media. | ||
345 | /// </summary> | ||
346 | public string DiskPrompt | ||
347 | { | ||
348 | get | ||
349 | { | ||
350 | return this["DiskPrompt"]; | ||
351 | } | ||
352 | set | ||
353 | { | ||
354 | this["DiskPrompt"] = value; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | /// <summary> | ||
359 | /// Gets or sets the most recently used source location for the product. | ||
360 | /// </summary> | ||
361 | public string LastUsedSource | ||
362 | { | ||
363 | get | ||
364 | { | ||
365 | return this["LastUsedSource"]; | ||
366 | } | ||
367 | set | ||
368 | { | ||
369 | this["LastUsedSource"] = value; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /// <summary> | ||
374 | /// Gets or sets the name of the Windows Installer package or patch package | ||
375 | /// on the source. | ||
376 | /// </summary> | ||
377 | public string PackageName | ||
378 | { | ||
379 | get | ||
380 | { | ||
381 | return this["PackageName"]; | ||
382 | } | ||
383 | set | ||
384 | { | ||
385 | this["PackageName"] = value; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | /// <summary> | ||
390 | /// Gets the type of the last-used source. | ||
391 | /// </summary> | ||
392 | /// <remarks><p> | ||
393 | /// <ul> | ||
394 | /// <li>"n" = network location</li> | ||
395 | /// <li>"u" = URL location</li> | ||
396 | /// <li>"m" = media location</li> | ||
397 | /// <li>(empty string) = no last used source</li> | ||
398 | /// </ul> | ||
399 | /// </p></remarks> | ||
400 | public string LastUsedType | ||
401 | { | ||
402 | get | ||
403 | { | ||
404 | return this["LastUsedType"]; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | /// <summary> | ||
409 | /// Gets or sets source list information properties of a product or patch installation. | ||
410 | /// </summary> | ||
411 | /// <param name="property">The source list information property name.</param> | ||
412 | /// <exception cref="ArgumentOutOfRangeException">An unknown product, patch, or property was requested</exception> | ||
413 | /// <remarks><p> | ||
414 | /// Win32 MSI API: | ||
415 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistgetinfo.asp">MsiSourceListGetInfo</a> | ||
416 | /// </p></remarks> | ||
417 | public string this[string property] | ||
418 | { | ||
419 | get | ||
420 | { | ||
421 | StringBuilder buf = new StringBuilder(""); | ||
422 | uint bufSize = 0; | ||
423 | uint ret = NativeMethods.MsiSourceListGetInfo( | ||
424 | this.installation.InstallationCode, | ||
425 | this.installation.UserSid, | ||
426 | this.installation.Context, | ||
427 | (uint) this.installation.InstallationType, | ||
428 | property, | ||
429 | buf, | ||
430 | ref bufSize); | ||
431 | if (ret != 0) | ||
432 | { | ||
433 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
434 | { | ||
435 | buf.Capacity = (int) ++bufSize; | ||
436 | ret = NativeMethods.MsiSourceListGetInfo( | ||
437 | this.installation.InstallationCode, | ||
438 | this.installation.UserSid, | ||
439 | this.installation.Context, | ||
440 | (uint) this.installation.InstallationType, | ||
441 | property, | ||
442 | buf, | ||
443 | ref bufSize); | ||
444 | } | ||
445 | |||
446 | if (ret != 0) | ||
447 | { | ||
448 | if (ret == (uint) NativeMethods.Error.UNKNOWN_PRODUCT || | ||
449 | ret == (uint) NativeMethods.Error.UNKNOWN_PROPERTY) | ||
450 | { | ||
451 | throw new ArgumentOutOfRangeException("property"); | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | return buf.ToString(); | ||
460 | } | ||
461 | set | ||
462 | { | ||
463 | uint ret = NativeMethods.MsiSourceListSetInfo( | ||
464 | this.installation.InstallationCode, | ||
465 | this.installation.UserSid, | ||
466 | this.installation.Context, | ||
467 | (uint) this.installation.InstallationType, | ||
468 | property, | ||
469 | value); | ||
470 | if (ret != 0) | ||
471 | { | ||
472 | if (ret == (uint) NativeMethods.Error.UNKNOWN_PRODUCT || | ||
473 | ret == (uint) NativeMethods.Error.UNKNOWN_PROPERTY) | ||
474 | { | ||
475 | throw new ArgumentOutOfRangeException("property"); | ||
476 | } | ||
477 | else | ||
478 | { | ||
479 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
480 | } | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | |||
485 | private void ClearSourceType(NativeMethods.SourceType type) | ||
486 | { | ||
487 | uint ret = NativeMethods.MsiSourceListClearAllEx( | ||
488 | this.installation.InstallationCode, | ||
489 | this.installation.UserSid, | ||
490 | this.installation.Context, | ||
491 | (uint) type | (uint) this.installation.InstallationType); | ||
492 | if (ret != 0) | ||
493 | { | ||
494 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
495 | } | ||
496 | } | ||
497 | |||
498 | private uint EnumSources(StringBuilder sourceBuf, uint i, NativeMethods.SourceType sourceType) | ||
499 | { | ||
500 | int enumType = (this.installation.InstallationType | (int) sourceType); | ||
501 | uint sourceBufSize = (uint) sourceBuf.Capacity; | ||
502 | uint ret = NativeMethods.MsiSourceListEnumSources( | ||
503 | this.installation.InstallationCode, | ||
504 | this.installation.UserSid, | ||
505 | this.installation.Context, | ||
506 | (uint) enumType, | ||
507 | i, | ||
508 | sourceBuf, | ||
509 | ref sourceBufSize); | ||
510 | if (ret == (uint) NativeMethods.Error.MORE_DATA) | ||
511 | { | ||
512 | sourceBuf.Capacity = (int) ++sourceBufSize; | ||
513 | ret = NativeMethods.MsiSourceListEnumSources( | ||
514 | this.installation.InstallationCode, | ||
515 | this.installation.UserSid, | ||
516 | this.installation.Context, | ||
517 | (uint) enumType, | ||
518 | i, | ||
519 | sourceBuf, | ||
520 | ref sourceBufSize); | ||
521 | } | ||
522 | return ret; | ||
523 | } | ||
524 | } | ||
525 | } | ||