Massive refactoring to introduce the concept of IBackend
Rob Mensching committed
Oct 14, 2017 at 16:12 UTC
dbde9e7104b907bbbaea17e21247d8cafc8b3a4c
128 files changed
+4326
-2918
WixToolset.Core.sln
+57
-1
@@ -1,6 +1,6 @@
1
Microsoft Visual Studio Solution File, Format Version 12.00
2
# Visual Studio 15
3
-VisualStudioVersion = 15.0.26730.12
3
+VisualStudioVersion = 15.0.26730.16
4
MinimumVisualStudioVersion = 15.0.26124.0
5
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core", "src\WixToolset.Core\WixToolset.Core.csproj", "{0B524850-5B9A-472B-85CC-D25920A1DFE1}"
6
EndProject
@@ -8,24 +8,80 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wix", "src\wix\wix.csproj",
8
EndProject
9
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.BuildTasks", "src\WixToolset.BuildTasks\WixToolset.BuildTasks.csproj", "{C199C723-73E1-4E79-AF86-898DBA007FC3}"
10
EndProject
11
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core.WindowsInstaller", "src\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj", "{5617F2A7-46A0-4D07-B9E0-E982D15641E4}"
12
+EndProject
13
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core.Burn", "src\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj", "{BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}"
14
+EndProject
15
Global
16
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17
Debug|Any CPU = Debug|Any CPU
18
+ Debug|x64 = Debug|x64
19
+ Debug|x86 = Debug|x86
20
Release|Any CPU = Release|Any CPU
21
+ Release|x64 = Release|x64
22
+ Release|x86 = Release|x86
23
EndGlobalSection
24
GlobalSection(ProjectConfigurationPlatforms) = postSolution
25
{0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26
{0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
27
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|x64.ActiveCfg = Debug|Any CPU
28
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|x64.Build.0 = Debug|Any CPU
29
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|x86.ActiveCfg = Debug|Any CPU
30
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|x86.Build.0 = Debug|Any CPU
31
{0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
32
{0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|Any CPU.Build.0 = Release|Any CPU
33
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|x64.ActiveCfg = Release|Any CPU
34
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|x64.Build.0 = Release|Any CPU
35
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|x86.ActiveCfg = Release|Any CPU
36
+ {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|x86.Build.0 = Release|Any CPU
37
{A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38
{A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
39
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|x64.ActiveCfg = Debug|Any CPU
40
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|x64.Build.0 = Debug|Any CPU
41
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|x86.ActiveCfg = Debug|Any CPU
42
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|x86.Build.0 = Debug|Any CPU
43
{A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
44
{A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|Any CPU.Build.0 = Release|Any CPU
45
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|x64.ActiveCfg = Release|Any CPU
46
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|x64.Build.0 = Release|Any CPU
47
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|x86.ActiveCfg = Release|Any CPU
48
+ {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|x86.Build.0 = Release|Any CPU
49
{C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50
{C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
51
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|x64.ActiveCfg = Debug|Any CPU
52
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|x64.Build.0 = Debug|Any CPU
53
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|x86.ActiveCfg = Debug|Any CPU
54
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|x86.Build.0 = Debug|Any CPU
55
{C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
56
{C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|Any CPU.Build.0 = Release|Any CPU
57
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|x64.ActiveCfg = Release|Any CPU
58
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|x64.Build.0 = Release|Any CPU
59
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|x86.ActiveCfg = Release|Any CPU
60
+ {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|x86.Build.0 = Release|Any CPU
61
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
63
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Debug|x64.ActiveCfg = Debug|Any CPU
64
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Debug|x64.Build.0 = Debug|Any CPU
65
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Debug|x86.ActiveCfg = Debug|Any CPU
66
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Debug|x86.Build.0 = Debug|Any CPU
67
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
68
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Release|Any CPU.Build.0 = Release|Any CPU
69
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Release|x64.ActiveCfg = Release|Any CPU
70
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Release|x64.Build.0 = Release|Any CPU
71
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Release|x86.ActiveCfg = Release|Any CPU
72
+ {5617F2A7-46A0-4D07-B9E0-E982D15641E4}.Release|x86.Build.0 = Release|Any CPU
73
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
75
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Debug|x64.ActiveCfg = Debug|Any CPU
76
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Debug|x64.Build.0 = Debug|Any CPU
77
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Debug|x86.ActiveCfg = Debug|Any CPU
78
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Debug|x86.Build.0 = Debug|Any CPU
79
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
80
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Release|Any CPU.Build.0 = Release|Any CPU
81
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Release|x64.ActiveCfg = Release|Any CPU
82
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Release|x64.Build.0 = Release|Any CPU
83
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Release|x86.ActiveCfg = Release|Any CPU
84
+ {BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}.Release|x86.Build.0 = Release|Any CPU
85
EndGlobalSection
86
GlobalSection(SolutionProperties) = preSolution
87
HideSolutionNode = FALSE
src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj
+2
@@ -24,6 +24,8 @@
24
25
<ItemGroup>
26
<ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" />
27
+ <ProjectReference Include="..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" />
28
+ <ProjectReference Include="..\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj" />
29
</ItemGroup>
30
31
<ItemGroup>
src/WixToolset.Core.Burn/BackendFactory.cs
new
+30
@@ -0,0 +1,30 @@
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.Burn
4
+{
5
+ using System;
6
+ using System.IO;
7
+ using WixToolset.Extensibility;
8
+
9
+ internal class BackendFactory : IBackendFactory
10
+ {
11
+ public bool TryCreateBackend(string outputType, string outputFile, IBindContext context, out IBackend backend)
12
+ {
13
+ if (String.IsNullOrEmpty(outputType))
14
+ {
15
+ outputType = Path.GetExtension(outputFile);
16
+ }
17
+
18
+ switch (outputType.ToLowerInvariant())
19
+ {
20
+ case "bundle":
21
+ case ".exe":
22
+ backend = new BundleBackend();
23
+ return true;
24
+ }
25
+
26
+ backend = null;
27
+ return false;
28
+ }
29
+ }
30
+}
src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
renamed
+87
-50
@@ -1,6 +1,6 @@
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.Bind
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Collections.Generic;
@@ -9,23 +9,83 @@ namespace WixToolset.Bind
9
using System.IO;
10
using System.Linq;
11
using System.Reflection;
12
- using WixToolset.Bind.Bundles;
12
+ using WixToolset.Bind;
13
+ using WixToolset.Core.Bind;
14
+ using WixToolset.Core.Burn.Bundles;
15
using WixToolset.Data;
16
+ using WixToolset.Data.Bind;
17
using WixToolset.Data.Rows;
18
using WixToolset.Extensibility;
19
20
+ // TODO: (4.0) Refactor so that these don't need to be copied.
21
+ // Copied verbatim from ext\UtilExtension\wixext\UtilCompiler.cs
22
+ [Flags]
23
+ internal enum WixFileSearchAttributes
24
+ {
25
+ Default = 0x001,
26
+ MinVersionInclusive = 0x002,
27
+ MaxVersionInclusive = 0x004,
28
+ MinSizeInclusive = 0x008,
29
+ MaxSizeInclusive = 0x010,
30
+ MinDateInclusive = 0x020,
31
+ MaxDateInclusive = 0x040,
32
+ WantVersion = 0x080,
33
+ WantExists = 0x100,
34
+ IsDirectory = 0x200,
35
+ }
36
+
37
+ [Flags]
38
+ internal enum WixRegistrySearchAttributes
39
+ {
40
+ Raw = 0x01,
41
+ Compatible = 0x02,
42
+ ExpandEnvironmentVariables = 0x04,
43
+ WantValue = 0x08,
44
+ WantExists = 0x10,
45
+ Win64 = 0x20,
46
+ }
47
+
48
+ internal enum WixComponentSearchAttributes
49
+ {
50
+ KeyPath = 0x1,
51
+ State = 0x2,
52
+ WantDirectory = 0x4,
53
+ }
54
+
55
+ [Flags]
56
+ internal enum WixProductSearchAttributes
57
+ {
58
+ Version = 0x1,
59
+ Language = 0x2,
60
+ State = 0x4,
61
+ Assignment = 0x8,
62
+ UpgradeCode = 0x10,
63
+ }
64
+
65
/// <summary>
66
/// Binds a this.bundle.
67
/// </summary>
20
- internal class BindBundleCommand : ICommand
68
+ internal class BindBundleCommand
69
{
70
+ public BindBundleCommand(IBindContext context)
71
+ {
72
+ this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions();
73
+
74
+ this.DelayedFields = context.DelayedFields;
75
+ this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
76
+
77
+ this.BackendExtensions = context.ExtensionManager.Create<IBurnBackendExtension>();
78
+ }
79
+
80
public CompressionLevel DefaultCompressionLevel { private get; set; }
81
24
- public IEnumerable<IBinderExtension> Extensions { private get; set; }
82
+ public IEnumerable<IDelayedField> DelayedFields { get; }
83
+
84
+ public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
85
26
- public BinderFileManagerCore FileManagerCore { private get; set; }
86
+ private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
87
28
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
88
+ public IEnumerable<IBinderExtension> Extensions { private get; set; }
89
90
public Output Output { private get; set; }
91
@@ -35,9 +95,9 @@ namespace WixToolset.Bind
95
96
public TableDefinitionCollection TableDefinitions { private get; set; }
97
38
- public string TempFilesLocation { private get; set; }
98
+ public string IntermediateFolder { private get; set; }
99
40
- public WixVariableResolver WixVariableResolver { private get; set; }
100
+ public IBindVariableResolver WixVariableResolver { private get; set; }
101
102
public IEnumerable<FileTransfer> FileTransfers { get; private set; }
103
@@ -79,32 +139,9 @@ namespace WixToolset.Bind
139
return;
140
}
141
82
- // Localize fields, resolve wix variables, and resolve file paths.
83
- ExtractEmbeddedFiles filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
84
-
85
- IEnumerable<DelayedField> delayedFields;
86
- {
87
- ResolveFieldsCommand command = new ResolveFieldsCommand();
88
- command.Tables = this.Output.Tables;
89
- command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
90
- command.FileManagerCore = this.FileManagerCore;
91
- command.FileManagers = this.FileManagers;
92
- command.SupportDelayedResolution = true;
93
- command.TempFilesLocation = this.TempFilesLocation;
94
- command.WixVariableResolver = this.WixVariableResolver;
95
- command.Execute();
96
-
97
- delayedFields = command.DelayedFields;
98
- }
99
-
100
- if (Messaging.Instance.EncounteredError)
101
- {
102
- return;
103
- }
104
-
142
// If there are any fields to resolve later, create the cache to populate during bind.
143
IDictionary<string, string> variableCache = null;
107
- if (delayedFields.Any())
144
+ if (this.DelayedFields.Any())
145
{
146
variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
147
}
@@ -116,8 +153,8 @@ namespace WixToolset.Bind
153
154
// Extract files that come from cabinet files (this does not extract files from merge modules).
155
{
119
- ExtractEmbeddedFilesCommand extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand();
120
- extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
156
+ var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand();
157
+ extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles;
158
extractEmbeddedFilesCommand.Execute();
159
}
160
@@ -198,10 +235,10 @@ namespace WixToolset.Bind
235
236
case WixBundlePackageType.Msi:
237
{
201
- ProcessMsiPackageCommand command = new ProcessMsiPackageCommand();
238
+ var command = new ProcessMsiPackageCommand();
239
command.AuthoredPayloads = payloads;
240
command.Facade = facade;
204
- command.FileManager = this.FileManagers.First();
241
+ command.BackendExtensions = this.BackendExtensions;
242
command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]);
243
command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]);
244
command.PayloadTable = this.Output.Tables["WixBundlePayload"];
@@ -377,11 +414,11 @@ namespace WixToolset.Bind
414
}
415
416
// Resolve any delayed fields before generating the manifest.
380
- if (delayedFields.Any())
417
+ if (this.DelayedFields.Any())
418
{
382
- ResolveDelayedFieldsCommand resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand();
419
+ var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand();
420
resolveDelayedFieldsCommand.OutputType = this.Output.Type;
384
- resolveDelayedFieldsCommand.DelayedFields = delayedFields;
421
+ resolveDelayedFieldsCommand.DelayedFields = this.DelayedFields;
422
resolveDelayedFieldsCommand.ModularizationGuid = null;
423
resolveDelayedFieldsCommand.VariableCache = variableCache;
424
resolveDelayedFieldsCommand.Execute();
@@ -406,17 +443,17 @@ namespace WixToolset.Bind
443
command.Output = this.Output;
444
command.Payloads = payloads;
445
command.TableDefinitions = this.TableDefinitions;
409
- command.TempFilesLocation = this.TempFilesLocation;
446
+ command.TempFilesLocation = this.IntermediateFolder;
447
command.Execute();
448
449
WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
450
payloads.Add(baManifestPayload);
451
}
452
416
- foreach (BinderExtension extension in this.Extensions)
417
- {
418
- extension.Finish(Output);
419
- }
453
+ //foreach (BinderExtension extension in this.Extensions)
454
+ //{
455
+ // extension.PostBind(this.Context);
456
+ //}
457
458
// Create all the containers except the UX container first so the manifest (that goes in the UX container)
459
// can contain all size and hash information about the non-UX containers.
@@ -441,7 +478,7 @@ namespace WixToolset.Bind
478
}
479
else if (Compiler.BurnUXContainerId == container.Id)
480
{
444
- container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name);
481
+ container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
482
container.AttachedContainerIndex = 0;
483
484
// Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first
@@ -466,7 +503,7 @@ namespace WixToolset.Bind
503
}
504
else
505
{
469
- container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name);
506
+ container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
507
508
// Add detached containers to the list of file transfers.
509
if (ContainerType.Detached == container.Type)
@@ -491,10 +528,10 @@ namespace WixToolset.Bind
528
}
529
530
// Create the bundle manifest then UX container.
494
- string manifestPath = Path.Combine(this.TempFilesLocation, "bundle-manifest.xml");
531
+ string manifestPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml");
532
{
496
- CreateBurnManifestCommand command = new CreateBurnManifestCommand();
497
- command.FileManagers = this.FileManagers;
533
+ var command = new CreateBurnManifestCommand();
534
+ command.BackendExtensions = this.BackendExtensions;
535
command.Output = this.Output;
536
537
command.BundleInfo = bundleRow;
@@ -519,7 +556,7 @@ namespace WixToolset.Bind
556
string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString();
557
558
string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe");
522
- string bundleTempPath = Path.Combine(this.TempFilesLocation, Path.GetFileName(this.OutputPath));
559
+ string bundleTempPath = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath));
560
561
Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile));
562
src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Xml;
src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Collections.ObjectModel;
src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Xml;
src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Xml;
src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Xml;
src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Xml;
src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.Burn
4
{
5
using System;
6
using System.Diagnostics;
src/WixToolset.Core.Burn/BundleBackend.cs
new
+58
@@ -0,0 +1,58 @@
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.Burn
4
+{
5
+ using System;
6
+ using System.IO;
7
+ using WixToolset.Core.Burn.Bundles;
8
+ using WixToolset.Core.Burn.Inscribe;
9
+ using WixToolset.Data;
10
+ using WixToolset.Data.Bind;
11
+ using WixToolset.Extensibility;
12
+
13
+ internal class BundleBackend : IBackend
14
+ {
15
+ public BindResult Bind(IBindContext context)
16
+ {
17
+ BindBundleCommand command = new BindBundleCommand(context);
18
+ //command.DefaultCompressionLevel = context.DefaultCompressionLevel;
19
+ //command.Extensions = context.Extensions;
20
+ //command.IntermediateFolder = context.IntermediateFolder;
21
+ //command.Output = context.IntermediateRepresentation;
22
+ //command.OutputPath = context.OutputPath;
23
+ //command.PdbFile = context.OutputPdbPath;
24
+ //command.WixVariableResolver = context.WixVariableResolver;
25
+ command.Execute();
26
+
27
+ return new BindResult(command.FileTransfers, command.ContentFilePaths);
28
+ }
29
+
30
+ public bool Inscribe(IInscribeContext context)
31
+ {
32
+ if (String.IsNullOrEmpty(context.SignedEngineFile))
33
+ {
34
+ var command = new InscribeBundleCommand(context);
35
+ return command.Execute();
36
+ }
37
+ else
38
+ {
39
+ var command = new InscribeBundleEngineCommand(context);
40
+ return command.Execute();
41
+ }
42
+ }
43
+
44
+ public Output Unbind(IUnbindContext context)
45
+ {
46
+ string uxExtractPath = Path.Combine(context.ExportBasePath, "UX");
47
+ string acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer");
48
+
49
+ using (BurnReader reader = BurnReader.Open(context.InputFilePath))
50
+ {
51
+ reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder);
52
+ reader.ExtractAttachedContainer(acExtractPath, context.IntermediateFolder);
53
+ }
54
+
55
+ return null;
56
+ }
57
+ }
58
+}
src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Bundles
9
using WixToolset.Data;
10
using WixToolset.Data.Rows;
11
12
- internal class AutomaticallySlipstreamPatchesCommand : ICommand
12
+ internal class AutomaticallySlipstreamPatchesCommand
13
{
14
public IEnumerable<PackageFacade> PackageFacades { private get; set; }
15
src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Diagnostics;
src/WixToolset.Core.Burn/Bundles/BurnReader.cs
renamed
+13
-3
@@ -1,13 +1,13 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections;
7
using System.Collections.Generic;
8
using System.IO;
9
using System.Xml;
10
- using WixToolset.Cab;
10
+ using WixToolset.Core.Cab;
11
12
/// <summary>
13
/// Burn PE reader for the WiX toolset.
@@ -49,6 +49,11 @@ namespace WixToolset.Bind.Bundles
49
}
50
}
51
52
+ internal static BurnReader Open(object inputFilePath)
53
+ {
54
+ throw new NotImplementedException();
55
+ }
56
+
57
/// <summary>
58
/// Opens a Burn reader.
59
/// </summary>
@@ -96,7 +101,7 @@ namespace WixToolset.Bind.Bundles
101
BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize);
102
}
103
99
- using (WixExtractCab extract = new WixExtractCab())
104
+ using (var extract = new WixExtractCab())
105
{
106
extract.Extract(tempCabPath, outputDirectory);
107
}
@@ -144,6 +149,11 @@ namespace WixToolset.Bind.Bundles
149
return true;
150
}
151
152
+ internal void ExtractUXContainer(string uxExtractPath, object intermediateFolder)
153
+ {
154
+ throw new NotImplementedException();
155
+ }
156
+
157
/// <summary>
158
/// Gets the attached container from the exe and extracts its contents to the output directory.
159
/// </summary>
src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Diagnostics;
src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
@@ -12,7 +12,7 @@ namespace WixToolset.Bind.Bundles
12
using WixToolset.Data;
13
using WixToolset.Data.Rows;
14
15
- internal class CreateBootstrapperApplicationManifestCommand : ICommand
15
+ internal class CreateBootstrapperApplicationManifestCommand
16
{
17
public WixBundleRow BundleRow { private get; set; }
18
src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
renamed
+5
-5
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
@@ -13,9 +13,9 @@ namespace WixToolset.Bind.Bundles
13
using WixToolset.Data.Rows;
14
using WixToolset.Extensibility;
15
16
- internal class CreateBurnManifestCommand : ICommand
16
+ internal class CreateBurnManifestCommand
17
{
18
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
18
+ public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; }
19
20
public Output Output { private get; set; }
21
@@ -671,9 +671,9 @@ namespace WixToolset.Bind.Bundles
671
private string ResolveUrl(string url, string fallbackUrl, string packageId, string payloadId, string fileName)
672
{
673
string resolved = null;
674
- foreach (IBinderFileManager fileManager in this.FileManagers)
674
+ foreach (var extension in this.BackendExtensions)
675
{
676
- resolved = fileManager.ResolveUrl(url, fallbackUrl, packageId, payloadId, fileName);
676
+ resolved = extension.ResolveUrl(url, fallbackUrl, packageId, payloadId, fileName);
677
if (!String.IsNullOrEmpty(resolved))
678
{
679
break;
src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs
renamed
+4
-4
@@ -1,20 +1,20 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.Diagnostics;
8
using System.IO;
9
using System.Linq;
10
- using WixToolset.Cab;
10
+ using WixToolset.Core.Cab;
11
using WixToolset.Data;
12
using WixToolset.Data.Rows;
13
14
/// <summary>
15
/// Creates cabinet files.
16
/// </summary>
17
- internal class CreateContainerCommand : ICommand
17
+ internal class CreateContainerCommand
18
{
19
public CompressionLevel DefaultCompressionLevel { private get; set; }
20
@@ -37,7 +37,7 @@ namespace WixToolset.Bind.Bundles
37
++payloadCount;
38
}
39
40
- using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(this.OutputPath), Path.GetDirectoryName(this.OutputPath), payloadCount, 0, 0, this.DefaultCompressionLevel))
40
+ using (var cab = new WixCreateCab(Path.GetFileName(this.OutputPath), Path.GetDirectoryName(this.OutputPath), payloadCount, 0, 0, this.DefaultCompressionLevel))
41
{
42
// If a manifest was provided always add it as "payload 0" to the container.
43
if (!String.IsNullOrEmpty(this.ManifestFile))
src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
renamed
+2
-2
@@ -1,12 +1,12 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System.Collections.Generic;
6
using WixToolset.Data;
7
using WixToolset.Data.Rows;
8
9
- internal class GetPackageFacadesCommand : ICommand
9
+ internal class GetPackageFacadesCommand
10
{
11
public Table PackageTable { private get; set; }
12
src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
renamed
+2
-2
@@ -1,13 +1,13 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
7
using WixToolset.Data;
8
using WixToolset.Data.Rows;
9
10
- internal class OrderPackagesAndRollbackBoundariesCommand : ICommand
10
+ internal class OrderPackagesAndRollbackBoundariesCommand
11
{
12
public Table WixGroupTable { private get; set; }
13
src/WixToolset.Core.Burn/Bundles/PackageFacade.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using WixToolset.Data.Rows;
6
src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using WixToolset.Data;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Bundles
9
/// <summary>
10
/// Initializes package state from the Exe contents.
11
/// </summary>
12
- internal class ProcessExePackageCommand : ICommand
12
+ internal class ProcessExePackageCommand
13
{
14
public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
15
src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
renamed
+23
-7
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections;
@@ -12,14 +12,15 @@ namespace WixToolset.Bind.Bundles
12
using WixToolset.Data;
13
using WixToolset.Data.Rows;
14
using WixToolset.Extensibility;
15
- using WixToolset.Msi;
15
using WixToolset.Core.Native;
16
using Dtf = WixToolset.Dtf.WindowsInstaller;
17
+ using WixToolset.Bind;
18
+ using WixToolset.Data.Bind;
19
20
/// <summary>
21
/// Initializes package state from the MSI contents.
22
/// </summary>
22
- internal class ProcessMsiPackageCommand : ICommand
23
+ internal class ProcessMsiPackageCommand
24
{
25
private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'";
26
@@ -27,7 +28,7 @@ namespace WixToolset.Bind.Bundles
28
29
public PackageFacade Facade { private get; set; }
30
30
- public IBinderFileManager FileManager { private get; set; }
31
+ public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; }
32
33
public Table MsiFeatureTable { private get; set; }
34
@@ -365,7 +366,7 @@ namespace WixToolset.Bind.Bundles
366
if (!payloadNames.Contains(cabinetName))
367
{
368
string generatedId = Common.GenerateIdentifier("cab", packagePayload.Id, cabinet);
368
- string payloadSourceFile = FileManager.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.Package.SourceLineNumbers, BindStage.Normal);
369
+ string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.Package.SourceLineNumbers, BindStage.Normal);
370
371
WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers);
372
payload.Id = generatedId;
@@ -407,7 +408,7 @@ namespace WixToolset.Bind.Bundles
408
break;
409
}
410
410
- string sourceName = Installer.GetName(record.GetString(3), true, longNamesInImage);
411
+ string sourceName = Common.GetName(record.GetString(3), true, longNamesInImage);
412
directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName));
413
}
414
}
@@ -441,7 +442,7 @@ namespace WixToolset.Bind.Bundles
442
if (!payloadNames.Contains(name))
443
{
444
string generatedId = Common.GenerateIdentifier("f", packagePayload.Id, record.GetString(2));
444
- string payloadSourceFile = FileManager.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.Package.SourceLineNumbers, BindStage.Normal);
445
+ string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.Package.SourceLineNumbers, BindStage.Normal);
446
447
WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers);
448
payload.Id = generatedId;
@@ -510,6 +511,21 @@ namespace WixToolset.Bind.Bundles
511
}
512
}
513
514
+ private string ResolveRelatedFile(string sourceFile, string relatedSource, string type, SourceLineNumber sourceLineNumbers, BindStage stage)
515
+ {
516
+ foreach (var extension in this.BackendExtensions)
517
+ {
518
+ var relatedFile = extension.ResolveRelatedFile(sourceFile, relatedSource, type, sourceLineNumbers, stage);
519
+
520
+ if (!String.IsNullOrEmpty(relatedFile))
521
+ {
522
+ return relatedFile;
523
+ }
524
+ }
525
+
526
+ return null;
527
+ }
528
+
529
/// <summary>
530
/// Queries a Windows Installer database for a Property value.
531
/// </summary>
src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
@@ -16,7 +16,7 @@ namespace WixToolset.Bind.Bundles
16
/// <summary>
17
/// Initializes package state from the Msp contents.
18
/// </summary>
19
- internal class ProcessMspPackageCommand : ICommand
19
+ internal class ProcessMspPackageCommand
20
{
21
private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'";
22
private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false);
src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using WixToolset.Data;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Bundles
9
/// <summary>
10
/// Processes the Msu packages to add properties and payloads from the Msu packages.
11
/// </summary>
12
- internal class ProcessMsuPackageCommand : ICommand
12
+ internal class ProcessMsuPackageCommand
13
{
14
public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
15
src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
renamed
+4
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
@@ -9,10 +9,12 @@ namespace WixToolset.Bind.Bundles
9
using System.Security.Cryptography;
10
using System.Security.Cryptography.X509Certificates;
11
using System.Text;
12
+ using WixToolset.Bind;
13
using WixToolset.Data;
14
+ using WixToolset.Data.Bind;
15
using WixToolset.Data.Rows;
16
15
- internal class ProcessPayloadsCommand : ICommand
17
+ internal class ProcessPayloadsCommand
18
{
19
private static readonly Version EmptyVersion = new Version(0, 0, 0, 0);
20
src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Bundles
3
+namespace WixToolset.Core.Burn.Bundles
4
{
5
using System;
6
using System.Collections.Generic;
@@ -11,7 +11,7 @@ namespace WixToolset.Bind.Bundles
11
using WixToolset.Data;
12
using WixToolset.Data.Rows;
13
14
- internal class VerifyPayloadsWithCatalogCommand : ICommand
14
+ internal class VerifyPayloadsWithCatalogCommand
15
{
16
public IEnumerable<WixBundleCatalogRow> Catalogs { private get; set; }
17
src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
new
+53
@@ -0,0 +1,53 @@
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.Burn.Inscribe
4
+{
5
+ using System.IO;
6
+ using WixToolset.Core.Burn.Bundles;
7
+ using WixToolset.Extensibility;
8
+
9
+ internal class InscribeBundleCommand
10
+ {
11
+ public InscribeBundleCommand(IInscribeContext context)
12
+ {
13
+ this.Context = context;
14
+ }
15
+
16
+ private IInscribeContext Context { get; }
17
+
18
+ public bool Execute()
19
+ {
20
+ bool inscribed = false;
21
+ string tempFile = Path.Combine(this.Context.IntermediateFolder, "bundle_engine_signed.exe");
22
+
23
+ using (BurnReader reader = BurnReader.Open(this.Context.InputFilePath))
24
+ {
25
+ File.Copy(this.Context.SignedEngineFile, tempFile, true);
26
+
27
+ // If there was an attached container on the original (unsigned) bundle, put it back.
28
+ if (reader.AttachedContainerSize > 0)
29
+ {
30
+ reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
31
+
32
+ using (BurnWriter writer = BurnWriter.Open(tempFile))
33
+ {
34
+ writer.RememberThenResetSignature();
35
+ writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
36
+ inscribed = true;
37
+ }
38
+ }
39
+ }
40
+
41
+ Directory.CreateDirectory(Path.GetDirectoryName(this.Context.OutputFile));
42
+ if (File.Exists(this.Context.OutputFile))
43
+ {
44
+ File.Delete(this.Context.OutputFile);
45
+ }
46
+
47
+ File.Move(tempFile, this.Context.OutputFile);
48
+ WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { this.Context.OutputFile }, 1);
49
+
50
+ return inscribed;
51
+ }
52
+ }
53
+}
src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
new
+61
@@ -0,0 +1,61 @@
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.Burn.Inscribe
4
+{
5
+ using System;
6
+ using System.IO;
7
+ using WixToolset.Core.Burn.Bundles;
8
+ using WixToolset.Extensibility;
9
+
10
+ internal class InscribeBundleEngineCommand
11
+ {
12
+ public InscribeBundleEngineCommand(IInscribeContext context)
13
+ {
14
+ this.Context = context;
15
+ }
16
+
17
+ private IInscribeContext Context { get; }
18
+
19
+ public bool Execute()
20
+ {
21
+ string tempFile = Path.Combine(this.Context.IntermediateFolder, "bundle_engine_unsigned.exe");
22
+
23
+ using (BurnReader reader = BurnReader.Open(this.Context.InputFilePath))
24
+ using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
25
+ {
26
+ reader.Stream.Seek(0, SeekOrigin.Begin);
27
+
28
+ byte[] buffer = new byte[4 * 1024];
29
+ int total = 0;
30
+ int read = 0;
31
+ do
32
+ {
33
+ read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
34
+
35
+ read = reader.Stream.Read(buffer, 0, read);
36
+ writer.Write(buffer, 0, read);
37
+
38
+ total += read;
39
+ } while (total < reader.EngineSize && 0 < read);
40
+
41
+ if (total != reader.EngineSize)
42
+ {
43
+ throw new InvalidOperationException("Failed to copy engine out of bundle.");
44
+ }
45
+
46
+ // TODO: update writer with detached container signatures.
47
+ }
48
+
49
+ Directory.CreateDirectory(Path.GetDirectoryName(this.Context.OutputFile));
50
+ if (File.Exists(this.Context.OutputFile))
51
+ {
52
+ File.Delete(this.Context.OutputFile);
53
+ }
54
+
55
+ File.Move(tempFile, this.Context.OutputFile);
56
+ WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { this.Context.OutputFile }, 1);
57
+
58
+ return true;
59
+ }
60
+ }
61
+}
src/WixToolset.Core.Burn/VerifyInterop.cs
renamed
src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
new
+36
@@ -0,0 +1,36 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+<Project Sdk="Microsoft.NET.Sdk">
5
+ <PropertyGroup>
6
+ <TargetFramework>netstandard2.0</TargetFramework>
7
+ <Description>Core Burn</Description>
8
+ <Title>WiX Toolset Core Burn</Title>
9
+ </PropertyGroup>
10
+
11
+ <PropertyGroup>
12
+ <NoWarn>NU1701</NoWarn>
13
+ </PropertyGroup>
14
+
15
+ <ItemGroup>
16
+ <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
17
+ <PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
18
+
19
+ <ProjectReference Include="$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj') " />
20
+ <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj') " />
21
+
22
+ <ProjectReference Include="$(WixToolsetRootFolder)\Core.Native\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core.Native\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj') " />
23
+
24
+ <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" />
25
+ <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core.Native\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj') " />
26
+ </ItemGroup>
27
+
28
+ <ItemGroup>
29
+ <PackageReference Include="WixToolset.Dtf.Resources" Version="4.0.*" />
30
+ <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" />
31
+ </ItemGroup>
32
+
33
+ <ItemGroup>
34
+ <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.41" PrivateAssets="all" />
35
+ </ItemGroup>
36
+</Project>
src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
renamed
+3
-3
@@ -1,18 +1,18 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.Globalization;
8
- using System.IO;
8
+ using WixToolset.Core.Bind;
9
using WixToolset.Data;
10
using WixToolset.Data.Rows;
11
12
/// <summary>
13
/// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows.
14
/// </summary>
15
- public class AssignMediaCommand : ICommand
15
+ public class AssignMediaCommand
16
{
17
public AssignMediaCommand()
18
{
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
renamed
+88
-117
@@ -1,6 +1,6 @@
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.Bind
3
+namespace WixToolset.Core.WindowsInstaller.Bind
4
{
5
using System;
6
using System.Collections;
@@ -9,8 +9,11 @@ namespace WixToolset.Bind
9
using System.Globalization;
10
using System.IO;
11
using System.Linq;
12
- using WixToolset.Bind.Databases;
12
+ using WixToolset.Bind;
13
+ using WixToolset.Core.Bind;
14
+ using WixToolset.Core.WindowsInstaller.Databases;
15
using WixToolset.Data;
16
+ using WixToolset.Data.Bind;
17
using WixToolset.Data.Rows;
18
using WixToolset.Extensibility;
19
using WixToolset.Msi;
@@ -18,46 +21,72 @@ namespace WixToolset.Bind
21
/// <summary>
22
/// Binds a databse.
23
/// </summary>
21
- internal class BindDatabaseCommand : ICommand
24
+ internal class BindDatabaseCommand
25
{
26
// As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
27
private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
28
26
- public int Codepage { private get; set; }
29
+ public BindDatabaseCommand(IBindContext context, Validator validator)
30
+ {
31
+ this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions();
32
+
33
+ this.BindPaths = context.BindPaths;
34
+ this.CabbingThreadCount = context.CabbingThreadCount;
35
+ this.CabCachePath = context.CabCachePath;
36
+ this.Codepage = context.Codepage;
37
+ this.DefaultCompressionLevel = context.DefaultCompressionLevel;
38
+ this.DelayedFields = context.DelayedFields;
39
+ this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
40
+ this.Extensions = context.Extensions;
41
+ this.Output = context.IntermediateRepresentation;
42
+ this.OutputPath = context.OutputPath;
43
+ this.PdbFile = context.OutputPdbPath;
44
+ this.IntermediateFolder = context.IntermediateFolder;
45
+ this.Validator = validator;
46
+ this.WixVariableResolver = context.WixVariableResolver;
47
+
48
+ this.BackendExtensions = context.ExtensionManager.Create<IWindowsInstallerBackendExtension>();
49
+ }
50
28
- public int CabbingThreadCount { private get; set; }
51
+ private IEnumerable<BindPath> BindPaths { get; }
52
30
- public CompressionLevel DefaultCompressionLevel { private get; set; }
53
+ private int Codepage { get; }
54
32
- public bool DeltaBinaryPatch { get; set; }
55
+ private int CabbingThreadCount { get; }
56
34
- public IEnumerable<IBinderExtension> Extensions { private get; set; }
57
+ private string CabCachePath { get; }
58
36
- public BinderFileManagerCore FileManagerCore { private get; set; }
59
+ private CompressionLevel DefaultCompressionLevel { get; }
60
38
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
61
+ public IEnumerable<IDelayedField> DelayedFields { get; }
62
40
- public IEnumerable<InspectorExtension> InspectorExtensions { private get; set; }
63
+ public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
64
+
65
+ public bool DeltaBinaryPatch { get; set; }
66
42
- public Localizer Localizer { private get; set; }
67
+ private IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { get; }
68
44
- public string PdbFile { private get; set; }
69
+ private IEnumerable<IBinderExtension> Extensions { get; }
70
46
- public Output Output { private get; set; }
71
+ private IEnumerable<InspectorExtension> InspectorExtensions { get; }
72
48
- public string OutputPath { private get; set; }
73
+ private string PdbFile { get; }
74
50
- public bool SuppressAddingValidationRows { private get; set; }
75
+ private Output Output { get; }
76
52
- public bool SuppressLayout { private get; set; }
77
+ private string OutputPath { get; }
78
54
- public TableDefinitionCollection TableDefinitions { private get; set; }
79
+ private bool SuppressAddingValidationRows { get; }
80
56
- public string TempFilesLocation { private get; set; }
81
+ private bool SuppressLayout { get; }
82
58
- public Validator Validator { private get; set; }
83
+ private TableDefinitionCollection TableDefinitions { get; }
84
60
- public WixVariableResolver WixVariableResolver { private get; set; }
85
+ private string IntermediateFolder { get; }
86
+
87
+ private Validator Validator { get; }
88
+
89
+ private IBindVariableResolver WixVariableResolver { get; }
90
91
public IEnumerable<FileTransfer> FileTransfers { get; private set; }
92
@@ -69,43 +98,9 @@ namespace WixToolset.Bind
98
99
HashSet<string> suppressedTableNames = new HashSet<string>();
100
72
- // Localize fields, resolve wix variables, and resolve file paths.
73
- ExtractEmbeddedFiles filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
74
-
75
- IEnumerable<DelayedField> delayedFields;
76
- {
77
- ResolveFieldsCommand command = new ResolveFieldsCommand();
78
- command.Tables = this.Output.Tables;
79
- command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
80
- command.FileManagerCore = this.FileManagerCore;
81
- command.FileManagers = this.FileManagers;
82
- command.SupportDelayedResolution = true;
83
- command.TempFilesLocation = this.TempFilesLocation;
84
- command.WixVariableResolver = this.WixVariableResolver;
85
- command.Execute();
86
-
87
- delayedFields = command.DelayedFields;
88
- }
89
-
90
- if (OutputType.Patch == this.Output.Type)
91
- {
92
- foreach (SubStorage transform in this.Output.SubStorages)
93
- {
94
- ResolveFieldsCommand command = new ResolveFieldsCommand();
95
- command.Tables = transform.Data.Tables;
96
- command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
97
- command.FileManagerCore = this.FileManagerCore;
98
- command.FileManagers = this.FileManagers;
99
- command.SupportDelayedResolution = false;
100
- command.TempFilesLocation = this.TempFilesLocation;
101
- command.WixVariableResolver = this.WixVariableResolver;
102
- command.Execute();
103
- }
104
- }
105
-
101
// If there are any fields to resolve later, create the cache to populate during bind.
102
IDictionary<string, string> variableCache = null;
108
- if (delayedFields.Any())
103
+ if (this.DelayedFields.Any())
104
{
105
variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
106
}
@@ -231,7 +226,7 @@ namespace WixToolset.Bind
226
// Add the property name and value to the variableCache.
227
if (null != variableCache)
228
{
234
- string key = String.Concat("property.", Demodularize(this.Output.Type, modularizationGuid, propertyRow.Property));
229
+ string key = String.Concat("property.", Common.Demodularize(this.Output.Type, modularizationGuid, propertyRow.Property));
230
variableCache[key] = propertyRow.Value;
231
}
232
}
@@ -240,7 +235,7 @@ namespace WixToolset.Bind
235
// Extract files that come from cabinet files (this does not extract files from merge modules).
236
{
237
ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand();
243
- command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
238
+ command.FilesWithEmbeddedFiles = this.ExpectedEmbeddedFiles;
239
command.Execute();
240
}
241
@@ -258,7 +253,7 @@ namespace WixToolset.Bind
253
command.WixMergeTable = wixMergeTable;
254
command.OutputInstallerVersion = installerVersion;
255
command.SuppressLayout = this.SuppressLayout;
261
- command.TempFilesLocation = this.TempFilesLocation;
256
+ command.TempFilesLocation = this.IntermediateFolder;
257
command.Execute();
258
259
fileFacades.AddRange(command.MergeModulesFileFacades);
@@ -303,11 +298,11 @@ namespace WixToolset.Bind
298
299
this.UpdateControlText(this.Output);
300
306
- if (delayedFields.Any())
301
+ if (this.DelayedFields.Any())
302
{
303
ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand();
304
command.OutputType = this.Output.Type;
310
- command.DelayedFields = delayedFields;
305
+ command.DelayedFields = this.DelayedFields;
306
command.ModularizationGuid = null;
307
command.VariableCache = variableCache;
308
command.Execute();
@@ -343,7 +338,7 @@ namespace WixToolset.Bind
338
{
339
Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);
340
346
- foreach (BinderExtension extension in this.Extensions)
341
+ foreach (IBinderExtension extension in this.Extensions)
342
{
343
extension.AfterResolvedFields(this.Output);
344
}
@@ -379,14 +374,14 @@ namespace WixToolset.Bind
374
return;
375
}
376
382
- Directory.CreateDirectory(this.TempFilesLocation);
377
+ Directory.CreateDirectory(this.IntermediateFolder);
378
379
if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch)
380
{
381
CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand();
382
command.FileFacades = fileFacades;
383
command.WixPatchIdTable = this.Output.Tables["WixPatchId"];
389
- command.TempFilesLocation = this.TempFilesLocation;
384
+ command.TempFilesLocation = this.IntermediateFolder;
385
command.Execute();
386
}
387
@@ -396,17 +391,18 @@ namespace WixToolset.Bind
391
{
392
Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles());
393
399
- CreateCabinetsCommand command = new CreateCabinetsCommand();
394
+ var command = new CreateCabinetsCommand();
395
command.CabbingThreadCount = this.CabbingThreadCount;
396
+ command.CabCachePath = this.CabCachePath;
397
command.DefaultCompressionLevel = this.DefaultCompressionLevel;
398
command.Output = this.Output;
403
- command.FileManagers = this.FileManagers;
399
+ command.BackendExtensions = this.BackendExtensions;
400
command.LayoutDirectory = layoutDirectory;
401
command.Compressed = compressed;
402
command.FileRowsByCabinet = filesByCabinetMedia;
403
command.ResolveMedia = this.ResolveMedia;
404
command.TableDefinitions = this.TableDefinitions;
409
- command.TempFilesLocation = this.TempFilesLocation;
405
+ command.TempFilesLocation = this.IntermediateFolder;
406
command.WixMediaTable = this.Output.Tables["WixMedia"];
407
command.Execute();
408
@@ -450,14 +446,14 @@ namespace WixToolset.Bind
446
}
447
}
448
453
- foreach (BinderExtension extension in this.Extensions)
454
- {
455
- extension.Finish(this.Output);
456
- }
449
+ //foreach (BinderExtension extension in this.Extensions)
450
+ //{
451
+ // extension.PostBind(this.Context);
452
+ //}
453
454
// generate database file
455
Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase());
460
- string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(this.OutputPath));
456
+ string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath));
457
this.GenerateDatabase(this.Output, tempDatabaseFile, false, false);
458
459
FileTransfer transfer;
@@ -501,14 +497,14 @@ namespace WixToolset.Bind
497
}
498
499
// inspect the MSI prior to running ICEs
504
- InspectorCore inspectorCore = new InspectorCore();
505
- foreach (InspectorExtension inspectorExtension in this.InspectorExtensions)
506
- {
507
- inspectorExtension.Core = inspectorCore;
508
- inspectorExtension.InspectDatabase(tempDatabaseFile, pdb);
500
+ //InspectorCore inspectorCore = new InspectorCore();
501
+ //foreach (InspectorExtension inspectorExtension in this.InspectorExtensions)
502
+ //{
503
+ // inspectorExtension.Core = inspectorCore;
504
+ // inspectorExtension.InspectDatabase(tempDatabaseFile, pdb);
505
510
- inspectorExtension.Core = null; // reset.
511
- }
506
+ // inspectorExtension.Core = null; // reset.
507
+ //}
508
509
if (Messaging.Instance.EncounteredError)
510
{
@@ -540,7 +536,7 @@ namespace WixToolset.Bind
536
// Process uncompressed files.
537
if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
538
{
543
- ProcessUncompressedFilesCommand command = new ProcessUncompressedFilesCommand();
539
+ var command = new ProcessUncompressedFilesCommand();
540
command.Compressed = compressed;
541
command.FileFacades = uncompressedFiles;
542
command.LayoutDirectory = layoutDirectory;
@@ -570,8 +566,8 @@ namespace WixToolset.Bind
566
foreach (Row row in dialogTable.Rows)
567
{
568
string dialog = (string)row[0];
573
- LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, null);
574
- if (null != localizedControl)
569
+
570
+ if (this.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
571
{
572
if (CompilerConstants.IntegerNotSet != localizedControl.X)
573
{
@@ -610,8 +606,8 @@ namespace WixToolset.Bind
606
{
607
string dialog = (string)row[0];
608
string control = (string)row[1];
613
- LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, control);
614
- if (null != localizedControl)
609
+
610
+ if (this.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
611
{
612
if (CompilerConstants.IntegerNotSet != localizedControl.X)
613
{
@@ -651,10 +647,8 @@ namespace WixToolset.Bind
647
/// <param name="allFileRows">True if copying from transform to patch, false the other way.</param>
648
private IEnumerable<FileFacade> CopyFromTransformData(Output output)
649
{
654
- CopyTransformDataCommand command = new CopyTransformDataCommand();
650
+ var command = new CopyTransformDataCommand();
651
command.CopyOutFileRows = true;
656
- command.FileManagerCore = this.FileManagerCore;
657
- command.FileManagers = this.FileManagers;
652
command.Output = output;
653
command.TableDefinitions = this.TableDefinitions;
654
command.Execute();
@@ -669,35 +663,13 @@ namespace WixToolset.Bind
663
/// <param name="allFileRows">True if copying from transform to patch, false the other way.</param>
664
private void CopyToTransformData(Output output)
665
{
672
- CopyTransformDataCommand command = new CopyTransformDataCommand();
666
+ var command = new CopyTransformDataCommand();
667
command.CopyOutFileRows = false;
674
- command.FileManagerCore = this.FileManagerCore;
675
- command.FileManagers = this.FileManagers;
668
command.Output = output;
669
command.TableDefinitions = this.TableDefinitions;
670
command.Execute();
671
}
672
681
- /// <summary>
682
- /// Takes an id, and demodularizes it (if possible).
683
- /// </summary>
684
- /// <remarks>
685
- /// If the output type is a module, returns a demodularized version of an id. Otherwise, returns the id.
686
- /// </remarks>
687
- /// <param name="outputType">The type of the output to bind.</param>
688
- /// <param name="modularizationGuid">The modularization GUID.</param>
689
- /// <param name="id">The id to demodularize.</param>
690
- /// <returns>The demodularized id.</returns>
691
- internal static string Demodularize(OutputType outputType, string modularizationGuid, string id)
692
- {
693
- if (OutputType.Module == outputType && id.EndsWith(String.Concat(".", modularizationGuid), StringComparison.Ordinal))
694
- {
695
- id = id.Substring(0, id.Length - 37);
696
- }
697
-
698
- return id;
699
- }
700
-
673
private void UpdateMediaSequences(OutputType outputType, IEnumerable<FileFacade> fileFacades, RowDictionary<MediaRow> mediaRows)
674
{
675
// Calculate sequence numbers and media disk id layout for all file media information objects.
@@ -855,7 +827,7 @@ namespace WixToolset.Bind
827
continue;
828
}
829
858
- string targetName = Installer.GetName((string)row[2], false, true);
830
+ string targetName = Common.GetName((string)row[2], false, true);
831
directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName));
832
}
833
}
@@ -919,7 +891,7 @@ namespace WixToolset.Bind
891
{
892
// calculate the key file's canonical target path
893
string directoryPath = Binder.GetDirectoryPath(directories, componentIdGenSeeds, componentRow.Directory, true);
922
- string fileName = Installer.GetName(fileRow.FileName, false, true).ToLower(CultureInfo.InvariantCulture);
894
+ string fileName = Common.GetName(fileRow.FileName, false, true).ToLower(CultureInfo.InvariantCulture);
895
path = Path.Combine(directoryPath, fileName);
896
897
// find paths that are not canonicalized
@@ -1256,9 +1228,9 @@ namespace WixToolset.Bind
1228
{
1229
string layout = null;
1230
1259
- foreach (IBinderFileManager fileManager in this.FileManagers)
1231
+ foreach (var extension in this.BackendExtensions)
1232
{
1261
- layout = fileManager.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory);
1233
+ layout = extension.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory);
1234
if (!String.IsNullOrEmpty(layout))
1235
{
1236
break;
@@ -1294,16 +1266,15 @@ namespace WixToolset.Bind
1266
/// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
1267
private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
1268
{
1297
- GenerateDatabaseCommand command = new GenerateDatabaseCommand();
1269
+ var command = new GenerateDatabaseCommand();
1270
command.Extensions = this.Extensions;
1299
- command.FileManagers = this.FileManagers;
1271
command.Output = output;
1272
command.OutputPath = databaseFile;
1273
command.KeepAddedColumns = keepAddedColumns;
1274
command.UseSubDirectory = useSubdirectory;
1275
command.SuppressAddingValidationRows = this.SuppressAddingValidationRows;
1276
command.TableDefinitions = this.TableDefinitions;
1306
- command.TempFilesLocation = this.TempFilesLocation;
1277
+ command.TempFilesLocation = this.IntermediateFolder;
1278
command.Codepage = this.Codepage;
1279
command.Execute();
1280
}
src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
renamed
+2
-2
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Globalization;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Databases
9
/// <summary>
10
/// Binds the summary information table of a database.
11
/// </summary>
12
- internal class BindSummaryInfoCommand : ICommand
12
+ internal class BindSummaryInfoCommand
13
{
14
/// <summary>
15
/// The output to bind.
src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
renamed
+9
-12
@@ -1,6 +1,6 @@
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.Bind
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -11,12 +11,10 @@ namespace WixToolset.Bind
11
using WixToolset.Msi;
12
using WixToolset.Core.Native;
13
14
- internal class BindTransformCommand : ICommand
14
+ internal class BindTransformCommand
15
{
16
public IEnumerable<IBinderExtension> Extensions { private get; set; }
17
18
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
19
-
18
public TableDefinitionCollection TableDefinitions { private get; set; }
19
20
public string TempFilesLocation { private get; set; }
@@ -379,10 +377,10 @@ namespace WixToolset.Bind
377
}
378
}
379
382
- foreach (BinderExtension extension in this.Extensions)
383
- {
384
- extension.Finish(this.Transform);
385
- }
380
+ //foreach (BinderExtension extension in this.Extensions)
381
+ //{
382
+ // extension.PostBind(this.Context);
383
+ //}
384
385
// Any errors encountered up to this point can cause errors during generation.
386
if (Messaging.Instance.EncounteredError)
@@ -437,9 +435,9 @@ namespace WixToolset.Bind
435
private bool CompareFiles(string targetFile, string updatedFile)
436
{
437
bool? compared = null;
440
- foreach (IBinderFileManager fileManager in this.FileManagers)
438
+ foreach (var extension in this.Extensions)
439
{
442
- compared = fileManager.CompareFiles(targetFile, updatedFile);
440
+ compared = extension.CompareFiles(targetFile, updatedFile);
441
if (compared.HasValue)
442
{
443
break;
@@ -456,10 +454,9 @@ namespace WixToolset.Bind
454
455
private void GenerateDatabase(Output output, string outputPath, bool keepAddedColumns)
456
{
459
- GenerateDatabaseCommand command = new GenerateDatabaseCommand();
457
+ var command = new GenerateDatabaseCommand();
458
command.Codepage = output.Codepage;
459
command.Extensions = this.Extensions;
462
- command.FileManagers = this.FileManagers;
460
command.KeepAddedColumns = keepAddedColumns;
461
command.Output = output;
462
command.OutputPath = outputPath;
src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
renamed
+4
-3
@@ -1,15 +1,15 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections;
7
using System.IO;
8
using System.Linq;
9
using System.Threading;
10
- using WixToolset.Cab;
10
+ using WixToolset.Core.Bind;
11
+ using WixToolset.Core.Cab;
12
using WixToolset.Data;
12
- using WixToolset.Data.Rows;
13
14
/// <summary>
15
/// Builds cabinets using multiple threads. This implements a thread pool that generates cabinets with multiple
@@ -25,6 +25,7 @@ namespace WixToolset.Bind.Databases
25
private IntPtr newCabNamesCallBackAddress;
26
27
public int MaximumCabinetSizeForLargeFileSplitting { get; set; }
28
+
29
public int MaximumUncompressedMediaSize { get; set; }
30
31
/// <summary>
src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs
new
+122
@@ -0,0 +1,122 @@
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.Bind
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.IO;
8
+ using System.Linq;
9
+ using WixToolset.Core.Cab;
10
+ using WixToolset.Core.Bind;
11
+ using WixToolset.Data;
12
+ using WixToolset.Extensibility;
13
+
14
+ public class CabinetResolver
15
+ {
16
+ public CabinetResolver(string cabCachePath, IEnumerable<IWindowsInstallerBackendExtension> backendExtensions)
17
+ {
18
+ this.CabCachePath = cabCachePath;
19
+
20
+ this.BackendExtensions = backendExtensions;
21
+ }
22
+
23
+ private string CabCachePath { get; }
24
+
25
+ private IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { get; }
26
+
27
+ public ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades)
28
+ {
29
+ var filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList();
30
+
31
+ ResolvedCabinet resolved = null;
32
+
33
+ foreach (var extension in this.BackendExtensions)
34
+ {
35
+ resolved = extension.ResolveCabinet(cabinetPath, filesWithPath);
36
+
37
+ if (null != resolved)
38
+ {
39
+ return resolved;
40
+ }
41
+ }
42
+
43
+ // By default cabinet should be built and moved to the suggested location.
44
+ resolved = new ResolvedCabinet() { BuildOption = CabinetBuildOption.BuildAndMove, Path = cabinetPath };
45
+
46
+ // If a cabinet cache path was provided, change the location for the cabinet
47
+ // to be built to and check if there is a cabinet that can be reused.
48
+ if (!String.IsNullOrEmpty(this.CabCachePath))
49
+ {
50
+ string cabinetName = Path.GetFileName(cabinetPath);
51
+ resolved.Path = Path.Combine(this.CabCachePath, cabinetName);
52
+
53
+ if (CheckFileExists(resolved.Path))
54
+ {
55
+ // Assume that none of the following are true:
56
+ // 1. any files are added or removed
57
+ // 2. order of files changed or names changed
58
+ // 3. modified time changed
59
+ bool cabinetValid = true;
60
+
61
+ // Need to force garbage collection of WixEnumerateCab to ensure the handle
62
+ // associated with it is closed before it is reused.
63
+ using (var wixEnumerateCab = new WixEnumerateCab())
64
+ {
65
+ List<CabinetFileInfo> fileList = wixEnumerateCab.Enumerate(resolved.Path);
66
+
67
+ if (filesWithPath.Count() != fileList.Count)
68
+ {
69
+ cabinetValid = false;
70
+ }
71
+ else
72
+ {
73
+ int i = 0;
74
+ foreach (BindFileWithPath file in filesWithPath)
75
+ {
76
+ // First check that the file identifiers match because that is quick and easy.
77
+ CabinetFileInfo cabFileInfo = fileList[i];
78
+ cabinetValid = (cabFileInfo.FileId == file.Id);
79
+ if (cabinetValid)
80
+ {
81
+ // Still valid so ensure the file sizes are the same.
82
+ FileInfo fileInfo = new FileInfo(file.Path);
83
+ cabinetValid = (cabFileInfo.Size == fileInfo.Length);
84
+ if (cabinetValid)
85
+ {
86
+ // Still valid so ensure the source time stamp hasn't changed. Thus we need
87
+ // to convert the source file time stamp into a cabinet compatible data/time.
88
+ Native.CabInterop.DateTimeToCabDateAndTime(fileInfo.LastWriteTime, out var sourceCabDate, out var sourceCabTime);
89
+ cabinetValid = (cabFileInfo.Date == sourceCabDate && cabFileInfo.Time == sourceCabTime);
90
+ }
91
+ }
92
+
93
+ if (!cabinetValid)
94
+ {
95
+ break;
96
+ }
97
+
98
+ i++;
99
+ }
100
+ }
101
+ }
102
+
103
+ resolved.BuildOption = cabinetValid ? CabinetBuildOption.Copy : CabinetBuildOption.BuildAndCopy;
104
+ }
105
+ }
106
+
107
+ return resolved;
108
+ }
109
+
110
+ private static bool CheckFileExists(string path)
111
+ {
112
+ try
113
+ {
114
+ return File.Exists(path);
115
+ }
116
+ catch (ArgumentException)
117
+ {
118
+ throw new WixException(WixErrors.IllegalCharactersInPath(path));
119
+ }
120
+ }
121
+ }
122
+}
src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs
renamed
+2
-1
@@ -1,8 +1,9 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System.Collections.Generic;
6
+ using WixToolset.Core.Bind;
7
using WixToolset.Data;
8
using WixToolset.Data.Rows;
9
src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections;
src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
renamed
+8
-8
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -9,15 +9,14 @@ namespace WixToolset.Bind.Databases
9
using WixToolset.Data.Rows;
10
using WixToolset.Extensibility;
11
using WixToolset.Core.Native;
12
+ using WixToolset.Core.Bind;
13
13
- internal class CopyTransformDataCommand : ICommand
14
+ internal class CopyTransformDataCommand
15
{
16
public bool CopyOutFileRows { private get; set; }
17
17
- public BinderFileManagerCore FileManagerCore { private get; set; }
18
-
19
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
20
-
18
+ public IEnumerable<IBinderExtension> Extensions { private get; set; }
19
+
20
public Output Output { private get; set; }
21
22
public TableDefinitionCollection TableDefinitions { private get; set; }
@@ -586,9 +585,10 @@ namespace WixToolset.Bind.Databases
585
private bool CompareFiles(string targetFile, string updatedFile)
586
{
587
bool? compared = null;
589
- foreach (IBinderFileManager fileManager in this.FileManagers)
588
+ foreach (var extension in this.Extensions)
589
{
591
- compared = fileManager.CompareFiles(targetFile, updatedFile);
590
+ compared = extension.CompareFiles(targetFile, updatedFile);
591
+
592
if (compared.HasValue)
593
{
594
break;
src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
renamed
+33
-23
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -9,15 +9,21 @@ namespace WixToolset.Bind.Databases
9
using System.Linq;
10
using System.Runtime.InteropServices;
11
using System.Threading;
12
+ using WixToolset.Core.Bind;
13
+ using WixToolset.Core.WindowsInstaller.Bind;
14
using WixToolset.Data;
15
+ using WixToolset.Data.Bind;
16
using WixToolset.Data.Rows;
17
using WixToolset.Extensibility;
18
19
/// <summary>
20
/// Creates cabinet files.
21
/// </summary>
19
- internal class CreateCabinetsCommand : ICommand
22
+ internal class CreateCabinetsCommand
23
{
24
+ public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB
25
+ public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB)
26
+
27
private List<FileTransfer> fileTransfers;
28
29
private FileSplitCabNamesCallback newCabNamesCallBack;
@@ -28,7 +34,7 @@ namespace WixToolset.Bind.Databases
34
{
35
this.fileTransfers = new List<FileTransfer>();
36
31
- this.newCabNamesCallBack = NewCabNamesCallBack;
37
+ this.newCabNamesCallBack = this.NewCabNamesCallBack;
38
}
39
40
/// <summary>
@@ -36,6 +42,8 @@ namespace WixToolset.Bind.Databases
42
/// </summary>
43
public int CabbingThreadCount { private get; set; }
44
45
+ public string CabCachePath { private get; set; }
46
+
47
public string TempFilesLocation { private get; set; }
48
49
/// <summary>
@@ -44,9 +52,9 @@ namespace WixToolset.Bind.Databases
52
/// </summary>
53
public CompressionLevel DefaultCompressionLevel { private get; set; }
54
47
- public Output Output { private get; set; }
55
+ public IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { private get; set; }
56
49
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
57
+ public Output Output { private get; set; }
58
59
public string LayoutDirectory { private get; set; }
60
@@ -60,7 +68,7 @@ namespace WixToolset.Bind.Databases
68
69
public Table WixMediaTable { private get; set; }
70
63
- public IEnumerable<FileTransfer> FileTransfers { get { return this.fileTransfers; } }
71
+ public IEnumerable<FileTransfer> FileTransfers => this.fileTransfers;
72
73
/// <param name="output">Output to generate image for.</param>
74
/// <param name="fileTransfers">Array of files to be transfered.</param>
@@ -204,7 +212,9 @@ namespace WixToolset.Bind.Databases
212
}
213
}
214
207
- ResolvedCabinet resolvedCabinet = this.ResolveCabinet(tempCabinetFileX, fileFacades);
215
+ var cabinetResolver = new CabinetResolver(this.CabCachePath, this.BackendExtensions);
216
+
217
+ ResolvedCabinet resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades);
218
219
// create a cabinet work item if it's not being skipped
220
if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption)
@@ -255,23 +265,23 @@ namespace WixToolset.Bind.Databases
265
return cabinetWorkItem;
266
}
267
258
- private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades)
259
- {
260
- ResolvedCabinet resolved = null;
268
+ //private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades)
269
+ //{
270
+ // ResolvedCabinet resolved = null;
271
262
- List<BindFileWithPath> filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList();
272
+ // List<BindFileWithPath> filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList();
273
264
- foreach (IBinderFileManager fileManager in this.FileManagers)
265
- {
266
- resolved = fileManager.ResolveCabinet(cabinetPath, filesWithPath);
267
- if (null != resolved)
268
- {
269
- break;
270
- }
271
- }
274
+ // foreach (var extension in this.BackendExtensions)
275
+ // {
276
+ // resolved = extension.ResolveCabinet(cabinetPath, filesWithPath);
277
+ // if (null != resolved)
278
+ // {
279
+ // break;
280
+ // }
281
+ // }
282
273
- return resolved;
274
- }
283
+ // return resolved;
284
+ //}
285
286
/// <summary>
287
/// Delegate for Cabinet Split Callback
@@ -451,7 +461,7 @@ namespace WixToolset.Bind.Databases
461
}
462
catch (OverflowException)
463
{
454
- throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, CompilerCore.MaxValueOfMaxCabSizeForLargeFileSplitting));
464
+ throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, MaxValueOfMaxCabSizeForLargeFileSplitting));
465
}
466
467
try
@@ -482,7 +492,7 @@ namespace WixToolset.Bind.Databases
492
else
493
{
494
maxCabSizeForLargeFileSplitting = 0;
485
- maxUncompressedMediaSize = CompilerCore.DefaultMaximumUncompressedMediaSize;
495
+ maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize;
496
}
497
}
498
}
src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
renamed
+5
-4
@@ -1,18 +1,19 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.Globalization;
8
using System.IO;
9
+ using WixToolset.Core.Bind;
10
using WixToolset.Data;
11
using WixToolset.Data.Rows;
12
13
/// <summary>
14
/// Creates delta patches and updates the appropriate rows to point to the newly generated patches.
15
/// </summary>
15
- internal class CreateDeltaPatchesCommand : ICommand
16
+ internal class CreateDeltaPatchesCommand
17
{
18
public IEnumerable<FileFacade> FileFacades { private get; set; }
19
@@ -23,7 +24,7 @@ namespace WixToolset.Bind.Databases
24
public void Execute()
25
{
26
bool optimizePatchSizeForLargeFiles = false;
26
- PatchAPI.PatchInterop.PatchSymbolFlagsType apiPatchingSymbolFlags = 0;
27
+ PatchSymbolFlagsType apiPatchingSymbolFlags = 0;
28
29
if (null != this.WixPatchIdTable)
30
{
@@ -37,7 +38,7 @@ namespace WixToolset.Bind.Databases
38
39
if (null != row[3])
40
{
40
- apiPatchingSymbolFlags = (PatchAPI.PatchInterop.PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture);
41
+ apiPatchingSymbolFlags = (PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture);
42
}
43
}
44
}
src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs
renamed
+2
-2
@@ -1,13 +1,13 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
7
using WixToolset.Data;
8
using WixToolset.Data.Rows;
9
10
- internal class CreateSpecialPropertiesCommand : ICommand
10
+ internal class CreateSpecialPropertiesCommand
11
{
12
public Table PropertyTable { private get; set; }
13
src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
renamed
+5
-4
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -9,17 +9,18 @@ namespace WixToolset.Bind.Databases
9
using System.IO;
10
using System.Linq;
11
using System.Runtime.InteropServices;
12
- using WixToolset.Cab;
12
using WixToolset.Data;
13
using WixToolset.Data.Rows;
14
using WixToolset.MergeMod;
15
using WixToolset.Msi;
16
using WixToolset.Core.Native;
17
+ using WixToolset.Core.Bind;
18
+ using WixToolset.Core.Cab;
19
20
/// <summary>
21
/// Retrieve files information and extract them from merge modules.
22
/// </summary>
22
- internal class ExtractMergeModuleFilesCommand : ICommand
23
+ internal class ExtractMergeModuleFilesCommand
24
{
25
public IEnumerable<FileFacade> FileFacades { private get; set; }
26
@@ -193,7 +194,7 @@ namespace WixToolset.Bind.Databases
194
string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId);
195
Directory.CreateDirectory(mergeIdPath);
196
196
- using (WixExtractCab extractCab = new WixExtractCab())
197
+ using (var extractCab = new WixExtractCab())
198
{
199
try
200
{
src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
renamed
+2
-5
@@ -1,6 +1,6 @@
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.Bind
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -13,14 +13,12 @@ namespace WixToolset.Bind
13
using WixToolset.Msi;
14
using WixToolset.Core.Native;
15
16
- internal class GenerateDatabaseCommand : ICommand
16
+ internal class GenerateDatabaseCommand
17
{
18
public int Codepage { private get; set; }
19
20
public IEnumerable<IBinderExtension> Extensions { private get; set; }
21
22
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
23
-
22
/// <summary>
23
/// Whether to keep columns added in a transform.
24
/// </summary>
@@ -295,7 +293,6 @@ namespace WixToolset.Bind
293
{
294
BindTransformCommand command = new BindTransformCommand();
295
command.Extensions = this.Extensions;
298
- command.FileManagers = this.FileManagers;
296
command.TempFilesLocation = this.TempFilesLocation;
297
command.Transform = transform;
298
command.OutputPath = outputPath;
src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
renamed
+3
-2
@@ -1,15 +1,16 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.Globalization;
8
using System.Linq;
9
+ using WixToolset.Core.Bind;
10
using WixToolset.Data;
11
using WixToolset.Data.Rows;
12
12
- internal class GetFileFacadesCommand : ICommand
13
+ internal class GetFileFacadesCommand
14
{
15
public Table FileTable { private get; set; }
16
src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
renamed
+3
-2
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -20,11 +20,12 @@ namespace WixToolset.Bind.Databases
20
using WixToolset.MergeMod;
21
using WixToolset.Msi;
22
using WixToolset.Core.Native;
23
+ using WixToolset.Core.Bind;
24
25
/// <summary>
26
/// Update file information.
27
/// </summary>
27
- internal class MergeModulesCommand : ICommand
28
+ internal class MergeModulesCommand
29
{
30
public IEnumerable<FileFacade> FileFacades { private get; set; }
31
src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
renamed
+6
-3
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections;
@@ -10,11 +10,14 @@ namespace WixToolset.Bind.Databases
10
using WixToolset.Data.Rows;
11
using WixToolset.Msi;
12
using WixToolset.Core.Native;
13
+ using WixToolset.Bind;
14
+ using WixToolset.Core.Bind;
15
+ using WixToolset.Data.Bind;
16
17
/// <summary>
18
/// Defines the file transfers necessary to layout the uncompressed files.
19
/// </summary>
17
- internal class ProcessUncompressedFilesCommand : ICommand
20
+ internal class ProcessUncompressedFilesCommand
21
{
22
public string DatabasePath { private get; set; }
23
@@ -55,7 +58,7 @@ namespace WixToolset.Bind.Databases
58
break;
59
}
60
58
- string sourceName = Installer.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage);
61
+ string sourceName = Common.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage);
62
63
directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName));
64
}
src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs
renamed
+2
-2
@@ -1,13 +1,13 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.IO;
7
using WixToolset.Data;
8
using WixToolset.Data.Rows;
9
10
- internal class UpdateControlTextCommand : ICommand
10
+ internal class UpdateControlTextCommand
11
{
12
public Table BBControlTable { private get; set; }
13
src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
renamed
+10
-9
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.WindowsInstaller.Databases
4
{
5
using System;
6
using System.Collections.Generic;
@@ -12,6 +12,7 @@ namespace WixToolset.Bind.Databases
12
using System.Xml;
13
using System.Xml.XPath;
14
using WixToolset.Clr.Interop;
15
+ using WixToolset.Core.Bind;
16
using WixToolset.Data;
17
using WixToolset.Data.Rows;
18
using WixToolset.Msi;
@@ -19,7 +20,7 @@ namespace WixToolset.Bind.Databases
20
/// <summary>
21
/// Update file information.
22
/// </summary>
22
- internal class UpdateFileFacadesCommand : ICommand
23
+ internal class UpdateFileFacadesCommand
24
{
25
public IEnumerable<FileFacade> FileFacades { private get; set; }
26
@@ -52,17 +53,17 @@ namespace WixToolset.Bind.Databases
53
}
54
catch (ArgumentException)
55
{
55
- Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source));
56
+ Messaging.Instance.OnMessage(WixDataErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source));
57
return;
58
}
59
catch (PathTooLongException)
60
{
60
- Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source));
61
+ Messaging.Instance.OnMessage(WixDataErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source));
62
return;
63
}
64
catch (NotSupportedException)
65
{
65
- Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source));
66
+ Messaging.Instance.OnMessage(WixDataErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source));
67
return;
68
}
69
@@ -195,13 +196,13 @@ namespace WixToolset.Bind.Databases
196
{
197
if (!String.IsNullOrEmpty(file.File.Version))
198
{
198
- string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File));
199
+ string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File));
200
this.VariableCache[key] = file.File.Version;
201
}
202
203
if (!String.IsNullOrEmpty(file.File.Language))
204
{
204
- string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", BindDatabaseCommand.Demodularize(this.Output.Type, ModularizationGuid, file.File.File));
205
+ string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", Common.Demodularize(this.Output.Type, ModularizationGuid, file.File.File));
206
this.VariableCache[key] = file.File.Language;
207
}
208
}
@@ -326,7 +327,7 @@ namespace WixToolset.Bind.Databases
327
// add the assembly name to the information cache
328
if (null != this.VariableCache)
329
{
329
- string fileId = BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File);
330
+ string fileId = Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File);
331
string key = String.Concat("assemblyfullname.", fileId);
332
string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]);
333
if (assemblyNameValues.ContainsKey("processorArchitecture"))
@@ -523,7 +524,7 @@ namespace WixToolset.Bind.Databases
524
525
if (this.VariableCache != null)
526
{
526
- string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)).ToLowerInvariant();
527
+ string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)).ToLowerInvariant();
528
this.VariableCache[key] = (string)assemblyNameRow[2];
529
}
530
}
src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs
renamed
src/WixToolset.Core.WindowsInstaller/Differ.cs
renamed
+1
-11
@@ -6,6 +6,7 @@ namespace WixToolset
6
using System.Collections;
7
using System.Collections.Generic;
8
using System.Globalization;
9
+ using WixToolset.Core;
10
using WixToolset.Data;
11
using WixToolset.Data.Rows;
12
using WixToolset.Extensibility;
@@ -155,17 +156,6 @@ namespace WixToolset
156
this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags);
157
}
158
158
- // inspect the transform
159
- InspectorCore inspectorCore = new InspectorCore();
160
- foreach (InspectorExtension inspectorExtension in this.inspectorExtensions)
161
- {
162
- inspectorExtension.Core = inspectorCore;
163
- inspectorExtension.InspectOutput(transform);
164
-
165
- // reset
166
- inspectorExtension.Core = null;
167
- }
168
-
159
return transform;
160
}
161
src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
new
+282
@@ -0,0 +1,282 @@
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.Inscribe
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Globalization;
8
+ using System.IO;
9
+ using System.Runtime.InteropServices;
10
+ using System.Security.Cryptography.X509Certificates;
11
+ using WixToolset.Core.Native;
12
+ using WixToolset.Data;
13
+ using WixToolset.Extensibility;
14
+ using WixToolset.Msi;
15
+
16
+ internal class InscribeMsiPackageCommand
17
+ {
18
+ public InscribeMsiPackageCommand(IInscribeContext context)
19
+ {
20
+ this.Context = context;
21
+ this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions();
22
+ }
23
+
24
+ private IInscribeContext Context { get; }
25
+
26
+ private TableDefinitionCollection TableDefinitions { get; }
27
+
28
+ public bool Execute()
29
+ {
30
+ // Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
31
+ bool foundUnsignedExternals = false;
32
+ bool shouldCommit = false;
33
+
34
+ FileAttributes attributes = File.GetAttributes(this.Context.InputFilePath);
35
+ if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
36
+ {
37
+ this.Context.Messaging.OnMessage(WixErrors.ReadOnlyOutputFile(this.Context.InputFilePath));
38
+ return shouldCommit;
39
+ }
40
+
41
+ using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.Transact))
42
+ {
43
+ // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
44
+ int codepage = 1252;
45
+
46
+ // list of certificates for this database (hash/identifier)
47
+ Dictionary<string, string> certificates = new Dictionary<string, string>();
48
+
49
+ // Reset the in-memory tables for this new database
50
+ Table digitalSignatureTable = new Table(null, this.TableDefinitions["MsiDigitalSignature"]);
51
+ Table digitalCertificateTable = new Table(null, this.TableDefinitions["MsiDigitalCertificate"]);
52
+
53
+ // If any digital signature records exist that are not of the media type, preserve them
54
+ if (database.TableExists("MsiDigitalSignature"))
55
+ {
56
+ using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
57
+ {
58
+ while (true)
59
+ {
60
+ using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
61
+ {
62
+ if (null == digitalSignatureRecord)
63
+ {
64
+ break;
65
+ }
66
+
67
+ Row digitalSignatureRow = null;
68
+ digitalSignatureRow = digitalSignatureTable.CreateRow(null);
69
+
70
+ string table = digitalSignatureRecord.GetString(0);
71
+ string signObject = digitalSignatureRecord.GetString(1);
72
+
73
+ digitalSignatureRow[0] = table;
74
+ digitalSignatureRow[1] = signObject;
75
+ digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
76
+
77
+ if (false == digitalSignatureRecord.IsNull(3))
78
+ {
79
+ // Export to a file, because the MSI API's require us to provide a file path on disk
80
+ string hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature");
81
+ string hashFileName = string.Concat(table, ".", signObject, ".bin");
82
+
83
+ Directory.CreateDirectory(hashPath);
84
+ hashPath = Path.Combine(hashPath, hashFileName);
85
+
86
+ using (FileStream fs = File.Create(hashPath))
87
+ {
88
+ int bytesRead;
89
+ byte[] buffer = new byte[1024 * 4];
90
+
91
+ while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
92
+ {
93
+ fs.Write(buffer, 0, bytesRead);
94
+ }
95
+ }
96
+
97
+ digitalSignatureRow[3] = hashFileName;
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ // If any digital certificates exist, extract and preserve them
105
+ if (database.TableExists("MsiDigitalCertificate"))
106
+ {
107
+ using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
108
+ {
109
+ while (true)
110
+ {
111
+ using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
112
+ {
113
+ if (null == digitalCertificateRecord)
114
+ {
115
+ break;
116
+ }
117
+
118
+ string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
119
+
120
+ // Export to a file, because the MSI API's require us to provide a file path on disk
121
+ string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
122
+ Directory.CreateDirectory(certPath);
123
+ certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
124
+
125
+ using (FileStream fs = File.Create(certPath))
126
+ {
127
+ int bytesRead;
128
+ byte[] buffer = new byte[1024 * 4];
129
+
130
+ while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
131
+ {
132
+ fs.Write(buffer, 0, bytesRead);
133
+ }
134
+ }
135
+
136
+ // Add it to our "add to MsiDigitalCertificate" table dictionary
137
+ Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
138
+ digitalCertificateRow[0] = certificateId;
139
+
140
+ // Now set the file path on disk where this binary stream will be picked up at import time
141
+ digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
142
+
143
+ // Load the cert to get it's thumbprint
144
+ X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
145
+ X509Certificate2 cert2 = new X509Certificate2(cert);
146
+
147
+ certificates.Add(cert2.Thumbprint, certificateId);
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
154
+ {
155
+ while (true)
156
+ {
157
+ using (Record mediaRecord = mediaView.Fetch())
158
+ {
159
+ if (null == mediaRecord)
160
+ {
161
+ break;
162
+ }
163
+
164
+ X509Certificate2 cert2 = null;
165
+ Row digitalSignatureRow = null;
166
+
167
+ string cabName = mediaRecord.GetString(4); // get the name of the cab
168
+ // If there is no cabinet or it's an internal cab, skip it.
169
+ if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
170
+ {
171
+ continue;
172
+ }
173
+
174
+ string cabId = mediaRecord.GetString(1); // get the ID of the cab
175
+ string cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName);
176
+
177
+ // If the cabs aren't there, throw an error but continue to catch the other errors
178
+ if (!File.Exists(cabPath))
179
+ {
180
+ this.Context.Messaging.OnMessage(WixErrors.WixFileNotFound(cabPath));
181
+ continue;
182
+ }
183
+
184
+ try
185
+ {
186
+ // Get the certificate from the cab
187
+ X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
188
+ cert2 = new X509Certificate2(signedFileCert);
189
+ }
190
+ catch (System.Security.Cryptography.CryptographicException e)
191
+ {
192
+ uint HResult = unchecked((uint)Marshal.GetHRForException(e));
193
+
194
+ // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
195
+ if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
196
+ {
197
+ foundUnsignedExternals = true;
198
+ continue;
199
+ }
200
+
201
+ // todo: exactly which HRESULT corresponds to this issue?
202
+ // If it's one of these exact platforms, warn the user that it may be due to their OS.
203
+ if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
204
+ (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
205
+ {
206
+ this.Context.Messaging.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
207
+ }
208
+ else // otherwise, generic error
209
+ {
210
+ this.Context.Messaging.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
211
+ }
212
+ }
213
+
214
+ // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
215
+ if (!certificates.ContainsKey(cert2.Thumbprint))
216
+ {
217
+ // generate a stable identifier
218
+ string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
219
+
220
+ // Add it to our "add to MsiDigitalCertificate" table dictionary
221
+ Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
222
+ digitalCertificateRow[0] = certificateGeneratedId;
223
+
224
+ // Export to a file, because the MSI API's require us to provide a file path on disk
225
+ string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
226
+ Directory.CreateDirectory(certPath);
227
+ certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
228
+ File.Delete(certPath);
229
+
230
+ using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
231
+ {
232
+ writer.Write(cert2.RawData);
233
+ writer.Close();
234
+ }
235
+
236
+ // Now set the file path on disk where this binary stream will be picked up at import time
237
+ digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
238
+
239
+ certificates.Add(cert2.Thumbprint, certificateGeneratedId);
240
+ }
241
+
242
+ digitalSignatureRow = digitalSignatureTable.CreateRow(null);
243
+
244
+ digitalSignatureRow[0] = "Media";
245
+ digitalSignatureRow[1] = cabId;
246
+ digitalSignatureRow[2] = certificates[cert2.Thumbprint];
247
+ }
248
+ }
249
+ }
250
+
251
+ if (digitalCertificateTable.Rows.Count > 0)
252
+ {
253
+ database.ImportTable(codepage, digitalCertificateTable, this.Context.IntermediateFolder, true);
254
+ shouldCommit = true;
255
+ }
256
+
257
+ if (digitalSignatureTable.Rows.Count > 0)
258
+ {
259
+ database.ImportTable(codepage, digitalSignatureTable, this.Context.IntermediateFolder, true);
260
+ shouldCommit = true;
261
+ }
262
+
263
+ // TODO: if we created the table(s), then we should add the _Validation records for them.
264
+
265
+ certificates = null;
266
+
267
+ // If we did find external cabs but none of them were signed, give a warning
268
+ if (foundUnsignedExternals)
269
+ {
270
+ this.Context.Messaging.OnMessage(WixWarnings.ExternalCabsAreNotSigned(this.Context.InputFilePath));
271
+ }
272
+
273
+ if (shouldCommit)
274
+ {
275
+ database.Commit();
276
+ }
277
+ }
278
+
279
+ return shouldCommit;
280
+ }
281
+ }
282
+}
src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
renamed
-121
@@ -359,126 +359,5 @@ namespace WixToolset.Msi
359
{
360
return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd);
361
}
362
-
363
- /// <summary>
364
- /// Get the source/target and short/long file names from an MSI Filename column.
365
- /// </summary>
366
- /// <param name="value">The Filename value.</param>
367
- /// <returns>An array of strings of length 4. The contents are: short target, long target, short source, and long source.</returns>
368
- /// <remarks>
369
- /// If any particular file name part is not parsed, its set to null in the appropriate location of the returned array of strings.
370
- /// However, the returned array will always be of length 4.
371
- /// </remarks>
372
- internal static string[] GetNames(string value)
373
- {
374
- string[] names = new string[4];
375
- int targetSeparator = value.IndexOf(":", StringComparison.Ordinal);
376
-
377
- // split source and target
378
- string sourceName = null;
379
- string targetName = value;
380
- if (0 <= targetSeparator)
381
- {
382
- sourceName = value.Substring(targetSeparator + 1);
383
- targetName = value.Substring(0, targetSeparator);
384
- }
385
-
386
- // split the source short and long names
387
- string sourceLongName = null;
388
- if (null != sourceName)
389
- {
390
- int sourceLongNameSeparator = sourceName.IndexOf("|", StringComparison.Ordinal);
391
- if (0 <= sourceLongNameSeparator)
392
- {
393
- sourceLongName = sourceName.Substring(sourceLongNameSeparator + 1);
394
- sourceName = sourceName.Substring(0, sourceLongNameSeparator);
395
- }
396
- }
397
-
398
- // split the target short and long names
399
- int targetLongNameSeparator = targetName.IndexOf("|", StringComparison.Ordinal);
400
- string targetLongName = null;
401
- if (0 <= targetLongNameSeparator)
402
- {
403
- targetLongName = targetName.Substring(targetLongNameSeparator + 1);
404
- targetName = targetName.Substring(0, targetLongNameSeparator);
405
- }
406
-
407
- // remove the long source name when its identical to the long source name
408
- if (null != sourceName && sourceName == sourceLongName)
409
- {
410
- sourceLongName = null;
411
- }
412
-
413
- // remove the long target name when its identical to the long target name
414
- if (null != targetName && targetName == targetLongName)
415
- {
416
- targetLongName = null;
417
- }
418
-
419
- // remove the source names when they are identical to the target names
420
- if (sourceName == targetName && sourceLongName == targetLongName)
421
- {
422
- sourceName = null;
423
- sourceLongName = null;
424
- }
425
-
426
- // target name(s)
427
- if ("." != targetName)
428
- {
429
- names[0] = targetName;
430
- }
431
-
432
- if (null != targetLongName && "." != targetLongName)
433
- {
434
- names[1] = targetLongName;
435
- }
436
-
437
- // source name(s)
438
- if (null != sourceName)
439
- {
440
- names[2] = sourceName;
441
- }
442
-
443
- if (null != sourceLongName && "." != sourceLongName)
444
- {
445
- names[3] = sourceLongName;
446
- }
447
-
448
- return names;
449
- }
450
-
451
- /// <summary>
452
- /// Get a source/target and short/long file name from an MSI Filename column.
453
- /// </summary>
454
- /// <param name="value">The Filename value.</param>
455
- /// <param name="source">true to get a source name; false to get a target name</param>
456
- /// <param name="longName">true to get a long name; false to get a short name</param>
457
- /// <returns>The name.</returns>
458
- internal static string GetName(string value, bool source, bool longName)
459
- {
460
- string[] names = GetNames(value);
461
-
462
- if (source)
463
- {
464
- if (longName && null != names[3])
465
- {
466
- return names[3];
467
- }
468
- else if (null != names[2])
469
- {
470
- return names[2];
471
- }
472
- }
473
-
474
- if (longName && null != names[1])
475
- {
476
- return names[1];
477
- }
478
- else
479
- {
480
- return names[0];
481
- }
482
- }
362
}
363
}
src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
renamed
src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
renamed
-78
@@ -3,11 +3,8 @@
3
namespace WixToolset.Msi
4
{
5
using System;
6
- using System.ComponentModel;
7
- using System.Diagnostics.CodeAnalysis;
6
using System.Globalization;
7
using System.Text;
10
- using System.Runtime.InteropServices;
8
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
9
using WixToolset.Core.Native;
10
@@ -245,79 +242,4 @@ namespace WixToolset.Msi
242
}
243
}
244
}
248
-
249
- /// <summary>
250
- /// Summary information values for the CharCount property in transforms.
251
- /// </summary>
252
- [Flags]
253
- [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
254
- public enum TransformFlags
255
- {
256
- /// <summary>Ignore error when adding a row that exists.</summary>
257
- ErrorAddExistingRow = 0x1,
258
-
259
- /// <summary>Ignore error when deleting a row that does not exist.</summary>
260
- ErrorDeleteMissingRow = 0x2,
261
-
262
- /// <summary>Ignore error when adding a table that exists. </summary>
263
- ErrorAddExistingTable = 0x4,
264
-
265
- /// <summary>Ignore error when deleting a table that does not exist. </summary>
266
- ErrorDeleteMissingTable = 0x8,
267
-
268
- /// <summary>Ignore error when updating a row that does not exist. </summary>
269
- ErrorUpdateMissingRow = 0x10,
270
-
271
- /// <summary>Ignore error when transform and database code pages do not match, and their code pages are neutral.</summary>
272
- ErrorChangeCodePage = 0x20,
273
-
274
- /// <summary>Default language must match base database. </summary>
275
- ValidateLanguage = 0x10000,
276
-
277
- /// <summary>Product must match base database.</summary>
278
- ValidateProduct = 0x20000,
279
-
280
- /// <summary>Check major version only. </summary>
281
- ValidateMajorVersion = 0x80000,
282
-
283
- /// <summary>Check major and minor versions only. </summary>
284
- ValidateMinorVersion = 0x100000,
285
-
286
- /// <summary>Check major, minor, and update versions.</summary>
287
- ValidateUpdateVersion = 0x200000,
288
-
289
- /// <summary>Installed version lt base version. </summary>
290
- ValidateNewLessBaseVersion = 0x400000,
291
-
292
- /// <summary>Installed version lte base version. </summary>
293
- ValidateNewLessEqualBaseVersion = 0x800000,
294
-
295
- /// <summary>Installed version eq base version. </summary>
296
- ValidateNewEqualBaseVersion = 0x1000000,
297
-
298
- /// <summary>Installed version gte base version.</summary>
299
- ValidateNewGreaterEqualBaseVersion = 0x2000000,
300
-
301
- /// <summary>Installed version gt base version.</summary>
302
- ValidateNewGreaterBaseVersion = 0x4000000,
303
-
304
- /// <summary>UpgradeCode must match base database.</summary>
305
- ValidateUpgradeCode = 0x8000000,
306
-
307
- /// <summary>Masks all version checks on ProductVersion.</summary>
308
- ProductVersionMask = ValidateMajorVersion | ValidateMinorVersion | ValidateUpdateVersion,
309
-
310
- /// <summary>Masks all operations on ProductVersion.</summary>
311
- ProductVersionOperatorMask = ValidateNewLessBaseVersion | ValidateNewLessEqualBaseVersion | ValidateNewEqualBaseVersion | ValidateNewGreaterEqualBaseVersion | ValidateNewGreaterBaseVersion,
312
-
313
- /// <summary>Default value for instance transforms.</summary>
314
- InstanceTransformDefault = ErrorAddExistingRow | ErrorDeleteMissingRow | ErrorAddExistingTable | ErrorDeleteMissingTable | ErrorUpdateMissingRow | ErrorChangeCodePage | ValidateProduct | ValidateUpdateVersion | ValidateNewGreaterEqualBaseVersion,
315
-
316
- /// <summary>Default value for language transforms.</summary>
317
- LanguageTransformDefault = ErrorAddExistingRow | ErrorDeleteMissingRow | ErrorAddExistingTable | ErrorDeleteMissingTable | ErrorUpdateMissingRow | ErrorChangeCodePage | ValidateProduct,
318
-
319
- /// <summary>Default value for patch transforms.</summary>
320
- PatchTransformDefault = ErrorAddExistingRow | ErrorDeleteMissingRow | ErrorAddExistingTable | ErrorDeleteMissingTable | ErrorUpdateMissingRow | ValidateProduct | ValidateUpdateVersion | ValidateNewEqualBaseVersion | ValidateUpgradeCode,
321
- }
322
-
245
}
src/WixToolset.Core.WindowsInstaller/Msi/View.cs
renamed
src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
new
+36
@@ -0,0 +1,36 @@
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
4
+{
5
+ using WixToolset.Core.WindowsInstaller.Bind;
6
+ using WixToolset.Core.WindowsInstaller.Inscribe;
7
+ using WixToolset.Core.WindowsInstaller.Unbind;
8
+ using WixToolset.Data;
9
+ using WixToolset.Data.Bind;
10
+ using WixToolset.Extensibility;
11
+
12
+ internal class MsiBackend : IBackend
13
+ {
14
+ public BindResult Bind(IBindContext context)
15
+ {
16
+ var validator = Validator.CreateFromContext(context, "darice.cub");
17
+
18
+ var command = new BindDatabaseCommand(context, validator);
19
+ command.Execute();
20
+
21
+ return new BindResult(command.FileTransfers, command.ContentFilePaths);
22
+ }
23
+
24
+ public bool Inscribe(IInscribeContext context)
25
+ {
26
+ var command = new InscribeMsiPackageCommand(context);
27
+ return command.Execute();
28
+ }
29
+
30
+ public Output Unbind(IUnbindContext context)
31
+ {
32
+ var command = new UnbindMsiOrMsmCommand(context);
33
+ return command.Execute();
34
+ }
35
+ }
36
+}
src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
new
+34
@@ -0,0 +1,34 @@
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
4
+{
5
+ using WixToolset.Core.WindowsInstaller.Bind;
6
+ using WixToolset.Core.WindowsInstaller.Unbind;
7
+ using WixToolset.Data;
8
+ using WixToolset.Data.Bind;
9
+ using WixToolset.Extensibility;
10
+
11
+ internal class MsmBackend : IBackend
12
+ {
13
+ public BindResult Bind(IBindContext context)
14
+ {
15
+ var validator = Validator.CreateFromContext(context, "mergemod.cub");
16
+
17
+ var command = new BindDatabaseCommand(context, validator);
18
+ command.Execute();
19
+
20
+ return new BindResult(command.FileTransfers, command.ContentFilePaths);
21
+ }
22
+
23
+ public bool Inscribe(IInscribeContext context)
24
+ {
25
+ return false;
26
+ }
27
+
28
+ public Output Unbind(IUnbindContext context)
29
+ {
30
+ var command = new UnbindMsiOrMsmCommand(context);
31
+ return command.Execute();
32
+ }
33
+ }
34
+}
src/WixToolset.Core.WindowsInstaller/MspBackend.cs
new
+111
@@ -0,0 +1,111 @@
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
4
+{
5
+ using System;
6
+ using System.ComponentModel;
7
+ using System.IO;
8
+ using WixToolset.Core.Native;
9
+ using WixToolset.Core.WindowsInstaller.Unbind;
10
+ using WixToolset.Data;
11
+ using WixToolset.Data.Bind;
12
+ using WixToolset.Extensibility;
13
+ using WixToolset.Msi;
14
+ using WixToolset.Ole32;
15
+
16
+ internal class MspBackend : IBackend
17
+ {
18
+ public BindResult Bind(IBindContext context)
19
+ {
20
+ throw new NotImplementedException();
21
+ }
22
+
23
+ public bool Inscribe(IInscribeContext context)
24
+ {
25
+ throw new NotImplementedException();
26
+ }
27
+
28
+ public Output Unbind(IUnbindContext context)
29
+ {
30
+ Output patch;
31
+
32
+ // patch files are essentially database files (use a special flag to let the API know its a patch file)
33
+ try
34
+ {
35
+ using (Database database = new Database(context.InputFilePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile))
36
+ {
37
+ var unbindCommand = new UnbindDatabaseCommand(context.Messaging, database, context.InputFilePath, OutputType.Patch, context.ExportBasePath, context.IntermediateFolder, context.IsAdminImage, context.SuppressDemodularization, skipSummaryInfo: false);
38
+ patch = unbindCommand.Execute();
39
+ }
40
+ }
41
+ catch (Win32Exception e)
42
+ {
43
+ if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
44
+ {
45
+ throw new WixException(WixErrors.OpenDatabaseFailed(context.InputFilePath));
46
+ }
47
+
48
+ throw;
49
+ }
50
+
51
+ // retrieve the transforms (they are in substorages)
52
+ using (Storage storage = Storage.Open(context.InputFilePath, StorageMode.Read | StorageMode.ShareDenyWrite))
53
+ {
54
+ Table summaryInformationTable = patch.Tables["_SummaryInformation"];
55
+ foreach (Row row in summaryInformationTable.Rows)
56
+ {
57
+ if (8 == (int)row[0]) // PID_LASTAUTHOR
58
+ {
59
+ string value = (string)row[1];
60
+
61
+ foreach (string decoratedSubStorageName in value.Split(';'))
62
+ {
63
+ string subStorageName = decoratedSubStorageName.Substring(1);
64
+ string transformFile = Path.Combine(context.IntermediateFolder, String.Concat("Transform", Path.DirectorySeparatorChar, subStorageName, ".mst"));
65
+
66
+ // ensure the parent directory exists
67
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(transformFile));
68
+
69
+ // copy the substorage to a new storage for the transform file
70
+ using (Storage subStorage = storage.OpenStorage(subStorageName))
71
+ {
72
+ using (Storage transformStorage = Storage.CreateDocFile(transformFile, StorageMode.ReadWrite | StorageMode.ShareExclusive | StorageMode.Create))
73
+ {
74
+ subStorage.CopyTo(transformStorage);
75
+ }
76
+ }
77
+
78
+ // unbind the transform
79
+ var unbindCommand= new UnbindTransformCommand(context.Messaging, transformFile, (null == context.ExportBasePath ? null : Path.Combine(context.ExportBasePath, subStorageName)), context.IntermediateFolder);
80
+ var transform = unbindCommand.Execute();
81
+
82
+ patch.SubStorages.Add(new SubStorage(subStorageName, transform));
83
+ }
84
+
85
+ break;
86
+ }
87
+ }
88
+ }
89
+
90
+ // extract the files from the cabinets
91
+ // TODO: use per-transform export paths for support of multi-product patches
92
+ if (null != context.ExportBasePath && !context.SuppressExtractCabinets)
93
+ {
94
+ using (Database database = new Database(context.InputFilePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile))
95
+ {
96
+ foreach (SubStorage subStorage in patch.SubStorages)
97
+ {
98
+ // only patch transforms should carry files
99
+ if (subStorage.Name.StartsWith("#", StringComparison.Ordinal))
100
+ {
101
+ var extractCommand = new ExtractCabinetsCommand(subStorage.Data, database, context.InputFilePath, context.ExportBasePath, context.IntermediateFolder);
102
+ extractCommand.Execute();
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ return patch;
109
+ }
110
+ }
111
+}
\ No newline at end of file
src/WixToolset.Core.WindowsInstaller/MstBackend.cs
new
+37
@@ -0,0 +1,37 @@
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
4
+{
5
+ using System;
6
+ using WixToolset.Core.WindowsInstaller.Databases;
7
+ using WixToolset.Core.WindowsInstaller.Unbind;
8
+ using WixToolset.Data;
9
+ using WixToolset.Data.Bind;
10
+ using WixToolset.Extensibility;
11
+
12
+ internal class MstBackend : IBackend
13
+ {
14
+ public BindResult Bind(IBindContext context)
15
+ {
16
+ var command = new BindTransformCommand();
17
+ command.Extensions = context.Extensions;
18
+ command.TempFilesLocation = context.IntermediateFolder;
19
+ command.Transform = context.IntermediateRepresentation;
20
+ command.OutputPath = context.OutputPath;
21
+ command.Execute();
22
+
23
+ return new BindResult(Array.Empty<FileTransfer>(), Array.Empty<string>());
24
+ }
25
+
26
+ public bool Inscribe(IInscribeContext context)
27
+ {
28
+ throw new NotImplementedException();
29
+ }
30
+
31
+ public Output Unbind(IUnbindContext context)
32
+ {
33
+ var command = new UnbindMsiOrMsmCommand(context);
34
+ return command.Execute();
35
+ }
36
+ }
37
+}
\ No newline at end of file
src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs
renamed
src/WixToolset.Core.WindowsInstaller/Patch.cs
renamed
+1
-40
@@ -9,35 +9,8 @@ namespace WixToolset.Data
9
using System.Globalization;
10
using WixToolset.Data.Rows;
11
using WixToolset.Extensibility;
12
- using WixToolset.Msi;
12
using WixToolset.Core.Native;
14
-
15
- /// <summary>
16
- /// Values for the OptimizeCA MsiPatchMetdata property, which indicates whether custom actions can be skipped when applying the patch.
17
- /// </summary>
18
- [Flags]
19
- internal enum OptimizeCA
20
- {
21
- /// <summary>
22
- /// No custom actions are skipped.
23
- /// </summary>
24
- None = 0,
25
-
26
- /// <summary>
27
- /// Skip property (type 51) and directory (type 35) assignment custom actions.
28
- /// </summary>
29
- SkipAssignment = 1,
30
-
31
- /// <summary>
32
- /// Skip immediate custom actions that are not property or directory assignment custom actions.
33
- /// </summary>
34
- SkipImmediate = 2,
35
-
36
- /// <summary>
37
- /// Skip custom actions that run within the script.
38
- /// </summary>
39
- SkipDeferred = 4,
40
- }
13
+ using WixToolset.Msi;
14
15
/// <summary>
16
/// Contains output tables and logic for building an MSP package.
@@ -80,8 +53,6 @@ namespace WixToolset.Data
53
[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
54
public void AttachTransforms(List<PatchTransform> transforms)
55
{
83
- InspectorCore inspectorCore = new InspectorCore();
84
-
56
// Track if at least one transform gets attached.
57
bool attachedTransform = false;
58
@@ -429,16 +400,6 @@ namespace WixToolset.Data
400
Row savedbyRow = patchSummaryInfo.CreateRow(null);
401
savedbyRow[0] = (int)SummaryInformation.Patch.TransformNames;
402
savedbyRow[1] = String.Join(";", (string[])transformNames.ToArray(typeof(string)));
432
-
433
- // inspect the patch and filtered transforms
434
- foreach (InspectorExtension inspectorExtension in this.inspectorExtensions)
435
- {
436
- inspectorExtension.Core = inspectorCore;
437
- inspectorExtension.InspectOutput(this.patch);
438
-
439
- // reset
440
- inspectorExtension.Core = null;
441
- }
403
}
404
405
/// <summary>
src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs
renamed
+1
-14
@@ -7,6 +7,7 @@ namespace WixToolset.PatchAPI
7
using System.Diagnostics.CodeAnalysis;
8
using System.Globalization;
9
using System.Runtime.InteropServices;
10
+ using WixToolset.Core;
11
12
/// <summary>
13
/// Interop class for the mspatchc.dll.
@@ -322,20 +323,6 @@ namespace WixToolset.PatchAPI
323
324
internal const uint PATCH_OPTION_VALID_FLAGS = 0xC0FF0007;
325
325
- //
326
- // The following flags are used with PATCH_OPTION_DATA SymbolOptionFlags:
327
- //
328
-
329
- [Flags]
330
- public enum PatchSymbolFlagsType :uint
331
- {
332
- PATCH_SYMBOL_NO_IMAGEHLP = 0x00000001, // don't use imagehlp.dll
333
- PATCH_SYMBOL_NO_FAILURES = 0x00000002, // don't fail patch due to imagehlp failures
334
- PATCH_SYMBOL_UNDECORATED_TOO = 0x00000004, // after matching decorated symbols, try to match remaining by undecorated names
335
- PATCH_SYMBOL_RESERVED1 = 0x80000000, // (used internally)
336
- MaxValue = PATCH_SYMBOL_NO_IMAGEHLP | PATCH_SYMBOL_NO_FAILURES | PATCH_SYMBOL_UNDECORATED_TOO
337
- }
338
-
326
//
327
// The following flags are used with PATCH_OPTION_DATA ExtendedOptionFlags:
328
//
src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
new
+146
@@ -0,0 +1,146 @@
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.Unbind
4
+{
5
+ using System;
6
+ using System.Collections;
7
+ using System.Collections.Specialized;
8
+ using System.Globalization;
9
+ using System.IO;
10
+ using WixToolset.Core.Cab;
11
+ using WixToolset.Data;
12
+ using WixToolset.Data.Rows;
13
+ using WixToolset.Msi;
14
+
15
+ internal class ExtractCabinetsCommand
16
+ {
17
+ public ExtractCabinetsCommand(Output output, Database database, string inputFilePath, string exportBasePath, string intermediateFolder)
18
+ {
19
+ this.Output = output;
20
+ this.Database = database;
21
+ this.InputFilePath = inputFilePath;
22
+ this.ExportBasePath = exportBasePath;
23
+ this.IntermediateFolder = intermediateFolder;
24
+ }
25
+
26
+ private Output Output { get; }
27
+
28
+ private Database Database { get; }
29
+
30
+ private string InputFilePath { get; }
31
+
32
+ private string ExportBasePath { get; }
33
+
34
+ private string IntermediateFolder { get; }
35
+
36
+ public void Execute()
37
+ {
38
+ string databaseBasePath = Path.GetDirectoryName(this.InputFilePath);
39
+ StringCollection cabinetFiles = new StringCollection();
40
+ SortedList embeddedCabinets = new SortedList();
41
+
42
+ // index all of the cabinet files
43
+ if (OutputType.Module == this.Output.Type)
44
+ {
45
+ embeddedCabinets.Add(0, "MergeModule.CABinet");
46
+ }
47
+ else if (null != this.Output.Tables["Media"])
48
+ {
49
+ foreach (MediaRow mediaRow in this.Output.Tables["Media"].Rows)
50
+ {
51
+ if (null != mediaRow.Cabinet)
52
+ {
53
+ if (OutputType.Product == this.Output.Type ||
54
+ (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation))
55
+ {
56
+ if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal))
57
+ {
58
+ embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1));
59
+ }
60
+ else
61
+ {
62
+ cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet));
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ // extract the embedded cabinet files from the database
70
+ if (0 < embeddedCabinets.Count)
71
+ {
72
+ using (View streamsView = this.Database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?"))
73
+ {
74
+ foreach (int diskId in embeddedCabinets.Keys)
75
+ {
76
+ using (Record record = new Record(1))
77
+ {
78
+ record.SetString(1, (string)embeddedCabinets[diskId]);
79
+ streamsView.Execute(record);
80
+ }
81
+
82
+ using (Record record = streamsView.Fetch())
83
+ {
84
+ if (null != record)
85
+ {
86
+ // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not case-sensitive,
87
+ // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work
88
+ string cabinetFile = Path.Combine(this.IntermediateFolder, String.Concat("Media", Path.DirectorySeparatorChar, diskId.ToString(CultureInfo.InvariantCulture), ".cab"));
89
+
90
+ // ensure the parent directory exists
91
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile));
92
+
93
+ using (FileStream fs = System.IO.File.Create(cabinetFile))
94
+ {
95
+ int bytesRead;
96
+ byte[] buffer = new byte[512];
97
+
98
+ while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length)))
99
+ {
100
+ fs.Write(buffer, 0, bytesRead);
101
+ }
102
+ }
103
+
104
+ cabinetFiles.Add(cabinetFile);
105
+ }
106
+ else
107
+ {
108
+ // TODO: warning about missing embedded cabinet
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ // extract the cabinet files
116
+ if (0 < cabinetFiles.Count)
117
+ {
118
+ string fileDirectory = Path.Combine(this.ExportBasePath, "File");
119
+
120
+ // delete the directory and its files to prevent cab extraction due to an existing file
121
+ if (Directory.Exists(fileDirectory))
122
+ {
123
+ Directory.Delete(fileDirectory, true);
124
+ }
125
+
126
+ // ensure the directory exists or extraction will fail
127
+ Directory.CreateDirectory(fileDirectory);
128
+
129
+ foreach (string cabinetFile in cabinetFiles)
130
+ {
131
+ using (var extractCab = new WixExtractCab())
132
+ {
133
+ try
134
+ {
135
+ extractCab.Extract(cabinetFile, fileDirectory);
136
+ }
137
+ catch (FileNotFoundException)
138
+ {
139
+ throw new WixException(WixErrors.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetFile));
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+}
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
new
+791
@@ -0,0 +1,791 @@
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.Unbind
4
+{
5
+ using System;
6
+ using System.Collections;
7
+ using System.Collections.Generic;
8
+ using System.Globalization;
9
+ using System.IO;
10
+ using System.Text.RegularExpressions;
11
+ using WixToolset.Core.Native;
12
+ using WixToolset.Data;
13
+ using WixToolset.Data.Rows;
14
+ using WixToolset.Msi;
15
+
16
+ internal class UnbindDatabaseCommand
17
+ {
18
+ public UnbindDatabaseCommand(Messaging messaging, Database database, string databasePath, OutputType outputType, string exportBasePath, string intermediateFolder, bool isAdminImage, bool suppressDemodularization, bool skipSummaryInfo)
19
+ {
20
+ this.Messaging = messaging;
21
+ this.Database = database;
22
+ this.DatabasePath = databasePath;
23
+ this.OutputType = outputType;
24
+ this.ExportBasePath = exportBasePath;
25
+ this.IntermediateFolder = intermediateFolder;
26
+ this.IsAdminImage = isAdminImage;
27
+ this.SuppressDemodularization = suppressDemodularization;
28
+ this.SkipSummaryInfo = skipSummaryInfo;
29
+
30
+ this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions();
31
+ }
32
+
33
+ public Messaging Messaging { get; }
34
+
35
+ public Database Database { get; }
36
+
37
+ public string DatabasePath { get; }
38
+
39
+ public OutputType OutputType { get; }
40
+
41
+ public string ExportBasePath { get; }
42
+
43
+ public string IntermediateFolder { get; }
44
+
45
+ public bool IsAdminImage { get; }
46
+
47
+ public bool SuppressDemodularization { get; }
48
+
49
+ public bool SkipSummaryInfo { get; }
50
+
51
+ public TableDefinitionCollection TableDefinitions { get; }
52
+
53
+ private int SectionCount { get; set; }
54
+
55
+ public Output Execute()
56
+ {
57
+ string modularizationGuid = null;
58
+ Output output = new Output(new SourceLineNumber(this.DatabasePath));
59
+ View validationView = null;
60
+
61
+ // set the output type
62
+ output.Type = this.OutputType;
63
+
64
+ // get the codepage
65
+ this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt");
66
+ using (StreamReader sr = File.OpenText(Path.Combine(this.IntermediateFolder, "_ForceCodepage.idt")))
67
+ {
68
+ string line;
69
+
70
+ while (null != (line = sr.ReadLine()))
71
+ {
72
+ string[] data = line.Split('\t');
73
+
74
+ if (2 == data.Length)
75
+ {
76
+ output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture);
77
+ }
78
+ }
79
+ }
80
+
81
+ // get the summary information table if it exists; it won't if unbinding a transform
82
+ if (!this.SkipSummaryInfo)
83
+ {
84
+ using (SummaryInformation summaryInformation = new SummaryInformation(this.Database))
85
+ {
86
+ Table table = new Table(null, this.TableDefinitions["_SummaryInformation"]);
87
+
88
+ for (int i = 1; 19 >= i; i++)
89
+ {
90
+ string value = summaryInformation.GetProperty(i);
91
+
92
+ if (0 < value.Length)
93
+ {
94
+ Row row = table.CreateRow(output.SourceLineNumbers);
95
+ row[0] = i;
96
+ row[1] = value;
97
+ }
98
+ }
99
+
100
+ output.Tables.Add(table);
101
+ }
102
+ }
103
+
104
+ try
105
+ {
106
+ // open a view on the validation table if it exists
107
+ if (this.Database.TableExists("_Validation"))
108
+ {
109
+ validationView = this.Database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?");
110
+ }
111
+
112
+ // get the normal tables
113
+ using (View tablesView = this.Database.OpenExecuteView("SELECT * FROM _Tables"))
114
+ {
115
+ while (true)
116
+ {
117
+ using (Record tableRecord = tablesView.Fetch())
118
+ {
119
+ if (null == tableRecord)
120
+ {
121
+ break;
122
+ }
123
+
124
+ string tableName = tableRecord.GetString(1);
125
+
126
+ using (View tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName)))
127
+ {
128
+ List<ColumnDefinition> columns;
129
+ using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES),
130
+ columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES))
131
+ {
132
+ // index the primary keys
133
+ HashSet<string> tablePrimaryKeys = new HashSet<string>();
134
+ using (Record primaryKeysRecord = this.Database.PrimaryKeys(tableName))
135
+ {
136
+ int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount();
137
+
138
+ for (int i = 1; i <= primaryKeysFieldCount; i++)
139
+ {
140
+ tablePrimaryKeys.Add(primaryKeysRecord.GetString(i));
141
+ }
142
+ }
143
+
144
+ int columnCount = columnNameRecord.GetFieldCount();
145
+ columns = new List<ColumnDefinition>(columnCount);
146
+ for (int i = 1; i <= columnCount; i++)
147
+ {
148
+ string columnName = columnNameRecord.GetString(i);
149
+ string idtType = columnTypeRecord.GetString(i);
150
+
151
+ ColumnType columnType;
152
+ int length;
153
+ bool nullable;
154
+
155
+ ColumnCategory columnCategory = ColumnCategory.Unknown;
156
+ ColumnModularizeType columnModularizeType = ColumnModularizeType.None;
157
+ bool primary = tablePrimaryKeys.Contains(columnName);
158
+ bool minValueSet = false;
159
+ int minValue = -1;
160
+ bool maxValueSet = false;
161
+ int maxValue = -1;
162
+ string keyTable = null;
163
+ bool keyColumnSet = false;
164
+ int keyColumn = -1;
165
+ string category = null;
166
+ string set = null;
167
+ string description = null;
168
+
169
+ // get the column type, length, and whether its nullable
170
+ switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture))
171
+ {
172
+ case 'i':
173
+ columnType = ColumnType.Number;
174
+ break;
175
+ case 'l':
176
+ columnType = ColumnType.Localized;
177
+ break;
178
+ case 's':
179
+ columnType = ColumnType.String;
180
+ break;
181
+ case 'v':
182
+ columnType = ColumnType.Object;
183
+ break;
184
+ default:
185
+ // TODO: error
186
+ columnType = ColumnType.Unknown;
187
+ break;
188
+ }
189
+ length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture);
190
+ nullable = Char.IsUpper(idtType[0]);
191
+
192
+ // try to get validation information
193
+ if (null != validationView)
194
+ {
195
+ using (Record validationRecord = new Record(2))
196
+ {
197
+ validationRecord.SetString(1, tableName);
198
+ validationRecord.SetString(2, columnName);
199
+
200
+ validationView.Execute(validationRecord);
201
+ }
202
+
203
+ using (Record validationRecord = validationView.Fetch())
204
+ {
205
+ if (null != validationRecord)
206
+ {
207
+ string validationNullable = validationRecord.GetString(3);
208
+ minValueSet = !validationRecord.IsNull(4);
209
+ minValue = (minValueSet ? validationRecord.GetInteger(4) : -1);
210
+ maxValueSet = !validationRecord.IsNull(5);
211
+ maxValue = (maxValueSet ? validationRecord.GetInteger(5) : -1);
212
+ keyTable = (!validationRecord.IsNull(6) ? validationRecord.GetString(6) : null);
213
+ keyColumnSet = !validationRecord.IsNull(7);
214
+ keyColumn = (keyColumnSet ? validationRecord.GetInteger(7) : -1);
215
+ category = (!validationRecord.IsNull(8) ? validationRecord.GetString(8) : null);
216
+ set = (!validationRecord.IsNull(9) ? validationRecord.GetString(9) : null);
217
+ description = (!validationRecord.IsNull(10) ? validationRecord.GetString(10) : null);
218
+
219
+ // check the validation nullable value against the column definition
220
+ if (null == validationNullable)
221
+ {
222
+ // TODO: warn for illegal validation nullable column
223
+ }
224
+ else if ((nullable && "Y" != validationNullable) || (!nullable && "N" != validationNullable))
225
+ {
226
+ // TODO: warn for mismatch between column definition and validation nullable
227
+ }
228
+
229
+ // convert category to ColumnCategory
230
+ if (null != category)
231
+ {
232
+ try
233
+ {
234
+ columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true);
235
+ }
236
+ catch (ArgumentException)
237
+ {
238
+ columnCategory = ColumnCategory.Unknown;
239
+ }
240
+ }
241
+ }
242
+ else
243
+ {
244
+ // TODO: warn about no validation information
245
+ }
246
+ }
247
+ }
248
+
249
+ // guess the modularization type
250
+ if ("Icon" == keyTable && 1 == keyColumn)
251
+ {
252
+ columnModularizeType = ColumnModularizeType.Icon;
253
+ }
254
+ else if ("Condition" == columnName)
255
+ {
256
+ columnModularizeType = ColumnModularizeType.Condition;
257
+ }
258
+ else if (ColumnCategory.Formatted == columnCategory || ColumnCategory.FormattedSDDLText == columnCategory)
259
+ {
260
+ columnModularizeType = ColumnModularizeType.Property;
261
+ }
262
+ else if (ColumnCategory.Identifier == columnCategory)
263
+ {
264
+ columnModularizeType = ColumnModularizeType.Column;
265
+ }
266
+
267
+ columns.Add(new ColumnDefinition(columnName, columnType, length, primary, nullable, columnModularizeType, (ColumnType.Localized == columnType), minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, columnCategory, set, description, true, true));
268
+ }
269
+ }
270
+
271
+ TableDefinition tableDefinition = new TableDefinition(tableName, columns, false, false);
272
+
273
+ // use our table definitions if core properties are the same; this allows us to take advantage
274
+ // of wix concepts like localizable columns which current code assumes
275
+ if (this.TableDefinitions.Contains(tableName) && 0 == tableDefinition.CompareTo(this.TableDefinitions[tableName]))
276
+ {
277
+ tableDefinition = this.TableDefinitions[tableName];
278
+ }
279
+
280
+ Table table = new Table(null, tableDefinition);
281
+
282
+ while (true)
283
+ {
284
+ using (Record rowRecord = tableView.Fetch())
285
+ {
286
+ if (null == rowRecord)
287
+ {
288
+ break;
289
+ }
290
+
291
+ int recordCount = rowRecord.GetFieldCount();
292
+ Row row = table.CreateRow(output.SourceLineNumbers);
293
+
294
+ for (int i = 0; recordCount > i && row.Fields.Length > i; i++)
295
+ {
296
+ if (rowRecord.IsNull(i + 1))
297
+ {
298
+ if (!row.Fields[i].Column.Nullable)
299
+ {
300
+ // TODO: display an error for a null value in a non-nullable field OR
301
+ // display a warning and put an empty string in the value to let the compiler handle it
302
+ // (the second option is risky because the later code may make certain assumptions about
303
+ // the contents of a row value)
304
+ }
305
+ }
306
+ else
307
+ {
308
+ switch (row.Fields[i].Column.Type)
309
+ {
310
+ case ColumnType.Number:
311
+ bool success = false;
312
+ int intValue = rowRecord.GetInteger(i + 1);
313
+ if (row.Fields[i].Column.IsLocalizable)
314
+ {
315
+ success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
316
+ }
317
+ else
318
+ {
319
+ success = row.BestEffortSetField(i, intValue);
320
+ }
321
+
322
+ if (!success)
323
+ {
324
+ this.Messaging.OnMessage(WixWarnings.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name));
325
+ }
326
+ break;
327
+ case ColumnType.Object:
328
+ string sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";
329
+
330
+ if (null != this.ExportBasePath)
331
+ {
332
+ string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
333
+ sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile);
334
+
335
+ // ensure the parent directory exists
336
+ System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
337
+
338
+ using (FileStream fs = System.IO.File.Create(sourceFile))
339
+ {
340
+ int bytesRead;
341
+ byte[] buffer = new byte[512];
342
+
343
+ while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
344
+ {
345
+ fs.Write(buffer, 0, bytesRead);
346
+ }
347
+ }
348
+ }
349
+
350
+ row[i] = sourceFile;
351
+ break;
352
+ default:
353
+ string value = rowRecord.GetString(i + 1);
354
+
355
+ switch (row.Fields[i].Column.Category)
356
+ {
357
+ case ColumnCategory.Guid:
358
+ value = value.ToUpper(CultureInfo.InvariantCulture);
359
+ break;
360
+ }
361
+
362
+ // de-modularize
363
+ if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
364
+ {
365
+ Regex modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");
366
+
367
+ if (null == modularizationGuid)
368
+ {
369
+ Match match = modularization.Match(value);
370
+ if (match.Success)
371
+ {
372
+ modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
373
+ }
374
+ }
375
+
376
+ value = modularization.Replace(value, String.Empty);
377
+ }
378
+
379
+ // escape "$(" for the preprocessor
380
+ value = value.Replace("$(", "$$(");
381
+
382
+ // escape things that look like wix variables
383
+ MatchCollection matches = Common.WixVariableRegex.Matches(value);
384
+ for (int j = matches.Count - 1; 0 <= j; j--)
385
+ {
386
+ value = value.Insert(matches[j].Index, "!");
387
+ }
388
+
389
+ row[i] = value;
390
+ break;
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+
397
+ output.Tables.Add(table);
398
+ }
399
+
400
+ }
401
+ }
402
+ }
403
+ }
404
+ finally
405
+ {
406
+ if (null != validationView)
407
+ {
408
+ validationView.Close();
409
+ }
410
+ }
411
+
412
+ // set the modularization guid as the PackageCode
413
+ if (null != modularizationGuid)
414
+ {
415
+ Table table = output.Tables["_SummaryInformation"];
416
+
417
+ foreach (Row row in table.Rows)
418
+ {
419
+ if (9 == (int)row[0]) // PID_REVNUMBER
420
+ {
421
+ row[1] = modularizationGuid;
422
+ }
423
+ }
424
+ }
425
+
426
+ if (this.IsAdminImage)
427
+ {
428
+ GenerateWixFileTable(this.DatabasePath, output);
429
+ GenerateSectionIds(output);
430
+ }
431
+
432
+ return output;
433
+ }
434
+
435
+ /// <summary>
436
+ /// Generates the WixFile table based on a path to an admin image msi and an Output.
437
+ /// </summary>
438
+ /// <param name="databaseFile">The path to the msi database file in an admin image.</param>
439
+ /// <param name="output">The Output that represents the msi database.</param>
440
+ private void GenerateWixFileTable(string databaseFile, Output output)
441
+ {
442
+ string adminRootPath = Path.GetDirectoryName(databaseFile);
443
+
444
+ Hashtable componentDirectoryIndex = new Hashtable();
445
+ Table componentTable = output.Tables["Component"];
446
+ foreach (Row row in componentTable.Rows)
447
+ {
448
+ componentDirectoryIndex.Add(row[0], row[2]);
449
+ }
450
+
451
+ // Index full source paths for all directories
452
+ Hashtable directoryDirectoryParentIndex = new Hashtable();
453
+ Hashtable directoryFullPathIndex = new Hashtable();
454
+ Hashtable directorySourceNameIndex = new Hashtable();
455
+ Table directoryTable = output.Tables["Directory"];
456
+ foreach (Row row in directoryTable.Rows)
457
+ {
458
+ directoryDirectoryParentIndex.Add(row[0], row[1]);
459
+ if (null == row[1])
460
+ {
461
+ directoryFullPathIndex.Add(row[0], adminRootPath);
462
+ }
463
+ else
464
+ {
465
+ directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2]));
466
+ }
467
+ }
468
+
469
+ foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex)
470
+ {
471
+ if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key))
472
+ {
473
+ GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex);
474
+ }
475
+ }
476
+
477
+ Table fileTable = output.Tables["File"];
478
+ Table wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]);
479
+ foreach (Row row in fileTable.Rows)
480
+ {
481
+ WixFileRow wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]);
482
+ wixFileRow.File = (string)row[0];
483
+ wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]];
484
+ wixFileRow.Source = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2]));
485
+
486
+ if (!File.Exists(wixFileRow.Source))
487
+ {
488
+ throw new WixException(WixErrors.WixFileNotFound(wixFileRow.Source));
489
+ }
490
+
491
+ wixFileTable.Rows.Add(wixFileRow);
492
+ }
493
+ }
494
+
495
+ /// <summary>
496
+ /// Gets the full path of a directory. Populates the full path index with the directory's full path and all of its parent directorie's full paths.
497
+ /// </summary>
498
+ /// <param name="directory">The directory identifier.</param>
499
+ /// <param name="directoryDirectoryParentIndex">The Hashtable containing all the directory to directory parent mapping.</param>
500
+ /// <param name="directorySourceNameIndex">The Hashtable containing all the directory to source name mapping.</param>
501
+ /// <param name="directoryFullPathIndex">The Hashtable containing a mapping between all of the directories and their previously calculated full paths.</param>
502
+ /// <returns>The full path to the directory.</returns>
503
+ private string GetAdminFullPath(string directory, Hashtable directoryDirectoryParentIndex, Hashtable directorySourceNameIndex, Hashtable directoryFullPathIndex)
504
+ {
505
+ string parent = (string)directoryDirectoryParentIndex[directory];
506
+ string sourceName = (string)directorySourceNameIndex[directory];
507
+
508
+ string parentFullPath;
509
+ if (directoryFullPathIndex.ContainsKey(parent))
510
+ {
511
+ parentFullPath = (string)directoryFullPathIndex[parent];
512
+ }
513
+ else
514
+ {
515
+ parentFullPath = GetAdminFullPath(parent, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex);
516
+ }
517
+
518
+ if (null == sourceName)
519
+ {
520
+ sourceName = String.Empty;
521
+ }
522
+
523
+ string fullPath = Path.Combine(parentFullPath, sourceName);
524
+ directoryFullPathIndex.Add(directory, fullPath);
525
+
526
+ return fullPath;
527
+ }
528
+
529
+ /// <summary>
530
+ /// Get the source name in an admin image.
531
+ /// </summary>
532
+ /// <param name="value">The Filename value.</param>
533
+ /// <returns>The source name of the directory in an admin image.</returns>
534
+ private static string GetAdminSourceName(string value)
535
+ {
536
+ string name = null;
537
+ string[] names;
538
+ string shortname = null;
539
+ string shortsourcename = null;
540
+ string sourcename = null;
541
+
542
+ names = Common.GetNames(value);
543
+
544
+ if (null != names[0] && "." != names[0])
545
+ {
546
+ if (null != names[1])
547
+ {
548
+ shortname = names[0];
549
+ }
550
+ else
551
+ {
552
+ name = names[0];
553
+ }
554
+ }
555
+
556
+ if (null != names[1])
557
+ {
558
+ name = names[1];
559
+ }
560
+
561
+ if (null != names[2])
562
+ {
563
+ if (null != names[3])
564
+ {
565
+ shortsourcename = names[2];
566
+ }
567
+ else
568
+ {
569
+ sourcename = names[2];
570
+ }
571
+ }
572
+
573
+ if (null != names[3])
574
+ {
575
+ sourcename = names[3];
576
+ }
577
+
578
+ if (null != sourcename)
579
+ {
580
+ return sourcename;
581
+ }
582
+ else if (null != shortsourcename)
583
+ {
584
+ return shortsourcename;
585
+ }
586
+ else if (null != name)
587
+ {
588
+ return name;
589
+ }
590
+ else
591
+ {
592
+ return shortname;
593
+ }
594
+ }
595
+
596
+ /// <summary>
597
+ /// Creates section ids on rows which form logical groupings of resources.
598
+ /// </summary>
599
+ /// <param name="output">The Output that represents the msi database.</param>
600
+ private void GenerateSectionIds(Output output)
601
+ {
602
+ // First assign and index section ids for the tables that are in their own sections.
603
+ AssignSectionIdsToTable(output.Tables["Binary"], 0);
604
+ Hashtable componentSectionIdIndex = AssignSectionIdsToTable(output.Tables["Component"], 0);
605
+ Hashtable customActionSectionIdIndex = AssignSectionIdsToTable(output.Tables["CustomAction"], 0);
606
+ AssignSectionIdsToTable(output.Tables["Directory"], 0);
607
+ Hashtable featureSectionIdIndex = AssignSectionIdsToTable(output.Tables["Feature"], 0);
608
+ AssignSectionIdsToTable(output.Tables["Icon"], 0);
609
+ Hashtable digitalCertificateSectionIdIndex = AssignSectionIdsToTable(output.Tables["MsiDigitalCertificate"], 0);
610
+ AssignSectionIdsToTable(output.Tables["Property"], 0);
611
+
612
+ // Now handle all the tables that rely on the first set of indexes but also produce their own indexes. Order matters here.
613
+ Hashtable fileSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["File"], componentSectionIdIndex, 1, 0);
614
+ Hashtable appIdSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["Class"], componentSectionIdIndex, 2, 5);
615
+ Hashtable odbcDataSourceSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDataSource"], componentSectionIdIndex, 1, 0);
616
+ Hashtable odbcDriverSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDriver"], componentSectionIdIndex, 1, 0);
617
+ Hashtable registrySectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["Registry"], componentSectionIdIndex, 5, 0);
618
+ Hashtable serviceInstallSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ServiceInstall"], componentSectionIdIndex, 11, 0);
619
+
620
+ // Now handle all the tables which only rely on previous indexes and order does not matter.
621
+ foreach (Table table in output.Tables)
622
+ {
623
+ switch (table.Name)
624
+ {
625
+ case "WixFile":
626
+ case "MsiFileHash":
627
+ ConnectTableToSection(table, fileSectionIdIndex, 0);
628
+ break;
629
+ case "MsiAssembly":
630
+ case "MsiAssemblyName":
631
+ ConnectTableToSection(table, componentSectionIdIndex, 0);
632
+ break;
633
+ case "MsiPackageCertificate":
634
+ case "MsiPatchCertificate":
635
+ ConnectTableToSection(table, digitalCertificateSectionIdIndex, 1);
636
+ break;
637
+ case "CreateFolder":
638
+ case "FeatureComponents":
639
+ case "MoveFile":
640
+ case "ReserveCost":
641
+ case "ODBCTranslator":
642
+ ConnectTableToSection(table, componentSectionIdIndex, 1);
643
+ break;
644
+ case "TypeLib":
645
+ ConnectTableToSection(table, componentSectionIdIndex, 2);
646
+ break;
647
+ case "Shortcut":
648
+ case "Environment":
649
+ ConnectTableToSection(table, componentSectionIdIndex, 3);
650
+ break;
651
+ case "RemoveRegistry":
652
+ ConnectTableToSection(table, componentSectionIdIndex, 4);
653
+ break;
654
+ case "ServiceControl":
655
+ ConnectTableToSection(table, componentSectionIdIndex, 5);
656
+ break;
657
+ case "IniFile":
658
+ case "RemoveIniFile":
659
+ ConnectTableToSection(table, componentSectionIdIndex, 7);
660
+ break;
661
+ case "AppId":
662
+ ConnectTableToSection(table, appIdSectionIdIndex, 0);
663
+ break;
664
+ case "Condition":
665
+ ConnectTableToSection(table, featureSectionIdIndex, 0);
666
+ break;
667
+ case "ODBCSourceAttribute":
668
+ ConnectTableToSection(table, odbcDataSourceSectionIdIndex, 0);
669
+ break;
670
+ case "ODBCAttribute":
671
+ ConnectTableToSection(table, odbcDriverSectionIdIndex, 0);
672
+ break;
673
+ case "AdminExecuteSequence":
674
+ case "AdminUISequence":
675
+ case "AdvtExecuteSequence":
676
+ case "AdvtUISequence":
677
+ case "InstallExecuteSequence":
678
+ case "InstallUISequence":
679
+ ConnectTableToSection(table, customActionSectionIdIndex, 0);
680
+ break;
681
+ case "LockPermissions":
682
+ case "MsiLockPermissions":
683
+ foreach (Row row in table.Rows)
684
+ {
685
+ string lockObject = (string)row[0];
686
+ string tableName = (string)row[1];
687
+ switch (tableName)
688
+ {
689
+ case "File":
690
+ row.SectionId = (string)fileSectionIdIndex[lockObject];
691
+ break;
692
+ case "Registry":
693
+ row.SectionId = (string)registrySectionIdIndex[lockObject];
694
+ break;
695
+ case "ServiceInstall":
696
+ row.SectionId = (string)serviceInstallSectionIdIndex[lockObject];
697
+ break;
698
+ }
699
+ }
700
+ break;
701
+ }
702
+ }
703
+
704
+ // Now pass the output to each unbinder extension to allow them to analyze the output and determine thier proper section ids.
705
+ //foreach (IUnbinderExtension extension in this.unbinderExtensions)
706
+ //{
707
+ // extension.GenerateSectionIds(output);
708
+ //}
709
+ }
710
+
711
+ /// <summary>
712
+ /// Creates new section ids on all the rows in a table.
713
+ /// </summary>
714
+ /// <param name="table">The table to add sections to.</param>
715
+ /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param>
716
+ /// <returns>A Hashtable containing the tables key for each row paired with its assigned section id.</returns>
717
+ private Hashtable AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex)
718
+ {
719
+ Hashtable hashtable = new Hashtable();
720
+ if (null != table)
721
+ {
722
+ foreach (Row row in table.Rows)
723
+ {
724
+ row.SectionId = GetNewSectionId();
725
+ hashtable.Add(row[rowPrimaryKeyIndex], row.SectionId);
726
+ }
727
+ }
728
+ return hashtable;
729
+ }
730
+
731
+ /// <summary>
732
+ /// Connects a table's rows to an already sectioned table.
733
+ /// </summary>
734
+ /// <param name="table">The table containing rows that need to be connected to sections.</param>
735
+ /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param>
736
+ /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param>
737
+ private static void ConnectTableToSection(Table table, Hashtable sectionIdIndex, int rowIndex)
738
+ {
739
+ if (null != table)
740
+ {
741
+ foreach (Row row in table.Rows)
742
+ {
743
+ if (sectionIdIndex.ContainsKey(row[rowIndex]))
744
+ {
745
+ row.SectionId = (string)sectionIdIndex[row[rowIndex]];
746
+ }
747
+ }
748
+ }
749
+ }
750
+
751
+ /// <summary>
752
+ /// Connects a table's rows to an already sectioned table and produces an index for other tables to connect to it.
753
+ /// </summary>
754
+ /// <param name="table">The table containing rows that need to be connected to sections.</param>
755
+ /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param>
756
+ /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param>
757
+ /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param>
758
+ /// <returns>A Hashtable containing the tables key for each row paired with its assigned section id.</returns>
759
+ private static Hashtable ConnectTableToSectionAndIndex(Table table, Hashtable sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex)
760
+ {
761
+ Hashtable newHashTable = new Hashtable();
762
+ if (null != table)
763
+ {
764
+ foreach (Row row in table.Rows)
765
+ {
766
+ if (!sectionIdIndex.ContainsKey(row[rowIndex]))
767
+ {
768
+ continue;
769
+ }
770
+
771
+ row.SectionId = (string)sectionIdIndex[row[rowIndex]];
772
+ if (null != row[rowPrimaryKeyIndex])
773
+ {
774
+ newHashTable.Add(row[rowPrimaryKeyIndex], row.SectionId);
775
+ }
776
+ }
777
+ }
778
+ return newHashTable;
779
+ }
780
+
781
+ /// <summary>
782
+ /// Creates a new section identifier to be used when adding a section to an output.
783
+ /// </summary>
784
+ /// <returns>A string representing a new section id.</returns>
785
+ private string GetNewSectionId()
786
+ {
787
+ this.SectionCount++;
788
+ return "wix.section." + this.SectionCount.ToString(CultureInfo.InvariantCulture);
789
+ }
790
+ }
791
+}
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
new
+53
@@ -0,0 +1,53 @@
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.Unbind
4
+{
5
+ using System;
6
+ using System.ComponentModel;
7
+ using WixToolset.Core.Native;
8
+ using WixToolset.Data;
9
+ using WixToolset.Extensibility;
10
+ using WixToolset.Msi;
11
+
12
+ internal class UnbindMsiOrMsmCommand
13
+ {
14
+ public UnbindMsiOrMsmCommand(IUnbindContext context)
15
+ {
16
+ this.Context = context;
17
+ }
18
+
19
+ public IUnbindContext Context { get; }
20
+
21
+ public Output Execute()
22
+ {
23
+ Output output;
24
+
25
+ try
26
+ {
27
+ using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.ReadOnly))
28
+ {
29
+ var unbindCommand = new UnbindDatabaseCommand(this.Context.Messaging, database, this.Context.InputFilePath, OutputType.Product, this.Context.ExportBasePath, this.Context.IntermediateFolder, this.Context.IsAdminImage, this.Context.SuppressDemodularization, skipSummaryInfo: false);
30
+ output = unbindCommand.Execute();
31
+
32
+ // extract the files from the cabinets
33
+ if (!String.IsNullOrEmpty(this.Context.ExportBasePath) && !this.Context.SuppressExtractCabinets)
34
+ {
35
+ var extractCommand = new ExtractCabinetsCommand(output, database, this.Context.InputFilePath, this.Context.ExportBasePath, this.Context.IntermediateFolder);
36
+ extractCommand.Execute();
37
+ }
38
+ }
39
+ }
40
+ catch (Win32Exception e)
41
+ {
42
+ if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
43
+ {
44
+ throw new WixException(WixErrors.OpenDatabaseFailed(this.Context.InputFilePath));
45
+ }
46
+
47
+ throw;
48
+ }
49
+
50
+ return output;
51
+ }
52
+ }
53
+}
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
new
+317
@@ -0,0 +1,317 @@
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.Unbind
4
+{
5
+ using System;
6
+ using System.Collections;
7
+ using System.Collections.Generic;
8
+ using System.ComponentModel;
9
+ using System.Globalization;
10
+ using System.IO;
11
+ using System.Linq;
12
+ using System.Text.RegularExpressions;
13
+ using WixToolset.Core.Native;
14
+ using WixToolset.Core.WindowsInstaller.Databases;
15
+ using WixToolset.Data;
16
+ using WixToolset.Data.Rows;
17
+ using WixToolset.Extensibility;
18
+ using WixToolset.Msi;
19
+
20
+ internal class UnbindTransformCommand
21
+ {
22
+ public UnbindTransformCommand(Messaging messaging, string transformFile, string exportBasePath, string intermediateFolder)
23
+ {
24
+ this.Messaging = messaging;
25
+ this.TransformFile = transformFile;
26
+ this.ExportBasePath = exportBasePath;
27
+ this.IntermediateFolder = intermediateFolder;
28
+
29
+ this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions();
30
+ }
31
+
32
+ private Messaging Messaging { get; }
33
+
34
+ private string TransformFile { get; }
35
+
36
+ private string ExportBasePath { get; }
37
+
38
+ private string IntermediateFolder { get; }
39
+
40
+ private TableDefinitionCollection TableDefinitions { get; }
41
+
42
+ private string EmptyFile { get; set; }
43
+
44
+ public Output Execute()
45
+ {
46
+ Output transform = new Output(new SourceLineNumber(this.TransformFile));
47
+ transform.Type = OutputType.Transform;
48
+
49
+ // get the summary information table
50
+ using (SummaryInformation summaryInformation = new SummaryInformation(this.TransformFile))
51
+ {
52
+ Table table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
53
+
54
+ for (int i = 1; 19 >= i; i++)
55
+ {
56
+ string value = summaryInformation.GetProperty(i);
57
+
58
+ if (0 < value.Length)
59
+ {
60
+ Row row = table.CreateRow(transform.SourceLineNumbers);
61
+ row[0] = i;
62
+ row[1] = value;
63
+ }
64
+ }
65
+ }
66
+
67
+ // create a schema msi which hopefully matches the table schemas in the transform
68
+ Output schemaOutput = new Output(null);
69
+ string msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi");
70
+ foreach (TableDefinition tableDefinition in this.TableDefinitions)
71
+ {
72
+ // skip unreal tables and the Patch table
73
+ if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name)
74
+ {
75
+ schemaOutput.EnsureTable(tableDefinition);
76
+ }
77
+ }
78
+
79
+ Hashtable addedRows = new Hashtable();
80
+ Table transformViewTable;
81
+
82
+ // Bind the schema msi.
83
+ this.GenerateDatabase(schemaOutput, msiDatabaseFile);
84
+
85
+ // apply the transform to the database and retrieve the modifications
86
+ using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
87
+ {
88
+ // apply the transform with the ViewTransform option to collect all the modifications
89
+ msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);
90
+
91
+ // unbind the database
92
+ var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true);
93
+ Output transformViewOutput = unbindCommand.Execute();
94
+
95
+ // index the added and possibly modified rows (added rows may also appears as modified rows)
96
+ transformViewTable = transformViewOutput.Tables["_TransformView"];
97
+ Hashtable modifiedRows = new Hashtable();
98
+ foreach (Row row in transformViewTable.Rows)
99
+ {
100
+ string tableName = (string)row[0];
101
+ string columnName = (string)row[1];
102
+ string primaryKeys = (string)row[2];
103
+
104
+ if ("INSERT" == columnName)
105
+ {
106
+ string index = String.Concat(tableName, ':', primaryKeys);
107
+
108
+ addedRows.Add(index, null);
109
+ }
110
+ else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row
111
+ {
112
+ string index = String.Concat(tableName, ':', primaryKeys);
113
+
114
+ modifiedRows[index] = row;
115
+ }
116
+ }
117
+
118
+ // create placeholder rows for modified rows to make the transform insert the updated values when its applied
119
+ foreach (Row row in modifiedRows.Values)
120
+ {
121
+ string tableName = (string)row[0];
122
+ string columnName = (string)row[1];
123
+ string primaryKeys = (string)row[2];
124
+
125
+ string index = String.Concat(tableName, ':', primaryKeys);
126
+
127
+ // ignore information for added rows
128
+ if (!addedRows.Contains(index))
129
+ {
130
+ Table table = schemaOutput.Tables[tableName];
131
+ this.CreateRow(table, primaryKeys, true);
132
+ }
133
+ }
134
+ }
135
+
136
+ // Re-bind the schema output with the placeholder rows.
137
+ this.GenerateDatabase(schemaOutput, msiDatabaseFile);
138
+
139
+ // apply the transform to the database and retrieve the modifications
140
+ using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
141
+ {
142
+ try
143
+ {
144
+ // apply the transform
145
+ msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All);
146
+
147
+ // commit the database to guard against weird errors with streams
148
+ msiDatabase.Commit();
149
+ }
150
+ catch (Win32Exception ex)
151
+ {
152
+ if (0x65B == ex.NativeErrorCode)
153
+ {
154
+ // this commonly happens when the transform was built
155
+ // against a database schema different from the internal
156
+ // table definitions
157
+ throw new WixException(WixErrors.TransformSchemaMismatch());
158
+ }
159
+ }
160
+
161
+ // unbind the database
162
+ var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true);
163
+ Output output = unbindCommand.Execute();
164
+
165
+ // index all the rows to easily find modified rows
166
+ Hashtable rows = new Hashtable();
167
+ foreach (Table table in output.Tables)
168
+ {
169
+ foreach (Row row in table.Rows)
170
+ {
171
+ rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
172
+ }
173
+ }
174
+
175
+ // process the _TransformView rows into transform rows
176
+ foreach (Row row in transformViewTable.Rows)
177
+ {
178
+ string tableName = (string)row[0];
179
+ string columnName = (string)row[1];
180
+ string primaryKeys = (string)row[2];
181
+
182
+ Table table = transform.EnsureTable(this.TableDefinitions[tableName]);
183
+
184
+ if ("CREATE" == columnName) // added table
185
+ {
186
+ table.Operation = TableOperation.Add;
187
+ }
188
+ else if ("DELETE" == columnName) // deleted row
189
+ {
190
+ Row deletedRow = this.CreateRow(table, primaryKeys, false);
191
+ deletedRow.Operation = RowOperation.Delete;
192
+ }
193
+ else if ("DROP" == columnName) // dropped table
194
+ {
195
+ table.Operation = TableOperation.Drop;
196
+ }
197
+ else if ("INSERT" == columnName) // added row
198
+ {
199
+ string index = String.Concat(tableName, ':', primaryKeys);
200
+ Row addedRow = (Row)rows[index];
201
+ addedRow.Operation = RowOperation.Add;
202
+ table.Rows.Add(addedRow);
203
+ }
204
+ else if (null != primaryKeys) // modified row
205
+ {
206
+ string index = String.Concat(tableName, ':', primaryKeys);
207
+
208
+ // the _TransformView table includes information for added rows
209
+ // that looks like modified rows so it sometimes needs to be ignored
210
+ if (!addedRows.Contains(index))
211
+ {
212
+ Row modifiedRow = (Row)rows[index];
213
+
214
+ // mark the field as modified
215
+ int indexOfModifiedValue = -1;
216
+ for (int i = 0; i < modifiedRow.TableDefinition.Columns.Count; ++i)
217
+ {
218
+ if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal))
219
+ {
220
+ indexOfModifiedValue = i;
221
+ break;
222
+ }
223
+ }
224
+ modifiedRow.Fields[indexOfModifiedValue].Modified = true;
225
+
226
+ // move the modified row into the transform the first time its encountered
227
+ if (RowOperation.None == modifiedRow.Operation)
228
+ {
229
+ modifiedRow.Operation = RowOperation.Modify;
230
+ table.Rows.Add(modifiedRow);
231
+ }
232
+ }
233
+ }
234
+ else // added column
235
+ {
236
+ ColumnDefinition column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal));
237
+ column.Added = true;
238
+ }
239
+ }
240
+ }
241
+
242
+ return transform;
243
+ }
244
+
245
+ private void GenerateDatabase(Output output, string databaseFile)
246
+ {
247
+ var command = new GenerateDatabaseCommand();
248
+ command.Extensions = Array.Empty<IBinderExtension>();
249
+ command.Output = output;
250
+ command.OutputPath = databaseFile;
251
+ command.KeepAddedColumns = true;
252
+ command.UseSubDirectory = false;
253
+ command.SuppressAddingValidationRows = true;
254
+ command.TableDefinitions = this.TableDefinitions;
255
+ command.TempFilesLocation = this.IntermediateFolder;
256
+ command.Codepage = -1;
257
+ command.Execute();
258
+ }
259
+
260
+ /// <summary>
261
+ /// Create a deleted or modified row.
262
+ /// </summary>
263
+ /// <param name="table">The table containing the row.</param>
264
+ /// <param name="primaryKeys">The primary keys of the row.</param>
265
+ /// <param name="setRequiredFields">Option to set all required fields with placeholder values.</param>
266
+ /// <returns>The new row.</returns>
267
+ private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields)
268
+ {
269
+ Row row = table.CreateRow(null);
270
+
271
+ string[] primaryKeyParts = primaryKeys.Split('\t');
272
+ int primaryKeyPartIndex = 0;
273
+
274
+ for (int i = 0; i < table.Definition.Columns.Count; i++)
275
+ {
276
+ ColumnDefinition columnDefinition = table.Definition.Columns[i];
277
+
278
+ if (columnDefinition.PrimaryKey)
279
+ {
280
+ if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable)
281
+ {
282
+ row[i] = Convert.ToInt32(primaryKeyParts[primaryKeyPartIndex++], CultureInfo.InvariantCulture);
283
+ }
284
+ else
285
+ {
286
+ row[i] = primaryKeyParts[primaryKeyPartIndex++];
287
+ }
288
+ }
289
+ else if (setRequiredFields)
290
+ {
291
+ if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable)
292
+ {
293
+ row[i] = 1;
294
+ }
295
+ else if (ColumnType.Object == columnDefinition.Type)
296
+ {
297
+ if (null == this.EmptyFile)
298
+ {
299
+ this.EmptyFile = Path.GetTempFileName() + ".empty";
300
+ using (FileStream fileStream = File.Create(this.EmptyFile))
301
+ {
302
+ }
303
+ }
304
+
305
+ row[i] = this.EmptyFile;
306
+ }
307
+ else
308
+ {
309
+ row[i] = "1";
310
+ }
311
+ }
312
+ }
313
+
314
+ return row;
315
+ }
316
+ }
317
+}
src/WixToolset.Core.WindowsInstaller/Validator.cs
renamed
+34
-50
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
using System.Collections.Generic;
@@ -12,8 +12,10 @@ namespace WixToolset
12
using System.Threading;
13
using WixToolset.Data;
14
using WixToolset.Extensibility;
15
- using WixToolset.Msi;
15
using WixToolset.Core.Native;
16
+ using WixToolset.Msi;
17
+ using System.Linq;
18
+ using System.Reflection;
19
20
/// <summary>
21
/// Runs internal consistency evaluators (ICEs) from cub files against a database.
@@ -23,9 +25,7 @@ namespace WixToolset
25
private string actionName;
26
private StringCollection cubeFiles;
27
private ValidatorExtension extension;
26
- private string[] ices;
28
private Output output;
28
- private string[] suppressedICEs;
29
private InstallUIHandler validationUIHandler;
30
private bool validationSessionComplete;
31
@@ -54,11 +54,7 @@ namespace WixToolset
54
/// </summary>
55
/// <value>The list of ICEs.</value>
56
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
57
- public string[] ICEs
58
- {
59
- get { return this.ices; }
60
- set { this.ices = value; }
61
- }
57
+ public ISet<string> ICEs { get; set; }
58
59
/// <summary>
60
/// Gets or sets the output used for finding source line information.
@@ -76,16 +72,12 @@ namespace WixToolset
72
/// </summary>
73
/// <value>The suppressed ICEs.</value>
74
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
79
- public string[] SuppressedICEs
80
- {
81
- get { return this.suppressedICEs; }
82
- set { this.suppressedICEs = value; }
83
- }
75
+ public ISet<string> SuppressedICEs { get; set; }
76
77
/// <summary>
78
/// Sets the temporary path for the Binder.
79
/// </summary>
88
- public string TempFilesLocation { private get; set; }
80
+ public string IntermediateFolder { private get; set; }
81
82
/// <summary>
83
/// Add a cube file to the validation run.
@@ -103,8 +95,6 @@ namespace WixToolset
95
/// <returns>true if validation succeeded; false otherwise.</returns>
96
public void Validate(string databaseFile)
97
{
106
- Dictionary<string, string> indexedICEs = new Dictionary<string, string>();
107
- Dictionary<string, string> indexedSuppressedICEs = new Dictionary<string, string>();
98
int previousUILevel = (int)InstallUILevels.Basic;
99
IntPtr previousHwnd = IntPtr.Zero;
100
InstallUIHandler previousUIHandler = null;
@@ -120,28 +110,10 @@ namespace WixToolset
110
this.extension.InitializeValidator();
111
112
// Ensure the temporary files can be created.
123
- Directory.CreateDirectory(this.TempFilesLocation);
124
-
125
- // index the ICEs
126
- if (null != this.ices)
127
- {
128
- foreach (string ice in this.ices)
129
- {
130
- indexedICEs[ice] = null;
131
- }
132
- }
133
-
134
- // index the suppressed ICEs
135
- if (null != this.suppressedICEs)
136
- {
137
- foreach (string suppressedICE in this.suppressedICEs)
138
- {
139
- indexedSuppressedICEs[suppressedICE] = null;
140
- }
141
- }
113
+ Directory.CreateDirectory(this.IntermediateFolder);
114
115
// copy the database to a temporary location so it can be manipulated
144
- string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(databaseFile));
116
+ string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(databaseFile));
117
File.Copy(databaseFile, tempDatabaseFile);
118
119
// remove the read-only property from the temporary database
@@ -236,7 +208,7 @@ namespace WixToolset
208
209
string action = record.GetString(1);
210
239
- if (!indexedSuppressedICEs.ContainsKey(action))
211
+ if ((this.SuppressedICEs == null || !this.SuppressedICEs.Contains(action)) && (this.ICEs == null || this.ICEs.Contains(action)))
212
{
213
actions.Add(action);
214
}
@@ -244,18 +216,6 @@ namespace WixToolset
216
}
217
}
218
247
- if (0 != indexedICEs.Count)
248
- {
249
- // Walk backwards and remove those that arent in the list
250
- for (int i = actions.Count - 1; 0 <= i; i--)
251
- {
252
- if (!indexedICEs.ContainsKey(actions[i]))
253
- {
254
- actions.RemoveAt(i);
255
- }
256
- }
257
- }
258
-
219
// disable the internal UI handler and set an external UI handler
220
previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd);
221
previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero);
@@ -370,6 +330,30 @@ namespace WixToolset
330
this.extension.OnMessage(e);
331
}
332
333
+ public static Validator CreateFromContext(IBindContext context, string cubeFilename)
334
+ {
335
+ Validator validator = null;
336
+
337
+ // Tell the binder about the validator if validation isn't suppressed
338
+ if (!context.SuppressValidation)
339
+ {
340
+ validator = new Validator();
341
+ validator.IntermediateFolder = Path.Combine(context.IntermediateFolder, "validate");
342
+
343
+ // set the default cube file
344
+ string thisPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
345
+ validator.AddCubeFile(Path.Combine(thisPath, cubeFilename));
346
+
347
+ // Set the ICEs
348
+ validator.ICEs = new SortedSet<string>(context.Ices);
349
+
350
+ // Set the suppressed ICEs and disable ICEs that have equivalent-or-better checks in WiX.
351
+ validator.SuppressedICEs = new SortedSet<string>(context.SuppressIces.Union(new[] { "ICE08", "ICE33", "ICE47", "ICE66" }));
352
+ }
353
+
354
+ return validator;
355
+ }
356
+
357
/// <summary>
358
/// The validation external UI handler.
359
/// </summary>
src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
renamed
src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs
new
+50
@@ -0,0 +1,50 @@
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
4
+{
5
+ using System;
6
+ using System.IO;
7
+ using WixToolset.Extensibility;
8
+
9
+ internal class WindowsInstallerBackendFactory : IBackendFactory
10
+ {
11
+ public bool TryCreateBackend(string outputType, string outputFile, IBindContext context, out IBackend backend)
12
+ {
13
+ if (String.IsNullOrEmpty(outputType))
14
+ {
15
+ outputType = Path.GetExtension(outputFile);
16
+ }
17
+
18
+ switch (outputType.ToLowerInvariant())
19
+ {
20
+ case "module":
21
+ case ".msm":
22
+ backend = new MsmBackend();
23
+ return true;
24
+
25
+ case "msipackage":
26
+ case "product":
27
+ case ".msi":
28
+ backend = new MsiBackend();
29
+ return true;
30
+
31
+ case "patch":
32
+ case ".msp":
33
+ backend = new MspBackend();
34
+ return true;
35
+
36
+ //case "patchcreation":
37
+ //case ".pcp":
38
+ // return new PatchCreationBackend();
39
+
40
+ case "transform":
41
+ case ".mst":
42
+ backend = new MstBackend();
43
+ return true;
44
+ }
45
+
46
+ backend = null;
47
+ return false;
48
+ }
49
+ }
50
+}
src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
new
+36
@@ -0,0 +1,36 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+<Project Sdk="Microsoft.NET.Sdk">
5
+ <PropertyGroup>
6
+ <TargetFramework>netstandard2.0</TargetFramework>
7
+ <Description>Core Windows Installer</Description>
8
+ <Title>WiX Toolset Core Windows Installer</Title>
9
+ </PropertyGroup>
10
+
11
+ <PropertyGroup>
12
+ <NoWarn>NU1701</NoWarn>
13
+ </PropertyGroup>
14
+
15
+ <ItemGroup>
16
+ <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
17
+ <PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
18
+
19
+ <ProjectReference Include="$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj') " />
20
+ <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj') " />
21
+
22
+ <ProjectReference Include="$(WixToolsetRootFolder)\Core.Native\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core.Native\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj') " />
23
+
24
+ <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" />
25
+ <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core.Native\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj') " />
26
+ </ItemGroup>
27
+
28
+ <ItemGroup>
29
+ <PackageReference Include="WixToolset.Dtf.Resources" Version="4.0.*" />
30
+ <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" />
31
+ </ItemGroup>
32
+
33
+ <ItemGroup>
34
+ <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.41" PrivateAssets="all" />
35
+ </ItemGroup>
36
+</Project>
src/WixToolset.Core/BackendContext.cs
new
+16
@@ -0,0 +1,16 @@
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
4
+{
5
+ using WixToolset.Data;
6
+
7
+ public class BackendContext
8
+ {
9
+ internal BackendContext()
10
+ {
11
+ this.Messaging = Messaging.Instance;
12
+ }
13
+
14
+ public Messaging Messaging { get; }
15
+ }
16
+}
src/WixToolset.Core/Bind/DelayedField.cs
+5
-8
@@ -1,18 +1,15 @@
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.Bind
3
+namespace WixToolset.Core.Bind
4
{
5
- using System;
6
- using System.Collections.Generic;
7
- using System.Linq;
8
- using System.Text;
5
using WixToolset.Data;
6
+ using WixToolset.Extensibility;
7
8
/// <summary>
9
/// Structure used to hold a row and field that contain binder variables, which need to be resolved
10
/// later, once the files have been resolved.
11
/// </summary>
15
- internal class DelayedField
12
+ internal class DelayedField : IDelayedField
13
{
14
/// <summary>
15
/// Basic constructor for struct
@@ -28,11 +25,11 @@ namespace WixToolset.Bind
25
/// <summary>
26
/// The row containing the field.
27
/// </summary>
31
- public Row Row { get; private set; }
28
+ public Row Row { get; }
29
30
/// <summary>
31
/// The field needing further resolving.
32
/// </summary>
36
- public Field Field { get; private set; }
33
+ public Field Field { get; }
34
}
35
}
src/WixToolset.Core/Bind/ExpectedExtractFile.cs
new
+16
@@ -0,0 +1,16 @@
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.Bind
4
+{
5
+ using System;
6
+ using WixToolset.Extensibility;
7
+
8
+ internal class ExpectedExtractFile : IExpectedExtractFile
9
+ {
10
+ public Uri Uri { get; set; }
11
+
12
+ public int EmbeddedFileIndex { get; set; }
13
+
14
+ public string OutputPath { get; set; }
15
+ }
16
+}
src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs
+22
-14
@@ -1,6 +1,6 @@
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.Bind
3
+namespace WixToolset.Core.Bind
4
{
5
using System;
6
using System.Collections.Generic;
@@ -13,11 +13,11 @@ namespace WixToolset.Bind
13
/// <summary>
14
/// Internal helper class used to extract embedded files.
15
/// </summary>
16
- internal sealed class ExtractEmbeddedFiles
16
+ internal class ExtractEmbeddedFiles
17
{
18
private Dictionary<Uri, SortedList<int, string>> filesWithEmbeddedFiles = new Dictionary<Uri, SortedList<int, string>>();
19
20
- public IEnumerable<Uri> Uris { get { return this.filesWithEmbeddedFiles.Keys; } }
20
+ public IEnumerable<Uri> Uris => this.filesWithEmbeddedFiles.Keys;
21
22
/// <summary>
23
/// Adds an embedded file index to track and returns the path where the embedded file will be extracted. Duplicates will return the same extract path.
@@ -53,15 +53,30 @@ namespace WixToolset.Bind
53
return extractPath;
54
}
55
56
- public IEnumerable<ExtractFile> GetExtractFilesForUri(Uri uri)
56
+ public IEnumerable<ExpectedExtractFile> GetExpectedEmbeddedFiles()
57
{
58
- SortedList<int, string> extracts;
59
- if (!filesWithEmbeddedFiles.TryGetValue(uri, out extracts))
58
+ foreach (var uriWithExtracts in filesWithEmbeddedFiles)
59
+ {
60
+ foreach (var extracts in uriWithExtracts.Value)
61
+ {
62
+ yield return new ExpectedExtractFile
63
+ {
64
+ Uri = uriWithExtracts.Key,
65
+ EmbeddedFileIndex = extracts.Key,
66
+ OutputPath = extracts.Value,
67
+ };
68
+ }
69
+ }
70
+ }
71
+
72
+ public IEnumerable<ExpectedExtractFile> GetExtractFilesForUri(Uri uri)
73
+ {
74
+ if (!filesWithEmbeddedFiles.TryGetValue(uri, out var extracts))
75
{
76
extracts = new SortedList<int, string>();
77
}
78
64
- return extracts.Select(e => new ExtractFile() { EmbeddedFileIndex = e.Key, OutputPath = e.Value });
79
+ return extracts.Select(e => new ExpectedExtractFile() { Uri = uri, EmbeddedFileIndex = e.Key, OutputPath = e.Value });
80
}
81
82
private string HashUri(string uri)
@@ -72,12 +87,5 @@ namespace WixToolset.Bind
87
return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_');
88
}
89
}
75
-
76
- internal struct ExtractFile
77
- {
78
- public int EmbeddedFileIndex { get; set; }
79
-
80
- public string OutputPath { get; set; }
81
- }
90
}
91
}
src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
+19
-10
@@ -1,19 +1,26 @@
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.Bind
3
+namespace WixToolset.Core.Bind
4
{
5
+ using System.Collections.Generic;
6
using System.IO;
7
+ using System.Linq;
8
using System.Reflection;
9
using WixToolset.Data;
10
+ using WixToolset.Extensibility;
11
9
- internal class ExtractEmbeddedFilesCommand : ICommand
12
+ public class ExtractEmbeddedFilesCommand
13
{
11
- public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; }
14
+ public IEnumerable<IExpectedExtractFile> FilesWithEmbeddedFiles { private get; set; }
15
16
public void Execute()
17
{
15
- foreach (var baseUri in this.FilesWithEmbeddedFiles.Uris)
18
+ var group = this.FilesWithEmbeddedFiles.GroupBy(e => e.Uri);
19
+
20
+ foreach (var expectedEmbeddedFileByUri in group)
21
{
22
+ var baseUri = expectedEmbeddedFileByUri.Key;
23
+
24
Stream stream = null;
25
try
26
{
@@ -34,18 +41,20 @@ namespace WixToolset.Bind
41
42
using (FileStructure fs = FileStructure.Read(stream))
43
{
37
- foreach (var embeddedFile in this.FilesWithEmbeddedFiles.GetExtractFilesForUri(baseUri))
44
+ var uniqueIndicies = new SortedSet<int>();
45
+
46
+ foreach (var embeddedFile in expectedEmbeddedFileByUri)
47
{
39
- fs.ExtractEmbeddedFile(embeddedFile.EmbeddedFileIndex, embeddedFile.OutputPath);
48
+ if (uniqueIndicies.Add(embeddedFile.EmbeddedFileIndex))
49
+ {
50
+ fs.ExtractEmbeddedFile(embeddedFile.EmbeddedFileIndex, embeddedFile.OutputPath);
51
+ }
52
}
53
}
54
}
55
finally
56
{
45
- if (null != stream)
46
- {
47
- stream.Close();
48
- }
57
+ stream?.Close();
58
}
59
}
60
}
src/WixToolset.Core/Bind/FileFacade.cs
renamed
+1
-1
@@ -1,6 +1,6 @@
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.Bind.Databases
3
+namespace WixToolset.Core.Bind
4
{
5
using System.Collections.Generic;
6
using WixToolset.Data;
src/WixToolset.Core/Bind/FileResolver.cs
new
+231
@@ -0,0 +1,231 @@
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.Bind
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.IO;
8
+ using System.Linq;
9
+ using System.Runtime.InteropServices;
10
+ using WixToolset.Data;
11
+ using WixToolset.Data.Bind;
12
+ using WixToolset.Extensibility;
13
+
14
+ internal class FileResolver
15
+ {
16
+ private const string BindPathOpenString = "!(bindpath.";
17
+
18
+ private FileResolver(IEnumerable<BindPath> bindPaths)
19
+ {
20
+ this.BindPaths = (bindPaths ?? Array.Empty<BindPath>()).ToLookup(b => b.Stage);
21
+ this.RebaseTarget = this.BindPaths[BindStage.Target].Any();
22
+ this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any();
23
+ }
24
+
25
+ public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<IBinderExtension> extensions) : this(bindPaths)
26
+ {
27
+ this.BinderExtensions = extensions ?? Array.Empty<IBinderExtension>();
28
+ }
29
+
30
+ public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<ILibrarianExtension> extensions) : this(bindPaths)
31
+ {
32
+ this.LibrarianExtensions = extensions ?? Array.Empty<ILibrarianExtension>();
33
+ }
34
+
35
+ private ILookup<BindStage, BindPath> BindPaths { get; }
36
+
37
+ public bool RebaseTarget { get; }
38
+
39
+ public bool RebaseUpdated { get; }
40
+
41
+ private IEnumerable<IBinderExtension> BinderExtensions { get; }
42
+
43
+ private IEnumerable<ILibrarianExtension> LibrarianExtensions { get; }
44
+
45
+ /// <summary>
46
+ /// Copies a file.
47
+ /// </summary>
48
+ /// <param name="source">The file to copy.</param>
49
+ /// <param name="destination">The destination file.</param>
50
+ /// <param name="overwrite">true if the destination file can be overwritten; otherwise, false.</param>
51
+ public bool CopyFile(string source, string destination, bool overwrite)
52
+ {
53
+ foreach (var extension in this.BinderExtensions)
54
+ {
55
+ if (extension.CopyFile(source, destination, overwrite))
56
+ {
57
+ return true;
58
+ }
59
+ }
60
+
61
+ if (overwrite && File.Exists(destination))
62
+ {
63
+ File.Delete(destination);
64
+ }
65
+
66
+ if (!CreateHardLink(destination, source, IntPtr.Zero))
67
+ {
68
+#if DEBUG
69
+ int er = Marshal.GetLastWin32Error();
70
+#endif
71
+
72
+ File.Copy(source, destination, overwrite);
73
+ }
74
+
75
+ return true;
76
+ }
77
+
78
+ /// <summary>
79
+ /// Moves a file.
80
+ /// </summary>
81
+ /// <param name="source">The file to move.</param>
82
+ /// <param name="destination">The destination file.</param>
83
+ public bool MoveFile(string source, string destination, bool overwrite)
84
+ {
85
+ foreach (var extension in this.BinderExtensions)
86
+ {
87
+ if (extension.MoveFile(source, destination, overwrite))
88
+ {
89
+ return true;
90
+ }
91
+ }
92
+
93
+ if (overwrite && File.Exists(destination))
94
+ {
95
+ File.Delete(destination);
96
+ }
97
+
98
+ var directory = Path.GetDirectoryName(destination);
99
+ if (!String.IsNullOrEmpty(directory))
100
+ {
101
+ Directory.CreateDirectory(directory);
102
+ }
103
+
104
+ File.Move(source, destination);
105
+
106
+ return true;
107
+ }
108
+
109
+ public string Resolve(SourceLineNumber sourceLineNumbers, string table, string path)
110
+ {
111
+ foreach (var extension in this.LibrarianExtensions)
112
+ {
113
+ var resolved = extension.Resolve(sourceLineNumbers, table, path);
114
+
115
+ if (null != resolved)
116
+ {
117
+ return resolved;
118
+ }
119
+ }
120
+
121
+ return this.ResolveUsingBindPaths(path, table, sourceLineNumbers, BindStage.Normal);
122
+ }
123
+
124
+ /// <summary>
125
+ /// Resolves the source path of a file using binder extensions.
126
+ /// </summary>
127
+ /// <param name="source">Original source value.</param>
128
+ /// <param name="type">Optional type of source file being resolved.</param>
129
+ /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param>
130
+ /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param>
131
+ /// <returns>Should return a valid path for the stream to be imported.</returns>
132
+ public string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
133
+ {
134
+ foreach (var extension in this.BinderExtensions)
135
+ {
136
+ var resolved = extension.ResolveFile(source, type, sourceLineNumbers, bindStage);
137
+
138
+ if (null != resolved)
139
+ {
140
+ return resolved;
141
+ }
142
+ }
143
+
144
+ return this.ResolveUsingBindPaths(source, type, sourceLineNumbers, bindStage);
145
+ }
146
+
147
+ private string ResolveUsingBindPaths(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
148
+ {
149
+ string resolved = null;
150
+
151
+ // If the file exists, we're good to go.
152
+ if (CheckFileExists(source))
153
+ {
154
+ resolved = source;
155
+ }
156
+ else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist.
157
+ {
158
+ resolved = null;
159
+ }
160
+ else // not a rooted path so let's try applying all the different source resolution options.
161
+ {
162
+ string bindName = String.Empty;
163
+ var path = source;
164
+ string pathWithoutSourceDir = null;
165
+
166
+ if (source.StartsWith(BindPathOpenString, StringComparison.Ordinal))
167
+ {
168
+ int closeParen = source.IndexOf(')', BindPathOpenString.Length);
169
+ if (-1 != closeParen)
170
+ {
171
+ bindName = source.Substring(BindPathOpenString.Length, closeParen - BindPathOpenString.Length);
172
+ path = source.Substring(BindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace.
173
+ path = path.TrimStart('\\'); // remove starting '\\' char so the path doesn't look rooted.
174
+ }
175
+ }
176
+ else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal))
177
+ {
178
+ pathWithoutSourceDir = path.Substring(10);
179
+ }
180
+
181
+ var bindPaths = this.BindPaths[bindStage];
182
+
183
+ foreach (var bindPath in bindPaths)
184
+ {
185
+ if (!String.IsNullOrEmpty(pathWithoutSourceDir))
186
+ {
187
+ var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir);
188
+
189
+ if (CheckFileExists(filePath))
190
+ {
191
+ resolved = filePath;
192
+ }
193
+ }
194
+
195
+ if (String.IsNullOrEmpty(resolved))
196
+ {
197
+ var filePath = Path.Combine(bindPath.Path, path);
198
+
199
+ if (CheckFileExists(filePath))
200
+ {
201
+ resolved = filePath;
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ if (null == resolved)
208
+ {
209
+ throw new WixFileNotFoundException(sourceLineNumbers, source, type);
210
+ }
211
+
212
+ // Didn't find the file.
213
+ return resolved;
214
+ }
215
+
216
+ private static bool CheckFileExists(string path)
217
+ {
218
+ try
219
+ {
220
+ return File.Exists(path);
221
+ }
222
+ catch (ArgumentException)
223
+ {
224
+ throw new WixException(WixErrors.IllegalCharactersInPath(path));
225
+ }
226
+ }
227
+
228
+ [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
229
+ private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
230
+ }
231
+}
src/WixToolset.Core/Bind/FileTransfer.cs
deleted
-113
@@ -1,113 +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.Bind
4
-{
5
- using System;
6
- using System.IO;
7
- using WixToolset;
8
- using WixToolset.Data;
9
-
10
- /// <summary>
11
- /// Structure used for all file transfer information.
12
- /// </summary>
13
- internal class FileTransfer
14
- {
15
- /// <summary>Source path to file.</summary>
16
- public string Source { get; set; }
17
-
18
- /// <summary>Destination path for file.</summary>
19
- public string Destination { get; set; }
20
-
21
- /// <summary>Flag if file should be moved (optimal).</summary>
22
- public bool Move { get; set; }
23
-
24
- /// <summary>Optional source line numbers where this file transfer orginated.</summary>
25
- public SourceLineNumber SourceLineNumbers { get; set; }
26
-
27
- /// <summary>Optional type of file this transfer is moving or copying.</summary>
28
- public string Type { get; set; }
29
-
30
- /// <summary>Indicates whether the file transer was a built by this build or copied from other some build.</summary>
31
- internal bool Built { get; set; }
32
-
33
- /// <summary>Set during layout of media when the file transfer when the source and target resolve to the same path.</summary>
34
- internal bool Redundant { get; set; }
35
-
36
- /// <summary>
37
- /// Prefer the TryCreate() method to create FileTransfer objects.
38
- /// </summary>
39
- /// <param name="source">Source path to file.</param>
40
- /// <param name="destination">Destination path for file.</param>
41
- /// <param name="move">File if file should be moved (optimal).</param>
42
- /// <param name="type">Optional type of file this transfer is transferring.</param>
43
- /// <param name="sourceLineNumbers">Optional source line numbers wher this transfer originated.</param>
44
- public FileTransfer(string source, string destination, bool move, string type = null, SourceLineNumber sourceLineNumbers = null)
45
- {
46
- this.Source = source;
47
- this.Destination = destination;
48
- this.Move = move;
49
-
50
- this.Type = type;
51
- this.SourceLineNumbers = sourceLineNumbers;
52
- }
53
-
54
- /// <summary>
55
- /// Creates a file transfer if the source and destination are different.
56
- /// </summary>
57
- /// <param name="source">Source path to file.</param>
58
- /// <param name="destination">Destination path for file.</param>
59
- /// <param name="move">File if file should be moved (optimal).</param>
60
- /// <param name="type">Optional type of file this transfer is transferring.</param>
61
- /// <param name="sourceLineNumbers">Optional source line numbers wher this transfer originated.</param>
62
- /// <returns>true if the source and destination are the different, false if no file transfer is created.</returns>
63
- public static bool TryCreate(string source, string destination, bool move, string type, SourceLineNumber sourceLineNumbers, out FileTransfer transfer)
64
- {
65
- string sourceFullPath = GetValidatedFullPath(sourceLineNumbers, source);
66
-
67
- string fileLayoutFullPath = GetValidatedFullPath(sourceLineNumbers, destination);
68
-
69
- // if the current source path (where we know that the file already exists) and the resolved
70
- // path as dictated by the Directory table are not the same, then propagate the file. The
71
- // image that we create may have already been done by some other process other than the linker, so
72
- // there is no reason to copy the files to the resolved source if they are already there.
73
- if (String.Equals(sourceFullPath, fileLayoutFullPath, StringComparison.OrdinalIgnoreCase))
74
- {
75
- transfer = null;
76
- return false;
77
- }
78
-
79
- transfer = new FileTransfer(source, destination, move, type, sourceLineNumbers);
80
- return true;
81
- }
82
-
83
- private static string GetValidatedFullPath(SourceLineNumber sourceLineNumbers, string path)
84
- {
85
- string result;
86
-
87
- try
88
- {
89
- result = Path.GetFullPath(path);
90
-
91
- string filename = Path.GetFileName(result);
92
-
93
- foreach (string reservedName in Common.ReservedFileNames)
94
- {
95
- if (reservedName.Equals(filename, StringComparison.OrdinalIgnoreCase))
96
- {
97
- throw new WixException(WixErrors.InvalidFileName(sourceLineNumbers, path));
98
- }
99
- }
100
- }
101
- catch (System.ArgumentException)
102
- {
103
- throw new WixException(WixErrors.InvalidFileName(sourceLineNumbers, path));
104
- }
105
- catch (System.IO.PathTooLongException)
106
- {
107
- throw new WixException(WixErrors.PathTooLong(sourceLineNumbers, path));
108
- }
109
-
110
- return result;
111
- }
112
- }
113
-}
src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
+7
-8
@@ -1,23 +1,22 @@
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.Bind
3
+namespace WixToolset.Core.Bind
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.Globalization;
8
- using System.Linq;
9
- using System.Text;
8
using WixToolset.Data;
9
+ using WixToolset.Extensibility;
10
11
/// <summary>
12
/// Resolves the fields which had variables that needed to be resolved after the file information
13
/// was loaded.
14
/// </summary>
16
- internal class ResolveDelayedFieldsCommand : ICommand
15
+ public class ResolveDelayedFieldsCommand : ICommand
16
{
17
public OutputType OutputType { private get; set;}
18
20
- public IEnumerable<DelayedField> DelayedFields { private get; set;}
19
+ public IEnumerable<IDelayedField> DelayedFields { private get; set;}
20
21
public IDictionary<string, string> VariableCache { private get; set; }
22
@@ -29,9 +28,9 @@ namespace WixToolset.Bind
28
/// <param name="modularizationGuid">The modularization guid (used in case of a merge module).</param>
29
public void Execute()
30
{
32
- List<DelayedField> deferredFields = new List<DelayedField>();
31
+ var deferredFields = new List<IDelayedField>();
32
34
- foreach (DelayedField delayedField in this.DelayedFields)
33
+ foreach (IDelayedField delayedField in this.DelayedFields)
34
{
35
try
36
{
@@ -43,7 +42,7 @@ namespace WixToolset.Bind
42
string value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, (string)delayedField.Field.Data, this.VariableCache);
43
44
// update the variable cache with the new value
46
- string key = String.Concat("property.", BindDatabaseCommand.Demodularize(this.OutputType, this.ModularizationGuid, (string)propertyRow[0]));
45
+ string key = String.Concat("property.", Common.Demodularize(this.OutputType, this.ModularizationGuid, (string)propertyRow[0]));
46
this.VariableCache[key] = value;
47
48
// update the field data
src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
+40
-31
@@ -1,29 +1,32 @@
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.Bind
3
+namespace WixToolset.Core.Bind
4
{
5
using System.Collections.Generic;
6
using WixToolset.Data;
7
+ using WixToolset.Data.Bind;
8
using WixToolset.Extensibility;
9
10
/// <summary>
11
/// Resolve source fields in the tables included in the output
12
/// </summary>
12
- internal class ResolveFieldsCommand : ICommand
13
+ internal class ResolveFieldsCommand
14
{
14
- public TableIndexedCollection Tables { private get; set; }
15
+ public bool BuildingPatch { private get; set; }
16
16
- public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; }
17
+ public IBindVariableResolver BindVariableResolver { private get; set; }
18
18
- public BinderFileManagerCore FileManagerCore { private get; set; }
19
+ public IEnumerable<BindPath> BindPaths { private get; set; }
20
20
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
21
+ public IEnumerable<IBinderExtension> Extensions { private get; set; }
22
22
- public bool SupportDelayedResolution { private get; set; }
23
+ public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; }
24
+
25
+ public string IntermediateFolder { private get; set; }
26
24
- public string TempFilesLocation { private get; set; }
27
+ public TableIndexedCollection Tables { private get; set; }
28
26
- public WixVariableResolver WixVariableResolver { private get; set; }
29
+ public bool SupportDelayedResolution { private get; set; }
30
31
public IEnumerable<DelayedField> DelayedFields { get; private set; }
32
@@ -31,6 +34,8 @@ namespace WixToolset.Bind
34
{
35
List<DelayedField> delayedFields = this.SupportDelayedResolution ? new List<DelayedField>() : null;
36
37
+ var fileResolver = new FileResolver(this.BindPaths, this.Extensions);
38
+
39
foreach (Table table in this.Tables)
40
{
41
foreach (Row row in table.Rows)
@@ -46,7 +51,7 @@ namespace WixToolset.Bind
51
// resolve localization and wix variables
52
if (field.Data is string)
53
{
49
- field.Data = this.WixVariableResolver.ResolveVariables(row.SourceLineNumbers, field.AsString(), false, ref isDefault, ref delayedResolve);
54
+ field.Data = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, field.AsString(), false, out isDefault, out delayedResolve);
55
if (delayedResolve)
56
{
57
delayedFields.Add(new DelayedField(row, field));
@@ -74,7 +79,7 @@ namespace WixToolset.Bind
79
// File is embedded and path to it was not modified above.
80
if (objectField.EmbeddedFileIndex.HasValue && isDefault)
81
{
77
- string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.BaseUri, objectField.EmbeddedFileIndex.Value, this.TempFilesLocation);
82
+ string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.BaseUri, objectField.EmbeddedFileIndex.Value, this.IntermediateFolder);
83
84
// Set the path to the embedded file once where it will be extracted.
85
objectField.Data = extractPath;
@@ -83,7 +88,7 @@ namespace WixToolset.Bind
88
{
89
try
90
{
86
- if (OutputType.Patch != this.FileManagerCore.Output.Type) // Normal binding for non-Patch scenario such as link (light.exe)
91
+ if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe)
92
{
93
// keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file
94
if (null == objectField.UnresolvedData)
@@ -92,12 +97,12 @@ namespace WixToolset.Bind
97
}
98
99
// resolve the path to the file
95
- objectField.Data = this.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal);
100
+ objectField.Data = fileResolver.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal);
101
}
97
- else if (!(this.FileManagerCore.RebaseTarget || this.FileManagerCore.RebaseUpdated)) // Normal binding for Patch Scenario (normal patch, no re-basing logic)
102
+ else if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated) // Normal binding for Patch Scenario (normal patch, no re-basing logic)
103
{
104
// resolve the path to the file
100
- objectField.Data = this.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal);
105
+ objectField.Data = fileResolver.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal);
106
}
107
else // Re-base binding path scenario caused by pyro.exe -bt -bu
108
{
@@ -106,7 +111,7 @@ namespace WixToolset.Bind
111
112
// if -bu is used in pyro command, this condition holds true and the tool
113
// will use pre-resolved source for new wixpdb file
109
- if (this.FileManagerCore.RebaseUpdated)
114
+ if (fileResolver.RebaseUpdated)
115
{
116
// try to use the unResolved Source if it exists.
117
// New version of wixpdb file keeps a copy of pre-resolved Source. i.e. !(bindpath.test)\foo.dll
@@ -117,7 +122,7 @@ namespace WixToolset.Bind
122
}
123
}
124
120
- objectField.Data = this.ResolveFile(filePathToResolve, table.Name, row.SourceLineNumbers, BindStage.Updated);
125
+ objectField.Data = fileResolver.ResolveFile(filePathToResolve, table.Name, row.SourceLineNumbers, BindStage.Updated);
126
}
127
}
128
catch (WixFileNotFoundException)
@@ -127,10 +132,10 @@ namespace WixToolset.Bind
132
}
133
}
134
130
- isDefault = true;
135
if (null != objectField.PreviousData)
136
{
133
- objectField.PreviousData = this.WixVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, ref isDefault);
137
+ objectField.PreviousData = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, out isDefault);
138
+
139
if (!Messaging.Instance.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field.
140
{
141
// file is compressed in a cabinet (and not modified above)
@@ -142,7 +147,7 @@ namespace WixToolset.Bind
147
objectField.PreviousBaseUri = objectField.BaseUri;
148
}
149
145
- string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.PreviousBaseUri, objectField.PreviousEmbeddedFileIndex.Value, this.TempFilesLocation);
150
+ string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.PreviousBaseUri, objectField.PreviousEmbeddedFileIndex.Value, this.IntermediateFolder);
151
152
// set the path to the file once its extracted from the cabinet
153
objectField.PreviousData = extractPath;
@@ -151,14 +156,14 @@ namespace WixToolset.Bind
156
{
157
try
158
{
154
- if (!this.FileManagerCore.RebaseTarget && !this.FileManagerCore.RebaseUpdated)
159
+ if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated)
160
{
161
// resolve the path to the file
157
- objectField.PreviousData = this.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal);
162
+ objectField.PreviousData = fileResolver.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal);
163
}
164
else
165
{
161
- if (this.FileManagerCore.RebaseTarget)
166
+ if (fileResolver.RebaseTarget)
167
{
168
// if -bt is used, it come here
169
// Try to use the original unresolved source from either target build or update build
@@ -172,7 +177,7 @@ namespace WixToolset.Bind
177
}
178
179
// resolve the path to the file
175
- objectField.PreviousData = this.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Target);
180
+ objectField.PreviousData = fileResolver.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Target);
181
182
}
183
}
@@ -192,24 +197,28 @@ namespace WixToolset.Bind
197
this.DelayedFields = delayedFields;
198
}
199
200
+#if false
201
private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage = BindStage.Normal)
202
{
203
string path = null;
198
- foreach (IBinderFileManager fileManager in this.FileManagers)
204
+ foreach (var extension in this.Extensions)
205
{
200
- path = fileManager.ResolveFile(source, type, sourceLineNumbers, bindStage);
206
+ path = extension.ResolveFile(source, type, sourceLineNumbers, bindStage);
207
if (null != path)
208
{
209
break;
210
}
211
}
212
207
- if (null == path)
208
- {
209
- throw new WixFileNotFoundException(sourceLineNumbers, source, type);
210
- }
213
+ throw new NotImplementedException(); // need to do default binder stuff
214
+
215
+ //if (null == path)
216
+ //{
217
+ // throw new WixFileNotFoundException(sourceLineNumbers, source, type);
218
+ //}
219
212
- return path;
220
+ //return path;
221
}
222
+#endif
223
}
224
}
src/WixToolset.Core/Bind/ResolveResult.cs
new
+14
@@ -0,0 +1,14 @@
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.Bind
4
+{
5
+ using System.Collections.Generic;
6
+ using WixToolset.Extensibility;
7
+
8
+ public class ResolveResult
9
+ {
10
+ public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; set; }
11
+
12
+ public IEnumerable<IDelayedField> DelayedFields { get; set; }
13
+ }
14
+}
\ No newline at end of file
src/WixToolset.Core/Bind/ResolvedDirectory.cs
+1
-1
@@ -5,7 +5,7 @@ namespace WixToolset.Bind
5
/// <summary>
6
/// Structure used for resolved directory information.
7
/// </summary>
8
- internal struct ResolvedDirectory
8
+ public struct ResolvedDirectory
9
{
10
/// <summary>The directory parent.</summary>
11
public string DirectoryParent;
src/WixToolset.Core/Bind/TransferFilesCommand.cs
+21
-40
@@ -1,29 +1,37 @@
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.Bind
3
+namespace WixToolset.Core.Bind
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.IO;
8
using System.Security.AccessControl;
9
using WixToolset.Data;
10
+ using WixToolset.Data.Bind;
11
using WixToolset.Extensibility;
12
12
- internal class TransferFilesCommand : ICommand
13
+ internal class TransferFilesCommand
14
{
14
- public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
15
+ public TransferFilesCommand(IEnumerable<BindPath> bindPaths, IEnumerable<IBinderExtension> extensions, IEnumerable<FileTransfer> fileTransfers, bool suppressAclReset)
16
+ {
17
+ this.FileResolver = new FileResolver(bindPaths, extensions);
18
+ this.FileTransfers = fileTransfers;
19
+ this.SuppressAclReset = suppressAclReset;
20
+ }
21
+
22
+ private FileResolver FileResolver { get; }
23
16
- public IEnumerable<FileTransfer> FileTransfers { private get; set; }
24
+ private IEnumerable<FileTransfer> FileTransfers { get; }
25
18
- public bool SuppressAclReset { private get; set; }
26
+ private bool SuppressAclReset { get; }
27
28
public void Execute()
29
{
30
List<string> destinationFiles = new List<string>();
31
24
- foreach (FileTransfer fileTransfer in this.FileTransfers)
32
+ foreach (var fileTransfer in this.FileTransfers)
33
{
26
- string fileSource = this.ResolveFile(fileTransfer.Source, fileTransfer.Type, fileTransfer.SourceLineNumbers, BindStage.Normal);
34
+ string fileSource = this.FileResolver.ResolveFile(fileTransfer.Source, fileTransfer.Type, fileTransfer.SourceLineNumbers, BindStage.Normal);
35
36
// If the source and destination are identical, then there's nothing to do here
37
if (0 == String.Compare(fileSource, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase))
@@ -165,44 +173,17 @@ namespace WixToolset.Bind
173
}
174
}
175
168
- private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
176
+ private void TransferFile(bool move, string source, string destination)
177
{
170
- string path = null;
171
- foreach (IBinderFileManager fileManager in this.FileManagers)
172
- {
173
- path = fileManager.ResolveFile(source, type, sourceLineNumbers, bindStage);
174
- if (null != path)
175
- {
176
- break;
177
- }
178
- }
178
+ bool complete = false;
179
180
- if (null == path)
180
+ if (move)
181
{
182
- throw new WixFileNotFoundException(sourceLineNumbers, source, type);
182
+ complete = this.FileResolver.MoveFile(source, destination, true);
183
}
184
-
185
- return path;
186
- }
187
-
188
- private void TransferFile(bool move, string source, string destination)
189
- {
190
- bool complete = false;
191
- foreach (IBinderFileManager fileManager in this.FileManagers)
184
+ else
185
{
193
- if (move)
194
- {
195
- complete = fileManager.MoveFile(source, destination, true);
196
- }
197
- else
198
- {
199
- complete = fileManager.CopyFile(source, destination, true);
200
- }
201
-
202
- if (complete)
203
- {
204
- break;
205
- }
186
+ complete = this.FileResolver.CopyFile(source, destination, true);
187
}
188
189
if (!complete)
src/WixToolset.Core/BindContext.cs
new
+57
@@ -0,0 +1,57 @@
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
4
+{
5
+ using System.Collections.Generic;
6
+ using WixToolset.Data;
7
+ using WixToolset.Extensibility;
8
+
9
+ public class BindContext : IBindContext
10
+ {
11
+ public Messaging Messaging { get; set; }
12
+
13
+ public IEnumerable<BindPath> BindPaths { get; set; }
14
+
15
+ public int CabbingThreadCount { get; set; }
16
+
17
+ public string CabCachePath { get; set; }
18
+
19
+ public int Codepage { get; set; }
20
+
21
+ public CompressionLevel DefaultCompressionLevel { get; set; }
22
+
23
+ public IEnumerable<IDelayedField> DelayedFields { get; set; }
24
+
25
+ public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; set; }
26
+
27
+ public IExtensionManager ExtensionManager { get; set; }
28
+
29
+ public IEnumerable<IBinderExtension> Extensions { get; set; }
30
+
31
+ public IEnumerable<string> Ices { get; set; }
32
+
33
+ public string IntermediateFolder { get; set; }
34
+
35
+ public Output IntermediateRepresentation { get; set; }
36
+
37
+ public string OutputPath { get; set; }
38
+
39
+ public string OutputPdbPath { get; set; }
40
+
41
+ public bool SuppressAclReset { get; set; }
42
+
43
+ public IEnumerable<string> SuppressIces { get; set; }
44
+
45
+ public bool SuppressValidation { get; set; }
46
+
47
+ public IBindVariableResolver WixVariableResolver { get; set; }
48
+
49
+ public string ContentsFile { get; set; }
50
+
51
+ public string OutputsFile { get; set; }
52
+
53
+ public string BuiltOutputsFile { get; set; }
54
+
55
+ public string WixprojectFile { get; set; }
56
+ }
57
+}
src/WixToolset.Core/Binder.cs
+256
-133
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core
4
{
5
using System;
6
using System.Collections;
@@ -11,105 +11,72 @@ namespace WixToolset
11
using System.Linq;
12
using System.Reflection;
13
using WixToolset.Bind;
14
+ using WixToolset.Core.Bind;
15
using WixToolset.Data;
16
+ using WixToolset.Data.Bind;
17
using WixToolset.Data.Rows;
18
using WixToolset.Extensibility;
17
- using WixToolset.Msi;
18
-
19
- // TODO: (4.0) Refactor so that these don't need to be copied.
20
- // Copied verbatim from ext\UtilExtension\wixext\UtilCompiler.cs
21
- [Flags]
22
- internal enum WixFileSearchAttributes
23
- {
24
- Default = 0x001,
25
- MinVersionInclusive = 0x002,
26
- MaxVersionInclusive = 0x004,
27
- MinSizeInclusive = 0x008,
28
- MaxSizeInclusive = 0x010,
29
- MinDateInclusive = 0x020,
30
- MaxDateInclusive = 0x040,
31
- WantVersion = 0x080,
32
- WantExists = 0x100,
33
- IsDirectory = 0x200,
34
- }
35
-
36
- [Flags]
37
- internal enum WixRegistrySearchAttributes
38
- {
39
- Raw = 0x01,
40
- Compatible = 0x02,
41
- ExpandEnvironmentVariables = 0x04,
42
- WantValue = 0x08,
43
- WantExists = 0x10,
44
- Win64 = 0x20,
45
- }
46
-
47
- internal enum WixComponentSearchAttributes
48
- {
49
- KeyPath = 0x1,
50
- State = 0x2,
51
- WantDirectory = 0x4,
52
- }
53
-
54
- [Flags]
55
- internal enum WixProductSearchAttributes
56
- {
57
- Version = 0x1,
58
- Language = 0x2,
59
- State = 0x4,
60
- Assignment = 0x8,
61
- UpgradeCode = 0x10,
62
- }
19
20
/// <summary>
21
/// Binder of the WiX toolset.
22
/// </summary>
23
public sealed class Binder
24
{
69
- private BinderCore core;
70
- private BinderFileManagerCore fileManagerCore;
71
- private List<IBinderExtension> extensions;
72
- private List<IBinderFileManager> fileManagers;
73
- private List<InspectorExtension> inspectorExtensions;
25
+ //private BinderCore core;
26
+ //private List<IBinderExtension> extensions;
27
+ //private List<IBinderFileManager> fileManagers;
28
29
public Binder()
30
{
77
- this.DefaultCompressionLevel = CompressionLevel.High;
31
+ //this.DefaultCompressionLevel = CompressionLevel.High;
32
79
- this.BindPaths = new List<BindPath>();
80
- this.TargetBindPaths = new List<BindPath>();
81
- this.UpdatedBindPaths = new List<BindPath>();
33
+ //this.BindPaths = new List<BindPath>();
34
+ //this.TargetBindPaths = new List<BindPath>();
35
+ //this.UpdatedBindPaths = new List<BindPath>();
36
83
- this.extensions = new List<IBinderExtension>();
84
- this.fileManagers = new List<IBinderFileManager>();
85
- this.inspectorExtensions = new List<InspectorExtension>();
37
+ //this.extensions = new List<IBinderExtension>();
38
+ //this.fileManagers = new List<IBinderFileManager>();
39
+ //this.inspectorExtensions = new List<InspectorExtension>();
40
87
- this.Ices = new List<string>();
88
- this.SuppressIces = new List<string>();
41
+ //this.Ices = new List<string>();
42
+ //this.SuppressIces = new List<string>();
43
}
44
91
- public string ContentsFile { private get; set; }
45
+ public Binder(BindContext context)
46
+ {
47
+ this.Context = context;
48
+
49
+ this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions();
50
+ }
51
+
52
+ private BindContext Context { get; }
53
+
54
+ private TableDefinitionCollection TableDefinitions { get; }
55
+
56
+ //public IEnumerable<IBackendFactory> BackendFactories { get; set; }
57
93
- public string OutputsFile { private get; set; }
58
+ //public string ContentsFile { private get; set; }
59
95
- public string BuiltOutputsFile { private get; set; }
60
+ //public string OutputsFile { private get; set; }
61
97
- public string WixprojectFile { private get; set; }
62
+ //public string BuiltOutputsFile { private get; set; }
63
+
64
+ //public string WixprojectFile { private get; set; }
65
66
/// <summary>
67
/// Gets the list of bindpaths.
68
/// </summary>
102
- public List<BindPath> BindPaths { get; private set; }
69
+ //public List<BindPath> BindPaths { get; private set; }
70
71
/// <summary>
72
/// Gets the list of target bindpaths.
73
/// </summary>
107
- public List<BindPath> TargetBindPaths { get; private set; }
74
+ //public List<BindPath> TargetBindPaths { get; private set; }
75
76
/// <summary>
77
/// Gets the list of updated bindpaths.
78
/// </summary>
112
- public List<BindPath> UpdatedBindPaths { get; private set; }
79
+ //public List<BindPath> UpdatedBindPaths { get; private set; }
80
81
/// <summary>
82
/// Gets or sets the option to enable building binary delta patches.
@@ -132,17 +99,17 @@ namespace WixToolset
99
/// Gets or sets the default compression level to use for cabinets
100
/// that don't have their compression level explicitly set.
101
/// </summary>
135
- public CompressionLevel DefaultCompressionLevel { get; set; }
102
+ //public CompressionLevel DefaultCompressionLevel { get; set; }
103
104
/// <summary>
105
/// Gets and sets the location to save the WixPdb.
106
/// </summary>
107
/// <value>The location in which to save the WixPdb. Null if the the WixPdb should not be output.</value>
141
- public string PdbFile { get; set; }
108
+ //public string PdbFile { get; set; }
109
143
- public List<string> Ices { get; private set; }
110
+ //public List<string> Ices { get; private set; }
111
145
- public List<string> SuppressIces { get; private set; }
112
+ //public List<string> SuppressIces { get; private set; }
113
114
/// <summary>
115
/// Gets and sets the option to suppress resetting ACLs by the binder.
@@ -185,24 +152,164 @@ namespace WixToolset
152
/// Gets or sets the Wix variable resolver.
153
/// </summary>
154
/// <value>The Wix variable resolver.</value>
188
- public WixVariableResolver WixVariableResolver { get; set; }
155
+ internal WixVariableResolver WixVariableResolver { get; set; }
156
157
/// <summary>
158
/// Add a binder extension.
159
/// </summary>
160
/// <param name="extension">New extension.</param>
194
- public void AddExtension(IBinderExtension extension)
195
- {
196
- this.extensions.Add(extension);
197
- }
161
+ //public void AddExtension(IBinderExtension extension)
162
+ //{
163
+ // this.extensions.Add(extension);
164
+ //}
165
166
/// <summary>
167
/// Add a file manager extension.
168
/// </summary>
169
/// <param name="extension">New file manager.</param>
203
- public void AddExtension(IBinderFileManager extension)
170
+ //public void AddExtension(IBinderFileManager extension)
171
+ //{
172
+ // this.fileManagers.Add(extension);
173
+ //}
174
+
175
+ public bool Bind()
176
+ {
177
+ //if (!String.IsNullOrEmpty(this.Context.FileManagerCore.CabCachePath))
178
+ //{
179
+ // Directory.CreateDirectory(this.Context.FileManagerCore.CabCachePath);
180
+ //}
181
+
182
+ //this.core = new BinderCore();
183
+ //this.core.FileManagerCore = this.Context.FileManagerCore;
184
+
185
+ this.WriteBuildInfoTable(this.Context.IntermediateRepresentation, this.Context.OutputPath);
186
+
187
+ // Prebind.
188
+ //
189
+ this.Context.Extensions = this.Context.ExtensionManager.Create<IBinderExtension>();
190
+
191
+ foreach (IBinderExtension extension in this.Context.Extensions)
192
+ {
193
+ extension.PreBind(this.Context);
194
+ }
195
+
196
+ // Resolve.
197
+ //
198
+ var resolveResult = this.Resolve();
199
+
200
+ this.Context.DelayedFields = resolveResult.DelayedFields;
201
+
202
+ this.Context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
203
+
204
+ // Backend.
205
+ //
206
+ var bindResult = this.BackendBind();
207
+
208
+ if (bindResult != null)
209
+ {
210
+ // Postbind.
211
+ //
212
+ foreach (IBinderExtension extension in this.Context.Extensions)
213
+ {
214
+ extension.PostBind(bindResult);
215
+ }
216
+
217
+ // Layout.
218
+ //
219
+ this.Layout(bindResult);
220
+ }
221
+
222
+ return Messaging.Instance.EncounteredError;
223
+ }
224
+
225
+ private ResolveResult Resolve()
226
+ {
227
+ var buildingPatch = (this.Context.IntermediateRepresentation.Type == OutputType.Patch);
228
+
229
+ var filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
230
+
231
+ IEnumerable<DelayedField> delayedFields;
232
+ {
233
+ var command = new ResolveFieldsCommand();
234
+ command.BuildingPatch = buildingPatch;
235
+ command.BindVariableResolver = this.Context.WixVariableResolver;
236
+ command.BindPaths = this.Context.BindPaths;
237
+ command.Extensions = this.Context.Extensions;
238
+ command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
239
+ command.IntermediateFolder = this.Context.IntermediateFolder;
240
+ command.Tables = this.Context.IntermediateRepresentation.Tables;
241
+ command.SupportDelayedResolution = true;
242
+ command.Execute();
243
+
244
+ delayedFields = command.DelayedFields;
245
+ }
246
+
247
+ if (this.Context.IntermediateRepresentation.SubStorages != null)
248
+ {
249
+ foreach (SubStorage transform in this.Context.IntermediateRepresentation.SubStorages)
250
+ {
251
+ var command = new ResolveFieldsCommand();
252
+ command.BuildingPatch = buildingPatch;
253
+ command.BindVariableResolver = this.Context.WixVariableResolver;
254
+ command.BindPaths = this.Context.BindPaths;
255
+ command.Extensions = this.Context.Extensions;
256
+ command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
257
+ command.IntermediateFolder = this.Context.IntermediateFolder;
258
+ command.Tables = transform.Data.Tables;
259
+ command.SupportDelayedResolution = false;
260
+ command.Execute();
261
+ }
262
+ }
263
+
264
+ var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles();
265
+
266
+ return new ResolveResult
267
+ {
268
+ ExpectedEmbeddedFiles = expectedEmbeddedFiles,
269
+ DelayedFields = delayedFields,
270
+ };
271
+ }
272
+
273
+ private BindResult BackendBind()
274
+ {
275
+ var backendFactories = this.Context.ExtensionManager.Create<IBackendFactory>();
276
+
277
+ foreach (var factory in backendFactories)
278
+ {
279
+ if (factory.TryCreateBackend(this.Context.IntermediateRepresentation.Type.ToString(), this.Context.OutputPath, null, out var backend))
280
+ {
281
+ var result = backend.Bind(this.Context);
282
+ return result;
283
+ }
284
+ }
285
+
286
+ // TODO: messaging that a backend could not be found to bind the output type?
287
+
288
+ return null;
289
+ }
290
+ private void Layout(BindResult result)
291
{
205
- this.fileManagers.Add(extension);
292
+ try
293
+ {
294
+ this.LayoutMedia(result.FileTransfers);
295
+ }
296
+ finally
297
+ {
298
+ if (!String.IsNullOrEmpty(this.Context.ContentsFile) && result.ContentFilePaths != null)
299
+ {
300
+ this.CreateContentsFile(this.Context.ContentsFile, result.ContentFilePaths);
301
+ }
302
+
303
+ if (!String.IsNullOrEmpty(this.Context.OutputsFile) && result.FileTransfers != null)
304
+ {
305
+ this.CreateOutputsFile(this.Context.OutputsFile, result.FileTransfers, this.Context.OutputPdbPath);
306
+ }
307
+
308
+ if (!String.IsNullOrEmpty(this.Context.BuiltOutputsFile) && result.FileTransfers != null)
309
+ {
310
+ this.CreateBuiltOutputsFile(this.Context.BuiltOutputsFile, result.FileTransfers, this.Context.OutputPdbPath);
311
+ }
312
+ }
313
}
314
315
/// <summary>
@@ -212,6 +319,7 @@ namespace WixToolset
319
/// <param name="file">The Windows Installer file to create.</param>
320
/// <remarks>The Binder.DeleteTempFiles method should be called after calling this method.</remarks>
321
/// <returns>true if binding completed successfully; false otherwise</returns>
322
+#if false
323
public bool Bind(Output output, string file)
324
{
325
// Ensure the cabinet cache path exists if we are going to use it.
@@ -220,20 +328,20 @@ namespace WixToolset
328
Directory.CreateDirectory(this.CabCachePath);
329
}
330
223
- this.fileManagerCore = new BinderFileManagerCore();
224
- this.fileManagerCore.CabCachePath = this.CabCachePath;
225
- this.fileManagerCore.Output = output;
226
- this.fileManagerCore.TempFilesLocation = this.TempFilesLocation;
227
- this.fileManagerCore.AddBindPaths(this.BindPaths, BindStage.Normal);
228
- this.fileManagerCore.AddBindPaths(this.TargetBindPaths, BindStage.Target);
229
- this.fileManagerCore.AddBindPaths(this.UpdatedBindPaths, BindStage.Updated);
230
- foreach (IBinderFileManager fileManager in this.fileManagers)
231
- {
232
- fileManager.Core = this.fileManagerCore;
233
- }
331
+ //var fileManagerCore = new BinderFileManagerCore();
332
+ //fileManagerCore.CabCachePath = this.CabCachePath;
333
+ //fileManagerCore.Output = output;
334
+ //fileManagerCore.TempFilesLocation = this.TempFilesLocation;
335
+ //fileManagerCore.AddBindPaths(this.BindPaths, BindStage.Normal);
336
+ //fileManagerCore.AddBindPaths(this.TargetBindPaths, BindStage.Target);
337
+ //fileManagerCore.AddBindPaths(this.UpdatedBindPaths, BindStage.Updated);
338
+ //foreach (IBinderFileManager fileManager in this.fileManagers)
339
+ //{
340
+ // fileManager.Core = fileManagerCore;
341
+ //}
342
343
this.core = new BinderCore();
236
- this.core.FileManagerCore = this.fileManagerCore;
344
+ this.core.FileManagerCore = fileManagerCore;
345
346
this.WriteBuildInfoTable(output, file);
347
@@ -246,54 +354,69 @@ namespace WixToolset
354
}
355
356
// Gather all the wix variables.
249
- Table wixVariableTable = output.Tables["WixVariable"];
250
- if (null != wixVariableTable)
357
+ //Table wixVariableTable = output.Tables["WixVariable"];
358
+ //if (null != wixVariableTable)
359
+ //{
360
+ // foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows)
361
+ // {
362
+ // this.WixVariableResolver.AddVariable(wixVariableRow);
363
+ // }
364
+ //}
365
+
366
+ //BindContext context = new BindContext();
367
+ //context.CabbingThreadCount = this.CabbingThreadCount;
368
+ //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
369
+ //context.Extensions = this.extensions;
370
+ //context.FileManagerCore = fileManagerCore;
371
+ //context.FileManagers = this.fileManagers;
372
+ //context.Ices = this.Ices;
373
+ //context.IntermediateFolder = this.TempFilesLocation;
374
+ //context.IntermediateRepresentation = output;
375
+ //context.Localizer = this.Localizer;
376
+ //context.OutputPath = file;
377
+ //context.OutputPdbPath = this.PdbFile;
378
+ //context.SuppressIces = this.SuppressIces;
379
+ //context.SuppressValidation = this.SuppressValidation;
380
+ //context.WixVariableResolver = this.WixVariableResolver;
381
+
382
+ BindResult result = null;
383
+
384
+ foreach (var factory in this.BackendFactories)
385
{
252
- foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows)
386
+ if (factory.TryCreateBackend(output.Type.ToString(), file, null, out var backend))
387
{
254
- this.WixVariableResolver.AddVariable(wixVariableRow);
388
+ result = backend.Bind(context);
389
+ break;
390
}
391
}
392
258
- IEnumerable<FileTransfer> fileTransfers = null;
259
- IEnumerable<string> contentPaths = null;
260
-
261
- switch (output.Type)
393
+ if (result == null)
394
{
263
- case OutputType.Bundle:
264
- this.BindBundle(output, file, out fileTransfers, out contentPaths);
265
- break;
266
-
267
- case OutputType.Transform:
268
- this.BindTransform(output, file);
269
- break;
395
+ // TODO: messaging that a backend could not be found to bind the output type?
396
271
- default:
272
- this.BindDatabase(output, file, out fileTransfers, out contentPaths);
273
- break;
397
+ return false;
398
}
399
276
-
400
// Layout media
401
try
402
{
280
- this.LayoutMedia(fileTransfers);
403
+ this.LayoutMedia(result.FileTransfers);
404
}
405
finally
406
{
284
- if (!String.IsNullOrEmpty(this.ContentsFile) && contentPaths != null)
407
+ if (!String.IsNullOrEmpty(this.ContentsFile) && result.ContentFilePaths != null)
408
{
286
- this.CreateContentsFile(this.ContentsFile, contentPaths);
409
+ this.CreateContentsFile(this.ContentsFile, result.ContentFilePaths);
410
}
411
289
- if (!String.IsNullOrEmpty(this.OutputsFile) && fileTransfers != null)
412
+ if (!String.IsNullOrEmpty(this.OutputsFile) && result.FileTransfers != null)
413
{
291
- this.CreateOutputsFile(this.OutputsFile, fileTransfers, this.PdbFile);
414
+ this.CreateOutputsFile(this.OutputsFile, result.FileTransfers, this.PdbFile);
415
}
416
294
- if (!String.IsNullOrEmpty(this.BuiltOutputsFile) && fileTransfers != null)
417
+ if (!String.IsNullOrEmpty(this.BuiltOutputsFile) && result.FileTransfers != null)
418
{
296
- this.CreateBuiltOutputsFile(this.BuiltOutputsFile, fileTransfers, this.PdbFile);
419
+ this.CreateBuiltOutputsFile(this.BuiltOutputsFile, result.FileTransfers, this.PdbFile);
420
}
421
}
422
@@ -301,6 +424,7 @@ namespace WixToolset
424
425
return Messaging.Instance.EncounteredError;
426
}
427
+#endif
428
429
/// <summary>
430
/// Does any housekeeping after Bind.
@@ -312,12 +436,12 @@ namespace WixToolset
436
{
437
if (!this.DeleteTempFiles())
438
{
315
- this.core.OnMessage(WixWarnings.FailedToDeleteTempDir(this.TempFilesLocation));
439
+ this.Context.Messaging.OnMessage(WixWarnings.FailedToDeleteTempDir(this.TempFilesLocation));
440
}
441
}
442
else
443
{
320
- this.core.OnMessage(WixVerboses.BinderTempDirLocatedAt(this.TempFilesLocation));
444
+ this.Context.Messaging.OnMessage(WixVerboses.BinderTempDirLocatedAt(this.TempFilesLocation));
445
}
446
}
447
@@ -327,7 +451,7 @@ namespace WixToolset
451
/// <returns>True if all files were deleted, false otherwise.</returns>
452
private bool DeleteTempFiles()
453
{
330
- bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this.core);
454
+ bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this.Context.Messaging);
455
return deleted;
456
}
457
@@ -338,7 +462,7 @@ namespace WixToolset
462
/// <param name="databaseFile">The output file if OutputFile not set.</param>
463
private void WriteBuildInfoTable(Output output, string outputFile)
464
{
341
- Table buildInfoTable = output.EnsureTable(this.core.TableDefinitions["WixBuildInfo"]);
465
+ Table buildInfoTable = output.EnsureTable(this.TableDefinitions["WixBuildInfo"]);
466
Row buildInfoRow = buildInfoTable.CreateRow(null);
467
468
Assembly executingAssembly = Assembly.GetExecutingAssembly();
@@ -346,17 +470,18 @@ namespace WixToolset
470
buildInfoRow[0] = fileVersion.FileVersion;
471
buildInfoRow[1] = outputFile;
472
349
- if (!String.IsNullOrEmpty(this.WixprojectFile))
473
+ if (!String.IsNullOrEmpty(this.Context.WixprojectFile))
474
{
351
- buildInfoRow[2] = this.WixprojectFile;
475
+ buildInfoRow[2] = this.Context.WixprojectFile;
476
}
477
354
- if (!String.IsNullOrEmpty(this.PdbFile))
478
+ if (!String.IsNullOrEmpty(this.Context.OutputPdbPath))
479
{
356
- buildInfoRow[3] = this.PdbFile;
480
+ buildInfoRow[3] = this.Context.OutputPdbPath;
481
}
482
}
483
484
+#if DELETE_THIS_CODE
485
/// <summary>
486
/// Binds a bundle.
487
/// </summary>
@@ -454,6 +579,7 @@ namespace WixToolset
579
command.OutputPath = outputPath;
580
command.Execute();
581
}
582
+#endif
583
584
/// <summary>
585
/// Final step in binding that transfers (moves/copies) all files generated into the appropriate
@@ -464,12 +590,9 @@ namespace WixToolset
590
{
591
if (null != transfers && transfers.Any())
592
{
467
- this.core.OnMessage(WixVerboses.LayingOutMedia());
593
+ this.Context.Messaging.OnMessage(WixVerboses.LayingOutMedia());
594
469
- TransferFilesCommand command = new TransferFilesCommand();
470
- command.FileManagers = this.fileManagers;
471
- command.FileTransfers = transfers;
472
- command.SuppressAclReset = this.SuppressAclReset;
595
+ var command = new TransferFilesCommand(this.Context.BindPaths, this.Context.Extensions, transfers, this.Context.SuppressAclReset);
596
command.Execute();
597
}
598
}
@@ -482,7 +605,7 @@ namespace WixToolset
605
/// <param name="directory">Directory identifier.</param>
606
/// <param name="canonicalize">Canonicalize the path for standard directories.</param>
607
/// <returns>Source path of a directory.</returns>
485
- internal static string GetDirectoryPath(Hashtable directories, Hashtable componentIdGenSeeds, string directory, bool canonicalize)
608
+ public static string GetDirectoryPath(Hashtable directories, Hashtable componentIdGenSeeds, string directory, bool canonicalize)
609
{
610
if (!directories.Contains(directory))
611
{
@@ -543,9 +666,9 @@ namespace WixToolset
666
/// <param name="compressed">Specifies the package is compressed.</param>
667
/// <param name="useLongName">Specifies the package uses long file names.</param>
668
/// <returns>Source path of file relative to package directory.</returns>
546
- internal static string GetFileSourcePath(Hashtable directories, string directoryId, string fileName, bool compressed, bool useLongName)
669
+ public static string GetFileSourcePath(Hashtable directories, string directoryId, string fileName, bool compressed, bool useLongName)
670
{
548
- string fileSourcePath = Installer.GetName(fileName, true, useLongName);
671
+ string fileSourcePath = Common.GetName(fileName, true, useLongName);
672
673
if (compressed)
674
{
src/WixToolset.Core/BinderFileManager.cs
+2
@@ -12,6 +12,7 @@ namespace WixToolset
12
using WixToolset.Data.Rows;
13
using WixToolset.Extensibility;
14
15
+#if false
16
/// <summary>
17
/// Base class for creating a binder file manager.
18
/// </summary>
@@ -367,4 +368,5 @@ namespace WixToolset
368
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
369
private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
370
}
371
+#endif
372
}
src/WixToolset.Core/BinderFileManagerCore.cs
+1
@@ -6,6 +6,7 @@ namespace WixToolset
6
using System.Collections.Generic;
7
using System.Linq;
8
using WixToolset.Data;
9
+ using WixToolset.Data.Bind;
10
using WixToolset.Extensibility;
11
12
public class BinderFileManagerCore : IBinderFileManagerCore
src/WixToolset.Core/Cab/CabinetFileInfo.cs
+10
-29
@@ -1,19 +1,12 @@
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
3
+namespace WixToolset.Core.Cab
4
{
5
- using System;
6
-
5
/// <summary>
6
/// Properties of a file in a cabinet.
7
/// </summary>
10
- internal sealed class CabinetFileInfo
8
+ public sealed class CabinetFileInfo
9
{
12
- private string fileId;
13
- private ushort date;
14
- private ushort time;
15
- private int size;
16
-
10
/// <summary>
11
/// Constructs CabinetFileInfo
12
/// </summary>
@@ -22,43 +15,31 @@ namespace WixToolset
15
/// <param name="time">Last modified time (MS-DOS time)</param>
16
public CabinetFileInfo(string fileId, ushort date, ushort time, int size)
17
{
25
- this.fileId = fileId;
26
- this.date = date;
27
- this.time = time;
28
- this.size = size;
18
+ this.FileId = fileId;
19
+ this.Date = date;
20
+ this.Time = time;
21
+ this.Size = size;
22
}
23
24
/// <summary>
25
/// Gets the file Id of the file.
26
/// </summary>
27
/// <value>file Id</value>
35
- public string FileId
36
- {
37
- get { return this.fileId; }
38
- }
28
+ public string FileId { get; }
29
30
/// <summary>
31
/// Gets modified date (DOS format).
32
/// </summary>
43
- public ushort Date
44
- {
45
- get { return this.date; }
46
- }
33
+ public ushort Date { get; }
34
35
/// <summary>
36
/// Gets modified time (DOS format).
37
/// </summary>
51
- public ushort Time
52
- {
53
- get { return this.time; }
54
- }
38
+ public ushort Time { get; }
39
40
/// <summary>
41
/// Gets the size of the file in bytes.
42
/// </summary>
59
- public int Size
60
- {
61
- get { return this.size; }
62
- }
43
+ public int Size { get; }
44
}
45
}
src/WixToolset.Core/Cab/WixCreateCab.cs
+2
-2
@@ -1,12 +1,12 @@
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.Cab
3
+namespace WixToolset.Core.Cab
4
{
5
using System;
6
using System.Globalization;
7
using System.IO;
8
using System.Runtime.InteropServices;
9
- using WixToolset.Bind.Databases;
9
+ using WixToolset.Core.Bind;
10
using WixToolset.Core.Native;
11
using WixToolset.Data;
12
src/WixToolset.Core/Cab/WixEnumerateCab.cs
+3
-3
@@ -1,6 +1,6 @@
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.Cab
3
+namespace WixToolset.Core.Cab
4
{
5
using System;
6
using System.Collections.Generic;
@@ -10,7 +10,7 @@ namespace WixToolset.Cab
10
/// <summary>
11
/// Wrapper class around interop with wixcab.dll to enumerate files from a cabinet.
12
/// </summary>
13
- internal sealed class WixEnumerateCab : IDisposable
13
+ public sealed class WixEnumerateCab : IDisposable
14
{
15
private bool disposed;
16
private List<CabinetFileInfo> fileInfoList;
@@ -38,7 +38,7 @@ namespace WixToolset.Cab
38
/// </summary>
39
/// <param name="cabinetFile">path to cabinet</param>
40
/// <returns>list of CabinetFileInfo</returns>
41
- internal List<CabinetFileInfo> Enumerate(string cabinetFile)
41
+ public List<CabinetFileInfo> Enumerate(string cabinetFile)
42
{
43
this.fileInfoList = new List<CabinetFileInfo>();
44
src/WixToolset.Core/Cab/WixExtractCab.cs
+1
-2
@@ -1,9 +1,8 @@
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.Cab
3
+namespace WixToolset.Core.Cab
4
{
5
using System;
6
- using System.Runtime.InteropServices;
6
using WixToolset.Core.Native;
7
8
/// <summary>
src/WixToolset.Core/CommandLine/BuildCommand.cs
+67
-75
@@ -7,13 +7,14 @@ namespace WixToolset.Core
7
using System.IO;
8
using System.Linq;
9
using WixToolset.Data;
10
+ using WixToolset.Data.Rows;
11
using WixToolset.Extensibility;
12
13
internal class BuildCommand : ICommandLineCommand
14
{
14
- public BuildCommand(ExtensionManager extensions, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, string outputPath, OutputType outputType, IEnumerable<string> cultures, bool bindFiles, IEnumerable<BindPath> bindPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile)
15
+ public BuildCommand(ExtensionManager extensions, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, string outputPath, OutputType outputType, string cabCachePath, IEnumerable<string> cultures, bool bindFiles, IEnumerable<BindPath> bindPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile)
16
{
16
- this.Extensions = extensions;
17
+ this.ExtensionManager = extensions;
18
this.LocFiles = locFiles;
19
this.LibraryFiles = libraryFiles;
20
this.PreprocessorVariables = preprocessorVariables;
@@ -21,6 +22,7 @@ namespace WixToolset.Core
22
this.OutputPath = outputPath;
23
this.OutputType = outputType;
24
25
+ this.CabCachePath = cabCachePath;
26
this.Cultures = cultures;
27
this.BindFiles = bindFiles;
28
this.BindPaths = bindPaths;
@@ -32,7 +34,7 @@ namespace WixToolset.Core
34
this.WixProjectFile = wixProjectFile;
35
}
36
35
- public ExtensionManager Extensions { get; }
37
+ public ExtensionManager ExtensionManager { get; }
38
39
public IEnumerable<string> LocFiles { get; }
40
@@ -46,6 +48,8 @@ namespace WixToolset.Core
48
49
private OutputType OutputType { get; }
50
51
+ public string CabCachePath { get; }
52
+
53
public IEnumerable<string> Cultures { get; }
54
55
public bool BindFiles { get; }
@@ -70,7 +74,9 @@ namespace WixToolset.Core
74
75
if (this.OutputType == OutputType.Library)
76
{
73
- this.LibraryPhase(intermediates, tableDefinitions);
77
+ var library = this.LibraryPhase(intermediates, tableDefinitions);
78
+
79
+ library?.Save(this.OutputPath);
80
}
81
else
82
{
@@ -105,51 +111,40 @@ namespace WixToolset.Core
111
return intermediates;
112
}
113
108
- private void LibraryPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
114
+ private Library LibraryPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
115
{
116
var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList();
117
118
// If there was an error adding localization files, then bail.
119
if (Messaging.Instance.EncounteredError)
120
{
115
- return;
121
+ return null;
122
}
123
118
- var sections = intermediates.SelectMany(i => i.Sections).ToList();
119
-
120
- LibraryBinaryFileResolver resolver = null;
121
-
122
- if (this.BindFiles)
123
- {
124
- resolver = new LibraryBinaryFileResolver();
125
- resolver.FileManagers = new List<IBinderFileManager> { new BinderFileManager() }; ;
126
- resolver.VariableResolver = new WixVariableResolver();
127
-
128
- BinderFileManagerCore core = new BinderFileManagerCore();
129
- core.AddBindPaths(this.BindPaths, BindStage.Normal);
130
-
131
- foreach (var fileManager in resolver.FileManagers)
132
- {
133
- fileManager.Core = core;
134
- }
135
- }
124
+ var resolver = CreateWixResolverWithVariables(null, null);
125
137
- var librarian = new Librarian();
126
+ var context = new LibraryContext();
127
+ context.BindFiles = this.BindFiles;
128
+ context.BindPaths = this.BindPaths;
129
+ context.Extensions = this.ExtensionManager.Create<ILibrarianExtension>();
130
+ context.Localizations = localizations;
131
+ context.Sections = intermediates.SelectMany(i => i.Sections).ToList();
132
+ context.WixVariableResolver = resolver;
133
139
- var library = librarian.Combine(sections, localizations, resolver);
134
+ var librarian = new Librarian(context);
135
141
- library?.Save(this.OutputPath);
136
+ return librarian.Combine();
137
}
138
139
private Output LinkPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
140
{
141
var sections = intermediates.SelectMany(i => i.Sections).ToList();
142
148
- sections.AddRange(SectionsFromLibraries(tableDefinitions));
143
+ sections.AddRange(this.SectionsFromLibraries(tableDefinitions));
144
145
var linker = new Linker();
146
152
- foreach (var data in this.Extensions.Create<IExtensionData>())
147
+ foreach (var data in this.ExtensionManager.Create<IExtensionData>())
148
{
149
linker.AddExtensionData(data);
150
}
@@ -159,6 +154,40 @@ namespace WixToolset.Core
154
return output;
155
}
156
157
+ private void BindPhase(Output output, TableDefinitionCollection tableDefinitions)
158
+ {
159
+ var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList();
160
+
161
+ var localizer = new Localizer(localizations);
162
+
163
+ var resolver = CreateWixResolverWithVariables(localizer, output);
164
+
165
+ var context = new BindContext();
166
+ context.Messaging = Messaging.Instance;
167
+ context.ExtensionManager = this.ExtensionManager;
168
+ context.BindPaths = this.BindPaths ?? Array.Empty<BindPath>();
169
+ //context.CabbingThreadCount = this.CabbingThreadCount;
170
+ context.CabCachePath = this.CabCachePath;
171
+ context.Codepage = localizer.Codepage;
172
+ //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
173
+ //context.Ices = this.Ices;
174
+ context.IntermediateFolder = this.IntermediateFolder;
175
+ context.IntermediateRepresentation = output;
176
+ context.OutputPath = this.OutputPath;
177
+ context.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb");
178
+ //context.SuppressIces = this.SuppressIces;
179
+ context.SuppressValidation = true;
180
+ //context.SuppressValidation = this.SuppressValidation;
181
+ context.WixVariableResolver = resolver;
182
+ context.ContentsFile = this.ContentsFile;
183
+ context.OutputsFile = this.OutputsFile;
184
+ context.BuiltOutputsFile = this.BuiltOutputsFile;
185
+ context.WixprojectFile = this.WixProjectFile;
186
+
187
+ var binder = new Binder(context);
188
+ binder.Bind();
189
+ }
190
+
191
private IEnumerable<Section> SectionsFromLibraries(TableDefinitionCollection tableDefinitions)
192
{
193
var sections = new List<Section>();
@@ -187,34 +216,6 @@ namespace WixToolset.Core
216
return sections;
217
}
218
190
- private void BindPhase(Output output, TableDefinitionCollection tableDefinitions)
191
- {
192
- var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList();
193
-
194
- var localizer = new Localizer(localizations);
195
-
196
- var resolver = new WixVariableResolver(localizer);
197
-
198
- var binder = new Binder();
199
- binder.TempFilesLocation = this.IntermediateFolder;
200
- binder.WixVariableResolver = resolver;
201
- binder.SuppressValidation = true;
202
-
203
- binder.ContentsFile = this.ContentsFile;
204
- binder.OutputsFile = this.OutputsFile;
205
- binder.BuiltOutputsFile = this.BuiltOutputsFile;
206
- binder.WixprojectFile = this.WixProjectFile;
207
-
208
- if (this.BindPaths != null)
209
- {
210
- binder.BindPaths.AddRange(this.BindPaths);
211
- }
212
-
213
- binder.AddExtension(new BinderFileManager());
214
-
215
- binder.Bind(output, this.OutputPath);
216
- }
217
-
219
private IEnumerable<Localization> LoadLocalizationFiles(TableDefinitionCollection tableDefinitions)
220
{
221
foreach (var loc in this.LocFiles)
@@ -225,30 +226,21 @@ namespace WixToolset.Core
226
}
227
}
228
228
- /// <summary>
229
- /// File resolution mechanism to create binary library.
230
- /// </summary>
231
- private class LibraryBinaryFileResolver : ILibraryBinaryFileResolver
229
+ private static WixVariableResolver CreateWixResolverWithVariables(Localizer localizer, Output output)
230
{
233
- public IEnumerable<IBinderFileManager> FileManagers { get; set; }
234
-
235
- public WixVariableResolver VariableResolver { get; set; }
231
+ var resolver = new WixVariableResolver(localizer);
232
237
- public string Resolve(SourceLineNumber sourceLineNumber, string table, string path)
233
+ // Gather all the wix variables.
234
+ Table wixVariableTable = output?.Tables["WixVariable"];
235
+ if (null != wixVariableTable)
236
{
239
- string resolvedPath = this.VariableResolver.ResolveVariables(sourceLineNumber, path, false);
240
-
241
- foreach (IBinderFileManager fileManager in this.FileManagers)
237
+ foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows)
238
{
243
- string finalPath = fileManager.ResolveFile(resolvedPath, table, sourceLineNumber, BindStage.Normal);
244
- if (!String.IsNullOrEmpty(finalPath))
245
- {
246
- return finalPath;
247
- }
239
+ resolver.AddVariable(wixVariableRow);
240
}
249
-
250
- return null;
241
}
242
+
243
+ return resolver;
244
}
245
}
246
}
src/WixToolset.Core/CommandLine/CommandLine.cs
+21
-1
@@ -6,6 +6,7 @@ namespace WixToolset.Core
6
using System.Collections.Generic;
7
using System.IO;
8
using System.Linq;
9
+ using System.Reflection;
10
using System.Text;
11
using System.Text.RegularExpressions;
12
using WixToolset.Data;
@@ -71,6 +72,7 @@ namespace WixToolset.Core
72
73
var intermediateFolder = String.Empty;
74
75
+ var cabCachePath = String.Empty;
76
var cultures = new List<string>();
77
var contentsFile = String.Empty;
78
var outputsFile = String.Empty;
@@ -98,6 +100,10 @@ namespace WixToolset.Core
100
cmdline.GetNextArgumentOrError(bindPaths);
101
return true;
102
103
+ case "cc":
104
+ cmdline.GetNextArgumentOrError(ref cabCachePath);
105
+ return true;
106
+
107
case "cultures":
108
cmdline.GetNextArgumentOrError(cultures);
109
return true;
@@ -190,12 +196,14 @@ namespace WixToolset.Core
196
{
197
case Commands.Build:
198
{
199
+ LoadStandardBackends(cli.ExtensionManager);
200
+
201
var sourceFiles = GatherSourceFiles(files, outputFolder);
202
var variables = GatherPreprocessorVariables(defines);
203
var bindPathList = GatherBindPaths(bindPaths);
204
var extensions = cli.ExtensionManager;
205
var type = CalculateOutputType(outputType, outputFile);
198
- return new BuildCommand(extensions, sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cultures, bindFiles, bindPathList, intermediateFolder, contentsFile, outputsFile, builtOutputsFile, wixProjectFile);
206
+ return new BuildCommand(extensions, sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cabCachePath, cultures, bindFiles, bindPathList, intermediateFolder, contentsFile, outputsFile, builtOutputsFile, wixProjectFile);
207
}
208
209
case Commands.Compile:
@@ -209,6 +217,18 @@ namespace WixToolset.Core
217
return null;
218
}
219
220
+ private static void LoadStandardBackends(ExtensionManager extensionManager)
221
+ {
222
+ var folder = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
223
+
224
+ foreach (var backendAssemblyName in new[] { "WixToolset.Core.Burn.dll", "WixToolset.Core.WindowsInstaller.dll" })
225
+ {
226
+ var path = Path.Combine(folder, backendAssemblyName);
227
+
228
+ extensionManager.Load(path);
229
+ }
230
+ }
231
+
232
private static OutputType CalculateOutputType(string outputType, string outputFile)
233
{
234
if (String.IsNullOrEmpty(outputType))
src/WixToolset.Core/Common.cs
+150
-12
@@ -17,7 +17,7 @@ namespace WixToolset
17
/// <summary>
18
/// Common Wix utility methods and types.
19
/// </summary>
20
- internal static class Common
20
+ public static class Common
21
{
22
//-------------------------------------------------------------------------------------------------
23
// Layout of an Access Mask (from http://technet.microsoft.com/en-us/library/cc783530(WS.10).aspx)
@@ -89,9 +89,7 @@ namespace WixToolset
89
// FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
90
internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", "FileAllRights", "ReadAttributes", "WriteAttributes" };
91
92
- internal static readonly string[] ReservedFileNames = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
93
-
94
- internal static readonly Regex WixVariableRegex = new Regex(@"(\!|\$)\((?<namespace>loc|wix|bind|bindpath)\.(?<fullname>(?<name>[_A-Za-z][0-9A-Za-z_]+)(\.(?<scope>[_A-Za-z][0-9A-Za-z_\.]*))?)(\=(?<value>.+?))?\)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
92
+ public static readonly Regex WixVariableRegex = new Regex(@"(\!|\$)\((?<namespace>loc|wix|bind|bindpath)\.(?<fullname>(?<name>[_A-Za-z][0-9A-Za-z_]+)(\.(?<scope>[_A-Za-z][0-9A-Za-z_\.]*))?)(\=(?<value>.+?))?\)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
93
94
internal const char CustomRowFieldSeparator = '\x85';
95
@@ -170,15 +168,14 @@ namespace WixToolset
168
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
169
/// <exception cref="NotSupportedException">The value doesn't not represent a valid code page name or integer value.</exception>
170
/// <exception cref="WixException">The code page is invalid for summary information.</exception>
173
- internal static int GetValidCodePage(string value, bool allowNoChange = false, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null)
171
+ public static int GetValidCodePage(string value, bool allowNoChange = false, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null)
172
{
175
- int codePage;
176
- Encoding encoding;
177
-
173
try
174
{
175
+ Encoding encoding;
176
+
177
// check if a integer as a string was passed
181
- if (Int32.TryParse(value, out codePage))
178
+ if (Int32.TryParse(value, out int codePage))
179
{
180
if (0 == codePage)
181
{
@@ -366,9 +363,9 @@ namespace WixToolset
363
/// Generate a new Windows Installer-friendly guid.
364
/// </summary>
365
/// <returns>A new guid.</returns>
369
- internal static string GenerateGuid()
366
+ public static string GenerateGuid()
367
{
371
- return Guid.NewGuid().ToString("B").ToUpper(CultureInfo.InvariantCulture);
368
+ return Guid.NewGuid().ToString("B").ToUpperInvariant();
369
}
370
371
/// <summary>
@@ -465,7 +462,7 @@ namespace WixToolset
462
}
463
}
464
468
- internal static string GetFileHash(string path)
465
+ public static string GetFileHash(string path)
466
{
467
using (SHA1Managed managed = new SHA1Managed())
468
{
@@ -477,6 +474,147 @@ namespace WixToolset
474
}
475
}
476
477
+ /// <summary>
478
+ /// Takes an id, and demodularizes it (if possible).
479
+ /// </summary>
480
+ /// <remarks>
481
+ /// If the output type is a module, returns a demodularized version of an id. Otherwise, returns the id.
482
+ /// </remarks>
483
+ /// <param name="outputType">The type of the output to bind.</param>
484
+ /// <param name="modularizationGuid">The modularization GUID.</param>
485
+ /// <param name="id">The id to demodularize.</param>
486
+ /// <returns>The demodularized id.</returns>
487
+ public static string Demodularize(OutputType outputType, string modularizationGuid, string id)
488
+ {
489
+ if (OutputType.Module == outputType && id.EndsWith(String.Concat(".", modularizationGuid), StringComparison.Ordinal))
490
+ {
491
+ id = id.Substring(0, id.Length - 37);
492
+ }
493
+
494
+ return id;
495
+ }
496
+
497
+ /// <summary>
498
+ /// Get the source/target and short/long file names from an MSI Filename column.
499
+ /// </summary>
500
+ /// <param name="value">The Filename value.</param>
501
+ /// <returns>An array of strings of length 4. The contents are: short target, long target, short source, and long source.</returns>
502
+ /// <remarks>
503
+ /// If any particular file name part is not parsed, its set to null in the appropriate location of the returned array of strings.
504
+ /// However, the returned array will always be of length 4.
505
+ /// </remarks>
506
+ public static string[] GetNames(string value)
507
+ {
508
+ string[] names = new string[4];
509
+ int targetSeparator = value.IndexOf(":", StringComparison.Ordinal);
510
+
511
+ // split source and target
512
+ string sourceName = null;
513
+ string targetName = value;
514
+ if (0 <= targetSeparator)
515
+ {
516
+ sourceName = value.Substring(targetSeparator + 1);
517
+ targetName = value.Substring(0, targetSeparator);
518
+ }
519
+
520
+ // split the source short and long names
521
+ string sourceLongName = null;
522
+ if (null != sourceName)
523
+ {
524
+ int sourceLongNameSeparator = sourceName.IndexOf("|", StringComparison.Ordinal);
525
+ if (0 <= sourceLongNameSeparator)
526
+ {
527
+ sourceLongName = sourceName.Substring(sourceLongNameSeparator + 1);
528
+ sourceName = sourceName.Substring(0, sourceLongNameSeparator);
529
+ }
530
+ }
531
+
532
+ // split the target short and long names
533
+ int targetLongNameSeparator = targetName.IndexOf("|", StringComparison.Ordinal);
534
+ string targetLongName = null;
535
+ if (0 <= targetLongNameSeparator)
536
+ {
537
+ targetLongName = targetName.Substring(targetLongNameSeparator + 1);
538
+ targetName = targetName.Substring(0, targetLongNameSeparator);
539
+ }
540
+
541
+ // remove the long source name when its identical to the long source name
542
+ if (null != sourceName && sourceName == sourceLongName)
543
+ {
544
+ sourceLongName = null;
545
+ }
546
+
547
+ // remove the long target name when its identical to the long target name
548
+ if (null != targetName && targetName == targetLongName)
549
+ {
550
+ targetLongName = null;
551
+ }
552
+
553
+ // remove the source names when they are identical to the target names
554
+ if (sourceName == targetName && sourceLongName == targetLongName)
555
+ {
556
+ sourceName = null;
557
+ sourceLongName = null;
558
+ }
559
+
560
+ // target name(s)
561
+ if ("." != targetName)
562
+ {
563
+ names[0] = targetName;
564
+ }
565
+
566
+ if (null != targetLongName && "." != targetLongName)
567
+ {
568
+ names[1] = targetLongName;
569
+ }
570
+
571
+ // source name(s)
572
+ if (null != sourceName)
573
+ {
574
+ names[2] = sourceName;
575
+ }
576
+
577
+ if (null != sourceLongName && "." != sourceLongName)
578
+ {
579
+ names[3] = sourceLongName;
580
+ }
581
+
582
+ return names;
583
+ }
584
+
585
+ /// <summary>
586
+ /// Get a source/target and short/long file name from an MSI Filename column.
587
+ /// </summary>
588
+ /// <param name="value">The Filename value.</param>
589
+ /// <param name="source">true to get a source name; false to get a target name</param>
590
+ /// <param name="longName">true to get a long name; false to get a short name</param>
591
+ /// <returns>The name.</returns>
592
+ public static string GetName(string value, bool source, bool longName)
593
+ {
594
+ string[] names = GetNames(value);
595
+
596
+ if (source)
597
+ {
598
+ if (longName && null != names[3])
599
+ {
600
+ return names[3];
601
+ }
602
+ else if (null != names[2])
603
+ {
604
+ return names[2];
605
+ }
606
+ }
607
+
608
+ if (longName && null != names[1])
609
+ {
610
+ return names[1];
611
+ }
612
+ else
613
+ {
614
+ return names[0];
615
+ }
616
+ }
617
+
618
/// <summary>
619
/// Get an attribute value.
620
/// </summary>
src/WixToolset.Core/Compiler.cs
+10
-14
@@ -11,11 +11,11 @@ namespace WixToolset
11
using System.IO;
12
using System.Text.RegularExpressions;
13
using System.Xml.Linq;
14
+ using WixToolset.Core;
15
+ using WixToolset.Core.Native;
16
using WixToolset.Data;
17
using WixToolset.Data.Rows;
18
using WixToolset.Extensibility;
17
- using WixToolset.Msi;
18
- using WixToolset.Core.Native;
19
using Wix = WixToolset.Data.Serialize;
20
21
/// <summary>
@@ -158,10 +158,7 @@ namespace WixToolset
158
[SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
159
public Intermediate Compile(XDocument source)
160
{
161
- if (null == source)
162
- {
163
- throw new ArgumentNullException("source");
164
- }
161
+ if (null == source) throw new ArgumentNullException(nameof(source));
162
163
bool encounteredError = false;
164
@@ -220,9 +217,7 @@ namespace WixToolset
217
{
218
if (field.Data is string)
219
{
223
- bool isDefault = false;
224
- bool delayedResolve = false;
225
- field.Data = this.componentIdPlaceholdersResolver.ResolveVariables(row.SourceLineNumbers, (string)field.Data, false, false, ref isDefault, ref delayedResolve);
220
+ field.Data = this.componentIdPlaceholdersResolver.ResolveVariables(row.SourceLineNumbers, (string)field.Data, false, false, out var defaultIgnored, out var delayedIgnored);
221
}
222
}
223
}
@@ -470,7 +465,8 @@ namespace WixToolset
465
case "Advertise":
466
appIdAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
467
break;
473
- case "Description": description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
468
+ case "Description":
469
+ description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
470
break;
471
case "DllSurrogate":
472
dllSurrogate = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
@@ -9471,13 +9467,13 @@ namespace WixToolset
9467
targetProductName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9468
break;
9469
case "ApiPatchingSymbolNoImagehlpFlag":
9474
- apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchAPI.PatchInterop.PatchSymbolFlagsType.PATCH_SYMBOL_NO_IMAGEHLP : 0;
9470
+ apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlagsType.PATCH_SYMBOL_NO_IMAGEHLP : 0;
9471
break;
9472
case "ApiPatchingSymbolNoFailuresFlag":
9477
- apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchAPI.PatchInterop.PatchSymbolFlagsType.PATCH_SYMBOL_NO_FAILURES : 0;
9473
+ apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlagsType.PATCH_SYMBOL_NO_FAILURES : 0;
9474
break;
9475
case "ApiPatchingSymbolUndecoratedTooFlag":
9480
- apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchAPI.PatchInterop.PatchSymbolFlagsType.PATCH_SYMBOL_UNDECORATED_TOO : 0;
9476
+ apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlagsType.PATCH_SYMBOL_UNDECORATED_TOO : 0;
9477
break;
9478
case "OptimizePatchSizeForLargeFiles":
9479
optimizePatchSizeForLargeFiles = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
@@ -11802,7 +11798,7 @@ namespace WixToolset
11798
private void ParseProductElement(XElement node)
11799
{
11800
SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11805
- int codepage = 0;
11801
+ int codepage = 65001;
11802
string productCode = null;
11803
string upgradeCode = null;
11804
string manufacturer = null;
src/WixToolset.Core/CompilerCore.cs
+5
-4
@@ -45,10 +45,6 @@ namespace WixToolset
45
internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/";
46
internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
47
48
- public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB
49
- public const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB
50
- public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB)
51
-
48
private static readonly Regex AmbiguousFilename = new Regex(@"^.{6}\~\d", RegexOptions.Compiled);
49
50
private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * "
@@ -67,6 +63,11 @@ namespace WixToolset
63
64
private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?<access>public|internal|protected|private)\s+)?(?<id>[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
65
66
+ public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB
67
+ public const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB
68
+ public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB)
69
+
70
+
71
// Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 113)
72
private static readonly List<String> BuiltinBundleVariables = new List<string>(
73
new string[] {
src/WixToolset.Core/Data/messages.xml
-12
@@ -957,12 +957,6 @@
957
<Parameter Type="System.String" Name="exceptionMessage" />
958
</Instance>
959
</Message>
960
- <Message Id="InvalidFileName" Number="85">
961
- <Instance>
962
- Invalid file name '{0}'.
963
- <Parameter Type="System.String" Name="fileName" />
964
- </Instance>
965
- </Message>
960
<Message Id="ReferenceLoopDetected" Number="86">
961
<Instance>
962
A circular reference of groups was detected. The infinite loop includes: {0}. Group references must form a directed acyclic graph.
@@ -2138,12 +2132,6 @@
2132
This patch is not uninstallable. The 'Patch' element's attribute 'AllowRemoval' should be set to 'no'.
2133
</Instance>
2134
</Message>
2141
- <Message Id="PathTooLong" Number="262">
2142
- <Instance>
2143
- '{0}' is too long, the fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
2144
- <Parameter Type="System.String" Name="fileName" />
2145
- </Instance>
2146
- </Message>
2135
<Message Id="FileTooLarge" Number="263">
2136
<Instance>
2137
'{0}' is too large, file size must be less than 2147483648.
src/WixToolset.Core/Decompiler.cs
+11
-12
@@ -3,7 +3,6 @@
3
namespace WixToolset
4
{
5
using System;
6
- using System.CodeDom.Compiler;
6
using System.Collections;
7
using System.Collections.Generic;
8
using System.Collections.Specialized;
@@ -15,9 +14,9 @@ namespace WixToolset
14
using WixToolset.Data;
15
using WixToolset.Data.Rows;
16
using WixToolset.Extensibility;
18
- using WixToolset.Msi;
17
using WixToolset.Core.Native;
18
using Wix = WixToolset.Data.Serialize;
19
+ using WixToolset.Core;
20
21
/// <summary>
22
/// Decompiles an msi database into WiX source.
@@ -5201,7 +5200,7 @@ namespace WixToolset
5200
5201
directory.Id = Convert.ToString(row[0]);
5202
5204
- string[] names = Installer.GetNames(Convert.ToString(row[2]));
5203
+ string[] names = Common.GetNames(Convert.ToString(row[2]));
5204
5205
if (String.Equals(directory.Id, "TARGETDIR", StringComparison.Ordinal) && !String.Equals(names[0], "SourceDir", StringComparison.Ordinal))
5206
{
@@ -5319,7 +5318,7 @@ namespace WixToolset
5318
5319
if (null != row[3])
5320
{
5322
- string[] names = Installer.GetNames(Convert.ToString(row[3]));
5321
+ string[] names = Common.GetNames(Convert.ToString(row[3]));
5322
if (null != names[0] && null != names[1])
5323
{
5324
copyFile.DestinationShortName = names[0];
@@ -5788,7 +5787,7 @@ namespace WixToolset
5787
5788
file.Id = fileRow.File;
5789
5791
- string[] names = Installer.GetNames(fileRow.FileName);
5790
+ string[] names = Common.GetNames(fileRow.FileName);
5791
if (null != names[0] && null != names[1])
5792
{
5793
file.ShortName = names[0];
@@ -5974,7 +5973,7 @@ namespace WixToolset
5973
5974
iniFile.Id = Convert.ToString(row[0]);
5975
5977
- string[] names = Installer.GetNames(Convert.ToString(row[1]));
5976
+ string[] names = Common.GetNames(Convert.ToString(row[1]));
5977
5978
if (null != names[0])
5979
{
@@ -6044,7 +6043,7 @@ namespace WixToolset
6043
6044
iniFileSearch.Id = Convert.ToString(row[0]);
6045
6047
- string[] names = Installer.GetNames(Convert.ToString(row[1]));
6046
+ string[] names = Common.GetNames(Convert.ToString(row[1]));
6047
if (null != names[0] && null != names[1])
6048
{
6049
iniFileSearch.ShortName = names[0];
@@ -6681,7 +6680,7 @@ namespace WixToolset
6680
6681
if (null != row[3])
6682
{
6684
- string[] names = Installer.GetNames(Convert.ToString(row[3]));
6683
+ string[] names = Common.GetNames(Convert.ToString(row[3]));
6684
if (null != names[0] && null != names[1])
6685
{
6686
copyFile.DestinationShortName = names[0];
@@ -8007,7 +8006,7 @@ namespace WixToolset
8006
8007
removeFile.Id = Convert.ToString(row[0]);
8008
8010
- string[] names = Installer.GetNames(Convert.ToString(row[2]));
8009
+ string[] names = Common.GetNames(Convert.ToString(row[2]));
8010
if (null != names[0] && null != names[1])
8011
{
8012
removeFile.ShortName = names[0];
@@ -8062,7 +8061,7 @@ namespace WixToolset
8061
8062
iniFile.Id = Convert.ToString(row[0]);
8063
8065
- string[] names = Installer.GetNames(Convert.ToString(row[1]));
8064
+ string[] names = Common.GetNames(Convert.ToString(row[1]));
8065
if (null != names[0] && null != names[1])
8066
{
8067
iniFile.ShortName = names[0];
@@ -8531,7 +8530,7 @@ namespace WixToolset
8530
8531
shortcut.Directory = Convert.ToString(row[1]);
8532
8534
- string[] names = Installer.GetNames(Convert.ToString(row[2]));
8533
+ string[] names = Common.GetNames(Convert.ToString(row[2]));
8534
if (null != names[0] && null != names[1])
8535
{
8536
shortcut.ShortName = names[0];
@@ -8654,7 +8653,7 @@ namespace WixToolset
8653
8654
fileSearch.Id = Convert.ToString(row[0]);
8655
8657
- string[] names = Installer.GetNames(Convert.ToString(row[1]));
8656
+ string[] names = Common.GetNames(Convert.ToString(row[1]));
8657
if (null != names[0])
8658
{
8659
// it is permissable to just have a long name
src/WixToolset.Core/Extensibility/HeatExtension.cs
-4
@@ -3,14 +3,10 @@
3
namespace WixToolset.Extensibility
4
{
5
using System;
6
- using System.Collections.Generic;
6
using System.IO;
7
using System.Reflection;
9
- using WixToolset;
8
using WixToolset.Data;
11
- using WixToolset.Extensibilty;
9
using WixToolset.Tools;
13
- using Wix = WixToolset.Data.Serialize;
10
11
/// <summary>
12
/// A command line option.
src/WixToolset.Core/Extensibility/IHeatCore.cs
+1
-1
@@ -1,6 +1,6 @@
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.Extensibilty
3
+namespace WixToolset.Extensibility
4
{
5
using WixToolset.Data;
6
src/WixToolset.Core/ExtensionManager.cs
+3
-3
@@ -8,8 +8,9 @@ namespace WixToolset
8
using System.Linq;
9
using System.Reflection;
10
using WixToolset.Data;
11
+ using WixToolset.Extensibility;
12
12
- public class ExtensionManager
13
+ public class ExtensionManager : IExtensionManager
14
{
15
private List<Assembly> extensionAssemblies = new List<Assembly>();
16
@@ -67,8 +68,7 @@ namespace WixToolset
68
/// <returns>Extensions created of the specified type.</returns>
69
public IEnumerable<T> Create<T>() where T : class
70
{
70
- var extensionType = typeof(T);
71
- var types = this.extensionAssemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && extensionType.IsAssignableFrom(t)));
71
+ var types = this.extensionAssemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && typeof(T).IsAssignableFrom(t)));
72
return types.Select(t => (T)Activator.CreateInstance(t)).ToList();
73
}
74
src/WixToolset.Core/HeatCore.cs
+1
-4
@@ -2,11 +2,8 @@
2
3
namespace WixToolset.Tools
4
{
5
- using System;
6
- using System.Reflection;
5
using WixToolset.Data;
8
- using WixToolset.Extensibilty;
9
- using Wix = WixToolset.Data.Serialize;
6
+ using WixToolset.Extensibility;
7
8
/// <summary>
9
/// The WiX Toolset Harvester application core.
src/WixToolset.Core/IncribeContext.cs
new
+20
@@ -0,0 +1,20 @@
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
4
+{
5
+ using WixToolset.Data;
6
+ using WixToolset.Extensibility;
7
+
8
+ internal class InscribeContext : IInscribeContext
9
+ {
10
+ public Messaging Messaging { get; } = Messaging.Instance;
11
+
12
+ public string IntermediateFolder { get; set; }
13
+
14
+ public string InputFilePath { get; set; }
15
+
16
+ public string SignedEngineFile { get; set; }
17
+
18
+ public string OutputFile { get; set; }
19
+ }
20
+}
src/WixToolset.Core/Inscriber.cs
+317
-324
@@ -2,17 +2,8 @@
2
3
namespace WixToolset
4
{
5
- using System;
6
- using System.CodeDom.Compiler;
7
- using System.Collections.Generic;
8
- using System.Globalization;
5
using System.IO;
10
- using System.Runtime.InteropServices;
11
- using System.Security.Cryptography.X509Certificates;
12
- using WixToolset.Bind.Bundles;
6
using WixToolset.Data;
14
- using WixToolset.Msi;
15
- using WixToolset.Core.Native;
7
8
/// <summary>
9
/// Converts a wixout representation of an MSM database into a ComponentGroup the form of WiX source.
@@ -81,41 +72,41 @@ namespace WixToolset
72
/// <returns>True if bundle was updated.</returns>
73
public bool InscribeBundleEngine(string bundleFile, string outputFile)
74
{
84
- string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_unsigned.exe");
85
-
86
- using (BurnReader reader = BurnReader.Open(bundleFile))
87
- using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
88
- {
89
- reader.Stream.Seek(0, SeekOrigin.Begin);
90
-
91
- byte[] buffer = new byte[4 * 1024];
92
- int total = 0;
93
- int read = 0;
94
- do
95
- {
96
- read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
97
-
98
- read = reader.Stream.Read(buffer, 0, read);
99
- writer.Write(buffer, 0, read);
100
-
101
- total += read;
102
- } while (total < reader.EngineSize && 0 < read);
103
-
104
- if (total != reader.EngineSize)
105
- {
106
- throw new InvalidOperationException("Failed to copy engine out of bundle.");
107
- }
108
-
109
- // TODO: update writer with detached container signatures.
110
- }
111
-
112
- Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
113
- if (File.Exists(outputFile))
114
- {
115
- File.Delete(outputFile);
116
- }
117
- File.Move(tempFile, outputFile);
118
- WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
75
+ //string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_unsigned.exe");
76
+
77
+ //using (BurnReader reader = BurnReader.Open(bundleFile))
78
+ //using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
79
+ //{
80
+ // reader.Stream.Seek(0, SeekOrigin.Begin);
81
+
82
+ // byte[] buffer = new byte[4 * 1024];
83
+ // int total = 0;
84
+ // int read = 0;
85
+ // do
86
+ // {
87
+ // read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
88
+
89
+ // read = reader.Stream.Read(buffer, 0, read);
90
+ // writer.Write(buffer, 0, read);
91
+
92
+ // total += read;
93
+ // } while (total < reader.EngineSize && 0 < read);
94
+
95
+ // if (total != reader.EngineSize)
96
+ // {
97
+ // throw new InvalidOperationException("Failed to copy engine out of bundle.");
98
+ // }
99
+
100
+ // // TODO: update writer with detached container signatures.
101
+ //}
102
+
103
+ //Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
104
+ //if (File.Exists(outputFile))
105
+ //{
106
+ // File.Delete(outputFile);
107
+ //}
108
+ //File.Move(tempFile, outputFile);
109
+ //WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
110
111
return true;
112
}
@@ -129,36 +120,37 @@ namespace WixToolset
120
/// <returns>True if bundle was updated.</returns>
121
public bool InscribeBundle(string bundleFile, string signedEngineFile, string outputFile)
122
{
132
- bool inscribed = false;
133
- string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_signed.exe");
134
-
135
- using (BurnReader reader = BurnReader.Open(bundleFile))
136
- {
137
- File.Copy(signedEngineFile, tempFile, true);
138
-
139
- // If there was an attached container on the original (unsigned) bundle, put it back.
140
- if (reader.AttachedContainerSize > 0)
141
- {
142
- reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
143
-
144
- using (BurnWriter writer = BurnWriter.Open(tempFile))
145
- {
146
- writer.RememberThenResetSignature();
147
- writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
148
- inscribed = true;
149
- }
150
- }
151
- }
152
-
153
- Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
154
- if (File.Exists(outputFile))
155
- {
156
- File.Delete(outputFile);
157
- }
158
- File.Move(tempFile, outputFile);
159
- WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
160
-
161
- return inscribed;
123
+ //bool inscribed = false;
124
+ //string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_signed.exe");
125
+
126
+ //using (BurnReader reader = BurnReader.Open(bundleFile))
127
+ //{
128
+ // File.Copy(signedEngineFile, tempFile, true);
129
+
130
+ // // If there was an attached container on the original (unsigned) bundle, put it back.
131
+ // if (reader.AttachedContainerSize > 0)
132
+ // {
133
+ // reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
134
+
135
+ // using (BurnWriter writer = BurnWriter.Open(tempFile))
136
+ // {
137
+ // writer.RememberThenResetSignature();
138
+ // writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
139
+ // inscribed = true;
140
+ // }
141
+ // }
142
+ //}
143
+
144
+ //Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
145
+ //if (File.Exists(outputFile))
146
+ //{
147
+ // File.Delete(outputFile);
148
+ //}
149
+ //File.Move(tempFile, outputFile);
150
+ //WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
151
+
152
+ //return inscribed;
153
+ return false;
154
}
155
156
/// <summary>
@@ -170,256 +162,257 @@ namespace WixToolset
162
/// <returns>True if database is updated.</returns>
163
public bool InscribeDatabase(string databaseFile, string outputFile, bool tidy)
164
{
173
- // Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
174
- bool foundUnsignedExternals = false;
175
- bool shouldCommit = false;
176
-
177
- FileAttributes attributes = File.GetAttributes(databaseFile);
178
- if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
179
- {
180
- this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile));
181
- return shouldCommit;
182
- }
183
-
184
- using (Database database = new Database(databaseFile, OpenDatabase.Transact))
185
- {
186
- // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
187
- int codepage = 1252;
188
-
189
- // list of certificates for this database (hash/identifier)
190
- Dictionary<string, string> certificates = new Dictionary<string, string>();
191
-
192
- // Reset the in-memory tables for this new database
193
- Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]);
194
- Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]);
195
-
196
- // If any digital signature records exist that are not of the media type, preserve them
197
- if (database.TableExists("MsiDigitalSignature"))
198
- {
199
- using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
200
- {
201
- while (true)
202
- {
203
- using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
204
- {
205
- if (null == digitalSignatureRecord)
206
- {
207
- break;
208
- }
209
-
210
- Row digitalSignatureRow = null;
211
- digitalSignatureRow = digitalSignatureTable.CreateRow(null);
212
-
213
- string table = digitalSignatureRecord.GetString(0);
214
- string signObject = digitalSignatureRecord.GetString(1);
215
-
216
- digitalSignatureRow[0] = table;
217
- digitalSignatureRow[1] = signObject;
218
- digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
219
-
220
- if (false == digitalSignatureRecord.IsNull(3))
221
- {
222
- // Export to a file, because the MSI API's require us to provide a file path on disk
223
- string hashPath = Path.Combine(this.TempFilesLocation, "MsiDigitalSignature");
224
- string hashFileName = string.Concat(table, ".", signObject, ".bin");
225
-
226
- Directory.CreateDirectory(hashPath);
227
- hashPath = Path.Combine(hashPath, hashFileName);
228
-
229
- using (FileStream fs = File.Create(hashPath))
230
- {
231
- int bytesRead;
232
- byte[] buffer = new byte[1024 * 4];
233
-
234
- while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
235
- {
236
- fs.Write(buffer, 0, bytesRead);
237
- }
238
- }
239
-
240
- digitalSignatureRow[3] = hashFileName;
241
- }
242
- }
243
- }
244
- }
245
- }
246
-
247
- // If any digital certificates exist, extract and preserve them
248
- if (database.TableExists("MsiDigitalCertificate"))
249
- {
250
- using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
251
- {
252
- while (true)
253
- {
254
- using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
255
- {
256
- if (null == digitalCertificateRecord)
257
- {
258
- break;
259
- }
260
-
261
- string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
262
-
263
- // Export to a file, because the MSI API's require us to provide a file path on disk
264
- string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
265
- Directory.CreateDirectory(certPath);
266
- certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
267
-
268
- using (FileStream fs = File.Create(certPath))
269
- {
270
- int bytesRead;
271
- byte[] buffer = new byte[1024 * 4];
272
-
273
- while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
274
- {
275
- fs.Write(buffer, 0, bytesRead);
276
- }
277
- }
278
-
279
- // Add it to our "add to MsiDigitalCertificate" table dictionary
280
- Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
281
- digitalCertificateRow[0] = certificateId;
282
-
283
- // Now set the file path on disk where this binary stream will be picked up at import time
284
- digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
285
-
286
- // Load the cert to get it's thumbprint
287
- X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
288
- X509Certificate2 cert2 = new X509Certificate2(cert);
289
-
290
- certificates.Add(cert2.Thumbprint, certificateId);
291
- }
292
- }
293
- }
294
- }
295
-
296
- using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
297
- {
298
- while (true)
299
- {
300
- using (Record mediaRecord = mediaView.Fetch())
301
- {
302
- if (null == mediaRecord)
303
- {
304
- break;
305
- }
306
-
307
- X509Certificate2 cert2 = null;
308
- Row digitalSignatureRow = null;
309
-
310
- string cabName = mediaRecord.GetString(4); // get the name of the cab
311
- // If there is no cabinet or it's an internal cab, skip it.
312
- if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
313
- {
314
- continue;
315
- }
316
-
317
- string cabId = mediaRecord.GetString(1); // get the ID of the cab
318
- string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName);
319
-
320
- // If the cabs aren't there, throw an error but continue to catch the other errors
321
- if (!File.Exists(cabPath))
322
- {
323
- this.OnMessage(WixErrors.WixFileNotFound(cabPath));
324
- continue;
325
- }
326
-
327
- try
328
- {
329
- // Get the certificate from the cab
330
- X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
331
- cert2 = new X509Certificate2(signedFileCert);
332
- }
333
- catch (System.Security.Cryptography.CryptographicException e)
334
- {
335
- uint HResult = unchecked((uint)Marshal.GetHRForException(e));
336
-
337
- // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
338
- if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
339
- {
340
- foundUnsignedExternals = true;
341
- continue;
342
- }
343
-
344
- // todo: exactly which HRESULT corresponds to this issue?
345
- // If it's one of these exact platforms, warn the user that it may be due to their OS.
346
- if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
347
- (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
348
- {
349
- this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
350
- }
351
- else // otherwise, generic error
352
- {
353
- this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
354
- }
355
- }
356
-
357
- // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
358
- if (!certificates.ContainsKey(cert2.Thumbprint))
359
- {
360
- // generate a stable identifier
361
- string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
362
-
363
- // Add it to our "add to MsiDigitalCertificate" table dictionary
364
- Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
365
- digitalCertificateRow[0] = certificateGeneratedId;
366
-
367
- // Export to a file, because the MSI API's require us to provide a file path on disk
368
- string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
369
- Directory.CreateDirectory(certPath);
370
- certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
371
- File.Delete(certPath);
372
-
373
- using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
374
- {
375
- writer.Write(cert2.RawData);
376
- writer.Close();
377
- }
378
-
379
- // Now set the file path on disk where this binary stream will be picked up at import time
380
- digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
381
-
382
- certificates.Add(cert2.Thumbprint, certificateGeneratedId);
383
- }
384
-
385
- digitalSignatureRow = digitalSignatureTable.CreateRow(null);
386
-
387
- digitalSignatureRow[0] = "Media";
388
- digitalSignatureRow[1] = cabId;
389
- digitalSignatureRow[2] = certificates[cert2.Thumbprint];
390
- }
391
- }
392
- }
393
-
394
- if (digitalCertificateTable.Rows.Count > 0)
395
- {
396
- database.ImportTable(codepage, digitalCertificateTable, this.TempFilesLocation, true);
397
- shouldCommit = true;
398
- }
399
-
400
- if (digitalSignatureTable.Rows.Count > 0)
401
- {
402
- database.ImportTable(codepage, digitalSignatureTable, this.TempFilesLocation, true);
403
- shouldCommit = true;
404
- }
405
-
406
- // TODO: if we created the table(s), then we should add the _Validation records for them.
407
-
408
- certificates = null;
409
-
410
- // If we did find external cabs but none of them were signed, give a warning
411
- if (foundUnsignedExternals)
412
- {
413
- this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile));
414
- }
415
-
416
- if (shouldCommit)
417
- {
418
- database.Commit();
419
- }
420
- }
421
-
422
- return shouldCommit;
165
+ //// Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
166
+ //bool foundUnsignedExternals = false;
167
+ //bool shouldCommit = false;
168
+
169
+ //FileAttributes attributes = File.GetAttributes(databaseFile);
170
+ //if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
171
+ //{
172
+ // this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile));
173
+ // return shouldCommit;
174
+ //}
175
+
176
+ //using (Database database = new Database(databaseFile, OpenDatabase.Transact))
177
+ //{
178
+ // // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
179
+ // int codepage = 1252;
180
+
181
+ // // list of certificates for this database (hash/identifier)
182
+ // Dictionary<string, string> certificates = new Dictionary<string, string>();
183
+
184
+ // // Reset the in-memory tables for this new database
185
+ // Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]);
186
+ // Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]);
187
+
188
+ // // If any digital signature records exist that are not of the media type, preserve them
189
+ // if (database.TableExists("MsiDigitalSignature"))
190
+ // {
191
+ // using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
192
+ // {
193
+ // while (true)
194
+ // {
195
+ // using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
196
+ // {
197
+ // if (null == digitalSignatureRecord)
198
+ // {
199
+ // break;
200
+ // }
201
+
202
+ // Row digitalSignatureRow = null;
203
+ // digitalSignatureRow = digitalSignatureTable.CreateRow(null);
204
+
205
+ // string table = digitalSignatureRecord.GetString(0);
206
+ // string signObject = digitalSignatureRecord.GetString(1);
207
+
208
+ // digitalSignatureRow[0] = table;
209
+ // digitalSignatureRow[1] = signObject;
210
+ // digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
211
+
212
+ // if (false == digitalSignatureRecord.IsNull(3))
213
+ // {
214
+ // // Export to a file, because the MSI API's require us to provide a file path on disk
215
+ // string hashPath = Path.Combine(this.TempFilesLocation, "MsiDigitalSignature");
216
+ // string hashFileName = string.Concat(table, ".", signObject, ".bin");
217
+
218
+ // Directory.CreateDirectory(hashPath);
219
+ // hashPath = Path.Combine(hashPath, hashFileName);
220
+
221
+ // using (FileStream fs = File.Create(hashPath))
222
+ // {
223
+ // int bytesRead;
224
+ // byte[] buffer = new byte[1024 * 4];
225
+
226
+ // while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
227
+ // {
228
+ // fs.Write(buffer, 0, bytesRead);
229
+ // }
230
+ // }
231
+
232
+ // digitalSignatureRow[3] = hashFileName;
233
+ // }
234
+ // }
235
+ // }
236
+ // }
237
+ // }
238
+
239
+ // // If any digital certificates exist, extract and preserve them
240
+ // if (database.TableExists("MsiDigitalCertificate"))
241
+ // {
242
+ // using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
243
+ // {
244
+ // while (true)
245
+ // {
246
+ // using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
247
+ // {
248
+ // if (null == digitalCertificateRecord)
249
+ // {
250
+ // break;
251
+ // }
252
+
253
+ // string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
254
+
255
+ // // Export to a file, because the MSI API's require us to provide a file path on disk
256
+ // string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
257
+ // Directory.CreateDirectory(certPath);
258
+ // certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
259
+
260
+ // using (FileStream fs = File.Create(certPath))
261
+ // {
262
+ // int bytesRead;
263
+ // byte[] buffer = new byte[1024 * 4];
264
+
265
+ // while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
266
+ // {
267
+ // fs.Write(buffer, 0, bytesRead);
268
+ // }
269
+ // }
270
+
271
+ // // Add it to our "add to MsiDigitalCertificate" table dictionary
272
+ // Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
273
+ // digitalCertificateRow[0] = certificateId;
274
+
275
+ // // Now set the file path on disk where this binary stream will be picked up at import time
276
+ // digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
277
+
278
+ // // Load the cert to get it's thumbprint
279
+ // X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
280
+ // X509Certificate2 cert2 = new X509Certificate2(cert);
281
+
282
+ // certificates.Add(cert2.Thumbprint, certificateId);
283
+ // }
284
+ // }
285
+ // }
286
+ // }
287
+
288
+ // using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
289
+ // {
290
+ // while (true)
291
+ // {
292
+ // using (Record mediaRecord = mediaView.Fetch())
293
+ // {
294
+ // if (null == mediaRecord)
295
+ // {
296
+ // break;
297
+ // }
298
+
299
+ // X509Certificate2 cert2 = null;
300
+ // Row digitalSignatureRow = null;
301
+
302
+ // string cabName = mediaRecord.GetString(4); // get the name of the cab
303
+ // // If there is no cabinet or it's an internal cab, skip it.
304
+ // if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
305
+ // {
306
+ // continue;
307
+ // }
308
+
309
+ // string cabId = mediaRecord.GetString(1); // get the ID of the cab
310
+ // string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName);
311
+
312
+ // // If the cabs aren't there, throw an error but continue to catch the other errors
313
+ // if (!File.Exists(cabPath))
314
+ // {
315
+ // this.OnMessage(WixErrors.WixFileNotFound(cabPath));
316
+ // continue;
317
+ // }
318
+
319
+ // try
320
+ // {
321
+ // // Get the certificate from the cab
322
+ // X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
323
+ // cert2 = new X509Certificate2(signedFileCert);
324
+ // }
325
+ // catch (System.Security.Cryptography.CryptographicException e)
326
+ // {
327
+ // uint HResult = unchecked((uint)Marshal.GetHRForException(e));
328
+
329
+ // // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
330
+ // if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
331
+ // {
332
+ // foundUnsignedExternals = true;
333
+ // continue;
334
+ // }
335
+
336
+ // // todo: exactly which HRESULT corresponds to this issue?
337
+ // // If it's one of these exact platforms, warn the user that it may be due to their OS.
338
+ // if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
339
+ // (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
340
+ // {
341
+ // this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
342
+ // }
343
+ // else // otherwise, generic error
344
+ // {
345
+ // this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
346
+ // }
347
+ // }
348
+
349
+ // // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
350
+ // if (!certificates.ContainsKey(cert2.Thumbprint))
351
+ // {
352
+ // // generate a stable identifier
353
+ // string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
354
+
355
+ // // Add it to our "add to MsiDigitalCertificate" table dictionary
356
+ // Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
357
+ // digitalCertificateRow[0] = certificateGeneratedId;
358
+
359
+ // // Export to a file, because the MSI API's require us to provide a file path on disk
360
+ // string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
361
+ // Directory.CreateDirectory(certPath);
362
+ // certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
363
+ // File.Delete(certPath);
364
+
365
+ // using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
366
+ // {
367
+ // writer.Write(cert2.RawData);
368
+ // writer.Close();
369
+ // }
370
+
371
+ // // Now set the file path on disk where this binary stream will be picked up at import time
372
+ // digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
373
+
374
+ // certificates.Add(cert2.Thumbprint, certificateGeneratedId);
375
+ // }
376
+
377
+ // digitalSignatureRow = digitalSignatureTable.CreateRow(null);
378
+
379
+ // digitalSignatureRow[0] = "Media";
380
+ // digitalSignatureRow[1] = cabId;
381
+ // digitalSignatureRow[2] = certificates[cert2.Thumbprint];
382
+ // }
383
+ // }
384
+ // }
385
+
386
+ // if (digitalCertificateTable.Rows.Count > 0)
387
+ // {
388
+ // database.ImportTable(codepage, digitalCertificateTable, this.TempFilesLocation, true);
389
+ // shouldCommit = true;
390
+ // }
391
+
392
+ // if (digitalSignatureTable.Rows.Count > 0)
393
+ // {
394
+ // database.ImportTable(codepage, digitalSignatureTable, this.TempFilesLocation, true);
395
+ // shouldCommit = true;
396
+ // }
397
+
398
+ // // TODO: if we created the table(s), then we should add the _Validation records for them.
399
+
400
+ // certificates = null;
401
+
402
+ // // If we did find external cabs but none of them were signed, give a warning
403
+ // if (foundUnsignedExternals)
404
+ // {
405
+ // this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile));
406
+ // }
407
+
408
+ // if (shouldCommit)
409
+ // {
410
+ // database.Commit();
411
+ // }
412
+ //}
413
+
414
+ //return shouldCommit;
415
+ return false;
416
}
417
418
/// <summary>
src/WixToolset.Core/Librarian.cs
+34
-9
@@ -1,10 +1,11 @@
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
3
+namespace WixToolset.Core
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.Linq;
8
+ using WixToolset.Core.Bind;
9
using WixToolset.Data;
10
using WixToolset.Link;
11
@@ -13,20 +14,41 @@ namespace WixToolset
14
/// </summary>
15
public sealed class Librarian
16
{
17
+ public Librarian(LibraryContext context)
18
+ {
19
+ this.Context = context;
20
+ }
21
+
22
+ private LibraryContext Context { get; }
23
+
24
/// <summary>
25
/// Create a library by combining several intermediates (objects).
26
/// </summary>
27
/// <param name="sections">The sections to combine into a library.</param>
28
/// <returns>Returns the new library.</returns>
21
- public Library Combine(IEnumerable<Section> sections, IEnumerable<Localization> localizations, ILibraryBinaryFileResolver resolver)
29
+ public Library Combine()
30
{
23
- var localizationsByCulture = CollateLocalizations(localizations);
31
+ foreach (var extension in this.Context.Extensions)
32
+ {
33
+ extension.PreCombine(this.Context);
34
+ }
35
+
36
+ var fileResolver = new FileResolver(this.Context.BindPaths, this.Context.Extensions);
37
25
- var embedFilePaths = ResolveFilePathsToEmbed(sections, resolver);
38
+ var localizationsByCulture = CollateLocalizations(this.Context.Localizations);
39
27
- var library = new Library(sections, localizationsByCulture, embedFilePaths);
40
+ var embedFilePaths = ResolveFilePathsToEmbed(this.Context.Sections, fileResolver);
41
29
- return this.Validate(library);
42
+ var library = new Library(this.Context.Sections, localizationsByCulture, embedFilePaths);
43
+
44
+ this.Validate(library);
45
+
46
+ foreach (var extension in this.Context.Extensions)
47
+ {
48
+ extension.PostCombine(library);
49
+ }
50
+
51
+ return library;
52
}
53
54
/// <summary>
@@ -70,12 +92,12 @@ namespace WixToolset
92
return localizationsByCulture;
93
}
94
73
- private static List<string> ResolveFilePathsToEmbed(IEnumerable<Section> sections, ILibraryBinaryFileResolver resolver)
95
+ private List<string> ResolveFilePathsToEmbed(IEnumerable<Section> sections, FileResolver fileResolver)
96
{
97
var embedFilePaths = new List<string>();
98
99
// Resolve paths to files that are to be embedded in the library.
78
- if (null != resolver)
100
+ if (this.Context.BindFiles)
101
{
102
foreach (Table table in sections.SelectMany(s => s.Tables))
103
{
@@ -85,7 +107,10 @@ namespace WixToolset
107
{
108
if (null != objectField.Data)
109
{
88
- string file = resolver.Resolve(row.SourceLineNumbers, table.Name, (string)objectField.Data);
110
+ string resolvedPath = this.Context.WixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)objectField.Data, false);
111
+
112
+ string file = fileResolver.Resolve(row.SourceLineNumbers, table.Name, resolvedPath);
113
+
114
if (!String.IsNullOrEmpty(file))
115
{
116
// File was successfully resolved so track the embedded index as the embedded file index.
src/WixToolset.Core/LibraryContext.cs
new
+23
@@ -0,0 +1,23 @@
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
4
+{
5
+ using System.Collections.Generic;
6
+ using WixToolset.Data;
7
+ using WixToolset.Extensibility;
8
+
9
+ public class LibraryContext : ILibraryContext
10
+ {
11
+ public bool BindFiles { get; set; }
12
+
13
+ public IEnumerable<BindPath> BindPaths { get; set; }
14
+
15
+ public IEnumerable<ILibrarianExtension> Extensions { get; set; }
16
+
17
+ public IEnumerable<Localization> Localizations { get; set; }
18
+
19
+ public IEnumerable<Section> Sections { get; set; }
20
+
21
+ public IBindVariableResolver WixVariableResolver { get; set; }
22
+ }
23
+}
src/WixToolset.Core/Linker.cs
+1
-1
@@ -78,7 +78,7 @@ namespace WixToolset
78
/// Gets or sets the Wix variable resolver.
79
/// </summary>
80
/// <value>The Wix variable resolver.</value>
81
- public WixVariableResolver WixVariableResolver { get; set; }
81
+ internal IBindVariableResolver WixVariableResolver { get; set; }
82
83
/// <summary>
84
/// Adds an extension.
src/WixToolset.Core/Localizer.cs
+25
-26
@@ -8,11 +8,12 @@ namespace WixToolset
8
using WixToolset.Data;
9
using WixToolset.Data.Rows;
10
using WixToolset.Core.Native;
11
+ using WixToolset.Extensibility;
12
13
/// <summary>
14
/// Parses localization files and localizes database values.
15
/// </summary>
15
- public sealed class Localizer
16
+ public sealed class Localizer : ILocalizer
17
{
18
public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl";
19
private static string XmlElementName = "WixLocalization";
@@ -55,7 +56,28 @@ namespace WixToolset
56
/// Gets the codepage.
57
/// </summary>
58
/// <value>The codepage.</value>
58
- public int Codepage { get; private set; }
59
+ public int Codepage { get; }
60
+
61
+ /// <summary>
62
+ /// Get a localized data value.
63
+ /// </summary>
64
+ /// <param name="id">The name of the localization variable.</param>
65
+ /// <returns>The localized data value or null if it wasn't found.</returns>
66
+ public string GetLocalizedValue(string id)
67
+ {
68
+ return this.variables.TryGetValue(id, out var wixVariableRow) ? wixVariableRow.Value : null;
69
+ }
70
+
71
+ /// <summary>
72
+ /// Get a localized control.
73
+ /// </summary>
74
+ /// <param name="dialog">The optional id of the control's dialog.</param>
75
+ /// <param name="control">The id of the control.</param>
76
+ /// <returns>The localized control or null if it wasn't found.</returns>
77
+ public LocalizedControl GetLocalizedControl(string dialog, string control)
78
+ {
79
+ return this.localizedControls.TryGetValue(LocalizedControl.GetKey(dialog, control), out var localizedControl) ? localizedControl : null;
80
+ }
81
82
/// <summary>
83
/// Loads a localization file from a path on disk.
@@ -96,28 +118,6 @@ namespace WixToolset
118
return localization;
119
}
120
99
- /// <summary>
100
- /// Get a localized data value.
101
- /// </summary>
102
- /// <param name="id">The name of the localization variable.</param>
103
- /// <returns>The localized data value or null if it wasn't found.</returns>
104
- public string GetLocalizedValue(string id)
105
- {
106
- return this.variables.TryGetValue(id, out var wixVariableRow) ? wixVariableRow.Value : null;
107
- }
108
-
109
- /// <summary>
110
- /// Get a localized control.
111
- /// </summary>
112
- /// <param name="dialog">The optional id of the control's dialog.</param>
113
- /// <param name="control">The id of the control.</param>
114
- /// <returns>The localized control or null if it wasn't found.</returns>
115
- public LocalizedControl GetLocalizedControl(string dialog, string control)
116
- {
117
- LocalizedControl localizedControl;
118
- return this.localizedControls.TryGetValue(LocalizedControl.GetKey(dialog, control), out localizedControl) ? localizedControl : null;
119
- }
120
-
121
/// <summary>
122
/// Adds a WixVariableRow to a dictionary while performing the expected override checks.
123
/// </summary>
@@ -125,8 +125,7 @@ namespace WixToolset
125
/// <param name="wixVariableRow">Row to add to the variables dictionary.</param>
126
private static void AddWixVariable(IDictionary<string, WixVariableRow> variables, WixVariableRow wixVariableRow)
127
{
128
- WixVariableRow existingWixVariableRow;
129
- if (!variables.TryGetValue(wixVariableRow.Id, out existingWixVariableRow) || (existingWixVariableRow.Overridable && !wixVariableRow.Overridable))
128
+ if (!variables.TryGetValue(wixVariableRow.Id, out var existingWixVariableRow) || (existingWixVariableRow.Overridable && !wixVariableRow.Overridable))
129
{
130
variables[wixVariableRow.Id] = wixVariableRow;
131
}
src/WixToolset.Core/OptimizeCA.cs
new
+33
@@ -0,0 +1,33 @@
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
4
+{
5
+ using System;
6
+
7
+ /// <summary>
8
+ /// Values for the OptimizeCA MsiPatchMetdata property, which indicates whether custom actions can be skipped when applying the patch.
9
+ /// </summary>
10
+ [Flags]
11
+ public enum OptimizeCA
12
+ {
13
+ /// <summary>
14
+ /// No custom actions are skipped.
15
+ /// </summary>
16
+ None = 0,
17
+
18
+ /// <summary>
19
+ /// Skip property (type 51) and directory (type 35) assignment custom actions.
20
+ /// </summary>
21
+ SkipAssignment = 1,
22
+
23
+ /// <summary>
24
+ /// Skip immediate custom actions that are not property or directory assignment custom actions.
25
+ /// </summary>
26
+ SkipImmediate = 2,
27
+
28
+ /// <summary>
29
+ /// Skip custom actions that run within the script.
30
+ /// </summary>
31
+ SkipDeferred = 4,
32
+ }
33
+}
src/WixToolset.Core/PatchSymbolFlagsType.cs
new
+19
@@ -0,0 +1,19 @@
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
4
+{
5
+ using System;
6
+
7
+ //
8
+ // The following flags are used with PATCH_OPTION_DATA SymbolOptionFlags:
9
+ //
10
+ [Flags]
11
+ public enum PatchSymbolFlagsType : uint
12
+ {
13
+ PATCH_SYMBOL_NO_IMAGEHLP = 0x00000001, // don't use imagehlp.dll
14
+ PATCH_SYMBOL_NO_FAILURES = 0x00000002, // don't fail patch due to imagehlp failures
15
+ PATCH_SYMBOL_UNDECORATED_TOO = 0x00000004, // after matching decorated symbols, try to match remaining by undecorated names
16
+ PATCH_SYMBOL_RESERVED1 = 0x80000000, // (used internally)
17
+ MaxValue = PATCH_SYMBOL_NO_IMAGEHLP | PATCH_SYMBOL_NO_FAILURES | PATCH_SYMBOL_UNDECORATED_TOO
18
+ }
19
+}
src/WixToolset.Core/PatchTransform.cs
+2
-2
@@ -155,7 +155,7 @@ namespace WixToolset
155
if (!deletedComponent.ContainsKey(componentId))
156
{
157
bool foundRemoveFileEntry = false;
158
- string filename = Msi.Installer.GetName((string)row[2], false, true);
158
+ string filename = Common.GetName((string)row[2], false, true);
159
160
Table removeFileTable = this.Transform.Tables["RemoveFile"];
161
if (null != removeFileTable)
@@ -172,7 +172,7 @@ namespace WixToolset
172
// Check if there is a RemoveFile entry for this file
173
if (null != removeFileRow[2])
174
{
175
- string removeFileName = Msi.Installer.GetName((string)removeFileRow[2], false, true);
175
+ string removeFileName = Common.GetName((string)removeFileRow[2], false, true);
176
177
// Convert the MSI format for a wildcard string to Regex format.
178
removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\.");
src/WixToolset.Core/Properties/AssemblyInfo.cs
+1
-1
@@ -5,5 +5,5 @@ using System.Reflection;
5
using System.Runtime.InteropServices;
6
7
[assembly: AssemblyCulture("")]
8
-[assembly: CLSCompliant(true)]
8
+[assembly: CLSCompliant(false)]
9
[assembly: ComVisible(false)]
src/WixToolset.Core/TransformsFlags.cs
new
+81
@@ -0,0 +1,81 @@
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
4
+{
5
+ using System;
6
+ using System.Diagnostics.CodeAnalysis;
7
+
8
+ /// <summary>
9
+ /// Summary information values for the CharCount property in transforms.
10
+ /// </summary>
11
+ [Flags]
12
+ [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
13
+ public enum TransformFlags
14
+ {
15
+ /// <summary>Ignore error when adding a row that exists.</summary>
16
+ ErrorAddExistingRow = 0x1,
17
+
18
+ /// <summary>Ignore error when deleting a row that does not exist.</summary>
19
+ ErrorDeleteMissingRow = 0x2,
20
+
21
+ /// <summary>Ignore error when adding a table that exists. </summary>
22
+ ErrorAddExistingTable = 0x4,
23
+
24
+ /// <summary>Ignore error when deleting a table that does not exist. </summary>
25
+ ErrorDeleteMissingTable = 0x8,
26
+
27
+ /// <summary>Ignore error when updating a row that does not exist. </summary>
28
+ ErrorUpdateMissingRow = 0x10,
29
+
30
+ /// <summary>Ignore error when transform and database code pages do not match, and their code pages are neutral.</summary>
31
+ ErrorChangeCodePage = 0x20,
32
+
33
+ /// <summary>Default language must match base database. </summary>
34
+ ValidateLanguage = 0x10000,
35
+
36
+ /// <summary>Product must match base database.</summary>
37
+ ValidateProduct = 0x20000,
38
+
39
+ /// <summary>Check major version only. </summary>
40
+ ValidateMajorVersion = 0x80000,
41
+
42
+ /// <summary>Check major and minor versions only. </summary>
43
+ ValidateMinorVersion = 0x100000,
44
+
45
+ /// <summary>Check major, minor, and update versions.</summary>
46
+ ValidateUpdateVersion = 0x200000,
47
+
48
+ /// <summary>Installed version lt base version. </summary>
49
+ ValidateNewLessBaseVersion = 0x400000,
50
+
51
+ /// <summary>Installed version lte base version. </summary>
52
+ ValidateNewLessEqualBaseVersion = 0x800000,
53
+
54
+ /// <summary>Installed version eq base version. </summary>
55
+ ValidateNewEqualBaseVersion = 0x1000000,
56
+
57
+ /// <summary>Installed version gte base version.</summary>
58
+ ValidateNewGreaterEqualBaseVersion = 0x2000000,
59
+
60
+ /// <summary>Installed version gt base version.</summary>
61
+ ValidateNewGreaterBaseVersion = 0x4000000,
62
+
63
+ /// <summary>UpgradeCode must match base database.</summary>
64
+ ValidateUpgradeCode = 0x8000000,
65
+
66
+ /// <summary>Masks all version checks on ProductVersion.</summary>
67
+ ProductVersionMask = ValidateMajorVersion | ValidateMinorVersion | ValidateUpdateVersion,
68
+
69
+ /// <summary>Masks all operations on ProductVersion.</summary>
70
+ ProductVersionOperatorMask = ValidateNewLessBaseVersion | ValidateNewLessEqualBaseVersion | ValidateNewEqualBaseVersion | ValidateNewGreaterEqualBaseVersion | ValidateNewGreaterBaseVersion,
71
+
72
+ /// <summary>Default value for instance transforms.</summary>
73
+ InstanceTransformDefault = ErrorAddExistingRow | ErrorDeleteMissingRow | ErrorAddExistingTable | ErrorDeleteMissingTable | ErrorUpdateMissingRow | ErrorChangeCodePage | ValidateProduct | ValidateUpdateVersion | ValidateNewGreaterEqualBaseVersion,
74
+
75
+ /// <summary>Default value for language transforms.</summary>
76
+ LanguageTransformDefault = ErrorAddExistingRow | ErrorDeleteMissingRow | ErrorAddExistingTable | ErrorDeleteMissingTable | ErrorUpdateMissingRow | ErrorChangeCodePage | ValidateProduct,
77
+
78
+ /// <summary>Default value for patch transforms.</summary>
79
+ PatchTransformDefault = ErrorAddExistingRow | ErrorDeleteMissingRow | ErrorAddExistingTable | ErrorDeleteMissingTable | ErrorUpdateMissingRow | ValidateProduct | ValidateUpdateVersion | ValidateNewEqualBaseVersion | ValidateUpgradeCode,
80
+ }
81
+}
src/WixToolset.Core/UnbindContext.cs
new
+24
@@ -0,0 +1,24 @@
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
4
+{
5
+ using WixToolset.Data;
6
+ using WixToolset.Extensibility;
7
+
8
+ internal class UnbindContext : IUnbindContext
9
+ {
10
+ public Messaging Messaging { get; } = Messaging.Instance;
11
+
12
+ public string ExportBasePath { get; set; }
13
+
14
+ public string InputFilePath { get; set; }
15
+
16
+ public string IntermediateFolder { get; set; }
17
+
18
+ public bool IsAdminImage { get; set; }
19
+
20
+ public bool SuppressExtractCabinets { get; set; }
21
+
22
+ public bool SuppressDemodularization { get; set; }
23
+ }
24
+}
src/WixToolset.Core/Unbinder.cs
+20
-1379
@@ -1,37 +1,18 @@
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
3
+namespace WixToolset.Core
4
{
5
- using System;
6
- using System.CodeDom.Compiler;
5
using System.Collections;
8
- using System.Collections.Generic;
9
- using System.Collections.Specialized;
10
- using System.ComponentModel;
11
- using System.Globalization;
6
using System.IO;
13
- using System.Linq;
14
- using System.Text.RegularExpressions;
15
- using WixToolset.Bind;
16
- using WixToolset.Bind.Bundles;
17
- using WixToolset.Cab;
7
using WixToolset.Data;
19
- using WixToolset.Data.Rows;
8
using WixToolset.Extensibility;
21
- using WixToolset.Msi;
22
- using WixToolset.Core.Native;
23
- using WixToolset.Ole32;
9
+ using System.Collections.Generic;
10
11
/// <summary>
12
/// Unbinder core of the WiX toolset.
13
/// </summary>
28
- public sealed class Unbinder : IMessageHandler
14
+ public sealed class Unbinder
15
{
30
- private string emptyFile;
31
- private bool isAdminImage;
32
- private int sectionCount;
33
- private bool suppressDemodularization;
34
- private bool suppressExtractCabinets;
16
private TableDefinitionCollection tableDefinitions;
17
private ArrayList unbinderExtensions;
18
// private TempFileCollection tempFiles;
@@ -45,61 +26,32 @@ namespace WixToolset
26
this.unbinderExtensions = new ArrayList();
27
}
28
29
+ public IEnumerable<IBackendFactory> BackendFactories { get; }
30
+
31
/// <summary>
32
/// Gets or sets whether the input msi is an admin image.
33
/// </summary>
34
/// <value>Set to true if the input msi is part of an admin image.</value>
52
- public bool IsAdminImage
53
- {
54
- get { return this.isAdminImage; }
55
- set { this.isAdminImage = value; }
56
- }
35
+ public bool IsAdminImage { get; set; }
36
37
/// <summary>
38
/// Gets or sets the option to suppress demodularizing values.
39
/// </summary>
40
/// <value>The option to suppress demodularizing values.</value>
62
- public bool SuppressDemodularization
63
- {
64
- get { return this.suppressDemodularization; }
65
- set { this.suppressDemodularization = value; }
66
- }
41
+ public bool SuppressDemodularization { get; set; }
42
43
/// <summary>
44
/// Gets or sets the option to suppress extracting cabinets.
45
/// </summary>
46
/// <value>The option to suppress extracting cabinets.</value>
72
- public bool SuppressExtractCabinets
73
- {
74
- get { return this.suppressExtractCabinets; }
75
- set { this.suppressExtractCabinets = value; }
76
- }
47
+ public bool SuppressExtractCabinets { get; set; }
48
49
/// <summary>
50
/// Gets or sets the temporary path for the Binder. If left null, the binder
51
/// will use %TEMP% environment variable.
52
/// </summary>
53
/// <value>Path to temp files.</value>
83
- public string TempFilesLocation
84
- {
85
- get
86
- {
87
- // return null == this.tempFiles ? String.Empty : this.tempFiles.BasePath;
88
- return Path.GetTempPath();
89
- }
90
-
91
- // set
92
- // {
93
- // if (null == value)
94
- // {
95
- // this.tempFiles = new TempFileCollection();
96
- // }
97
- // else
98
- // {
99
- // this.tempFiles = new TempFileCollection(value);
100
- // }
101
- // }
102
- }
54
+ public string TempFilesLocation => Path.GetTempPath();
55
56
/// <summary>
57
/// Adds extension data.
@@ -156,1336 +108,25 @@ namespace WixToolset
108
// if we don't have the temporary files object yet, get one
109
Directory.CreateDirectory(this.TempFilesLocation); // ensure the base path is there
110
159
- if (OutputType.Patch == outputType)
160
- {
161
- return this.UnbindPatch(file, exportBasePath);
162
- }
163
- else if (OutputType.Transform == outputType)
164
- {
165
- return this.UnbindTransform(file, exportBasePath);
166
- }
167
- else if (OutputType.Bundle == outputType)
168
- {
169
- return this.UnbindBundle(file, exportBasePath);
170
- }
171
- else // other database types
172
- {
173
- return this.UnbindDatabase(file, outputType, exportBasePath);
174
- }
175
- }
176
-
177
- /// <summary>
178
- /// Cleans up the temp files used by the Decompiler.
179
- /// </summary>
180
- /// <returns>True if all files were deleted, false otherwise.</returns>
181
- /// <remarks>
182
- /// This should be called after every call to Decompile to ensure there
183
- /// are no conflicts between each decompiled database.
184
- /// </remarks>
185
- public bool DeleteTempFiles()
186
- {
187
-#if REDO_IN_NETCORE
188
- bool deleted = Common.DeleteTempFiles(this.tempFiles.BasePath, this);
189
-
190
- if (deleted)
191
- {
192
- this.tempFiles = null; // temp files have been deleted, no need to remember this now
193
- }
194
-
195
- return deleted;
196
-#endif
197
- return true;
198
- }
199
-
200
- /// <summary>
201
- /// Sends a message to the message delegate if there is one.
202
- /// </summary>
203
- /// <param name="mea">Message event arguments.</param>
204
- public void OnMessage(MessageEventArgs e)
205
- {
206
- Messaging.Instance.OnMessage(e);
207
- }
208
-
209
- /// <summary>
210
- /// Unbind an MSI database file.
211
- /// </summary>
212
- /// <param name="databaseFile">The database file.</param>
213
- /// <param name="outputType">The output type.</param>
214
- /// <param name="exportBasePath">The path where files should be exported.</param>
215
- /// <returns>The unbound database.</returns>
216
- private Output UnbindDatabase(string databaseFile, OutputType outputType, string exportBasePath)
217
- {
218
- Output output;
219
-
220
- try
221
- {
222
- using (Database database = new Database(databaseFile, OpenDatabase.ReadOnly))
223
- {
224
- output = this.UnbindDatabase(databaseFile, database, outputType, exportBasePath, false);
225
-
226
- // extract the files from the cabinets
227
- if (null != exportBasePath && !this.suppressExtractCabinets)
228
- {
229
- this.ExtractCabinets(output, database, databaseFile, exportBasePath);
230
- }
231
- }
232
- }
233
- catch (Win32Exception e)
234
- {
235
- if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
236
- {
237
- throw new WixException(WixErrors.OpenDatabaseFailed(databaseFile));
238
- }
239
-
240
- throw;
241
- }
242
-
243
- return output;
244
- }
245
-
246
- /// <summary>
247
- /// Unbind an MSI database file.
248
- /// </summary>
249
- /// <param name="databaseFile">The database file.</param>
250
- /// <param name="database">The opened database.</param>
251
- /// <param name="outputType">The type of output to create.</param>
252
- /// <param name="exportBasePath">The path where files should be exported.</param>
253
- /// <param name="skipSummaryInfo">Option to skip unbinding the _SummaryInformation table.</param>
254
- /// <returns>The output representing the database.</returns>
255
- private Output UnbindDatabase(string databaseFile, Database database, OutputType outputType, string exportBasePath, bool skipSummaryInfo)
256
- {
257
- string modularizationGuid = null;
258
- Output output = new Output(new SourceLineNumber(databaseFile));
259
- View validationView = null;
260
-
261
- // set the output type
262
- output.Type = outputType;
263
-
264
- // get the codepage
265
- database.Export("_ForceCodepage", this.TempFilesLocation, "_ForceCodepage.idt");
266
- using (StreamReader sr = File.OpenText(Path.Combine(this.TempFilesLocation, "_ForceCodepage.idt")))
267
- {
268
- string line;
269
-
270
- while (null != (line = sr.ReadLine()))
271
- {
272
- string[] data = line.Split('\t');
273
-
274
- if (2 == data.Length)
275
- {
276
- output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture);
277
- }
278
- }
279
- }
280
-
281
- // get the summary information table if it exists; it won't if unbinding a transform
282
- if (!skipSummaryInfo)
283
- {
284
- using (SummaryInformation summaryInformation = new SummaryInformation(database))
285
- {
286
- Table table = new Table(null, this.tableDefinitions["_SummaryInformation"]);
287
-
288
- for (int i = 1; 19 >= i; i++)
289
- {
290
- string value = summaryInformation.GetProperty(i);
291
-
292
- if (0 < value.Length)
293
- {
294
- Row row = table.CreateRow(output.SourceLineNumbers);
295
- row[0] = i;
296
- row[1] = value;
297
- }
298
- }
299
-
300
- output.Tables.Add(table);
301
- }
302
- }
303
-
304
- try
305
- {
306
- // open a view on the validation table if it exists
307
- if (database.TableExists("_Validation"))
308
- {
309
- validationView = database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?");
310
- }
311
-
312
- // get the normal tables
313
- using (View tablesView = database.OpenExecuteView("SELECT * FROM _Tables"))
314
- {
315
- while (true)
316
- {
317
- using (Record tableRecord = tablesView.Fetch())
318
- {
319
- if (null == tableRecord)
320
- {
321
- break;
322
- }
323
-
324
- string tableName = tableRecord.GetString(1);
325
-
326
- using (View tableView = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName)))
327
- {
328
- List<ColumnDefinition> columns;
329
- using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES),
330
- columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES))
331
- {
332
- // index the primary keys
333
- HashSet<string> tablePrimaryKeys = new HashSet<string>();
334
- using (Record primaryKeysRecord = database.PrimaryKeys(tableName))
335
- {
336
- int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount();
337
-
338
- for (int i = 1; i <= primaryKeysFieldCount; i++)
339
- {
340
- tablePrimaryKeys.Add(primaryKeysRecord.GetString(i));
341
- }
342
- }
343
-
344
- int columnCount = columnNameRecord.GetFieldCount();
345
- columns = new List<ColumnDefinition>(columnCount);
346
- for (int i = 1; i <= columnCount; i++)
347
- {
348
- string columnName = columnNameRecord.GetString(i);
349
- string idtType = columnTypeRecord.GetString(i);
350
-
351
- ColumnType columnType;
352
- int length;
353
- bool nullable;
354
-
355
- ColumnCategory columnCategory = ColumnCategory.Unknown;
356
- ColumnModularizeType columnModularizeType = ColumnModularizeType.None;
357
- bool primary = tablePrimaryKeys.Contains(columnName);
358
- bool minValueSet = false;
359
- int minValue = -1;
360
- bool maxValueSet = false;
361
- int maxValue = -1;
362
- string keyTable = null;
363
- bool keyColumnSet = false;
364
- int keyColumn = -1;
365
- string category = null;
366
- string set = null;
367
- string description = null;
368
-
369
- // get the column type, length, and whether its nullable
370
- switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture))
371
- {
372
- case 'i':
373
- columnType = ColumnType.Number;
374
- break;
375
- case 'l':
376
- columnType = ColumnType.Localized;
377
- break;
378
- case 's':
379
- columnType = ColumnType.String;
380
- break;
381
- case 'v':
382
- columnType = ColumnType.Object;
383
- break;
384
- default:
385
- // TODO: error
386
- columnType = ColumnType.Unknown;
387
- break;
388
- }
389
- length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture);
390
- nullable = Char.IsUpper(idtType[0]);
391
-
392
- // try to get validation information
393
- if (null != validationView)
394
- {
395
- using (Record validationRecord = new Record(2))
396
- {
397
- validationRecord.SetString(1, tableName);
398
- validationRecord.SetString(2, columnName);
399
-
400
- validationView.Execute(validationRecord);
401
- }
402
-
403
- using (Record validationRecord = validationView.Fetch())
404
- {
405
- if (null != validationRecord)
406
- {
407
- string validationNullable = validationRecord.GetString(3);
408
- minValueSet = !validationRecord.IsNull(4);
409
- minValue = (minValueSet ? validationRecord.GetInteger(4) : -1);
410
- maxValueSet = !validationRecord.IsNull(5);
411
- maxValue = (maxValueSet ? validationRecord.GetInteger(5) : -1);
412
- keyTable = (!validationRecord.IsNull(6) ? validationRecord.GetString(6) : null);
413
- keyColumnSet = !validationRecord.IsNull(7);
414
- keyColumn = (keyColumnSet ? validationRecord.GetInteger(7) : -1);
415
- category = (!validationRecord.IsNull(8) ? validationRecord.GetString(8) : null);
416
- set = (!validationRecord.IsNull(9) ? validationRecord.GetString(9) : null);
417
- description = (!validationRecord.IsNull(10) ? validationRecord.GetString(10) : null);
418
-
419
- // check the validation nullable value against the column definition
420
- if (null == validationNullable)
421
- {
422
- // TODO: warn for illegal validation nullable column
423
- }
424
- else if ((nullable && "Y" != validationNullable) || (!nullable && "N" != validationNullable))
425
- {
426
- // TODO: warn for mismatch between column definition and validation nullable
427
- }
428
-
429
- // convert category to ColumnCategory
430
- if (null != category)
431
- {
432
- try
433
- {
434
- columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true);
435
- }
436
- catch (ArgumentException)
437
- {
438
- columnCategory = ColumnCategory.Unknown;
439
- }
440
- }
441
- }
442
- else
443
- {
444
- // TODO: warn about no validation information
445
- }
446
- }
447
- }
448
-
449
- // guess the modularization type
450
- if ("Icon" == keyTable && 1 == keyColumn)
451
- {
452
- columnModularizeType = ColumnModularizeType.Icon;
453
- }
454
- else if ("Condition" == columnName)
455
- {
456
- columnModularizeType = ColumnModularizeType.Condition;
457
- }
458
- else if (ColumnCategory.Formatted == columnCategory || ColumnCategory.FormattedSDDLText == columnCategory)
459
- {
460
- columnModularizeType = ColumnModularizeType.Property;
461
- }
462
- else if (ColumnCategory.Identifier == columnCategory)
463
- {
464
- columnModularizeType = ColumnModularizeType.Column;
465
- }
466
-
467
- columns.Add(new ColumnDefinition(columnName, columnType, length, primary, nullable, columnModularizeType, (ColumnType.Localized == columnType), minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, columnCategory, set, description, true, true));
468
- }
469
- }
470
-
471
- TableDefinition tableDefinition = new TableDefinition(tableName, columns, false, false);
472
-
473
- // use our table definitions if core properties are the same; this allows us to take advantage
474
- // of wix concepts like localizable columns which current code assumes
475
- if (this.tableDefinitions.Contains(tableName) && 0 == tableDefinition.CompareTo(this.tableDefinitions[tableName]))
476
- {
477
- tableDefinition = this.tableDefinitions[tableName];
478
- }
479
-
480
- Table table = new Table(null, tableDefinition);
481
-
482
- while (true)
483
- {
484
- using (Record rowRecord = tableView.Fetch())
485
- {
486
- if (null == rowRecord)
487
- {
488
- break;
489
- }
490
-
491
- int recordCount = rowRecord.GetFieldCount();
492
- Row row = table.CreateRow(output.SourceLineNumbers);
493
-
494
- for (int i = 0; recordCount > i && row.Fields.Length > i; i++)
495
- {
496
- if (rowRecord.IsNull(i + 1))
497
- {
498
- if (!row.Fields[i].Column.Nullable)
499
- {
500
- // TODO: display an error for a null value in a non-nullable field OR
501
- // display a warning and put an empty string in the value to let the compiler handle it
502
- // (the second option is risky because the later code may make certain assumptions about
503
- // the contents of a row value)
504
- }
505
- }
506
- else
507
- {
508
- switch (row.Fields[i].Column.Type)
509
- {
510
- case ColumnType.Number:
511
- bool success = false;
512
- int intValue = rowRecord.GetInteger(i + 1);
513
- if (row.Fields[i].Column.IsLocalizable)
514
- {
515
- success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
516
- }
517
- else
518
- {
519
- success = row.BestEffortSetField(i, intValue);
520
- }
521
-
522
- if (!success)
523
- {
524
- this.OnMessage(WixWarnings.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name));
525
- }
526
- break;
527
- case ColumnType.Object:
528
- string sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";
529
-
530
- if (null != exportBasePath)
531
- {
532
- string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
533
- sourceFile = Path.Combine(exportBasePath, relativeSourceFile);
534
-
535
- // ensure the parent directory exists
536
- System.IO.Directory.CreateDirectory(Path.Combine(exportBasePath, tableName));
537
-
538
- using (FileStream fs = System.IO.File.Create(sourceFile))
539
- {
540
- int bytesRead;
541
- byte[] buffer = new byte[512];
542
-
543
- while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
544
- {
545
- fs.Write(buffer, 0, bytesRead);
546
- }
547
- }
548
- }
549
-
550
- row[i] = sourceFile;
551
- break;
552
- default:
553
- string value = rowRecord.GetString(i + 1);
111
+ var context = new UnbindContext();
112
+ context.InputFilePath = file;
113
+ context.ExportBasePath = exportBasePath;
114
+ context.IntermediateFolder = this.TempFilesLocation;
115
+ context.IsAdminImage = this.IsAdminImage;
116
+ context.SuppressDemodularization = this.SuppressDemodularization;
117
+ context.SuppressExtractCabinets = this.SuppressExtractCabinets;
118
555
- switch (row.Fields[i].Column.Category)
556
- {
557
- case ColumnCategory.Guid:
558
- value = value.ToUpper(CultureInfo.InvariantCulture);
559
- break;
560
- }
561
-
562
- // de-modularize
563
- if (!this.suppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
564
- {
565
- Regex modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");
566
-
567
- if (null == modularizationGuid)
568
- {
569
- Match match = modularization.Match(value);
570
- if (match.Success)
571
- {
572
- modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
573
- }
574
- }
575
-
576
- value = modularization.Replace(value, String.Empty);
577
- }
578
-
579
- // escape "$(" for the preprocessor
580
- value = value.Replace("$(", "$$(");
581
-
582
- // escape things that look like wix variables
583
- MatchCollection matches = Common.WixVariableRegex.Matches(value);
584
- for (int j = matches.Count - 1; 0 <= j; j--)
585
- {
586
- value = value.Insert(matches[j].Index, "!");
587
- }
588
-
589
- row[i] = value;
590
- break;
591
- }
592
- }
593
- }
594
- }
595
- }
596
-
597
- output.Tables.Add(table);
598
- }
599
-
600
- }
601
- }
602
- }
603
- }
604
- finally
605
- {
606
- if (null != validationView)
607
- {
608
- validationView.Close();
609
- }
610
- }
611
-
612
- // set the modularization guid as the PackageCode
613
- if (null != modularizationGuid)
119
+ foreach (var factory in this.BackendFactories)
120
{
615
- Table table = output.Tables["_SummaryInformation"];
616
-
617
- foreach (Row row in table.Rows)
121
+ if (factory.TryCreateBackend(outputType.ToString(), file, null, out var backend))
122
{
619
- if (9 == (int)row[0]) // PID_REVNUMBER
620
- {
621
- row[1] = modularizationGuid;
622
- }
123
+ return backend.Unbind(context);
124
}
125
}
126
626
- if (this.isAdminImage)
627
- {
628
- GenerateWixFileTable(databaseFile, output);
629
- GenerateSectionIds(output);
630
- }
631
-
632
- return output;
633
- }
634
-
635
- /// <summary>
636
- /// Creates section ids on rows which form logical groupings of resources.
637
- /// </summary>
638
- /// <param name="output">The Output that represents the msi database.</param>
639
- private void GenerateSectionIds(Output output)
640
- {
641
- // First assign and index section ids for the tables that are in their own sections.
642
- AssignSectionIdsToTable(output.Tables["Binary"], 0);
643
- Hashtable componentSectionIdIndex = AssignSectionIdsToTable(output.Tables["Component"], 0);
644
- Hashtable customActionSectionIdIndex = AssignSectionIdsToTable(output.Tables["CustomAction"], 0);
645
- AssignSectionIdsToTable(output.Tables["Directory"], 0);
646
- Hashtable featureSectionIdIndex = AssignSectionIdsToTable(output.Tables["Feature"], 0);
647
- AssignSectionIdsToTable(output.Tables["Icon"], 0);
648
- Hashtable digitalCertificateSectionIdIndex = AssignSectionIdsToTable(output.Tables["MsiDigitalCertificate"], 0);
649
- AssignSectionIdsToTable(output.Tables["Property"], 0);
650
-
651
- // Now handle all the tables that rely on the first set of indexes but also produce their own indexes. Order matters here.
652
- Hashtable fileSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["File"], componentSectionIdIndex, 1, 0);
653
- Hashtable appIdSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["Class"], componentSectionIdIndex, 2, 5);
654
- Hashtable odbcDataSourceSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDataSource"], componentSectionIdIndex, 1, 0);
655
- Hashtable odbcDriverSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDriver"], componentSectionIdIndex, 1, 0);
656
- Hashtable registrySectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["Registry"], componentSectionIdIndex, 5, 0);
657
- Hashtable serviceInstallSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ServiceInstall"], componentSectionIdIndex, 11, 0);
658
-
659
- // Now handle all the tables which only rely on previous indexes and order does not matter.
660
- foreach (Table table in output.Tables)
661
- {
662
- switch (table.Name)
663
- {
664
- case "WixFile":
665
- case "MsiFileHash":
666
- ConnectTableToSection(table, fileSectionIdIndex, 0);
667
- break;
668
- case "MsiAssembly":
669
- case "MsiAssemblyName":
670
- ConnectTableToSection(table, componentSectionIdIndex, 0);
671
- break;
672
- case "MsiPackageCertificate":
673
- case "MsiPatchCertificate":
674
- ConnectTableToSection(table, digitalCertificateSectionIdIndex, 1);
675
- break;
676
- case "CreateFolder":
677
- case "FeatureComponents":
678
- case "MoveFile":
679
- case "ReserveCost":
680
- case "ODBCTranslator":
681
- ConnectTableToSection(table, componentSectionIdIndex, 1);
682
- break;
683
- case "TypeLib":
684
- ConnectTableToSection(table, componentSectionIdIndex, 2);
685
- break;
686
- case "Shortcut":
687
- case "Environment":
688
- ConnectTableToSection(table, componentSectionIdIndex, 3);
689
- break;
690
- case "RemoveRegistry":
691
- ConnectTableToSection(table, componentSectionIdIndex, 4);
692
- break;
693
- case "ServiceControl":
694
- ConnectTableToSection(table, componentSectionIdIndex, 5);
695
- break;
696
- case "IniFile":
697
- case "RemoveIniFile":
698
- ConnectTableToSection(table, componentSectionIdIndex, 7);
699
- break;
700
- case "AppId":
701
- ConnectTableToSection(table, appIdSectionIdIndex, 0);
702
- break;
703
- case "Condition":
704
- ConnectTableToSection(table, featureSectionIdIndex, 0);
705
- break;
706
- case "ODBCSourceAttribute":
707
- ConnectTableToSection(table, odbcDataSourceSectionIdIndex, 0);
708
- break;
709
- case "ODBCAttribute":
710
- ConnectTableToSection(table, odbcDriverSectionIdIndex, 0);
711
- break;
712
- case "AdminExecuteSequence":
713
- case "AdminUISequence":
714
- case "AdvtExecuteSequence":
715
- case "AdvtUISequence":
716
- case "InstallExecuteSequence":
717
- case "InstallUISequence":
718
- ConnectTableToSection(table, customActionSectionIdIndex, 0);
719
- break;
720
- case "LockPermissions":
721
- case "MsiLockPermissions":
722
- foreach (Row row in table.Rows)
723
- {
724
- string lockObject = (string)row[0];
725
- string tableName = (string)row[1];
726
- switch (tableName)
727
- {
728
- case "File":
729
- row.SectionId = (string)fileSectionIdIndex[lockObject];
730
- break;
731
- case "Registry":
732
- row.SectionId = (string)registrySectionIdIndex[lockObject];
733
- break;
734
- case "ServiceInstall":
735
- row.SectionId = (string)serviceInstallSectionIdIndex[lockObject];
736
- break;
737
- }
738
- }
739
- break;
740
- }
741
- }
742
-
743
- // Now pass the output to each unbinder extension to allow them to analyze the output and determine thier proper section ids.
744
- foreach (IUnbinderExtension extension in this.unbinderExtensions)
745
- {
746
- extension.GenerateSectionIds(output);
747
- }
748
- }
749
-
750
- /// <summary>
751
- /// Creates new section ids on all the rows in a table.
752
- /// </summary>
753
- /// <param name="table">The table to add sections to.</param>
754
- /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param>
755
- /// <returns>A Hashtable containing the tables key for each row paired with its assigned section id.</returns>
756
- private Hashtable AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex)
757
- {
758
- Hashtable hashtable = new Hashtable();
759
- if (null != table)
760
- {
761
- foreach (Row row in table.Rows)
762
- {
763
- row.SectionId = GetNewSectionId();
764
- hashtable.Add(row[rowPrimaryKeyIndex], row.SectionId);
765
- }
766
- }
767
- return hashtable;
768
- }
769
-
770
- /// <summary>
771
- /// Connects a table's rows to an already sectioned table.
772
- /// </summary>
773
- /// <param name="table">The table containing rows that need to be connected to sections.</param>
774
- /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param>
775
- /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param>
776
- private static void ConnectTableToSection(Table table, Hashtable sectionIdIndex, int rowIndex)
777
- {
778
- if (null != table)
779
- {
780
- foreach (Row row in table.Rows)
781
- {
782
- if (sectionIdIndex.ContainsKey(row[rowIndex]))
783
- {
784
- row.SectionId = (string)sectionIdIndex[row[rowIndex]];
785
- }
786
- }
787
- }
788
- }
789
-
790
- /// <summary>
791
- /// Connects a table's rows to an already sectioned table and produces an index for other tables to connect to it.
792
- /// </summary>
793
- /// <param name="table">The table containing rows that need to be connected to sections.</param>
794
- /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param>
795
- /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param>
796
- /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param>
797
- /// <returns>A Hashtable containing the tables key for each row paired with its assigned section id.</returns>
798
- private static Hashtable ConnectTableToSectionAndIndex(Table table, Hashtable sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex)
799
- {
800
- Hashtable newHashTable = new Hashtable();
801
- if (null != table)
802
- {
803
- foreach (Row row in table.Rows)
804
- {
805
- if (!sectionIdIndex.ContainsKey(row[rowIndex]))
806
- {
807
- continue;
808
- }
809
-
810
- row.SectionId = (string)sectionIdIndex[row[rowIndex]];
811
- if (null != row[rowPrimaryKeyIndex])
812
- {
813
- newHashTable.Add(row[rowPrimaryKeyIndex], row.SectionId);
814
- }
815
- }
816
- }
817
- return newHashTable;
818
- }
819
-
820
- /// <summary>
821
- /// Creates a new section identifier to be used when adding a section to an output.
822
- /// </summary>
823
- /// <returns>A string representing a new section id.</returns>
824
- private string GetNewSectionId()
825
- {
826
- this.sectionCount++;
827
- return "wix.section." + this.sectionCount.ToString(CultureInfo.InvariantCulture);
828
- }
829
-
830
- /// <summary>
831
- /// Generates the WixFile table based on a path to an admin image msi and an Output.
832
- /// </summary>
833
- /// <param name="databaseFile">The path to the msi database file in an admin image.</param>
834
- /// <param name="output">The Output that represents the msi database.</param>
835
- private void GenerateWixFileTable(string databaseFile, Output output)
836
- {
837
- string adminRootPath = Path.GetDirectoryName(databaseFile);
838
-
839
- Hashtable componentDirectoryIndex = new Hashtable();
840
- Table componentTable = output.Tables["Component"];
841
- foreach (Row row in componentTable.Rows)
842
- {
843
- componentDirectoryIndex.Add(row[0], row[2]);
844
- }
845
-
846
- // Index full source paths for all directories
847
- Hashtable directoryDirectoryParentIndex = new Hashtable();
848
- Hashtable directoryFullPathIndex = new Hashtable();
849
- Hashtable directorySourceNameIndex = new Hashtable();
850
- Table directoryTable = output.Tables["Directory"];
851
- foreach (Row row in directoryTable.Rows)
852
- {
853
- directoryDirectoryParentIndex.Add(row[0], row[1]);
854
- if (null == row[1])
855
- {
856
- directoryFullPathIndex.Add(row[0], adminRootPath);
857
- }
858
- else
859
- {
860
- directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2]));
861
- }
862
- }
863
-
864
- foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex)
865
- {
866
- if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key))
867
- {
868
- GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex);
869
- }
870
- }
871
-
872
- Table fileTable = output.Tables["File"];
873
- Table wixFileTable = output.EnsureTable(this.tableDefinitions["WixFile"]);
874
- foreach (Row row in fileTable.Rows)
875
- {
876
- WixFileRow wixFileRow = new WixFileRow(null, this.tableDefinitions["WixFile"]);
877
- wixFileRow.File = (string)row[0];
878
- wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]];
879
- wixFileRow.Source = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2]));
880
-
881
- if (!File.Exists(wixFileRow.Source))
882
- {
883
- throw new WixException(WixErrors.WixFileNotFound(wixFileRow.Source));
884
- }
885
-
886
- wixFileTable.Rows.Add(wixFileRow);
887
- }
888
- }
889
-
890
- /// <summary>
891
- /// Gets the full path of a directory. Populates the full path index with the directory's full path and all of its parent directorie's full paths.
892
- /// </summary>
893
- /// <param name="directory">The directory identifier.</param>
894
- /// <param name="directoryDirectoryParentIndex">The Hashtable containing all the directory to directory parent mapping.</param>
895
- /// <param name="directorySourceNameIndex">The Hashtable containing all the directory to source name mapping.</param>
896
- /// <param name="directoryFullPathIndex">The Hashtable containing a mapping between all of the directories and their previously calculated full paths.</param>
897
- /// <returns>The full path to the directory.</returns>
898
- private string GetAdminFullPath(string directory, Hashtable directoryDirectoryParentIndex, Hashtable directorySourceNameIndex, Hashtable directoryFullPathIndex)
899
- {
900
- string parent = (string)directoryDirectoryParentIndex[directory];
901
- string sourceName = (string)directorySourceNameIndex[directory];
902
-
903
- string parentFullPath;
904
- if (directoryFullPathIndex.ContainsKey(parent))
905
- {
906
- parentFullPath = (string)directoryFullPathIndex[parent];
907
- }
908
- else
909
- {
910
- parentFullPath = GetAdminFullPath(parent, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex);
911
- }
912
-
913
- if (null == sourceName)
914
- {
915
- sourceName = String.Empty;
916
- }
917
-
918
- string fullPath = Path.Combine(parentFullPath, sourceName);
919
- directoryFullPathIndex.Add(directory, fullPath);
920
-
921
- return fullPath;
922
- }
923
-
924
- /// <summary>
925
- /// Get the source name in an admin image.
926
- /// </summary>
927
- /// <param name="value">The Filename value.</param>
928
- /// <returns>The source name of the directory in an admin image.</returns>
929
- private static string GetAdminSourceName(string value)
930
- {
931
- string name = null;
932
- string[] names;
933
- string shortname = null;
934
- string shortsourcename = null;
935
- string sourcename = null;
936
-
937
- names = Installer.GetNames(value);
938
-
939
- if (null != names[0] && "." != names[0])
940
- {
941
- if (null != names[1])
942
- {
943
- shortname = names[0];
944
- }
945
- else
946
- {
947
- name = names[0];
948
- }
949
- }
950
-
951
- if (null != names[1])
952
- {
953
- name = names[1];
954
- }
955
-
956
- if (null != names[2])
957
- {
958
- if (null != names[3])
959
- {
960
- shortsourcename = names[2];
961
- }
962
- else
963
- {
964
- sourcename = names[2];
965
- }
966
- }
967
-
968
- if (null != names[3])
969
- {
970
- sourcename = names[3];
971
- }
972
-
973
- if (null != sourcename)
974
- {
975
- return sourcename;
976
- }
977
- else if (null != shortsourcename)
978
- {
979
- return shortsourcename;
980
- }
981
- else if (null != name)
982
- {
983
- return name;
984
- }
985
- else
986
- {
987
- return shortname;
988
- }
989
- }
990
-
991
- /// <summary>
992
- /// Unbind an MSP patch file.
993
- /// </summary>
994
- /// <param name="patchFile">The patch file.</param>
995
- /// <param name="exportBasePath">The path where files should be exported.</param>
996
- /// <returns>The unbound patch.</returns>
997
- private Output UnbindPatch(string patchFile, string exportBasePath)
998
- {
999
- Output patch;
1000
-
1001
- // patch files are essentially database files (use a special flag to let the API know its a patch file)
1002
- try
1003
- {
1004
- using (Database database = new Database(patchFile, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile))
1005
- {
1006
- patch = this.UnbindDatabase(patchFile, database, OutputType.Patch, exportBasePath, false);
1007
- }
1008
- }
1009
- catch (Win32Exception e)
1010
- {
1011
- if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
1012
- {
1013
- throw new WixException(WixErrors.OpenDatabaseFailed(patchFile));
1014
- }
1015
-
1016
- throw;
1017
- }
1018
-
1019
- // retrieve the transforms (they are in substorages)
1020
- using (Storage storage = Storage.Open(patchFile, StorageMode.Read | StorageMode.ShareDenyWrite))
1021
- {
1022
- Table summaryInformationTable = patch.Tables["_SummaryInformation"];
1023
- foreach (Row row in summaryInformationTable.Rows)
1024
- {
1025
- if (8 == (int)row[0]) // PID_LASTAUTHOR
1026
- {
1027
- string value = (string)row[1];
1028
-
1029
- foreach (string decoratedSubStorageName in value.Split(';'))
1030
- {
1031
- string subStorageName = decoratedSubStorageName.Substring(1);
1032
- string transformFile = Path.Combine(this.TempFilesLocation, String.Concat("Transform", Path.DirectorySeparatorChar, subStorageName, ".mst"));
1033
-
1034
- // ensure the parent directory exists
1035
- System.IO.Directory.CreateDirectory(Path.GetDirectoryName(transformFile));
1036
-
1037
- // copy the substorage to a new storage for the transform file
1038
- using (Storage subStorage = storage.OpenStorage(subStorageName))
1039
- {
1040
- using (Storage transformStorage = Storage.CreateDocFile(transformFile, StorageMode.ReadWrite | StorageMode.ShareExclusive | StorageMode.Create))
1041
- {
1042
- subStorage.CopyTo(transformStorage);
1043
- }
1044
- }
1045
-
1046
- // unbind the transform
1047
- Output transform = this.UnbindTransform(transformFile, (null == exportBasePath ? null : Path.Combine(exportBasePath, subStorageName)));
1048
- patch.SubStorages.Add(new SubStorage(subStorageName, transform));
1049
- }
1050
-
1051
- break;
1052
- }
1053
- }
1054
- }
1055
-
1056
- // extract the files from the cabinets
1057
- // TODO: use per-transform export paths for support of multi-product patches
1058
- if (null != exportBasePath && !this.suppressExtractCabinets)
1059
- {
1060
- using (Database database = new Database(patchFile, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile))
1061
- {
1062
- foreach (SubStorage subStorage in patch.SubStorages)
1063
- {
1064
- // only patch transforms should carry files
1065
- if (subStorage.Name.StartsWith("#", StringComparison.Ordinal))
1066
- {
1067
- this.ExtractCabinets(subStorage.Data, database, patchFile, exportBasePath);
1068
- }
1069
- }
1070
- }
1071
- }
1072
-
1073
- return patch;
1074
- }
1075
-
1076
- /// <summary>
1077
- /// Unbind an MSI transform file.
1078
- /// </summary>
1079
- /// <param name="transformFile">The transform file.</param>
1080
- /// <param name="exportBasePath">The path where files should be exported.</param>
1081
- /// <returns>The unbound transform.</returns>
1082
- private Output UnbindTransform(string transformFile, string exportBasePath)
1083
- {
1084
- Output transform = new Output(new SourceLineNumber(transformFile));
1085
- transform.Type = OutputType.Transform;
1086
-
1087
- // get the summary information table
1088
- using (SummaryInformation summaryInformation = new SummaryInformation(transformFile))
1089
- {
1090
- Table table = transform.EnsureTable(this.tableDefinitions["_SummaryInformation"]);
1091
-
1092
- for (int i = 1; 19 >= i; i++)
1093
- {
1094
- string value = summaryInformation.GetProperty(i);
1095
-
1096
- if (0 < value.Length)
1097
- {
1098
- Row row = table.CreateRow(transform.SourceLineNumbers);
1099
- row[0] = i;
1100
- row[1] = value;
1101
- }
1102
- }
1103
- }
1104
-
1105
- // create a schema msi which hopefully matches the table schemas in the transform
1106
- Output schemaOutput = new Output(null);
1107
- string msiDatabaseFile = Path.Combine(this.TempFilesLocation, "schema.msi");
1108
- foreach (TableDefinition tableDefinition in this.tableDefinitions)
1109
- {
1110
- // skip unreal tables and the Patch table
1111
- if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name)
1112
- {
1113
- schemaOutput.EnsureTable(tableDefinition);
1114
- }
1115
- }
1116
-
1117
- Hashtable addedRows = new Hashtable();
1118
- Table transformViewTable;
1119
-
1120
- // Bind the schema msi.
1121
- this.GenerateDatabase(schemaOutput, msiDatabaseFile);
1122
-
1123
- // apply the transform to the database and retrieve the modifications
1124
- using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
1125
- {
1126
- // apply the transform with the ViewTransform option to collect all the modifications
1127
- msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);
1128
-
1129
- // unbind the database
1130
- Output transformViewOutput = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath, true);
1131
-
1132
- // index the added and possibly modified rows (added rows may also appears as modified rows)
1133
- transformViewTable = transformViewOutput.Tables["_TransformView"];
1134
- Hashtable modifiedRows = new Hashtable();
1135
- foreach (Row row in transformViewTable.Rows)
1136
- {
1137
- string tableName = (string)row[0];
1138
- string columnName = (string)row[1];
1139
- string primaryKeys = (string)row[2];
1140
-
1141
- if ("INSERT" == columnName)
1142
- {
1143
- string index = String.Concat(tableName, ':', primaryKeys);
1144
-
1145
- addedRows.Add(index, null);
1146
- }
1147
- else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row
1148
- {
1149
- string index = String.Concat(tableName, ':', primaryKeys);
1150
-
1151
- modifiedRows[index] = row;
1152
- }
1153
- }
1154
-
1155
- // create placeholder rows for modified rows to make the transform insert the updated values when its applied
1156
- foreach (Row row in modifiedRows.Values)
1157
- {
1158
- string tableName = (string)row[0];
1159
- string columnName = (string)row[1];
1160
- string primaryKeys = (string)row[2];
1161
-
1162
- string index = String.Concat(tableName, ':', primaryKeys);
1163
-
1164
- // ignore information for added rows
1165
- if (!addedRows.Contains(index))
1166
- {
1167
- Table table = schemaOutput.Tables[tableName];
1168
- this.CreateRow(table, primaryKeys, true);
1169
- }
1170
- }
1171
- }
1172
-
1173
- // Re-bind the schema output with the placeholder rows.
1174
- this.GenerateDatabase(schemaOutput, msiDatabaseFile);
1175
-
1176
- // apply the transform to the database and retrieve the modifications
1177
- using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
1178
- {
1179
- try
1180
- {
1181
- // apply the transform
1182
- msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All);
1183
-
1184
- // commit the database to guard against weird errors with streams
1185
- msiDatabase.Commit();
1186
- }
1187
- catch (Win32Exception ex)
1188
- {
1189
- if (0x65B == ex.NativeErrorCode)
1190
- {
1191
- // this commonly happens when the transform was built
1192
- // against a database schema different from the internal
1193
- // table definitions
1194
- throw new WixException(WixErrors.TransformSchemaMismatch());
1195
- }
1196
- }
1197
-
1198
- // unbind the database
1199
- Output output = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath, true);
1200
-
1201
- // index all the rows to easily find modified rows
1202
- Hashtable rows = new Hashtable();
1203
- foreach (Table table in output.Tables)
1204
- {
1205
- foreach (Row row in table.Rows)
1206
- {
1207
- rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
1208
- }
1209
- }
1210
-
1211
- // process the _TransformView rows into transform rows
1212
- foreach (Row row in transformViewTable.Rows)
1213
- {
1214
- string tableName = (string)row[0];
1215
- string columnName = (string)row[1];
1216
- string primaryKeys = (string)row[2];
1217
-
1218
- Table table = transform.EnsureTable(this.tableDefinitions[tableName]);
1219
-
1220
- if ("CREATE" == columnName) // added table
1221
- {
1222
- table.Operation = TableOperation.Add;
1223
- }
1224
- else if ("DELETE" == columnName) // deleted row
1225
- {
1226
- Row deletedRow = this.CreateRow(table, primaryKeys, false);
1227
- deletedRow.Operation = RowOperation.Delete;
1228
- }
1229
- else if ("DROP" == columnName) // dropped table
1230
- {
1231
- table.Operation = TableOperation.Drop;
1232
- }
1233
- else if ("INSERT" == columnName) // added row
1234
- {
1235
- string index = String.Concat(tableName, ':', primaryKeys);
1236
- Row addedRow = (Row)rows[index];
1237
- addedRow.Operation = RowOperation.Add;
1238
- table.Rows.Add(addedRow);
1239
- }
1240
- else if (null != primaryKeys) // modified row
1241
- {
1242
- string index = String.Concat(tableName, ':', primaryKeys);
1243
-
1244
- // the _TransformView table includes information for added rows
1245
- // that looks like modified rows so it sometimes needs to be ignored
1246
- if (!addedRows.Contains(index))
1247
- {
1248
- Row modifiedRow = (Row)rows[index];
1249
-
1250
- // mark the field as modified
1251
- int indexOfModifiedValue = -1;
1252
- for (int i = 0; i < modifiedRow.TableDefinition.Columns.Count; ++i)
1253
- {
1254
- if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal))
1255
- {
1256
- indexOfModifiedValue = i;
1257
- break;
1258
- }
1259
- }
1260
- modifiedRow.Fields[indexOfModifiedValue].Modified = true;
1261
-
1262
- // move the modified row into the transform the first time its encountered
1263
- if (RowOperation.None == modifiedRow.Operation)
1264
- {
1265
- modifiedRow.Operation = RowOperation.Modify;
1266
- table.Rows.Add(modifiedRow);
1267
- }
1268
- }
1269
- }
1270
- else // added column
1271
- {
1272
- ColumnDefinition column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal));
1273
- column.Added = true;
1274
- }
1275
- }
1276
- }
1277
-
1278
- return transform;
1279
- }
1280
-
1281
- private void GenerateDatabase(Output output, string databaseFile)
1282
- {
1283
- GenerateDatabaseCommand command = new GenerateDatabaseCommand();
1284
- command.Extensions = Enumerable.Empty<IBinderExtension>();
1285
- command.FileManagers = Enumerable.Empty<IBinderFileManager>();
1286
- command.Output = output;
1287
- command.OutputPath = databaseFile;
1288
- command.KeepAddedColumns = true;
1289
- command.UseSubDirectory = false;
1290
- command.SuppressAddingValidationRows = true;
1291
- command.TableDefinitions = this.tableDefinitions;
1292
- command.TempFilesLocation = this.TempFilesLocation;
1293
- command.Codepage = -1;
1294
- command.Execute();
1295
- }
1296
-
1297
- /// <summary>
1298
- /// Unbind a bundle.
1299
- /// </summary>
1300
- /// <param name="bundleFile">The bundle file.</param>
1301
- /// <param name="exportBasePath">The path where files should be exported.</param>
1302
- /// <returns>The unbound bundle.</returns>
1303
- private Output UnbindBundle(string bundleFile, string exportBasePath)
1304
- {
1305
- string uxExtractPath = Path.Combine(exportBasePath, "UX");
1306
- string acExtractPath = Path.Combine(exportBasePath, "AttachedContainer");
1307
-
1308
- using (BurnReader reader = BurnReader.Open(bundleFile))
1309
- {
1310
- reader.ExtractUXContainer(uxExtractPath, this.TempFilesLocation);
1311
- reader.ExtractAttachedContainer(acExtractPath, this.TempFilesLocation);
1312
- }
127
+ // TODO: Display message that could not find a unbinder for output type?
128
129
return null;
130
}
1316
-
1317
- /// <summary>
1318
- /// Create a deleted or modified row.
1319
- /// </summary>
1320
- /// <param name="table">The table containing the row.</param>
1321
- /// <param name="primaryKeys">The primary keys of the row.</param>
1322
- /// <param name="setRequiredFields">Option to set all required fields with placeholder values.</param>
1323
- /// <returns>The new row.</returns>
1324
- private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields)
1325
- {
1326
- Row row = table.CreateRow(null);
1327
-
1328
- string[] primaryKeyParts = primaryKeys.Split('\t');
1329
- int primaryKeyPartIndex = 0;
1330
-
1331
- for (int i = 0; i < table.Definition.Columns.Count; i++)
1332
- {
1333
- ColumnDefinition columnDefinition = table.Definition.Columns[i];
1334
-
1335
- if (columnDefinition.PrimaryKey)
1336
- {
1337
- if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable)
1338
- {
1339
- row[i] = Convert.ToInt32(primaryKeyParts[primaryKeyPartIndex++], CultureInfo.InvariantCulture);
1340
- }
1341
- else
1342
- {
1343
- row[i] = primaryKeyParts[primaryKeyPartIndex++];
1344
- }
1345
- }
1346
- else if (setRequiredFields)
1347
- {
1348
- if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable)
1349
- {
1350
- row[i] = 1;
1351
- }
1352
- else if (ColumnType.Object == columnDefinition.Type)
1353
- {
1354
- if (null == this.emptyFile)
1355
- {
1356
- this.emptyFile = Path.GetTempFileName() + ".empty";
1357
- using (FileStream fileStream = File.Create(this.emptyFile))
1358
- {
1359
- }
1360
- }
1361
-
1362
- row[i] = this.emptyFile;
1363
- }
1364
- else
1365
- {
1366
- row[i] = "1";
1367
- }
1368
- }
1369
- }
1370
-
1371
- return row;
1372
- }
1373
-
1374
- /// <summary>
1375
- /// Extract the cabinets from a database.
1376
- /// </summary>
1377
- /// <param name="output">The output to use when finding cabinets.</param>
1378
- /// <param name="database">The database containing the cabinets.</param>
1379
- /// <param name="databaseFile">The location of the database file.</param>
1380
- /// <param name="exportBasePath">The path where the files should be exported.</param>
1381
- private void ExtractCabinets(Output output, Database database, string databaseFile, string exportBasePath)
1382
- {
1383
- string databaseBasePath = Path.GetDirectoryName(databaseFile);
1384
- StringCollection cabinetFiles = new StringCollection();
1385
- SortedList embeddedCabinets = new SortedList();
1386
-
1387
- // index all of the cabinet files
1388
- if (OutputType.Module == output.Type)
1389
- {
1390
- embeddedCabinets.Add(0, "MergeModule.CABinet");
1391
- }
1392
- else if (null != output.Tables["Media"])
1393
- {
1394
- foreach (MediaRow mediaRow in output.Tables["Media"].Rows)
1395
- {
1396
- if (null != mediaRow.Cabinet)
1397
- {
1398
- if (OutputType.Product == output.Type ||
1399
- (OutputType.Transform == output.Type && RowOperation.Add == mediaRow.Operation))
1400
- {
1401
- if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal))
1402
- {
1403
- embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1));
1404
- }
1405
- else
1406
- {
1407
- cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet));
1408
- }
1409
- }
1410
- }
1411
- }
1412
- }
1413
-
1414
- // extract the embedded cabinet files from the database
1415
- if (0 < embeddedCabinets.Count)
1416
- {
1417
- using (View streamsView = database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?"))
1418
- {
1419
- foreach (int diskId in embeddedCabinets.Keys)
1420
- {
1421
- using (Record record = new Record(1))
1422
- {
1423
- record.SetString(1, (string)embeddedCabinets[diskId]);
1424
- streamsView.Execute(record);
1425
- }
1426
-
1427
- using (Record record = streamsView.Fetch())
1428
- {
1429
- if (null != record)
1430
- {
1431
- // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not case-sensitive,
1432
- // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work
1433
- string cabinetFile = Path.Combine(this.TempFilesLocation, String.Concat("Media", Path.DirectorySeparatorChar, diskId.ToString(CultureInfo.InvariantCulture), ".cab"));
1434
-
1435
- // ensure the parent directory exists
1436
- System.IO.Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile));
1437
-
1438
- using (FileStream fs = System.IO.File.Create(cabinetFile))
1439
- {
1440
- int bytesRead;
1441
- byte[] buffer = new byte[512];
1442
-
1443
- while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length)))
1444
- {
1445
- fs.Write(buffer, 0, bytesRead);
1446
- }
1447
- }
1448
-
1449
- cabinetFiles.Add(cabinetFile);
1450
- }
1451
- else
1452
- {
1453
- // TODO: warning about missing embedded cabinet
1454
- }
1455
- }
1456
- }
1457
- }
1458
- }
1459
-
1460
- // extract the cabinet files
1461
- if (0 < cabinetFiles.Count)
1462
- {
1463
- string fileDirectory = Path.Combine(exportBasePath, "File");
1464
-
1465
- // delete the directory and its files to prevent cab extraction due to an existing file
1466
- if (Directory.Exists(fileDirectory))
1467
- {
1468
- Directory.Delete(fileDirectory, true);
1469
- }
1470
-
1471
- // ensure the directory exists or extraction will fail
1472
- Directory.CreateDirectory(fileDirectory);
1473
-
1474
- foreach (string cabinetFile in cabinetFiles)
1475
- {
1476
- using (WixExtractCab extractCab = new WixExtractCab())
1477
- {
1478
- try
1479
- {
1480
- extractCab.Extract(cabinetFile, fileDirectory);
1481
- }
1482
- catch (FileNotFoundException)
1483
- {
1484
- throw new WixException(WixErrors.FileNotFound(new SourceLineNumber(databaseFile), cabinetFile));
1485
- }
1486
- }
1487
- }
1488
- }
1489
- }
131
}
132
}
src/WixToolset.Core/Uuid.cs
+1
-1
@@ -10,7 +10,7 @@ namespace WixToolset
10
/// <summary>
11
/// Implementation of RFC 4122 - A Universally Unique Identifier (UUID) URN Namespace.
12
/// </summary>
13
- internal sealed class Uuid
13
+ public sealed class Uuid
14
{
15
/// <summary>
16
/// Protect the constructor.
src/WixToolset.Core/WixStrings.Designer.cs
+5
-5
@@ -14,7 +14,7 @@ namespace WixToolset {
14
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
15
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
16
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
17
- internal class WixStrings {
17
+ public class WixStrings {
18
19
private static global::System.Resources.ResourceManager resourceMan;
20
@@ -127,7 +127,7 @@ namespace WixToolset {
127
/// <summary>
128
/// Looks up a localized string similar to Could not determine ProductCode from transform summary information.
129
/// </summary>
130
- internal static string EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo {
130
+ public static string EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo {
131
get {
132
return ResourceManager.GetString("EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo", resourceCulture);
133
}
@@ -226,7 +226,7 @@ namespace WixToolset {
226
/// <summary>
227
/// Looks up a localized string similar to Transform authored into multiple Media '{0}' and '{1}'..
228
/// </summary>
229
- internal static string EXP_TransformAuthoredIntoMultipleMedia {
229
+ public static string EXP_TransformAuthoredIntoMultipleMedia {
230
get {
231
return ResourceManager.GetString("EXP_TransformAuthoredIntoMultipleMedia", resourceCulture);
232
}
@@ -253,7 +253,7 @@ namespace WixToolset {
253
/// <summary>
254
/// Looks up a localized string similar to Encountered an unexpected error while merging '{0}'. More information about the merge and the failure can be found in the merge log: '{1}'.
255
/// </summary>
256
- internal static string EXP_UnexpectedMergerErrorInSourceFile {
256
+ public static string EXP_UnexpectedMergerErrorInSourceFile {
257
get {
258
return ResourceManager.GetString("EXP_UnexpectedMergerErrorInSourceFile", resourceCulture);
259
}
@@ -262,7 +262,7 @@ namespace WixToolset {
262
/// <summary>
263
/// Looks up a localized string similar to Encountered an unexpected merge error of type '{0}' for which there is currently no error message to display. More information about the merge and the failure can be found in the merge log: '{1}'.
264
/// </summary>
265
- internal static string EXP_UnexpectedMergerErrorWithType {
265
+ public static string EXP_UnexpectedMergerErrorWithType {
266
get {
267
return ResourceManager.GetString("EXP_UnexpectedMergerErrorWithType", resourceCulture);
268
}
src/WixToolset.Core/WixVariableResolver.cs
+25
-18
@@ -1,6 +1,6 @@
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
3
+namespace WixToolset.Core
4
{
5
using System;
6
using System.Collections.Generic;
@@ -10,11 +10,12 @@ namespace WixToolset
10
using System.Text.RegularExpressions;
11
using WixToolset.Data;
12
using WixToolset.Data.Rows;
13
+ using WixToolset.Extensibility;
14
15
/// <summary>
16
/// WiX variable resolver.
17
/// </summary>
17
- public sealed class WixVariableResolver
18
+ internal sealed class WixVariableResolver : IBindVariableResolver
19
{
20
private Dictionary<string, string> wixVariables;
21
@@ -31,7 +32,7 @@ namespace WixToolset
32
/// Gets or sets the localizer.
33
/// </summary>
34
/// <value>The localizer.</value>
34
- public Localizer Localizer { get; private set; }
35
+ private Localizer Localizer { get; }
36
37
/// <summary>
38
/// Gets the count of variables added to the resolver.
@@ -83,10 +84,7 @@ namespace WixToolset
84
/// <returns>The resolved value.</returns>
85
public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly)
86
{
86
- bool isDefault = false;
87
- bool delayedResolve = false;
88
-
89
- return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, ref isDefault, ref delayedResolve);
87
+ return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, out var defaultIgnored, out var delayedIgnored);
88
}
89
90
/// <summary>
@@ -97,11 +95,9 @@ namespace WixToolset
95
/// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param>
96
/// <param name="isDefault">true if the resolved value was the default.</param>
97
/// <returns>The resolved value.</returns>
100
- public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, ref bool isDefault)
98
+ public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, out bool isDefault)
99
{
102
- bool delayedResolve = false;
103
-
104
- return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, ref isDefault, ref delayedResolve);
100
+ return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, out isDefault, out var ignored);
101
}
102
103
/// <summary>
@@ -114,9 +110,9 @@ namespace WixToolset
110
/// <param name="isDefault">true if the resolved value was the default.</param>
111
/// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
112
/// <returns>The resolved value.</returns>
117
- public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, ref bool isDefault, ref bool delayedResolve)
113
+ public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, out bool isDefault, out bool delayedResolve)
114
{
119
- return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true, ref isDefault, ref delayedResolve);
115
+ return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true, out isDefault, out delayedResolve);
116
}
117
118
/// <summary>
@@ -129,7 +125,7 @@ namespace WixToolset
125
/// <param name="isDefault">true if the resolved value was the default.</param>
126
/// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
127
/// <returns>The resolved value.</returns>
132
- public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown, ref bool isDefault, ref bool delayedResolve)
128
+ public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown, out bool isDefault, out bool delayedResolve)
129
{
130
MatchCollection matches = Common.WixVariableRegex.Matches(value);
131
@@ -190,10 +186,7 @@ namespace WixToolset
186
Messaging.Instance.OnMessage(WixWarnings.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId));
187
}
188
193
- if (null != this.Localizer)
194
- {
195
- resolvedValue = this.Localizer.GetLocalizedValue(variableId);
196
- }
189
+ resolvedValue = this.Localizer?.GetLocalizedValue(variableId);
190
}
191
else if (!localizationOnly && "wix" == variableNamespace)
192
{
@@ -223,6 +216,7 @@ namespace WixToolset
216
}
217
else
218
{
219
+
220
// insert the resolved value if it was found or display an error
221
if (null != resolvedValue)
222
{
@@ -247,6 +241,19 @@ namespace WixToolset
241
return value;
242
}
243
244
+ /// <summary>
245
+ /// Try to find localization information for dialog and (optional) control.
246
+ /// </summary>
247
+ /// <param name="dialog">Dialog identifier.</param>
248
+ /// <param name="control">Optional control identifier.</param>
249
+ /// <param name="localizedControl">Found localization information.</param>
250
+ /// <returns>True if localized control was found, otherwise false.</returns>
251
+ public bool TryGetLocalizedControl(string dialog, string control, out LocalizedControl localizedControl)
252
+ {
253
+ localizedControl = this.Localizer?.GetLocalizedControl(dialog, control);
254
+ return localizedControl != null;
255
+ }
256
+
257
/// <summary>
258
/// Resolve the delay variables in a value.
259
/// </summary>
src/wix/wix.csproj
+2
@@ -21,6 +21,8 @@
21
22
<ItemGroup>
23
<ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" />
24
+ <ProjectReference Include="..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" />
25
+ <ProjectReference Include="..\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj" />
26
27
<ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
28
<PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />