Support merging merge modules
Rob Mensching committed
May 22, 2020 at 14:53 UTC
6b30680fd7a712b45538c3f0a89d652f0457a893
12 files changed
+105
-513
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+3
-2
@@ -505,7 +505,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
505
// necessary.
506
foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
507
{
508
- var sequenceTableName = sequence.ToString();
508
+ var sequenceTableName = (sequence == SequenceTable.AdvertiseExecuteSequence) ? "AdvtExecuteSequence" : sequence.ToString();
509
var sequenceTable = output.Tables[sequenceTableName];
510
511
if (null == sequenceTable)
@@ -519,8 +519,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
519
}
520
}
521
522
- var command = new MergeModulesCommand();
522
+ var command = new MergeModulesCommand(this.Messaging);
523
command.FileFacades = fileFacades;
524
+ command.IntermediateFolder = this.IntermediateFolder;
525
command.Output = output;
526
command.OutputPath = this.OutputPath;
527
command.SuppressedTableNames = suppressedTableNames;
src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
+1
@@ -5,6 +5,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5
using System;
6
using System.Collections;
7
using System.Globalization;
8
+ using WixToolset.Core.Native;
9
using WixToolset.Core.WindowsInstaller.Msi;
10
11
/// <summary>
src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+2
-1
@@ -48,7 +48,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
48
{
49
var mergeModulesFileFacades = new List<FileFacade>();
50
51
- var merge = MsmInterop.GetMsmMerge();
51
+ var interop = new MsmInterop();
52
+ var merge = interop.GetMsmMerge();
53
54
// Index all of the file rows to be able to detect collisions with files in the Merge Modules.
55
// It may seem a bit expensive to build up this index solely for the purpose of checking collisions
src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+8
-2
@@ -9,9 +9,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9
using System.Runtime.InteropServices;
10
using System.Text;
11
using WixToolset.Core.Bind;
12
+ using WixToolset.Core.Native;
13
using WixToolset.Core.WindowsInstaller.Msi;
14
using WixToolset.Data;
14
- using WixToolset.Data.Tuples;
15
using WixToolset.Data.WindowsInstaller;
16
using WixToolset.Data.WindowsInstaller.Rows;
17
using WixToolset.Extensibility.Services;
@@ -21,6 +21,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
21
/// </summary>
22
internal class MergeModulesCommand
23
{
24
+ public MergeModulesCommand(IMessaging messaging)
25
+ {
26
+ this.Messaging = messaging;
27
+ }
28
+
29
public IEnumerable<FileFacade> FileFacades { private get; set; }
30
31
public IMessaging Messaging { private get; set; }
@@ -51,7 +56,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
56
string logPath = null;
57
try
58
{
54
- merge = MsmInterop.GetMsmMerge();
59
+ var interop = new MsmInterop();
60
+ merge = interop.GetMsmMerge();
61
62
logPath = Path.Combine(this.IntermediateFolder, "merge.log");
63
merge.OpenLog(logPath);
src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs
+1
-1
@@ -74,7 +74,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
74
75
patchGroup.Add(facade);
76
}
77
- else
77
+ else if (!facade.FromModule)
78
{
79
var fileRow = fileRows.Get(facade.Id);
80
fileRow.Sequence = ++lastSequence;
src/WixToolset.Core.WindowsInstaller/Msi/MsmInterop.cs
deleted
-505
@@ -1,505 +0,0 @@
1
-// 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.
2
-
3
-namespace WixToolset.Core.WindowsInstaller.Msi
4
-{
5
- using System;
6
- using System.Runtime.InteropServices;
7
-
8
- /// <summary>
9
- /// Errors returned by merge operations.
10
- /// </summary>
11
- [Guid("0ADDA825-2C26-11D2-AD65-00A0C9AF11A6")]
12
- public enum MsmErrorType
13
- {
14
- /// <summary>
15
- /// A request was made to open a module with a language not supported by the module.
16
- /// No more general language is supported by the module.
17
- /// Adds msmErrorLanguageUnsupported to the Type property and the requested language
18
- /// to the Language Property (Error Object). All Error object properties are empty.
19
- /// The OpenModule function returns ERROR_INSTALL_LANGUAGE_UNSUPPORTED (as HRESULT).
20
- /// </summary>
21
- msmErrorLanguageUnsupported = 1,
22
-
23
- /// <summary>
24
- /// A request was made to open a module with a supported language but the module has
25
- /// an invalid language transform. Adds msmErrorLanguageFailed to the Type property
26
- /// and the applied transform's language to the Language Property of the Error object.
27
- /// This may not be the requested language if a more general language was used.
28
- /// All other properties of the Error object are empty. The OpenModule function
29
- /// returns ERROR_INSTALL_LANGUAGE_UNSUPPORTED (as HRESULT).
30
- /// </summary>
31
- msmErrorLanguageFailed = 2,
32
-
33
- /// <summary>
34
- /// The module cannot be merged because it excludes, or is excluded by, another module
35
- /// in the database. Adds msmErrorExclusion to the Type property of the Error object.
36
- /// The ModuleKeys property or DatabaseKeys property contains the primary keys of the
37
- /// excluded module's row in the ModuleExclusion table. If an existing module excludes
38
- /// the module being merged, the excluded module's ModuleSignature information is added
39
- /// to ModuleKeys. If the module being merged excludes an existing module, DatabaseKeys
40
- /// contains the excluded module's ModuleSignature information. All other properties
41
- /// are empty (or -1).
42
- /// </summary>
43
- msmErrorExclusion = 3,
44
-
45
- /// <summary>
46
- /// Merge conflict during merge. The value of the Type property is set to
47
- /// msmErrorTableMerge. The DatabaseTable property and DatabaseKeys property contain
48
- /// the table name and primary keys of the conflicting row in the database. The
49
- /// ModuleTable property and ModuleKeys property contain the table name and primary keys
50
- /// of the conflicting row in the module. The ModuleTable and ModuleKeys entries may be
51
- /// null if the row does not exist in the database. For example, if the conflict is in a
52
- /// generated FeatureComponents table entry. On Windows Installer version 2.0, when
53
- /// merging a configurable merge module, configuration may cause these properties to
54
- /// refer to rows that do not exist in the module.
55
- /// </summary>
56
- msmErrorTableMerge = 4,
57
-
58
- /// <summary>
59
- /// There was a problem resequencing a sequence table to contain the necessary merged
60
- /// actions. The Type property is set to msmErrorResequenceMerge. The DatabaseTable
61
- /// and DatabaseKeys properties contain the sequence table name and primary keys
62
- /// (action name) of the conflicting row. The ModuleTable and ModuleKeys properties
63
- /// contain the sequence table name and primary key (action name) of the conflicting row.
64
- /// On Windows Installer version 2.0, when merging a configurable merge module,
65
- /// configuration may cause these properties to refer to rows that do not exist in the module.
66
- /// </summary>
67
- msmErrorResequenceMerge = 5,
68
-
69
- /// <summary>
70
- /// Not used.
71
- /// </summary>
72
- msmErrorFileCreate = 6,
73
-
74
- /// <summary>
75
- /// There was a problem creating a directory to extract a file to disk. The Path property
76
- /// contains the directory that could not be created. All other properties are empty or -1.
77
- /// Not available with Windows Installer version 1.0.
78
- /// </summary>
79
- msmErrorDirCreate = 7,
80
-
81
- /// <summary>
82
- /// A feature name is required to complete the merge, but no feature name was provided.
83
- /// The Type property is set to msmErrorFeatureRequired. The DatabaseTable and DatabaseKeys
84
- /// contain the table name and primary keys of the conflicting row. The ModuleTable and
85
- /// ModuleKeys properties contain the table name and primary keys of the row cannot be merged.
86
- /// On Windows Installer version 2.0, when merging a configurable merge module, configuration
87
- /// may cause these properties to refer to rows that do not exist in the module.
88
- /// If the failure is in a generated FeatureComponents table, the DatabaseTable and
89
- /// DatabaseKeys properties are empty and the ModuleTable and ModuleKeys properties refer to
90
- /// the row in the Component table causing the failure.
91
- /// </summary>
92
- msmErrorFeatureRequired = 8,
93
-
94
- /// <summary>
95
- /// Available with Window Installer version 2.0. Substitution of a Null value into a
96
- /// non-nullable column. This enters msmErrorBadNullSubstitution in the Type property and
97
- /// enters "ModuleSubstitution" and the keys from the ModuleSubstitution table for this row
98
- /// into the ModuleTable property and ModuleKeys property. All other properties of the Error
99
- /// object are set to an empty string or -1. This error causes the immediate failure of the
100
- /// merge and the MergeEx function to return E_FAIL.
101
- /// </summary>
102
- msmErrorBadNullSubstitution = 9,
103
-
104
- /// <summary>
105
- /// Available with Window Installer version 2.0. Substitution of Text Format Type or Integer
106
- /// Format Type into a Binary Type data column. This type of error returns
107
- /// msmErrorBadSubstitutionType in the Type property and enters "ModuleSubstitution" and the
108
- /// keys from the ModuleSubstitution table for this row into the ModuleTable property.
109
- /// All other properties of the Error object are set to an empty string or -1. This error
110
- /// causes the immediate failure of the merge and the MergeEx function to return E_FAIL.
111
- /// </summary>
112
- msmErrorBadSubstitutionType = 10,
113
-
114
- /// <summary>
115
- /// Available with Window Installer Version 2.0. A row in the ModuleSubstitution table
116
- /// references a configuration item not defined in the ModuleConfiguration table.
117
- /// This type of error returns msmErrorMissingConfigItem in the Type property and enters
118
- /// "ModuleSubstitution" and the keys from the ModuleSubstitution table for this row into
119
- /// the ModuleTable property. All other properties of the Error object are set to an empty
120
- /// string or -1. This error causes the immediate failure of the merge and the MergeEx
121
- /// function to return E_FAIL.
122
- /// </summary>
123
- msmErrorMissingConfigItem = 11,
124
-
125
- /// <summary>
126
- /// Available with Window Installer version 2.0. The authoring tool has returned a Null
127
- /// value for an item marked with the msmConfigItemNonNullable attribute. An error of this
128
- /// type returns msmErrorBadNullResponse in the Type property and enters "ModuleSubstitution"
129
- /// and the keys from the ModuleSubstitution table for for the item into the ModuleTable property.
130
- /// All other properties of the Error object are set to an empty string or -1. This error
131
- /// causes the immediate failure of the merge and the MergeEx function to return E_FAIL.
132
- /// </summary>
133
- msmErrorBadNullResponse = 12,
134
-
135
- /// <summary>
136
- /// Available with Window Installer version 2.0. The authoring tool returned a failure code
137
- /// (not S_OK or S_FALSE) when asked for data. An error of this type will return
138
- /// msmErrorDataRequestFailed in the Type property and enters "ModuleSubstitution"
139
- /// and the keys from the ModuleSubstitution table for the item into the ModuleTable property.
140
- /// All other properties of the Error object are set to an empty string or -1. This error
141
- /// causes the immediate failure of the merge and the MergeEx function to return E_FAIL.
142
- /// </summary>
143
- msmErrorDataRequestFailed = 13,
144
-
145
- /// <summary>
146
- /// Available with Windows Installer 2.0 and later versions. Indicates that an attempt was
147
- /// made to merge a 64-bit module into a package that was not a 64-bit package. An error of
148
- /// this type returns msmErrorPlatformMismatch in the Type property. All other properties of
149
- /// the error object are set to an empty string or -1. This error causes the immediate failure
150
- /// of the merge and causes the Merge function or MergeEx function to return E_FAIL.
151
- /// </summary>
152
- msmErrorPlatformMismatch = 14,
153
- }
154
-
155
- /// <summary>
156
- /// IMsmMerge2 interface.
157
- /// </summary>
158
- [ComImport, Guid("351A72AB-21CB-47ab-B7AA-C4D7B02EA305")]
159
- public interface IMsmMerge2
160
- {
161
- /// <summary>
162
- /// The OpenDatabase method of the Merge object opens a Windows Installer installation
163
- /// database, located at a specified path, that is to be merged with a module.
164
- /// </summary>
165
- /// <param name="path">Path to the database being opened.</param>
166
- void OpenDatabase(string path);
167
-
168
- /// <summary>
169
- /// The OpenModule method of the Merge object opens a Windows Installer merge module
170
- /// in read-only mode. A module must be opened before it can be merged with an installation database.
171
- /// </summary>
172
- /// <param name="fileName">Fully qualified file name pointing to a merge module.</param>
173
- /// <param name="language">A valid language identifier (LANGID).</param>
174
- void OpenModule(string fileName, short language);
175
-
176
- /// <summary>
177
- /// The CloseDatabase method of the Merge object closes the currently open Windows Installer database.
178
- /// </summary>
179
- /// <param name="commit">true if changes should be saved, false otherwise.</param>
180
- void CloseDatabase(bool commit);
181
-
182
- /// <summary>
183
- /// The CloseModule method of the Merge object closes the currently open Windows Installer merge module.
184
- /// </summary>
185
- void CloseModule();
186
-
187
- /// <summary>
188
- /// The OpenLog method of the Merge object opens a log file that receives progress and error messages.
189
- /// If the log file already exists, the installer appends new messages. If the log file does not exist,
190
- /// the installer creates a log file.
191
- /// </summary>
192
- /// <param name="fileName">Fully qualified filename pointing to a file to open or create.</param>
193
- void OpenLog(string fileName);
194
-
195
- /// <summary>
196
- /// The CloseLog method of the Merge object closes the current log file.
197
- /// </summary>
198
- void CloseLog();
199
-
200
- /// <summary>
201
- /// The Log method of the Merge object writes a text string to the currently open log file.
202
- /// </summary>
203
- /// <param name="message">The text string to display.</param>
204
- void Log(string message);
205
-
206
- /// <summary>
207
- /// Gets the errors from the last merge operation.
208
- /// </summary>
209
- /// <value>The errors from the last merge operation.</value>
210
- IMsmErrors Errors
211
- {
212
- get;
213
- }
214
-
215
- /// <summary>
216
- /// Gets a collection of Dependency objects that enumerates a set of unsatisfied dependencies for the current database.
217
- /// </summary>
218
- /// <value>A collection of Dependency objects that enumerates a set of unsatisfied dependencies for the current database.</value>
219
- object Dependencies
220
- {
221
- get;
222
- }
223
-
224
- /// <summary>
225
- /// The Merge method of the Merge object executes a merge of the current database and current
226
- /// module. The merge attaches the components in the module to the feature identified by Feature.
227
- /// The root of the module's directory tree is redirected to the location given by RedirectDir.
228
- /// </summary>
229
- /// <param name="feature">The name of a feature in the database.</param>
230
- /// <param name="redirectDir">The key of an entry in the Directory table of the database.
231
- /// This parameter may be NULL or an empty string.</param>
232
- void Merge(string feature, string redirectDir);
233
-
234
- /// <summary>
235
- /// The Connect method of the Merge object connects a module to an additional feature.
236
- /// The module must have already been merged into the database or will be merged into the database.
237
- /// The feature must exist before calling this function.
238
- /// </summary>
239
- /// <param name="feature">The name of a feature already existing in the database.</param>
240
- void Connect(string feature);
241
-
242
- /// <summary>
243
- /// The ExtractCAB method of the Merge object extracts the embedded .cab file from a module and
244
- /// saves it as the specified file. The installer creates this file if it does not already exist
245
- /// and overwritten if it does exist.
246
- /// </summary>
247
- /// <param name="fileName">The fully qualified destination file.</param>
248
- void ExtractCAB(string fileName);
249
-
250
- /// <summary>
251
- /// The ExtractFiles method of the Merge object extracts the embedded .cab file from a module
252
- /// and then writes those files to the destination directory.
253
- /// </summary>
254
- /// <param name="path">The fully qualified destination directory.</param>
255
- void ExtractFiles(string path);
256
-
257
- /// <summary>
258
- /// The MergeEx method of the Merge object is equivalent to the Merge function, except that it
259
- /// takes an extra argument. The Merge method executes a merge of the current database and
260
- /// current module. The merge attaches the components in the module to the feature identified
261
- /// by Feature. The root of the module's directory tree is redirected to the location given by RedirectDir.
262
- /// </summary>
263
- /// <param name="feature">The name of a feature in the database.</param>
264
- /// <param name="redirectDir">The key of an entry in the Directory table of the database. This parameter may
265
- /// be NULL or an empty string.</param>
266
- /// <param name="configuration">The pConfiguration argument is an interface implemented by the client. The argument may
267
- /// be NULL. The presence of this argument indicates that the client is capable of supporting the configuration
268
- /// functionality, but does not obligate the client to provide configuration data for any specific configurable item.</param>
269
- void MergeEx(string feature, string redirectDir, IMsmConfigureModule configuration);
270
-
271
- /// <summary>
272
- /// The ExtractFilesEx method of the Merge object extracts the embedded .cab file from a module and
273
- /// then writes those files to the destination directory.
274
- /// </summary>
275
- /// <param name="path">The fully qualified destination directory.</param>
276
- /// <param name="longFileNames">Set to specify using long file names for path segments and final file names.</param>
277
- /// <param name="filePaths">This is a list of fully-qualified paths for the files that were successfully extracted.
278
- /// The list is empty if no files can be extracted. This argument may be null. No list is provided if pFilePaths is null.</param>
279
- void ExtractFilesEx(string path, bool longFileNames, ref IntPtr filePaths);
280
-
281
- /// <summary>
282
- /// Gets a collection ConfigurableItem objects, each of which represents a single row from the ModuleConfiguration table.
283
- /// </summary>
284
- /// <value>A collection ConfigurableItem objects, each of which represents a single row from the ModuleConfiguration table.</value>
285
- /// <remarks>Semantically, each interface in the enumerator represents an item that can be configured by the module consumer.
286
- /// The collection is a read-only collection and implements the standard read-only collection interfaces of Item(), Count() and _NewEnum().
287
- /// The IEnumMsmConfigItems enumerator implements Next(), Skip(), Reset(), and Clone() with the standard semantics.</remarks>
288
- object ConfigurableItems
289
- {
290
- get;
291
- }
292
-
293
- /// <summary>
294
- /// The CreateSourceImage method of the Merge object allows the client to extract the files from a module to
295
- /// a source image on disk after a merge, taking into account changes to the module that might have been made
296
- /// during module configuration. The list of files to be extracted is taken from the file table of the module
297
- /// during the merge process. The list of files consists of every file successfully copied from the file table
298
- /// of the module to the target database. File table entries that were not copied due to primary key conflicts
299
- /// with existing rows in the database are not a part of this list. At image creation time, the directory for
300
- /// each of these files comes from the open (post-merge) database. The path specified in the Path parameter is
301
- /// the root of the source image for the install. fLongFileNames determines whether or not long file names are
302
- /// used for both path segments and final file names. The function fails if no database is open, no module is
303
- /// open, or no merge has been performed.
304
- /// </summary>
305
- /// <param name="path">The path of the root of the source image for the install.</param>
306
- /// <param name="longFileNames">Determines whether or not long file names are used for both path segments and final file names. </param>
307
- /// <param name="filePaths">This is a list of fully-qualified paths for the files that were successfully extracted.
308
- /// The list is empty if no files can be extracted. This argument may be null. No list is provided if pFilePaths is null.</param>
309
- void CreateSourceImage(string path, bool longFileNames, ref IntPtr filePaths);
310
-
311
- /// <summary>
312
- /// The get_ModuleFiles function implements the ModuleFiles property of the GetFiles object. This function
313
- /// returns the primary keys in the File table of the currently open module. The primary keys are returned
314
- /// as a collection of strings. The module must be opened by a call to the OpenModule function before calling get_ModuleFiles.
315
- /// </summary>
316
- IMsmStrings ModuleFiles
317
- {
318
- get;
319
- }
320
- }
321
-
322
- /// <summary>
323
- /// Collection of merge errors.
324
- /// </summary>
325
- [ComImport, Guid("0ADDA82A-2C26-11D2-AD65-00A0C9AF11A6")]
326
- public interface IMsmErrors
327
- {
328
- /// <summary>
329
- /// Gets the IMsmError at the specified index.
330
- /// </summary>
331
- /// <param name="index">The one-based index of the IMsmError to get.</param>
332
- IMsmError this[int index]
333
- {
334
- get;
335
- }
336
-
337
- /// <summary>
338
- /// Gets the count of IMsmErrors in this collection.
339
- /// </summary>
340
- /// <value>The count of IMsmErrors in this collection.</value>
341
- int Count
342
- {
343
- get;
344
- }
345
- }
346
-
347
- /// <summary>
348
- /// A merge error.
349
- /// </summary>
350
- [ComImport, Guid("0ADDA828-2C26-11D2-AD65-00A0C9AF11A6")]
351
- public interface IMsmError
352
- {
353
- /// <summary>
354
- /// Gets the type of merge error.
355
- /// </summary>
356
- /// <value>The type of merge error.</value>
357
- MsmErrorType Type
358
- {
359
- get;
360
- }
361
-
362
- /// <summary>
363
- /// Gets the path information from the merge error.
364
- /// </summary>
365
- /// <value>The path information from the merge error.</value>
366
- string Path
367
- {
368
- get;
369
- }
370
-
371
- /// <summary>
372
- /// Gets the language information from the merge error.
373
- /// </summary>
374
- /// <value>The language information from the merge error.</value>
375
- short Language
376
- {
377
- get;
378
- }
379
-
380
- /// <summary>
381
- /// Gets the database table from the merge error.
382
- /// </summary>
383
- /// <value>The database table from the merge error.</value>
384
- string DatabaseTable
385
- {
386
- get;
387
- }
388
-
389
- /// <summary>
390
- /// Gets the collection of database keys from the merge error.
391
- /// </summary>
392
- /// <value>The collection of database keys from the merge error.</value>
393
- IMsmStrings DatabaseKeys
394
- {
395
- get;
396
- }
397
-
398
- /// <summary>
399
- /// Gets the module table from the merge error.
400
- /// </summary>
401
- /// <value>The module table from the merge error.</value>
402
- string ModuleTable
403
- {
404
- get;
405
- }
406
-
407
- /// <summary>
408
- /// Gets the collection of module keys from the merge error.
409
- /// </summary>
410
- /// <value>The collection of module keys from the merge error.</value>
411
- IMsmStrings ModuleKeys
412
- {
413
- get;
414
- }
415
- }
416
-
417
- /// <summary>
418
- /// A collection of strings.
419
- /// </summary>
420
- [ComImport, Guid("0ADDA827-2C26-11D2-AD65-00A0C9AF11A6")]
421
- public interface IMsmStrings
422
- {
423
- /// <summary>
424
- /// Gets the string at the specified index.
425
- /// </summary>
426
- /// <param name="index">The one-based index of the string to get.</param>
427
- string this[int index]
428
- {
429
- get;
430
- }
431
-
432
- /// <summary>
433
- /// Gets the count of strings in this collection.
434
- /// </summary>
435
- /// <value>The count of strings in this collection.</value>
436
- int Count
437
- {
438
- get;
439
- }
440
- }
441
-
442
- /// <summary>
443
- /// Callback for configurable merge modules.
444
- /// </summary>
445
- [ComImport, Guid("AC013209-18A7-4851-8A21-2353443D70A0"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
446
- public interface IMsmConfigureModule
447
- {
448
- /// <summary>
449
- /// Callback to retrieve text data for configurable merge modules.
450
- /// </summary>
451
- /// <param name="name">Name of the data to be retrieved.</param>
452
- /// <param name="configData">The data corresponding to the name.</param>
453
- /// <returns>The error code (HRESULT).</returns>
454
- [PreserveSig]
455
- int ProvideTextData([In, MarshalAs(UnmanagedType.BStr)] string name, [MarshalAs(UnmanagedType.BStr)] out string configData);
456
-
457
- /// <summary>
458
- /// Callback to retrieve integer data for configurable merge modules.
459
- /// </summary>
460
- /// <param name="name">Name of the data to be retrieved.</param>
461
- /// <param name="configData">The data corresponding to the name.</param>
462
- /// <returns>The error code (HRESULT).</returns>
463
- [PreserveSig]
464
- int ProvideIntegerData([In, MarshalAs(UnmanagedType.BStr)] string name, out int configData);
465
- }
466
-
467
- /// <summary>
468
- /// Merge merge modules into an MSI file.
469
- /// </summary>
470
- [ComImport, Guid("F94985D5-29F9-4743-9805-99BC3F35B678")]
471
- public class MsmMerge2
472
- {
473
- }
474
-
475
- /// <summary>
476
- /// Defines the standard COM IClassFactory interface.
477
- /// </summary>
478
- [ComImport, Guid("00000001-0000-0000-C000-000000000046")]
479
- [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
480
- public interface IClassFactory
481
- {
482
- [return:MarshalAs(UnmanagedType.IUnknown)]
483
- object CreateInstance(IntPtr unkOuter, [MarshalAs(UnmanagedType.LPStruct)] Guid iid);
484
- }
485
-
486
- /// <summary>
487
- /// Contains native methods for merge operations.
488
- /// </summary>
489
- public class MsmInterop
490
- {
491
- [DllImport("mergemod.dll", EntryPoint="DllGetClassObject", PreserveSig=false)]
492
- [return: MarshalAs(UnmanagedType.IUnknown)]
493
- private static extern object MergeModGetClassObject([MarshalAs(UnmanagedType.LPStruct)] Guid clsid, [MarshalAs(UnmanagedType.LPStruct)] Guid iid);
494
-
495
- /// <summary>
496
- /// Load the merge object directly from a local mergemod.dll without going through COM registration.
497
- /// </summary>
498
- /// <returns>Merge interface.</returns>
499
- public static IMsmMerge2 GetMsmMerge()
500
- {
501
- IClassFactory classFactory = (IClassFactory) MergeModGetClassObject(typeof(MsmMerge2).GUID, typeof(IClassFactory).GUID);
502
- return (IMsmMerge2) classFactory.CreateInstance(IntPtr.Zero, typeof(IMsmMerge2).GUID);
503
- }
504
- }
505
-}
src/WixToolset.Core/Linker.cs
+2
-2
@@ -356,7 +356,7 @@ namespace WixToolset.Core
356
case TupleDefinitionType.WixMerge:
357
if (SectionType.Product == resolvedSection.Type)
358
{
359
- this.ResolveFeatures(tuple, 0, 7, modulesToFeatures, null);
359
+ this.ResolveFeatures(tuple, -1, (int)WixMergeTupleFields.FeatureRef, modulesToFeatures, null);
360
}
361
break;
362
@@ -1316,7 +1316,7 @@ namespace WixToolset.Core
1316
/// <param name="multipleFeatureComponents">Hashtable of known components under multiple features.</param>
1317
private void ResolveFeatures(IntermediateTuple tuple, int connectionColumn, int featureColumn, ConnectToFeatureCollection connectToFeatures, Hashtable multipleFeatureComponents)
1318
{
1319
- var connectionId = tuple.AsString(connectionColumn);
1319
+ var connectionId = connectionColumn < 0 ? tuple.Id.Id : tuple.AsString(connectionColumn);
1320
var featureId = tuple.AsString(featureColumn);
1321
1322
if (EmptyGuid == featureId)
src/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs
+50
@@ -8,6 +8,9 @@ namespace WixToolsetTest.CoreIntegration
8
using Example.Extension;
9
using WixBuildTools.TestSupport;
10
using WixToolset.Core.TestPackage;
11
+ using WixToolset.Data;
12
+ using WixToolset.Data.Tuples;
13
+ using WixToolset.Data.WindowsInstaller;
14
using Xunit;
15
16
public class MsiQueryFixture
@@ -1171,5 +1174,52 @@ namespace WixToolsetTest.CoreIntegration
1174
}, secureProperties.Substring(prefix.Length).Split(';').OrderBy(p => p));
1175
}
1176
}
1177
+
1178
+ [Fact]
1179
+ public void CanMergeModule()
1180
+ {
1181
+ var folder = TestData.Get(@"TestData\SimpleMerge");
1182
+
1183
+ using (var fs = new DisposableFileSystem())
1184
+ {
1185
+ var intermediateFolder = fs.GetFolder();
1186
+ var msiPath = Path.Combine(intermediateFolder, @"bin\test.msi");
1187
+ var cabPath = Path.Combine(intermediateFolder, @"bin\cab1.cab");
1188
+
1189
+ var result = WixRunner.Execute(new[]
1190
+ {
1191
+ "build",
1192
+ Path.Combine(folder, "Package.wxs"),
1193
+ "-loc", Path.Combine(folder, "Package.en-us.wxl"),
1194
+ "-bindpath", Path.Combine(folder, ".data"),
1195
+ "-intermediateFolder", intermediateFolder,
1196
+ "-o", msiPath
1197
+ });
1198
+
1199
+ result.AssertSuccess();
1200
+
1201
+ Assert.True(File.Exists(msiPath));
1202
+ Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb")));
1203
+
1204
+ var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
1205
+ var section = intermediate.Sections.Single();
1206
+ Assert.Empty(section.Tuples.OfType<FileTuple>());
1207
+
1208
+ var data = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
1209
+ Assert.Null(data.Tables["File"]);
1210
+
1211
+ var results = Query.QueryDatabase(msiPath, new[] { "File" });
1212
+ Assert.Equal(new[]
1213
+ {
1214
+ "File:filyIq8rqcxxf903Hsn5K9L0SWV73g.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tModuleComponent.243FB739_4D05_472F_9CFB_EF6B1017B6DE\ttest.txt\t17\t\t\t512\t0"
1215
+ }, results);
1216
+
1217
+ var files = Query.GetCabinetFiles(cabPath);
1218
+ Assert.Equal(new[]
1219
+ {
1220
+ "filyIq8rqcxxf903Hsn5K9L0SWV73g.243FB739_4D05_472F_9CFB_EF6B1017B6DE"
1221
+ }, files.Select(f => f.Name).ToArray());
1222
+ }
1223
+ }
1224
}
1225
}
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleMerge/.data/test.msm
Binary files /dev/null and b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleMerge/.data/test.msm differ
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleMerge/Package.en-us.wxl
new
+11
@@ -0,0 +1,11 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+
3
+<!--
4
+This file contains the declaration of all the localizable strings.
5
+-->
6
+<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
7
+
8
+ <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
9
+ <String Id="FeatureTitle">MsiPackage</String>
10
+
11
+</WixLocalization>
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleMerge/Package.wxs
new
+24
@@ -0,0 +1,24 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3
+ <Product Id="*" Name="MsiPackage" Codepage="1252" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4
+ <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
5
+
6
+ <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
7
+ <MediaTemplate />
8
+
9
+ <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
10
+ <MergeRef Id="TestMsm" />
11
+ </Feature>
12
+ </Product>
13
+
14
+ <Fragment>
15
+ <Directory Id="TARGETDIR" Name="SourceDir">
16
+ <Directory Id="ProgramFilesFolder">
17
+ <Directory Id="INSTALLFOLDER" Name="MsiPackage">
18
+ <!-- -->
19
+ <Merge Id="TestMsm" Language="1033" SourceFile="test.msm" DiskId="1" />
20
+ </Directory>
21
+ </Directory>
22
+ </Directory>
23
+ </Fragment>
24
+</Wix>
src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+3
@@ -77,6 +77,9 @@
77
<Content Include="TestData\SimpleBundle\data\fakeba.dll" CopyToOutputDirectory="PreserveNewest" />
78
<Content Include="TestData\SimpleBundle\data\MsiPackage\Shared.dll" CopyToOutputDirectory="PreserveNewest" />
79
<Content Include="TestData\SimpleBundle\data\MsiPackage\test.txt" CopyToOutputDirectory="PreserveNewest" />
80
+ <Content Include="TestData\SimpleMerge\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
81
+ <Content Include="TestData\SimpleMerge\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
82
+ <Content Include="TestData\SimpleMerge\.data\test.msm" CopyToOutputDirectory="PreserveNewest" />
83
<Content Include="TestData\SimpleModule\data\test.txt" CopyToOutputDirectory="PreserveNewest" />
84
<Content Include="TestData\SimpleModule\Module.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
85
<Content Include="TestData\SimpleModule\Module.wxs" CopyToOutputDirectory="PreserveNewest" />