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>