@joebigelow / wix / commits / 343038b4

Add missing MSI functionality required by Core

Rob Mensching committed Mar 25, 2021 at 09:57 UTC 343038b42c1c6189ba90d2bcf96177123cb66c8d
4 files changed +199 -25
src/WixToolset.Core.Native/Msi/Database.cs
+10 -5
@@ -3,7 +3,6 @@
3 namespace WixToolset.Core.Native.Msi
4 {
5 using System;
6 - using System.Globalization;
6 using System.IO;
7 using System.Threading;
8
@@ -44,10 +43,9 @@ namespace WixToolset.Core.Native.Msi
43 var conditions = TransformErrorConditions.None;
44 using (var summaryInfo = new SummaryInformation(transformFile))
45 {
47 - var value = summaryInfo.GetProperty((int)SummaryInformation.Transform.ValidationFlags);
46 try
47 {
50 - var validationFlags = Int32.Parse(value, CultureInfo.InvariantCulture);
48 + var validationFlags = summaryInfo.GetNumericProperty(SummaryInformation.Transform.ValidationFlags);
49 conditions = (TransformErrorConditions)(validationFlags & 0xffff);
50 }
51 catch (FormatException)
@@ -192,13 +190,20 @@ namespace WixToolset.Core.Native.Msi
190 /// </summary>
191 /// <param name="mergeDatabase">The database to merge into the base database.</param>
192 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
195 - public void Merge(Database mergeDatabase, string tableName)
193 + /// <returns>True if there were merge conflicts, otherwise false.</returns>
194 + public bool Merge(Database mergeDatabase, string tableName)
195 {
196 var error = MsiInterop.MsiDatabaseMerge(this.Handle, mergeDatabase.Handle, tableName);
198 - if (0 != error)
197 + if (error == 1627)
198 + {
199 + return true;
200 + }
201 + else if (error != 0)
202 {
203 throw new MsiException(error);
204 }
205 +
206 + return false;
207 }
208
209 /// <summary>
src/WixToolset.Core.Native/Msi/Installer.cs
+25
@@ -23,6 +23,31 @@ namespace WixToolset.Core.Native.Msi
23 /// </summary>
24 public static class Installer
25 {
26 + /// <summary>
27 + /// Extacts the patch metadata as XML.
28 + /// </summary>
29 + /// <param name="path">Path to patch.</param>
30 + /// <returns>String XML.</returns>
31 + public static string ExtractPatchXml(string path)
32 + {
33 + var buffer = new StringBuilder(65535);
34 + var size = buffer.Capacity;
35 +
36 + var error = MsiInterop.MsiExtractPatchXMLData(path, 0, buffer, ref size);
37 + if (234 == error)
38 + {
39 + buffer.EnsureCapacity(++size);
40 + error = MsiInterop.MsiExtractPatchXMLData(path, 0, buffer, ref size);
41 + }
42 +
43 + if (error != 0)
44 + {
45 + throw new MsiException(error);
46 + }
47 +
48 + return buffer.ToString();
49 + }
50 +
51 /// <summary>
52 /// Takes the path to a file and returns a 128-bit hash of that file.
53 /// </summary>
src/WixToolset.Core.Native/Msi/MsiInterop.cs
+11
@@ -152,6 +152,17 @@ namespace WixToolset.Core.Native.Msi
152 [DllImport("msi.dll", EntryPoint = "MsiDatabaseOpenViewW", CharSet = CharSet.Unicode, ExactSpelling = true)]
153 internal static extern int MsiDatabaseOpenView(uint database, string query, out uint view);
154
155 + /// <summary>
156 + /// PInvoke of MsiExtractPatchXMLDataW.
157 + /// </summary>
158 + /// <param name="szPatchPath">Path to patch.</param>
159 + /// <param name="dwReserved">Reserved for future use.</param>
160 + /// <param name="szXMLData">Output XML data.</param>
161 + /// <param name="pcchXMLData">Count of characters in XML.</param>
162 + /// <returns></returns>
163 + [DllImport("msi.dll", EntryPoint = "MsiExtractPatchXMLDataW", CharSet = CharSet.Unicode, ExactSpelling = true)]
164 + internal static extern int MsiExtractPatchXMLData(string szPatchPath, int dwReserved, StringBuilder szXMLData, ref int pcchXMLData);
165 +
166 /// <summary>
167 /// PInvoke of MsiGetFileHashW.
168 /// </summary>
src/WixToolset.Core.Native/Msi/SummaryInformation.cs
+153 -20
@@ -12,6 +12,63 @@ namespace WixToolset.Core.Native.Msi
12 /// </summary>
13 public sealed class SummaryInformation : MsiHandle
14 {
15 + /// <summary>
16 + /// Summary information properties for products.
17 + /// </summary>
18 + public enum Package
19 + {
20 + /// <summary>PID_CODEPAGE = code page of the summary information stream</summary>
21 + CodePage = 1,
22 +
23 + /// <summary>PID_TITLE = a brief description of the package type</summary>
24 + Title = 2,
25 +
26 + /// <summary>PID_SUBJECT = package name</summary>
27 + PackageName = 3,
28 +
29 + /// <summary>PID_AUTHOR = manufacturer of the patch package</summary>
30 + Manufacturer = 4,
31 +
32 + /// <summary>PID_KEYWORDS = list of keywords used by file browser</summary>
33 + Keywords = 5,
34 +
35 + /// <summary>PID_COMMENTS = general purpose of the package</summary>
36 + Comments = 6,
37 +
38 + /// <summary>PID_TEMPLATE = supported platforms and languages</summary>
39 + PlatformsAndLanguages = 7,
40 +
41 + /// <summary>PID_LASTAUTHOR should be null for packages</summary>
42 + Reserved8 = 8,
43 +
44 + /// <summary>PID_REVNUMBER = GUID package code</summary>
45 + PackageCode = 9,
46 +
47 + /// <summary>PID_LASTPRINTED should be null for packages</summary>
48 + Reserved11 = 11,
49 +
50 + /// <summary>PID_CREATED datetime when package was created</summary>
51 + Created = 12,
52 +
53 + /// <summary>PID_SAVED datetime when package was last modified</summary>
54 + LastModified = 13,
55 +
56 + /// <summary>PID_PAGECOUNT minimum required Windows Installer</summary>
57 + InstallerRequirement = 14,
58 +
59 + /// <summary>PID_WORDCOUNT elevation privileges of package</summary>
60 + FileAndElevatedFlags = 15,
61 +
62 + /// <summary>PID_CHARCOUNT should be null for patches</summary>
63 + Reserved16 = 16,
64 +
65 + /// <summary>PID_APPLICATION tool used to create package</summary>
66 + BuildTool = 18,
67 +
68 + /// <summary>PID_SECURITY = read-only attribute of the package</summary>
69 + Security = 19,
70 + }
71 +
72 /// <summary>
73 /// Summary information properties for transforms.
74 /// </summary>
@@ -140,7 +197,7 @@ namespace WixToolset.Core.Native.Msi
197 {
198 if (null == db)
199 {
143 - throw new ArgumentNullException("db");
200 + throw new ArgumentNullException(nameof(db));
201 }
202
203 uint handle = 0;
@@ -160,7 +217,7 @@ namespace WixToolset.Core.Native.Msi
217 {
218 if (null == databaseFile)
219 {
163 - throw new ArgumentNullException("databaseFile");
220 + throw new ArgumentNullException(nameof(databaseFile));
221 }
222
223 uint handle = 0;
@@ -173,49 +230,125 @@ namespace WixToolset.Core.Native.Msi
230 }
231
232 /// <summary>
176 - /// Gets a summary information property.
233 + /// Gets a summary information package property.
234 /// </summary>
178 - /// <param name="index">Index of the summary information property.</param>
235 + /// <param name="property">The summary information package property.</param>
236 /// <returns>The summary information property.</returns>
180 - public string GetProperty(int index)
237 + public string GetProperty(Package property)
238 {
182 - var bufSize = 64;
183 - var stringValue = new StringBuilder(bufSize);
239 + return this.GetProperty((int)property);
240 + }
241
185 - FILETIME timeValue;
186 - timeValue.dwHighDateTime = 0;
187 - timeValue.dwLowDateTime = 0;
242 + /// <summary>
243 + /// Gets a summary information package property as a number.
244 + /// </summary>
245 + /// <param name="property">The summary information package property.</param>
246 + /// <returns>The summary information property.</returns>
247 + public long GetNumericProperty(Package property)
248 + {
249 + return this.GetNumericProperty((int)property);
250 + }
251
189 - var error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out var dataType, out var intValue, ref timeValue, stringValue, ref bufSize);
190 - if (234 == error)
191 - {
192 - stringValue.EnsureCapacity(++bufSize);
193 - error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
194 - }
252 + /// <summary>
253 + /// Gets a summary information patch property.
254 + /// </summary>
255 + /// <param name="property">The summary information patch property.</param>
256 + /// <returns>The summary information property.</returns>
257 + public string GetProperty(Patch property)
258 + {
259 + return this.GetProperty((int)property);
260 + }
261
196 - if (0 != error)
197 - {
198 - throw new MsiException(error);
199 - }
262 + /// <summary>
263 + /// Gets a summary information transform property.
264 + /// </summary>
265 + /// <param name="property">The summary information transform property.</param>
266 + /// <returns>The summary information property.</returns>
267 + public long GetNumericProperty(Transform property)
268 + {
269 + return this.GetNumericProperty((int)property);
270 + }
271 +
272 + /// <summary>
273 + /// Gets a summary information property.
274 + /// </summary>
275 + /// <param name="index">Index of the summary information property.</param>
276 + /// <returns>The summary information property.</returns>
277 + public string GetProperty(int index)
278 + {
279 + this.GetSummaryInformationValue(index, out var dataType, out var intValue, out var stringValue, out var timeValue);
280
281 switch ((VT)dataType)
282 {
283 case VT.EMPTY:
284 return String.Empty;
285 +
286 case VT.LPSTR:
287 return stringValue.ToString();
288 +
289 case VT.I2:
290 case VT.I4:
291 return Convert.ToString(intValue, CultureInfo.InvariantCulture);
292 +
293 case VT.FILETIME:
294 var longFileTime = (((long)timeValue.dwHighDateTime) << 32) | unchecked((uint)timeValue.dwLowDateTime);
295 var dateTime = DateTime.FromFileTime(longFileTime);
296 return dateTime.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture);
297 +
298 default:
299 throw new InvalidOperationException();
300 }
301 }
302
303 + /// <summary>
304 + /// Gets a summary information property as a number.
305 + /// </summary>
306 + /// <param name="index">Index of the summary information property.</param>
307 + /// <returns>The summary information property.</returns>
308 + public long GetNumericProperty(int index)
309 + {
310 + this.GetSummaryInformationValue(index, out var dataType, out var intValue, out var stringValue, out var timeValue);
311 +
312 + switch ((VT)dataType)
313 + {
314 + case VT.EMPTY:
315 + return 0;
316 +
317 + case VT.LPSTR:
318 + return Int64.Parse(stringValue.ToString(), CultureInfo.InvariantCulture);
319 +
320 + case VT.I2:
321 + case VT.I4:
322 + return intValue;
323 +
324 + case VT.FILETIME:
325 + return (((long)timeValue.dwHighDateTime) << 32) | unchecked((uint)timeValue.dwLowDateTime);
326 +
327 + default:
328 + throw new InvalidOperationException();
329 + }
330 + }
331 +
332 + private void GetSummaryInformationValue(int index, out uint dataType, out int intValue, out StringBuilder stringValue, out FILETIME timeValue)
333 + {
334 + var bufSize = 64;
335 + stringValue = new StringBuilder(bufSize);
336 + timeValue.dwHighDateTime = 0;
337 + timeValue.dwLowDateTime = 0;
338 +
339 + var error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
340 + if (234 == error)
341 + {
342 + stringValue.EnsureCapacity(++bufSize);
343 + error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
344 + }
345 +
346 + if (0 != error)
347 + {
348 + throw new MsiException(error);
349 + }
350 + }
351 +
352 /// <summary>
353 /// Variant types in the summary information table.
354 /// </summary>