1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
|
// 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.
namespace WixToolset.Msi
{
using System;
using System.Diagnostics;
using System.Text;
using WixToolset.Core.Native;
/// <summary>
/// Windows Installer message types.
/// </summary>
[Flags]
internal enum InstallMessage
{
/// <summary>
/// Premature termination, possibly fatal out of memory.
/// </summary>
FatalExit = 0x00000000,
/// <summary>
/// Formatted error message, [1] is message number in Error table.
/// </summary>
Error = 0x01000000,
/// <summary>
/// Formatted warning message, [1] is message number in Error table.
/// </summary>
Warning = 0x02000000,
/// <summary>
/// User request message, [1] is message number in Error table.
/// </summary>
User = 0x03000000,
/// <summary>
/// Informative message for log, not to be displayed.
/// </summary>
Info = 0x04000000,
/// <summary>
/// List of files in use that need to be replaced.
/// </summary>
FilesInUse = 0x05000000,
/// <summary>
/// Request to determine a valid source location.
/// </summary>
ResolveSource = 0x06000000,
/// <summary>
/// Insufficient disk space message.
/// </summary>
OutOfDiskSpace = 0x07000000,
/// <summary>
/// Progress: start of action, [1] action name, [2] description, [3] template for ACTIONDATA messages.
/// </summary>
ActionStart = 0x08000000,
/// <summary>
/// Action data. Record fields correspond to the template of ACTIONSTART message.
/// </summary>
ActionData = 0x09000000,
/// <summary>
/// Progress bar information. See the description of record fields below.
/// </summary>
Progress = 0x0A000000,
/// <summary>
/// To enable the Cancel button set [1] to 2 and [2] to 1. To disable the Cancel button set [1] to 2 and [2] to 0.
/// </summary>
CommonData = 0x0B000000,
/// <summary>
/// Sent prior to UI initialization, no string data.
/// </summary>
Initilize = 0x0C000000,
/// <summary>
/// Sent after UI termination, no string data.
/// </summary>
Terminate = 0x0D000000,
/// <summary>
/// Sent prior to display or authored dialog or wizard.
/// </summary>
ShowDialog = 0x0E000000
}
/// <summary>
/// Windows Installer log modes.
/// </summary>
[Flags]
internal enum InstallLogModes
{
/// <summary>
/// Premature termination of installation.
/// </summary>
FatalExit = (1 << ((int)InstallMessage.FatalExit >> 24)),
/// <summary>
/// The error messages are logged.
/// </summary>
Error = (1 << ((int)InstallMessage.Error >> 24)),
/// <summary>
/// The warning messages are logged.
/// </summary>
Warning = (1 << ((int)InstallMessage.Warning >> 24)),
/// <summary>
/// The user requests are logged.
/// </summary>
User = (1 << ((int)InstallMessage.User >> 24)),
/// <summary>
/// The status messages that are not displayed are logged.
/// </summary>
Info = (1 << ((int)InstallMessage.Info >> 24)),
/// <summary>
/// Request to determine a valid source location.
/// </summary>
ResolveSource = (1 << ((int)InstallMessage.ResolveSource >> 24)),
/// <summary>
/// The was insufficient disk space.
/// </summary>
OutOfDiskSpace = (1 << ((int)InstallMessage.OutOfDiskSpace >> 24)),
/// <summary>
/// The start of new installation actions are logged.
/// </summary>
ActionStart = (1 << ((int)InstallMessage.ActionStart >> 24)),
/// <summary>
/// The data record with the installation action is logged.
/// </summary>
ActionData = (1 << ((int)InstallMessage.ActionData >> 24)),
/// <summary>
/// The parameters for user-interface initialization are logged.
/// </summary>
CommonData = (1 << ((int)InstallMessage.CommonData >> 24)),
/// <summary>
/// Logs the property values at termination.
/// </summary>
PropertyDump = (1 << ((int)InstallMessage.Progress >> 24)),
/// <summary>
/// Sends large amounts of information to a log file not generally useful to users.
/// May be used for technical support.
/// </summary>
Verbose = (1 << ((int)InstallMessage.Initilize >> 24)),
/// <summary>
/// Sends extra debugging information, such as handle creation information, to the log file.
/// </summary>
ExtraDebug = (1 << ((int)InstallMessage.Terminate >> 24)),
/// <summary>
/// Progress bar information. This message includes information on units so far and total number of units.
/// See MsiProcessMessage for an explanation of the message format.
/// This message is only sent to an external user interface and is not logged.
/// </summary>
Progress = (1 << ((int)InstallMessage.Progress >> 24)),
/// <summary>
/// If this is not a quiet installation, then the basic UI has been initialized.
/// If this is a full UI installation, the full UI is not yet initialized.
/// This message is only sent to an external user interface and is not logged.
/// </summary>
Initialize = (1 << ((int)InstallMessage.Initilize >> 24)),
/// <summary>
/// If a full UI is being used, the full UI has ended.
/// If this is not a quiet installation, the basic UI has not yet ended.
/// This message is only sent to an external user interface and is not logged.
/// </summary>
Terminate = (1 << ((int)InstallMessage.Terminate >> 24)),
/// <summary>
/// Sent prior to display of the full UI dialog.
/// This message is only sent to an external user interface and is not logged.
/// </summary>
ShowDialog = (1 << ((int)InstallMessage.ShowDialog >> 24)),
/// <summary>
/// Files in use information. When this message is received, a FilesInUse Dialog should be displayed.
/// </summary>
FilesInUse = (1 << ((int)InstallMessage.FilesInUse >> 24))
}
/// <summary>
/// Windows Installer UI levels.
/// </summary>
[Flags]
internal enum InstallUILevels
{
/// <summary>
/// No change in the UI level. However, if phWnd is not Null, the parent window can change.
/// </summary>
NoChange = 0,
/// <summary>
/// The installer chooses an appropriate user interface level.
/// </summary>
Default = 1,
/// <summary>
/// Completely silent installation.
/// </summary>
None = 2,
/// <summary>
/// Simple progress and error handling.
/// </summary>
Basic = 3,
/// <summary>
/// Authored user interface with wizard dialog boxes suppressed.
/// </summary>
Reduced = 4,
/// <summary>
/// Authored user interface with wizards, progress, and errors.
/// </summary>
Full = 5,
/// <summary>
/// If combined with the Basic value, the installer shows simple progress dialog boxes but
/// does not display a Cancel button on the dialog. This prevents users from canceling the install.
/// Available with Windows Installer version 2.0.
/// </summary>
HideCancel = 0x20,
/// <summary>
/// If combined with the Basic value, the installer shows simple progress
/// dialog boxes but does not display any modal dialog boxes or error dialog boxes.
/// </summary>
ProgressOnly = 0x40,
/// <summary>
/// If combined with any above value, the installer displays a modal dialog
/// box at the end of a successful installation or if there has been an error.
/// No dialog box is displayed if the user cancels.
/// </summary>
EndDialog = 0x80,
/// <summary>
/// If this value is combined with the None value, the installer displays only the dialog
/// boxes used for source resolution. No other dialog boxes are shown. This value has no
/// effect if the UI level is not INSTALLUILEVEL_NONE. It is used with an external user
/// interface designed to handle all of the UI except for source resolution. In this case,
/// the installer handles source resolution. This value is only available with Windows Installer 2.0 and later.
/// </summary>
SourceResOnly = 0x100
}
/// <summary>
/// Represents the Windows Installer, provides wrappers to
/// create the top-level objects and access their methods.
/// </summary>
internal sealed class Installer
{
/// <summary>
/// Protect the constructor.
/// </summary>
private Installer()
{
}
/// <summary>
/// Takes the path to a file and returns a 128-bit hash of that file.
/// </summary>
/// <param name="filePath">Path to file that is to be hashed.</param>
/// <param name="options">The value in this column must be 0. This parameter is reserved for future use.</param>
/// <param name="hash">Int array that receives the returned file hash information.</param>
internal static void GetFileHash(string filePath, int options, out int[] hash)
{
MsiInterop.MSIFILEHASHINFO hashInterop = new MsiInterop.MSIFILEHASHINFO();
hashInterop.FileHashInfoSize = 20;
int error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), hashInterop);
if (0 != error)
{
throw new MsiException(error);
}
Debug.Assert(20 == hashInterop.FileHashInfoSize);
hash = new int[4];
hash[0] = hashInterop.Data0;
hash[1] = hashInterop.Data1;
hash[2] = hashInterop.Data2;
hash[3] = hashInterop.Data3;
}
/// <summary>
/// Returns the version string and language string in the format that the installer
/// expects to find them in the database. If you just want version information, set
/// lpLangBuf and pcchLangBuf to zero. If you just want language information, set
/// lpVersionBuf and pcchVersionBuf to zero.
/// </summary>
/// <param name="filePath">Specifies the path to the file.</param>
/// <param name="version">Returns the file version. Set to 0 for language information only.</param>
/// <param name="language">Returns the file language. Set to 0 for version information only.</param>
internal static void GetFileVersion(string filePath, out string version, out string language)
{
int versionLength = 20;
int languageLength = 20;
StringBuilder versionBuffer = new StringBuilder(versionLength);
StringBuilder languageBuffer = new StringBuilder(languageLength);
int error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength);
if (234 == error)
{
versionBuffer.EnsureCapacity(++versionLength);
languageBuffer.EnsureCapacity(++languageLength);
error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength);
}
else if (1006 == error)
{
// file has no version or language, so no error
error = 0;
}
if (0 != error)
{
throw new MsiException(error);
}
version = versionBuffer.ToString();
language = languageBuffer.ToString();
}
/// <summary>
/// Enables an external user-interface handler.
/// </summary>
/// <param name="installUIHandler">Specifies a callback function.</param>
/// <param name="messageFilter">Specifies which messages to handle using the external message handler.</param>
/// <param name="context">Pointer to an application context that is passed to the callback function.</param>
/// <returns>The return value is the previously set external handler, or null if there was no previously set handler.</returns>
internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context)
{
return MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context);
}
/// <summary>
/// Enables the installer's internal user interface.
/// </summary>
/// <param name="uiLevel">Specifies the level of complexity of the user interface.</param>
/// <param name="hwnd">Pointer to a window. This window becomes the owner of any user interface created.</param>
/// <returns>The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned.</returns>
internal static int SetInternalUI(int uiLevel, ref IntPtr hwnd)
{
return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd);
}
/// <summary>
/// Get the source/target and short/long file names from an MSI Filename column.
/// </summary>
/// <param name="value">The Filename value.</param>
/// <returns>An array of strings of length 4. The contents are: short target, long target, short source, and long source.</returns>
/// <remarks>
/// If any particular file name part is not parsed, its set to null in the appropriate location of the returned array of strings.
/// However, the returned array will always be of length 4.
/// </remarks>
internal static string[] GetNames(string value)
{
string[] names = new string[4];
int targetSeparator = value.IndexOf(":", StringComparison.Ordinal);
// split source and target
string sourceName = null;
string targetName = value;
if (0 <= targetSeparator)
{
sourceName = value.Substring(targetSeparator + 1);
targetName = value.Substring(0, targetSeparator);
}
// split the source short and long names
string sourceLongName = null;
if (null != sourceName)
{
int sourceLongNameSeparator = sourceName.IndexOf("|", StringComparison.Ordinal);
if (0 <= sourceLongNameSeparator)
{
sourceLongName = sourceName.Substring(sourceLongNameSeparator + 1);
sourceName = sourceName.Substring(0, sourceLongNameSeparator);
}
}
// split the target short and long names
int targetLongNameSeparator = targetName.IndexOf("|", StringComparison.Ordinal);
string targetLongName = null;
if (0 <= targetLongNameSeparator)
{
targetLongName = targetName.Substring(targetLongNameSeparator + 1);
targetName = targetName.Substring(0, targetLongNameSeparator);
}
// remove the long source name when its identical to the long source name
if (null != sourceName && sourceName == sourceLongName)
{
sourceLongName = null;
}
// remove the long target name when its identical to the long target name
if (null != targetName && targetName == targetLongName)
{
targetLongName = null;
}
// remove the source names when they are identical to the target names
if (sourceName == targetName && sourceLongName == targetLongName)
{
sourceName = null;
sourceLongName = null;
}
// target name(s)
if ("." != targetName)
{
names[0] = targetName;
}
if (null != targetLongName && "." != targetLongName)
{
names[1] = targetLongName;
}
// source name(s)
if (null != sourceName)
{
names[2] = sourceName;
}
if (null != sourceLongName && "." != sourceLongName)
{
names[3] = sourceLongName;
}
return names;
}
/// <summary>
/// Get a source/target and short/long file name from an MSI Filename column.
/// </summary>
/// <param name="value">The Filename value.</param>
/// <param name="source">true to get a source name; false to get a target name</param>
/// <param name="longName">true to get a long name; false to get a short name</param>
/// <returns>The name.</returns>
internal static string GetName(string value, bool source, bool longName)
{
string[] names = GetNames(value);
if (source)
{
if (longName && null != names[3])
{
return names[3];
}
else if (null != names[2])
{
return names[2];
}
}
if (longName && null != names[1])
{
return names[1];
}
else
{
return names[0];
}
}
}
}
|