| 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 | using System; |
| 4 | using System.IO; |
| 5 | using System.Data; |
| 6 | using System.Collections; |
| 7 | using System.Collections.Generic; |
| 8 | using System.Globalization; |
| 9 | using System.Windows.Forms; |
| 10 | using WixToolset.Dtf.WindowsInstaller; |
| 11 | |
| 12 | namespace WixToolset.Dtf.Tools.Inventory |
| 13 | { |
| 14 | /// <summary> |
| 15 | /// Provides inventory data about features of products installed on the system. |
| 16 | /// </summary> |
| 17 | public class FeaturesInventory : IInventoryDataProvider |
| 18 | { |
| 19 | private static object syncRoot = new object(); |
| 20 | |
| 21 | public FeaturesInventory() |
| 22 | { |
| 23 | } |
| 24 | |
| 25 | public string Description |
| 26 | { |
| 27 | get { return "Features of installed products"; } |
| 28 | } |
| 29 | |
| 30 | public string[] GetNodes(InventoryDataLoadStatusCallback statusCallback) |
| 31 | { |
| 32 | statusCallback(0, @"Products\...\Features"); |
| 33 | ArrayList nodes = new ArrayList(); |
| 34 | foreach (ProductInstallation product in ProductInstallation.AllProducts) |
| 35 | { |
| 36 | nodes.Add(String.Format(@"Products\{0}\Features", MsiUtils.GetProductName(product.ProductCode))); |
| 37 | } |
| 38 | statusCallback(nodes.Count, String.Empty); |
| 39 | return (string[]) nodes.ToArray(typeof(string)); |
| 40 | } |
| 41 | |
| 42 | public bool IsNodeSearchable(string searchRoot, string searchNode) |
| 43 | { |
| 44 | return true; |
| 45 | } |
| 46 | |
| 47 | public DataView GetData(string nodePath) |
| 48 | { |
| 49 | string[] path = nodePath.Split('\\'); |
| 50 | |
| 51 | if(path.Length == 3 && path[0] == "Products" && path[2] == "Features") |
| 52 | { |
| 53 | return GetProductFeaturesData(MsiUtils.GetProductCode(path[1])); |
| 54 | } |
| 55 | return null; |
| 56 | } |
| 57 | |
| 58 | public DataView GetProductFeaturesData(string productCode) |
| 59 | { |
| 60 | DataTable table = new DataTable("ProductFeatures"); |
| 61 | table.Locale = CultureInfo.InvariantCulture; |
| 62 | table.Columns.Add("ProductFeaturesFeatureTitle", typeof(string)); |
| 63 | table.Columns.Add("ProductFeaturesFeatureName", typeof(string)); |
| 64 | table.Columns.Add("ProductFeaturesInstallState", typeof(string)); |
| 65 | |
| 66 | try |
| 67 | { |
| 68 | IntPtr hWnd = IntPtr.Zero; |
| 69 | Installer.SetInternalUI(InstallUIOptions.Silent, ref hWnd); |
| 70 | lock(syncRoot) // Only one Installer session can be active at a time |
| 71 | { |
| 72 | using(Session session = Installer.OpenProduct(productCode)) |
| 73 | { |
| 74 | session.DoAction("CostInitialize"); |
| 75 | session.DoAction("FileCost"); |
| 76 | session.DoAction("CostFinalize"); |
| 77 | |
| 78 | IList<string> featuresAndTitles = session.Database.ExecuteStringQuery( |
| 79 | "SELECT `Title`, `Feature` FROM `Feature`"); |
| 80 | |
| 81 | for(int i = 0; i < featuresAndTitles.Count; i += 2) |
| 82 | { |
| 83 | InstallState featureState = session.Features[featuresAndTitles[i + 1]].CurrentState; |
| 84 | table.Rows.Add(new object[] { featuresAndTitles[i], featuresAndTitles[i+1], |
| 85 | (featureState == InstallState.Advertised ? "Advertised" : featureState.ToString()) }); |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | return new DataView(table, "", "ProductFeaturesFeatureTitle ASC", DataViewRowState.CurrentRows); |
| 90 | } |
| 91 | catch(InstallerException) { } |
| 92 | catch(IOException) { } |
| 93 | return null; |
| 94 | } |
| 95 | |
| 96 | public string GetLink(string nodePath, DataRow row) |
| 97 | { |
| 98 | string[] path = nodePath.Split('\\'); |
| 99 | |
| 100 | if(path.Length == 3 && path[0] == "Products" && path[2] == "Features") |
| 101 | { |
| 102 | return String.Format(@"Products\{0}\Features\{1}", path[1], row["ProductFeaturesFeatureName"]); |
| 103 | } |
| 104 | return null; |
| 105 | } |
| 106 | } |
| 107 | } |