// 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. using System; using System.IO; using System.Security; using System.Data; using System.Collections; using System.Globalization; using System.Windows.Forms; using WixToolset.Dtf.WindowsInstaller; namespace WixToolset.Dtf.Tools.Inventory { /// /// Provides inventory data about patches installed on the system. /// public class PatchesInventory : IInventoryDataProvider { public PatchesInventory() { } public string Description { get { return "Installed patches"; } } public string[] GetNodes(InventoryDataLoadStatusCallback statusCallback) { ArrayList nodes = new ArrayList(); statusCallback(nodes.Count, @"Products\...\Patches"); foreach (ProductInstallation product in ProductInstallation.AllProducts) { string productName = MsiUtils.GetProductName(product.ProductCode); bool addedRoot = false; foreach (PatchInstallation productPatch in PatchInstallation.GetPatches(null, product.ProductCode, null, UserContexts.All, PatchStates.Applied)) { if (!addedRoot) nodes.Add(String.Format(@"Products\{0}\Patches", productName)); nodes.Add(String.Format(@"Products\{0}\Patches\{1}", productName, productPatch.PatchCode)); } } statusCallback(nodes.Count, "Patches"); string[] allPatches = GetAllPatchesList(); if(allPatches.Length > 0) { nodes.Add("Patches"); foreach(string patchCode in allPatches) { nodes.Add(String.Format(@"Patches\{0}", patchCode)); nodes.Add(String.Format(@"Patches\{0}\Patched Products", patchCode)); } statusCallback(nodes.Count, String.Empty); } return (string[]) nodes.ToArray(typeof(string)); } public bool IsNodeSearchable(string searchRoot, string searchNode) { return true; } public DataView GetData(string nodePath) { string[] path = nodePath.Split('\\'); if(path.Length == 3 && path[0] == "Products" && path[2] == "Patches") { return this.GetProductPatchData(path[1]); } else if(path.Length == 4 && path[0] == "Products" && path[2] == "Patches") { return this.GetPatchData(path[3]); } else if(path.Length == 1 && path[0] == "Patches") { return this.GetAllPatchesData(); } else if(path.Length == 2 && path[0] == "Patches") { return this.GetPatchData(path[1]); } else if(path.Length == 3 && path[0] == "Patches" && path[2] == "Patched Products") { return this.GetPatchTargetData(path[1]); } return null; } private string[] GetAllPatchesList() { ArrayList patchList = new ArrayList(); foreach(PatchInstallation patch in PatchInstallation.AllPatches) { if(!patchList.Contains(patch.PatchCode)) { patchList.Add(patch.PatchCode); } } string[] patchArray = (string[]) patchList.ToArray(typeof(string)); Array.Sort(patchArray, 0, patchArray.Length, StringComparer.Ordinal); return patchArray; } private DataView GetAllPatchesData() { DataTable table = new DataTable("Patches"); table.Locale = CultureInfo.InvariantCulture; table.Columns.Add("PatchesPatchCode", typeof(string)); foreach(string patchCode in GetAllPatchesList()) { table.Rows.Add(new object[] { patchCode }); } return new DataView(table, "", "PatchesPatchCode ASC", DataViewRowState.CurrentRows); } private DataView GetProductPatchData(string productCode) { DataTable table = new DataTable("ProductPatches"); table.Locale = CultureInfo.InvariantCulture; table.Columns.Add("ProductPatchesPatchCode", typeof(string)); foreach(PatchInstallation patch in PatchInstallation.GetPatches(null, productCode, null, UserContexts.All, PatchStates.Applied)) { table.Rows.Add(new object[] { patch.PatchCode }); } return new DataView(table, "", "ProductPatchesPatchCode ASC", DataViewRowState.CurrentRows); } private DataView GetPatchData(string patchCode) { DataTable table = new DataTable("PatchProperties"); table.Locale = CultureInfo.InvariantCulture; table.Columns.Add("PatchPropertiesProperty", typeof(string)); table.Columns.Add("PatchPropertiesValue", typeof(string)); table.Rows.Add(new object[] { "PatchCode", patchCode }); PatchInstallation patch = new PatchInstallation(patchCode, null); string localPackage = null; foreach(string property in new string[] { "InstallDate", "LocalPackage", "State", "Transforms", "Uninstallable", }) { try { string value = patch[property]; table.Rows.Add(new object[] { property, (value != null ? value : "") }); if(property == "LocalPackage") localPackage = value; } catch(InstallerException iex) { table.Rows.Add(new object[] { property, iex.Message }); } catch(ArgumentException) { } } if(localPackage != null) { try { using(SummaryInfo patchSummaryInfo = new SummaryInfo(localPackage, false)) { table.Rows.Add(new object[] { "Title", patchSummaryInfo.Title }); table.Rows.Add(new object[] { "Subject", patchSummaryInfo.Subject }); table.Rows.Add(new object[] { "Author", patchSummaryInfo.Author }); table.Rows.Add(new object[] { "Comments", patchSummaryInfo.Comments }); table.Rows.Add(new object[] { "TargetProductCodes", patchSummaryInfo.Template }); string obsoletedPatchCodes = patchSummaryInfo.RevisionNumber.Substring(patchSummaryInfo.RevisionNumber.IndexOf('}') + 1); table.Rows.Add(new object[] { "ObsoletedPatchCodes", obsoletedPatchCodes }); table.Rows.Add(new object[] { "TransformNames", patchSummaryInfo.LastSavedBy }); } } catch(InstallerException) { } catch(IOException) { } catch(SecurityException) { } } return new DataView(table, "", "PatchPropertiesProperty ASC", DataViewRowState.CurrentRows); } private DataView GetPatchTargetData(string patchCode) { DataTable table = new DataTable("PatchTargets"); table.Locale = CultureInfo.InvariantCulture; table.Columns.Add("PatchTargetsProductName", typeof(string)); table.Columns.Add("PatchTargetsProductCode", typeof(string)); foreach (PatchInstallation patch in PatchInstallation.GetPatches(patchCode, null, null, UserContexts.All, PatchStates.Applied)) { if(patch.PatchCode == patchCode) { string productName = MsiUtils.GetProductName(patch.ProductCode); table.Rows.Add(new object[] { productName, patch.ProductCode }); } } return new DataView(table, "", "PatchTargetsProductName ASC", DataViewRowState.CurrentRows); } public string GetLink(string nodePath, DataRow row) { string[] path = nodePath.Split('\\'); if(path.Length == 3 && path[0] == "Products" && path[2] == "Patches") { return String.Format(@"Patches\{0}", row["ProductPatchesPatchCode"]); } else if(path.Length == 1 && path[0] == "Patches") { return String.Format(@"Patches\{0}", row["PatchesPatchCode"]); } else if(path.Length == 3 && path[0] == "Patches" && path[2] == "Patched Products") { return String.Format(@"Products\{0}", MsiUtils.GetProductCode((string) row["PatchTargetsProductCode"])); } return null; } } }