From 343038b42c1c6189ba90d2bcf96177123cb66c8d Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 25 Mar 2021 09:57:52 -0700 Subject: Add missing MSI functionality required by Core --- src/WixToolset.Core.Native/Msi/Database.cs | 15 +- src/WixToolset.Core.Native/Msi/Installer.cs | 25 +++ src/WixToolset.Core.Native/Msi/MsiInterop.cs | 11 ++ .../Msi/SummaryInformation.cs | 173 ++++++++++++++++++--- 4 files changed, 199 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/WixToolset.Core.Native/Msi/Database.cs b/src/WixToolset.Core.Native/Msi/Database.cs index a44e8cf9..b9c5c35b 100644 --- a/src/WixToolset.Core.Native/Msi/Database.cs +++ b/src/WixToolset.Core.Native/Msi/Database.cs @@ -3,7 +3,6 @@ namespace WixToolset.Core.Native.Msi { using System; - using System.Globalization; using System.IO; using System.Threading; @@ -44,10 +43,9 @@ namespace WixToolset.Core.Native.Msi var conditions = TransformErrorConditions.None; using (var summaryInfo = new SummaryInformation(transformFile)) { - var value = summaryInfo.GetProperty((int)SummaryInformation.Transform.ValidationFlags); try { - var validationFlags = Int32.Parse(value, CultureInfo.InvariantCulture); + var validationFlags = summaryInfo.GetNumericProperty(SummaryInformation.Transform.ValidationFlags); conditions = (TransformErrorConditions)(validationFlags & 0xffff); } catch (FormatException) @@ -192,13 +190,20 @@ namespace WixToolset.Core.Native.Msi /// /// The database to merge into the base database. /// The name of the table to receive merge conflict information. - public void Merge(Database mergeDatabase, string tableName) + /// True if there were merge conflicts, otherwise false. + public bool Merge(Database mergeDatabase, string tableName) { var error = MsiInterop.MsiDatabaseMerge(this.Handle, mergeDatabase.Handle, tableName); - if (0 != error) + if (error == 1627) + { + return true; + } + else if (error != 0) { throw new MsiException(error); } + + return false; } /// diff --git a/src/WixToolset.Core.Native/Msi/Installer.cs b/src/WixToolset.Core.Native/Msi/Installer.cs index 2bb41078..8a45aaa8 100644 --- a/src/WixToolset.Core.Native/Msi/Installer.cs +++ b/src/WixToolset.Core.Native/Msi/Installer.cs @@ -23,6 +23,31 @@ namespace WixToolset.Core.Native.Msi /// public static class Installer { + /// + /// Extacts the patch metadata as XML. + /// + /// Path to patch. + /// String XML. + public static string ExtractPatchXml(string path) + { + var buffer = new StringBuilder(65535); + var size = buffer.Capacity; + + var error = MsiInterop.MsiExtractPatchXMLData(path, 0, buffer, ref size); + if (234 == error) + { + buffer.EnsureCapacity(++size); + error = MsiInterop.MsiExtractPatchXMLData(path, 0, buffer, ref size); + } + + if (error != 0) + { + throw new MsiException(error); + } + + return buffer.ToString(); + } + /// /// Takes the path to a file and returns a 128-bit hash of that file. /// diff --git a/src/WixToolset.Core.Native/Msi/MsiInterop.cs b/src/WixToolset.Core.Native/Msi/MsiInterop.cs index 0d16fcb2..11ac4094 100644 --- a/src/WixToolset.Core.Native/Msi/MsiInterop.cs +++ b/src/WixToolset.Core.Native/Msi/MsiInterop.cs @@ -152,6 +152,17 @@ namespace WixToolset.Core.Native.Msi [DllImport("msi.dll", EntryPoint = "MsiDatabaseOpenViewW", CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int MsiDatabaseOpenView(uint database, string query, out uint view); + /// + /// PInvoke of MsiExtractPatchXMLDataW. + /// + /// Path to patch. + /// Reserved for future use. + /// Output XML data. + /// Count of characters in XML. + /// + [DllImport("msi.dll", EntryPoint = "MsiExtractPatchXMLDataW", CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern int MsiExtractPatchXMLData(string szPatchPath, int dwReserved, StringBuilder szXMLData, ref int pcchXMLData); + /// /// PInvoke of MsiGetFileHashW. /// diff --git a/src/WixToolset.Core.Native/Msi/SummaryInformation.cs b/src/WixToolset.Core.Native/Msi/SummaryInformation.cs index a7ba5717..da629df2 100644 --- a/src/WixToolset.Core.Native/Msi/SummaryInformation.cs +++ b/src/WixToolset.Core.Native/Msi/SummaryInformation.cs @@ -12,6 +12,63 @@ namespace WixToolset.Core.Native.Msi /// public sealed class SummaryInformation : MsiHandle { + /// + /// Summary information properties for products. + /// + public enum Package + { + /// PID_CODEPAGE = code page of the summary information stream + CodePage = 1, + + /// PID_TITLE = a brief description of the package type + Title = 2, + + /// PID_SUBJECT = package name + PackageName = 3, + + /// PID_AUTHOR = manufacturer of the patch package + Manufacturer = 4, + + /// PID_KEYWORDS = list of keywords used by file browser + Keywords = 5, + + /// PID_COMMENTS = general purpose of the package + Comments = 6, + + /// PID_TEMPLATE = supported platforms and languages + PlatformsAndLanguages = 7, + + /// PID_LASTAUTHOR should be null for packages + Reserved8 = 8, + + /// PID_REVNUMBER = GUID package code + PackageCode = 9, + + /// PID_LASTPRINTED should be null for packages + Reserved11 = 11, + + /// PID_CREATED datetime when package was created + Created = 12, + + /// PID_SAVED datetime when package was last modified + LastModified = 13, + + /// PID_PAGECOUNT minimum required Windows Installer + InstallerRequirement = 14, + + /// PID_WORDCOUNT elevation privileges of package + FileAndElevatedFlags = 15, + + /// PID_CHARCOUNT should be null for patches + Reserved16 = 16, + + /// PID_APPLICATION tool used to create package + BuildTool = 18, + + /// PID_SECURITY = read-only attribute of the package + Security = 19, + } + /// /// Summary information properties for transforms. /// @@ -140,7 +197,7 @@ namespace WixToolset.Core.Native.Msi { if (null == db) { - throw new ArgumentNullException("db"); + throw new ArgumentNullException(nameof(db)); } uint handle = 0; @@ -160,7 +217,7 @@ namespace WixToolset.Core.Native.Msi { if (null == databaseFile) { - throw new ArgumentNullException("databaseFile"); + throw new ArgumentNullException(nameof(databaseFile)); } uint handle = 0; @@ -173,49 +230,125 @@ namespace WixToolset.Core.Native.Msi } /// - /// Gets a summary information property. + /// Gets a summary information package property. /// - /// Index of the summary information property. + /// The summary information package property. /// The summary information property. - public string GetProperty(int index) + public string GetProperty(Package property) { - var bufSize = 64; - var stringValue = new StringBuilder(bufSize); + return this.GetProperty((int)property); + } - FILETIME timeValue; - timeValue.dwHighDateTime = 0; - timeValue.dwLowDateTime = 0; + /// + /// Gets a summary information package property as a number. + /// + /// The summary information package property. + /// The summary information property. + public long GetNumericProperty(Package property) + { + return this.GetNumericProperty((int)property); + } - var error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out var dataType, out var intValue, ref timeValue, stringValue, ref bufSize); - if (234 == error) - { - stringValue.EnsureCapacity(++bufSize); - error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); - } + /// + /// Gets a summary information patch property. + /// + /// The summary information patch property. + /// The summary information property. + public string GetProperty(Patch property) + { + return this.GetProperty((int)property); + } - if (0 != error) - { - throw new MsiException(error); - } + /// + /// Gets a summary information transform property. + /// + /// The summary information transform property. + /// The summary information property. + public long GetNumericProperty(Transform property) + { + return this.GetNumericProperty((int)property); + } + + /// + /// Gets a summary information property. + /// + /// Index of the summary information property. + /// The summary information property. + public string GetProperty(int index) + { + this.GetSummaryInformationValue(index, out var dataType, out var intValue, out var stringValue, out var timeValue); switch ((VT)dataType) { case VT.EMPTY: return String.Empty; + case VT.LPSTR: return stringValue.ToString(); + case VT.I2: case VT.I4: return Convert.ToString(intValue, CultureInfo.InvariantCulture); + case VT.FILETIME: var longFileTime = (((long)timeValue.dwHighDateTime) << 32) | unchecked((uint)timeValue.dwLowDateTime); var dateTime = DateTime.FromFileTime(longFileTime); return dateTime.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture); + default: throw new InvalidOperationException(); } } + /// + /// Gets a summary information property as a number. + /// + /// Index of the summary information property. + /// The summary information property. + public long GetNumericProperty(int index) + { + this.GetSummaryInformationValue(index, out var dataType, out var intValue, out var stringValue, out var timeValue); + + switch ((VT)dataType) + { + case VT.EMPTY: + return 0; + + case VT.LPSTR: + return Int64.Parse(stringValue.ToString(), CultureInfo.InvariantCulture); + + case VT.I2: + case VT.I4: + return intValue; + + case VT.FILETIME: + return (((long)timeValue.dwHighDateTime) << 32) | unchecked((uint)timeValue.dwLowDateTime); + + default: + throw new InvalidOperationException(); + } + } + + private void GetSummaryInformationValue(int index, out uint dataType, out int intValue, out StringBuilder stringValue, out FILETIME timeValue) + { + var bufSize = 64; + stringValue = new StringBuilder(bufSize); + timeValue.dwHighDateTime = 0; + timeValue.dwLowDateTime = 0; + + var error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); + if (234 == error) + { + stringValue.EnsureCapacity(++bufSize); + error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); + } + + if (0 != error) + { + throw new MsiException(error); + } + } + /// /// Variant types in the summary information table. /// -- cgit v1.2.3-55-g6feb