aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller/SourceList.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller/SourceList.cs525
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
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 /// 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>&quot;n&quot; = network location</li>
395 /// <li>&quot;u&quot; = URL location</li>
396 /// <li>&quot;m&quot; = 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}