aboutsummaryrefslogtreecommitdiff
path: root/src/samples/Dtf/Inventory/components.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/samples/Dtf/Inventory/components.cs')
-rw-r--r--src/samples/Dtf/Inventory/components.cs626
1 files changed, 626 insertions, 0 deletions
diff --git a/src/samples/Dtf/Inventory/components.cs b/src/samples/Dtf/Inventory/components.cs
new file mode 100644
index 00000000..c5147084
--- /dev/null
+++ b/src/samples/Dtf/Inventory/components.cs
@@ -0,0 +1,626 @@
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
3using System;
4using System.IO;
5using System.Data;
6using System.Text;
7using System.Collections;
8using System.Collections.Generic;
9using System.Globalization;
10using System.Windows.Forms;
11using Microsoft.Win32;
12using WixToolset.Dtf.WindowsInstaller;
13using View = WixToolset.Dtf.WindowsInstaller.View;
14
15namespace WixToolset.Dtf.Samples.Inventory
16{
17 /// <summary>
18 /// Provides inventory data about components of products installed on the system.
19 /// </summary>
20 public class ComponentsInventory : IInventoryDataProvider
21 {
22 private static object syncRoot = new object();
23
24 public ComponentsInventory()
25 {
26 }
27
28 public string Description
29 {
30 get { return "Components of installed products"; }
31 }
32
33 private Hashtable componentProductsMap;
34
35 public string[] GetNodes(InventoryDataLoadStatusCallback statusCallback)
36 {
37 ArrayList nodes = new ArrayList();
38 componentProductsMap = new Hashtable();
39 foreach(ProductInstallation product in ProductInstallation.AllProducts)
40 {
41 string productName = MsiUtils.GetProductName(product.ProductCode);
42 statusCallback(nodes.Count, String.Format(@"Products\{0}", productName));
43
44 try
45 {
46 IntPtr hWnd = IntPtr.Zero;
47 Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd);
48 lock(syncRoot) // Only one Installer session can be active at a time
49 {
50 using (Session session = Installer.OpenProduct(product.ProductCode))
51 {
52 statusCallback(nodes.Count, String.Format(@"Products\{0}\Features", productName));
53 IList<string> features = session.Database.ExecuteStringQuery("SELECT `Feature` FROM `Feature`");
54 string[] featuresArray = new string[features.Count];
55 features.CopyTo(featuresArray, 0);
56 Array.Sort(featuresArray, 0, featuresArray.Length, StringComparer.OrdinalIgnoreCase);
57 foreach (string feature in featuresArray)
58 {
59 nodes.Add(String.Format(@"Products\{0}\Features\{1}", productName, feature));
60 }
61 statusCallback(nodes.Count, String.Format(@"Products\{0}\Components", productName));
62 nodes.Add(String.Format(@"Products\{0}\Components", productName));
63 IList<string> components = session.Database.ExecuteStringQuery("SELECT `ComponentId` FROM `Component`");
64 for (int i = 0; i < components.Count; i++)
65 {
66 string component = components[i];
67 if (component.Length > 0)
68 {
69 nodes.Add(String.Format(@"Products\{0}\Components\{1}", productName, component));
70 ArrayList sharingProducts = (ArrayList) componentProductsMap[component];
71 if (sharingProducts == null)
72 {
73 sharingProducts = new ArrayList();
74 componentProductsMap[component] = sharingProducts;
75 }
76 sharingProducts.Add(product.ProductCode);
77 }
78 if (i % 100 == 0) statusCallback(nodes.Count, null);
79 }
80 nodes.Add(String.Format(@"Products\{0}\Files", productName));
81 nodes.Add(String.Format(@"Products\{0}\Registry", productName));
82 statusCallback(nodes.Count, String.Empty);
83 }
84 }
85 }
86 catch(InstallerException) { }
87 }
88 statusCallback(nodes.Count, @"Products\...\Components\...\Sharing");
89 foreach (DictionaryEntry componentProducts in componentProductsMap)
90 {
91 string component = (string) componentProducts.Key;
92 ArrayList products = (ArrayList) componentProducts.Value;
93 if(products.Count > 1)
94 {
95 foreach(string productCode in products)
96 {
97 nodes.Add(String.Format(@"Products\{0}\Components\{1}\Sharing", MsiUtils.GetProductName(productCode), component));
98 }
99 }
100 }
101 statusCallback(nodes.Count, String.Empty);
102 return (string[]) nodes.ToArray(typeof(string));
103 }
104
105 public bool IsNodeSearchable(string searchRoot, string searchNode)
106 {
107 string[] rootPath = searchRoot.Split('\\');
108 string[] nodePath = searchNode.Split('\\');
109 if(rootPath.Length < 3 && nodePath.Length >= 3 && nodePath[0] == "Products" && nodePath[2] == "Components")
110 {
111 // When searching an entire product, don't search the "Components" subtree --
112 // it just has duplicate data from the Files and Registry table. And if you
113 // really want to know about the component, it's only a click away from
114 // those other tables.
115 return false;
116 }
117 return true;
118 }
119
120 public DataView GetData(string nodePath)
121 {
122 string[] path = nodePath.Split('\\');
123
124 if(path.Length == 4 && path[0] == "Products" && path[2] == "Features")
125 {
126 return GetFeatureComponentsData(MsiUtils.GetProductCode(path[1]), path[3]);
127 }
128 else if(path.Length == 3 && path[0] == "Products" && path[2] == "Components")
129 {
130 return GetProductComponentsData(MsiUtils.GetProductCode(path[1]));
131 }
132 else if(path.Length == 4 && path[0] == "Products" && path[2] == "Components")
133 {
134 return GetComponentData(MsiUtils.GetProductCode(path[1]), path[3]);
135 }
136 else if(path.Length == 5 && path[0] == "Products" && path[2] == "Components" && path[4] == "Sharing")
137 {
138 return GetComponentProductsData(path[3]);
139 }
140 else if(path.Length == 3 && path[0] == "Products" && path[2] == "Files")
141 {
142 return GetProductFilesData(MsiUtils.GetProductCode(path[1]));
143 }
144 else if(path.Length == 3 && path[0] == "Products" && path[2] == "Registry")
145 {
146 return GetProductRegistryData(MsiUtils.GetProductCode(path[1]));
147 }
148 return null;
149 }
150
151 public DataView GetComponentData(string productCode, string componentCode)
152 {
153 DataTable table = new DataTable("ProductComponentItems");
154 table.Locale = CultureInfo.InvariantCulture;
155 table.Columns.Add("ProductComponentItemsIsKey", typeof(bool));
156 table.Columns.Add("ProductComponentItemsKey", typeof(string));
157 table.Columns.Add("ProductComponentItemsPath", typeof(string));
158 table.Columns.Add("ProductComponentItemsExists", typeof(bool));
159 table.Columns.Add("ProductComponentItemsDbVersion", typeof(string));
160 table.Columns.Add("ProductComponentItemsInstalledVersion", typeof(string));
161 table.Columns.Add("ProductComponentItemsInstalledMatch", typeof(bool));
162 try
163 {
164 IntPtr hWnd = IntPtr.Zero;
165 Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd);
166 lock(syncRoot) // Only one Installer session can be active at a time
167 {
168 using(Session session = Installer.OpenProduct(productCode))
169 {
170 session.DoAction("CostInitialize");
171 session.DoAction("FileCost");
172 session.DoAction("CostFinalize");
173
174 foreach(object[] row in this.GetComponentFilesRows(productCode, componentCode, session, false))
175 {
176 table.Rows.Add(row);
177 }
178 foreach(object[] row in this.GetComponentRegistryRows(productCode, componentCode, session, false))
179 {
180 table.Rows.Add(row);
181 }
182 }
183 }
184 return new DataView(table, "", "ProductComponentItemsPath ASC", DataViewRowState.CurrentRows);
185 }
186 catch(InstallerException) { }
187 return null;
188 }
189
190 private object[][] GetComponentFilesRows(string productCode, string componentCode, Session session, bool includeComponent)
191 {
192 ArrayList rows = new ArrayList();
193 string componentPath = new ComponentInstallation(componentCode, productCode).Path;
194
195 string componentKey = (string) session.Database.ExecuteScalar(
196 "SELECT `Component` FROM `Component` WHERE `ComponentId` = '{0}'", componentCode);
197 if(componentKey == null) return null;
198 int attributes = Convert.ToInt32(session.Database.ExecuteScalar(
199 "SELECT `Attributes` FROM `Component` WHERE `Component` = '{0}'", componentKey));
200 bool registryKeyPath = (attributes & (int) ComponentAttributes.RegistryKeyPath) != 0;
201 if(!registryKeyPath && componentPath.Length > 0) componentPath = Path.GetDirectoryName(componentPath);
202 string keyPath = (string) session.Database.ExecuteScalar(
203 "SELECT `KeyPath` FROM `Component` WHERE `Component` = '{0}'", componentKey);
204
205 using (View view = session.Database.OpenView("SELECT `File`, `FileName`, `Version`, `Language`, " +
206 "`Attributes` FROM `File` WHERE `Component_` = '{0}'", componentKey))
207 {
208 view.Execute();
209
210 foreach (Record rec in view) using (rec)
211 {
212 string fileKey = (string) rec["File"];
213 bool isKey = !registryKeyPath && keyPath == fileKey;
214
215 string dbVersion = (string) rec["Version"];
216 bool versionedFile = dbVersion.Length != 0;
217 if(versionedFile)
218 {
219 string language = (string) rec["Language"];
220 if(language.Length > 0)
221 {
222 dbVersion = dbVersion + " (" + language + ")";
223 }
224 }
225 else if(session.Database.Tables.Contains("MsiFileHash"))
226 {
227 IList<int> hash = session.Database.ExecuteIntegerQuery("SELECT `HashPart1`, `HashPart2`, " +
228 "`HashPart3`, `HashPart4` FROM `MsiFileHash` WHERE `File_` = '{0}'", fileKey);
229 if(hash != null && hash.Count == 4)
230 {
231 dbVersion = this.GetFileHashString(hash);
232 }
233 }
234
235 string filePath = GetLongFileName((string) rec["FileName"]);
236 bool exists = false;
237 bool installedMatch = false;
238 string installedVersion = "";
239 if(!registryKeyPath && componentPath.Length > 0)
240 {
241 filePath = Path.Combine(componentPath, filePath);
242
243 if(File.Exists(filePath))
244 {
245 exists = true;
246 if(versionedFile)
247 {
248 installedVersion = Installer.GetFileVersion(filePath);
249 string language = Installer.GetFileLanguage(filePath);
250 if(language.Length > 0)
251 {
252 installedVersion = installedVersion + " (" + language + ")";
253 }
254 }
255 else
256 {
257 int[] hash = new int[4];
258 Installer.GetFileHash(filePath, hash);
259 installedVersion = this.GetFileHashString(hash);
260 }
261 installedMatch = installedVersion == dbVersion;
262 }
263 }
264
265 object[] row;
266 if(includeComponent) row = new object[] { isKey, fileKey, filePath, exists, dbVersion, installedVersion, installedMatch, componentCode };
267 else row = new object[] { isKey, fileKey, filePath, exists, dbVersion, installedVersion, installedMatch };
268 rows.Add(row);
269 }
270 }
271
272 return (object[][]) rows.ToArray(typeof(object[]));
273 }
274
275 private string GetLongFileName(string fileName)
276 {
277 string[] fileNames = fileName.Split('|');
278 return fileNames.Length == 1? fileNames[0] : fileNames[1];
279 }
280
281 private string GetFileHashString(IList<int> hash)
282 {
283 return String.Format("{0:X8}{1:X8}{2:X8}{3:X8}", (uint) hash[0], (uint) hash[1], (uint) hash[2], (uint) hash[3]);
284 }
285
286 private object[][] GetComponentRegistryRows(string productCode, string componentCode, Session session, bool includeComponent)
287 {
288 ArrayList rows = new ArrayList();
289 string componentPath = new ComponentInstallation(componentCode, productCode).Path;
290
291 string componentKey = (string) session.Database.ExecuteScalar(
292 "SELECT `Component` FROM `Component` WHERE `ComponentId` = '{0}'", componentCode);
293 if(componentKey == null) return null;
294 int attributes = Convert.ToInt32(session.Database.ExecuteScalar(
295 "SELECT `Attributes` FROM `Component` WHERE `Component` = '{0}'", componentKey));
296 bool registryKeyPath = (attributes & (int) ComponentAttributes.RegistryKeyPath) != 0;
297 if(!registryKeyPath && componentPath.Length > 0) componentPath = Path.GetDirectoryName(componentPath);
298 string keyPath = (string) session.Database.ExecuteScalar(
299 "SELECT `KeyPath` FROM `Component` WHERE `Component` = '{0}'", componentKey);
300
301 using (View view = session.Database.OpenView("SELECT `Registry`, `Root`, `Key`, `Name`, " +
302 "`Value` FROM `Registry` WHERE `Component_` = '{0}'", componentKey))
303 {
304 view.Execute();
305
306 foreach (Record rec in view) using (rec)
307 {
308 string regName = (string) rec["Name"];
309 if(regName == "-") continue; // Don't list deleted keys
310
311 string regTableKey = (string) rec["Registry"];
312 bool isKey = registryKeyPath && keyPath == regTableKey;
313 string regPath = this.GetRegistryPath(session, (RegistryRoot) Convert.ToInt32(rec["Root"]),
314 (string) rec["Key"], (string) rec["Name"]);
315
316 string dbValue;
317 using(Record formatRec = new Record(0))
318 {
319 formatRec[0] = rec["Value"];
320 dbValue = session.FormatRecord(formatRec);
321 }
322
323 string installedValue = this.GetRegistryValue(regPath);
324 bool exists = installedValue != null;
325 if(!exists) installedValue = "";
326 bool match = installedValue == dbValue;
327
328 object[] row;
329 if(includeComponent) row = new object[] { isKey, regTableKey, regPath, exists, dbValue, installedValue, match, componentCode };
330 else row = new object[] { isKey, regTableKey, regPath, exists, dbValue, installedValue, match };
331 rows.Add(row);
332 }
333 }
334
335 return (object[][]) rows.ToArray(typeof(object[]));
336 }
337
338 private string GetRegistryPath(Session session, RegistryRoot root, string key, string name)
339 {
340 bool allUsers = session.EvaluateCondition("ALLUSERS = 1", true);
341 string rootName = "????";
342 switch(root)
343 {
344 case RegistryRoot.LocalMachine : rootName = "HKLM"; break;
345 case RegistryRoot.CurrentUser : rootName = "HKCU"; break;
346 case RegistryRoot.Users : rootName = "HKU"; break;
347 case RegistryRoot.UserOrMachine: rootName = (allUsers ? "HKLM" : "HKCU"); break;
348 case RegistryRoot.ClassesRoot : rootName = (allUsers ? @"HKLM\Software\Classes" : @"HKCU\Software\Classes"); break;
349 // TODO: Technically, RegistryRoot.ClassesRoot should be under HKLM on NT4.
350 }
351 if(name.Length == 0) name = "(Default)";
352 if(name == "+" || name == "*") name = "";
353 else name = " : " + name;
354 using(Record formatRec = new Record(0))
355 {
356 formatRec[0] = String.Format(@"{0}\{1}{2}", rootName, key, name);
357 return session.FormatRecord(formatRec);
358 }
359 }
360
361 private string GetRegistryValue(string regPath)
362 {
363 string valueName = null;
364 int iColon = regPath.IndexOf(" : ", StringComparison.Ordinal) + 1;
365 if(iColon > 0)
366 {
367 valueName = regPath.Substring(iColon + 2);
368 regPath = regPath.Substring(0, iColon - 1);
369 }
370 if(valueName == "(Default)") valueName = "";
371
372 RegistryKey root;
373 if(regPath.StartsWith(@"HKLM\", StringComparison.Ordinal))
374 {
375 root = Registry.LocalMachine;
376 regPath = regPath.Substring(5);
377 }
378 else if(regPath.StartsWith(@"HKCU\", StringComparison.Ordinal))
379 {
380 root = Registry.CurrentUser;
381 regPath = regPath.Substring(5);
382 }
383 else if(regPath.StartsWith(@"HKU\", StringComparison.Ordinal))
384 {
385 root = Registry.Users;
386 regPath = regPath.Substring(4);
387 }
388 else return null;
389
390 using(RegistryKey regKey = root.OpenSubKey(regPath))
391 {
392 if(regKey != null)
393 {
394 if(valueName == null)
395 {
396 // Just checking for the existence of the key.
397 return "";
398 }
399 object value = regKey.GetValue(valueName);
400 if(value is string[])
401 {
402 value = String.Join("[~]", (string[]) value);
403 }
404 else if(value is int)
405 {
406 value = "#" + value.ToString();
407 }
408 else if(value is byte[])
409 {
410 byte[] valueBytes = (byte[]) value;
411 StringBuilder byteString = new StringBuilder("#x");
412 for(int i = 0; i < valueBytes.Length; i++)
413 {
414 byteString.Append(valueBytes[i].ToString("x2"));
415 }
416 value = byteString.ToString();
417 }
418 return (value != null ? value.ToString() : null);
419 }
420 }
421 return null;
422 }
423
424 public DataView GetProductFilesData(string productCode)
425 {
426 DataTable table = new DataTable("ProductFiles");
427 table.Locale = CultureInfo.InvariantCulture;
428 table.Columns.Add("ProductFilesIsKey", typeof(bool));
429 table.Columns.Add("ProductFilesKey", typeof(string));
430 table.Columns.Add("ProductFilesPath", typeof(string));
431 table.Columns.Add("ProductFilesExists", typeof(bool));
432 table.Columns.Add("ProductFilesDbVersion", typeof(string));
433 table.Columns.Add("ProductFilesInstalledVersion", typeof(string));
434 table.Columns.Add("ProductFilesInstalledMatch", typeof(bool));
435 table.Columns.Add("ProductFilesComponentID", typeof(string));
436 try
437 {
438 IntPtr hWnd = IntPtr.Zero;
439 Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd);
440 lock(syncRoot) // Only one Installer session can be active at a time
441 {
442 using(Session session = Installer.OpenProduct(productCode))
443 {
444 session.DoAction("CostInitialize");
445 session.DoAction("FileCost");
446 session.DoAction("CostFinalize");
447
448 foreach(string componentCode in session.Database.ExecuteStringQuery("SELECT `ComponentId` FROM `Component`"))
449 {
450 foreach(object[] row in this.GetComponentFilesRows(productCode, componentCode, session, true))
451 {
452 table.Rows.Add(row);
453 }
454 }
455 }
456 }
457 return new DataView(table, "", "ProductFilesPath ASC", DataViewRowState.CurrentRows);
458 }
459 catch(InstallerException) { }
460 return null;
461 }
462
463 public DataView GetProductRegistryData(string productCode)
464 {
465 DataTable table = new DataTable("ProductRegistry");
466 table.Locale = CultureInfo.InvariantCulture;
467 table.Columns.Add("ProductRegistryIsKey", typeof(bool));
468 table.Columns.Add("ProductRegistryKey", typeof(string));
469 table.Columns.Add("ProductRegistryPath", typeof(string));
470 table.Columns.Add("ProductRegistryExists", typeof(bool));
471 table.Columns.Add("ProductRegistryDbVersion", typeof(string));
472 table.Columns.Add("ProductRegistryInstalledVersion", typeof(string));
473 table.Columns.Add("ProductRegistryInstalledMatch", typeof(bool));
474 table.Columns.Add("ProductRegistryComponentID", typeof(string));
475 try
476 {
477 IntPtr hWnd = IntPtr.Zero;
478 Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd);
479 lock(syncRoot) // Only one Installer session can be active at a time
480 {
481 using(Session session = Installer.OpenProduct(productCode))
482 {
483 session.DoAction("CostInitialize");
484 session.DoAction("FileCost");
485 session.DoAction("CostFinalize");
486
487 foreach(string componentCode in session.Database.ExecuteStringQuery("SELECT `ComponentId` FROM `Component`"))
488 {
489 foreach(object[] row in this.GetComponentRegistryRows(productCode, componentCode, session, true))
490 {
491 table.Rows.Add(row);
492 }
493 }
494 }
495 }
496 return new DataView(table, "", "ProductRegistryPath ASC", DataViewRowState.CurrentRows);
497 }
498 catch(InstallerException) { }
499 return null;
500 }
501
502 public DataView GetComponentProductsData(string componentCode)
503 {
504 DataTable table = new DataTable("ComponentProducts");
505 table.Locale = CultureInfo.InvariantCulture;
506 table.Columns.Add("ComponentProductsProductName", typeof(string));
507 table.Columns.Add("ComponentProductsProductCode", typeof(string));
508 table.Columns.Add("ComponentProductsComponentPath", typeof(string));
509
510 if(this.componentProductsMap != null)
511 {
512 ArrayList componentProducts = (ArrayList) this.componentProductsMap[componentCode];
513 foreach(string productCode in componentProducts)
514 {
515 string productName = MsiUtils.GetProductName(productCode);
516 string componentPath = new ComponentInstallation(componentCode, productCode).Path;
517 table.Rows.Add(new object[] { productName, productCode, componentPath });
518 }
519 return new DataView(table, "", "ComponentProductsProductName ASC", DataViewRowState.CurrentRows);
520 }
521 return null;
522 }
523
524 public DataView GetProductComponentsData(string productCode)
525 {
526 DataTable table = new DataTable("ProductComponents");
527 table.Locale = CultureInfo.InvariantCulture;
528 table.Columns.Add("ProductComponentsComponentName", typeof(string));
529 table.Columns.Add("ProductComponentsComponentID", typeof(string));
530 table.Columns.Add("ProductComponentsInstallState", typeof(string));
531
532 try
533 {
534 IntPtr hWnd = IntPtr.Zero;
535 Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd);
536 lock(syncRoot) // Only one Installer session can be active at a time
537 {
538 using(Session session = Installer.OpenProduct(productCode))
539 {
540 session.DoAction("CostInitialize");
541 session.DoAction("FileCost");
542 session.DoAction("CostFinalize");
543
544 IList<string> componentsAndIds = session.Database.ExecuteStringQuery(
545 "SELECT `Component`, `ComponentId` FROM `Component`");
546
547 for (int i = 0; i < componentsAndIds.Count; i += 2)
548 {
549 if(componentsAndIds[i+1] == "Temporary Id") continue;
550 InstallState compState = session.Components[componentsAndIds[i]].CurrentState;
551 table.Rows.Add(new object[] { componentsAndIds[i], componentsAndIds[i+1],
552 (compState == InstallState.Advertised ? "Advertised" : compState.ToString())});
553 }
554 }
555 }
556 return new DataView(table, "", "ProductComponentsComponentName ASC", DataViewRowState.CurrentRows);
557 }
558 catch(InstallerException) { }
559 return null;
560 }
561
562 public DataView GetFeatureComponentsData(string productCode, string feature)
563 {
564 DataTable table = new DataTable("ProductFeatureComponents");
565 table.Locale = CultureInfo.InvariantCulture;
566 table.Columns.Add("ProductFeatureComponentsComponentName", typeof(string));
567 table.Columns.Add("ProductFeatureComponentsComponentID", typeof(string));
568
569 try
570 {
571 IntPtr hWnd = IntPtr.Zero;
572 Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd);
573 lock(syncRoot) // Only one Installer session can be active at a time
574 {
575 using(Session session = Installer.OpenProduct(productCode))
576 {
577 IList<string> componentsAndIds = session.Database.ExecuteStringQuery(
578 "SELECT `FeatureComponents`.`Component_`, " +
579 "`Component`.`ComponentId` FROM `FeatureComponents`, `Component` " +
580 "WHERE `FeatureComponents`.`Component_` = `Component`.`Component` " +
581 "AND `FeatureComponents`.`Feature_` = '{0}'", feature);
582 for (int i = 0; i < componentsAndIds.Count; i += 2)
583 {
584 table.Rows.Add(new object[] { componentsAndIds[i], componentsAndIds[i+1] });
585 }
586 }
587 }
588 return new DataView(table, "", "ProductFeatureComponentsComponentName ASC", DataViewRowState.CurrentRows);
589 }
590 catch(InstallerException) { }
591 return null;
592 }
593
594 public string GetLink(string nodePath, DataRow row)
595 {
596 string[] path = nodePath.Split('\\');
597
598 if(path.Length == 3 && path[0] == "Products" && path[2] == "Components")
599 {
600 string component = (string) row["ProductComponentsComponentID"];
601 return String.Format(@"Products\{0}\Components\{1}", path[1], component);
602 }
603 else if(path.Length == 4 && path[0] == "Products" && path[2] == "Features")
604 {
605 string component = (string) row["ProductFeatureComponentsComponentID"];
606 return String.Format(@"Products\{0}\Components\{1}", path[1], component);
607 }
608 else if(path.Length == 3 && path[0] == "Products" && path[2] == "Files")
609 {
610 string component = (string) row["ProductFilesComponentID"];
611 return String.Format(@"Products\{0}\Components\{1}", path[1], component);
612 }
613 else if(path.Length == 3 && path[0] == "Products" && path[2] == "Registry")
614 {
615 string component = (string) row["ProductRegistryComponentID"];
616 return String.Format(@"Products\{0}\Components\{1}", path[1], component);
617 }
618 else if(path.Length == 5 && path[0] == "Products" && path[2] == "Components" && path[4] == "Sharing")
619 {
620 string product = (string) row["ComponentProductsProductCode"];
621 return String.Format(@"Products\{0}\Components\{1}", MsiUtils.GetProductName(product), path[3]);
622 }
623 return null;
624 }
625 }
626}