@joebigelow / wix / commits / dbde9e71

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 &apos;{0}&apos; and &apos;{1}&apos;..
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 &apos;{0}&apos;. More information about the merge and the failure can be found in the merge log: &apos;{1}&apos;.
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 &apos;{0}&apos; 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: &apos;{1}&apos;.
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') " />