aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--WixToolset.Core.sln58
-rw-r--r--src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj2
-rw-r--r--src/WixToolset.Core.Burn/BackendFactory.cs30
-rw-r--r--src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs (renamed from src/WixToolset.Core/Bind/BindBundleCommand.cs)137
-rw-r--r--src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs (renamed from src/WixToolset.Core/ProvidesDependency.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs (renamed from src/WixToolset.Core/ProvidesDependencyCollection.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs (renamed from src/WixToolset.Core/WixComponentSearchInfo.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs (renamed from src/WixToolset.Core/WixFileSearchInfo.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs (renamed from src/WixToolset.Core/WixProductSearchInfo.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs (renamed from src/WixToolset.Core/WixRegistrySearchInfo.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs (renamed from src/WixToolset.Core/WixSearchInfo.cs)2
-rw-r--r--src/WixToolset.Core.Burn/BundleBackend.cs58
-rw-r--r--src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnCommon.cs (renamed from src/WixToolset.Core/Bind/Bundles/BurnCommon.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnReader.cs (renamed from src/WixToolset.Core/Bind/Bundles/BurnReader.cs)16
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnWriter.cs (renamed from src/WixToolset.Core/Bind/Bundles/BurnWriter.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs)10
-rw-r--r--src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs)8
-rw-r--r--src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/PackageFacade.cs (renamed from src/WixToolset.Core/Bind/Bundles/PackageFacade.cs)2
-rw-r--r--src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs)30
-rw-r--r--src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs)6
-rw-r--r--src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs (renamed from src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs)4
-rw-r--r--src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs53
-rw-r--r--src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs61
-rw-r--r--src/WixToolset.Core.Burn/VerifyInterop.cs (renamed from src/WixToolset.Core/VerifyInterop.cs)0
-rw-r--r--src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj36
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs)6
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs (renamed from src/WixToolset.Core/Bind/BindDatabaseCommand.cs)205
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs)4
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs (renamed from src/WixToolset.Core/Bind/BindTransformCommand.cs)21
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs (renamed from src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs)7
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs122
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs (renamed from src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs)3
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs (renamed from src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs)2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs)16
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs)56
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs)9
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs)4
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs)9
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs (renamed from src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs)7
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs)5
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs)5
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs)9
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs)4
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs (renamed from src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs)19
-rw-r--r--src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs (renamed from src/WixToolset.Core/CLR/Interop/CLRInterop.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Differ.cs (renamed from src/WixToolset.Core/Differ.cs)12
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs282
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs (renamed from src/WixToolset.Core/MergeMod/NativeMethods.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Database.cs (renamed from src/WixToolset.Core/Msi/Database.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs (renamed from src/WixToolset.Core/Msi/Installer.cs)121
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs (renamed from src/WixToolset.Core/Msi/Interop/MsiInterop.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs (renamed from src/WixToolset.Core/Msi/MsiException.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs (renamed from src/WixToolset.Core/Msi/MsiHandle.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Record.cs (renamed from src/WixToolset.Core/Msi/Record.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Session.cs (renamed from src/WixToolset.Core/Msi/Session.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs (renamed from src/WixToolset.Core/Msi/SummaryInformation.cs)78
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/View.cs (renamed from src/WixToolset.Core/Msi/View.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MsiBackend.cs36
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MsmBackend.cs34
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MspBackend.cs111
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MstBackend.cs37
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs (renamed from src/WixToolset.Core/Ole32/Storage.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Patch.cs (renamed from src/WixToolset.Core/Patch.cs)41
-rw-r--r--src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs (renamed from src/WixToolset.Core/PatchAPI/PatchInterop.cs)15
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs146
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs791
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs53
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs317
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Validator.cs (renamed from src/WixToolset.Core/Validator.cs)84
-rw-r--r--src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs (renamed from src/WixToolset.Core/Extensibility/ValidatorExtension.cs)0
-rw-r--r--src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs50
-rw-r--r--src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj36
-rw-r--r--src/WixToolset.Core/BackendContext.cs16
-rw-r--r--src/WixToolset.Core/Bind/DelayedField.cs13
-rw-r--r--src/WixToolset.Core/Bind/ExpectedExtractFile.cs16
-rw-r--r--src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs36
-rw-r--r--src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs29
-rw-r--r--src/WixToolset.Core/Bind/FileFacade.cs (renamed from src/WixToolset.Core/Bind/Databases/FileFacade.cs)2
-rw-r--r--src/WixToolset.Core/Bind/FileResolver.cs231
-rw-r--r--src/WixToolset.Core/Bind/FileTransfer.cs113
-rw-r--r--src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs15
-rw-r--r--src/WixToolset.Core/Bind/ResolveFieldsCommand.cs71
-rw-r--r--src/WixToolset.Core/Bind/ResolveResult.cs14
-rw-r--r--src/WixToolset.Core/Bind/ResolvedDirectory.cs2
-rw-r--r--src/WixToolset.Core/Bind/TransferFilesCommand.cs61
-rw-r--r--src/WixToolset.Core/BindContext.cs57
-rw-r--r--src/WixToolset.Core/Binder.cs389
-rw-r--r--src/WixToolset.Core/BinderFileManager.cs2
-rw-r--r--src/WixToolset.Core/BinderFileManagerCore.cs1
-rw-r--r--src/WixToolset.Core/Cab/CabinetFileInfo.cs39
-rw-r--r--src/WixToolset.Core/Cab/WixCreateCab.cs4
-rw-r--r--src/WixToolset.Core/Cab/WixEnumerateCab.cs6
-rw-r--r--src/WixToolset.Core/Cab/WixExtractCab.cs3
-rw-r--r--src/WixToolset.Core/CommandLine/BuildCommand.cs142
-rw-r--r--src/WixToolset.Core/CommandLine/CommandLine.cs22
-rw-r--r--src/WixToolset.Core/Common.cs162
-rw-r--r--src/WixToolset.Core/Compiler.cs24
-rw-r--r--src/WixToolset.Core/CompilerCore.cs9
-rw-r--r--src/WixToolset.Core/Data/messages.xml12
-rw-r--r--src/WixToolset.Core/Decompiler.cs23
-rw-r--r--src/WixToolset.Core/Extensibility/HeatExtension.cs4
-rw-r--r--src/WixToolset.Core/Extensibility/IHeatCore.cs2
-rw-r--r--src/WixToolset.Core/ExtensionManager.cs6
-rw-r--r--src/WixToolset.Core/HeatCore.cs5
-rw-r--r--src/WixToolset.Core/IncribeContext.cs20
-rw-r--r--src/WixToolset.Core/Inscriber.cs641
-rw-r--r--src/WixToolset.Core/Librarian.cs43
-rw-r--r--src/WixToolset.Core/LibraryContext.cs23
-rw-r--r--src/WixToolset.Core/Linker.cs2
-rw-r--r--src/WixToolset.Core/Localizer.cs51
-rw-r--r--src/WixToolset.Core/OptimizeCA.cs33
-rw-r--r--src/WixToolset.Core/PatchSymbolFlagsType.cs19
-rw-r--r--src/WixToolset.Core/PatchTransform.cs4
-rw-r--r--src/WixToolset.Core/Properties/AssemblyInfo.cs2
-rw-r--r--src/WixToolset.Core/TransformsFlags.cs81
-rw-r--r--src/WixToolset.Core/UnbindContext.cs24
-rw-r--r--src/WixToolset.Core/Unbinder.cs1399
-rw-r--r--src/WixToolset.Core/Uuid.cs2
-rw-r--r--src/WixToolset.Core/WixStrings.Designer.cs10
-rw-r--r--src/WixToolset.Core/WixVariableResolver.cs43
-rw-r--r--src/wix/wix.csproj2
128 files changed, 4326 insertions, 2918 deletions
diff --git a/WixToolset.Core.sln b/WixToolset.Core.sln
index b94c31b6..8771dc4c 100644
--- a/WixToolset.Core.sln
+++ b/WixToolset.Core.sln
@@ -1,6 +1,6 @@
1Microsoft Visual Studio Solution File, Format Version 12.00 1Microsoft Visual Studio Solution File, Format Version 12.00
2# Visual Studio 15 2# Visual Studio 15
3VisualStudioVersion = 15.0.26730.12 3VisualStudioVersion = 15.0.26730.16
4MinimumVisualStudioVersion = 15.0.26124.0 4MinimumVisualStudioVersion = 15.0.26124.0
5Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core", "src\WixToolset.Core\WixToolset.Core.csproj", "{0B524850-5B9A-472B-85CC-D25920A1DFE1}" 5Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core", "src\WixToolset.Core\WixToolset.Core.csproj", "{0B524850-5B9A-472B-85CC-D25920A1DFE1}"
6EndProject 6EndProject
@@ -8,24 +8,80 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wix", "src\wix\wix.csproj",
8EndProject 8EndProject
9Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.BuildTasks", "src\WixToolset.BuildTasks\WixToolset.BuildTasks.csproj", "{C199C723-73E1-4E79-AF86-898DBA007FC3}" 9Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.BuildTasks", "src\WixToolset.BuildTasks\WixToolset.BuildTasks.csproj", "{C199C723-73E1-4E79-AF86-898DBA007FC3}"
10EndProject 10EndProject
11Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core.WindowsInstaller", "src\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj", "{5617F2A7-46A0-4D07-B9E0-E982D15641E4}"
12EndProject
13Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core.Burn", "src\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj", "{BC19D30D-C1B6-46DF-95B3-8EDF688E0FEC}"
14EndProject
11Global 15Global
12 GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 GlobalSection(SolutionConfigurationPlatforms) = preSolution
13 Debug|Any CPU = Debug|Any CPU 17 Debug|Any CPU = Debug|Any CPU
18 Debug|x64 = Debug|x64
19 Debug|x86 = Debug|x86
14 Release|Any CPU = Release|Any CPU 20 Release|Any CPU = Release|Any CPU
21 Release|x64 = Release|x64
22 Release|x86 = Release|x86
15 EndGlobalSection 23 EndGlobalSection
16 GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 GlobalSection(ProjectConfigurationPlatforms) = postSolution
17 {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Debug|Any CPU.Build.0 = 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
19 {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 {0B524850-5B9A-472B-85CC-D25920A1DFE1}.Release|Any CPU.Build.0 = 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
21 {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Debug|Any CPU.Build.0 = 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
23 {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 {A948BD6C-A430-4927-B7A5-F6FFAE7B01B1}.Release|Any CPU.Build.0 = 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
25 {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 {C199C723-73E1-4E79-AF86-898DBA007FC3}.Debug|Any CPU.Build.0 = 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
27 {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 {C199C723-73E1-4E79-AF86-898DBA007FC3}.Release|Any CPU.Build.0 = 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
29 EndGlobalSection 85 EndGlobalSection
30 GlobalSection(SolutionProperties) = preSolution 86 GlobalSection(SolutionProperties) = preSolution
31 HideSolutionNode = FALSE 87 HideSolutionNode = FALSE
diff --git a/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj b/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj
index 10dce157..c34b3bca 100644
--- a/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj
+++ b/src/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj
@@ -24,6 +24,8 @@
24 24
25 <ItemGroup> 25 <ItemGroup>
26 <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" /> 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" />
27 </ItemGroup> 29 </ItemGroup>
28 30
29 <ItemGroup> 31 <ItemGroup>
diff --git a/src/WixToolset.Core.Burn/BackendFactory.cs b/src/WixToolset.Core.Burn/BackendFactory.cs
new file mode 100644
index 00000000..042fa254
--- /dev/null
+++ b/src/WixToolset.Core.Burn/BackendFactory.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index 7ea0c830..212b1e81 100644
--- a/src/WixToolset.Core/Bind/BindBundleCommand.cs
+++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,23 +9,83 @@ namespace WixToolset.Bind
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using System.Reflection; 11 using System.Reflection;
12 using WixToolset.Bind.Bundles; 12 using WixToolset.Bind;
13 using WixToolset.Core.Bind;
14 using WixToolset.Core.Burn.Bundles;
13 using WixToolset.Data; 15 using WixToolset.Data;
16 using WixToolset.Data.Bind;
14 using WixToolset.Data.Rows; 17 using WixToolset.Data.Rows;
15 using WixToolset.Extensibility; 18 using WixToolset.Extensibility;
16 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
17 /// <summary> 65 /// <summary>
18 /// Binds a this.bundle. 66 /// Binds a this.bundle.
19 /// </summary> 67 /// </summary>
20 internal class BindBundleCommand : ICommand 68 internal class BindBundleCommand
21 { 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
22 public CompressionLevel DefaultCompressionLevel { private get; set; } 80 public CompressionLevel DefaultCompressionLevel { private get; set; }
23 81
24 public IEnumerable<IBinderExtension> Extensions { private get; set; } 82 public IEnumerable<IDelayedField> DelayedFields { get; }
83
84 public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
25 85
26 public BinderFileManagerCore FileManagerCore { private get; set; } 86 private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
27 87
28 public IEnumerable<IBinderFileManager> FileManagers { private get; set; } 88 public IEnumerable<IBinderExtension> Extensions { private get; set; }
29 89
30 public Output Output { private get; set; } 90 public Output Output { private get; set; }
31 91
@@ -35,9 +95,9 @@ namespace WixToolset.Bind
35 95
36 public TableDefinitionCollection TableDefinitions { private get; set; } 96 public TableDefinitionCollection TableDefinitions { private get; set; }
37 97
38 public string TempFilesLocation { private get; set; } 98 public string IntermediateFolder { private get; set; }
39 99
40 public WixVariableResolver WixVariableResolver { private get; set; } 100 public IBindVariableResolver WixVariableResolver { private get; set; }
41 101
42 public IEnumerable<FileTransfer> FileTransfers { get; private set; } 102 public IEnumerable<FileTransfer> FileTransfers { get; private set; }
43 103
@@ -79,32 +139,9 @@ namespace WixToolset.Bind
79 return; 139 return;
80 } 140 }
81 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
105 // If there are any fields to resolve later, create the cache to populate during bind. 142 // If there are any fields to resolve later, create the cache to populate during bind.
106 IDictionary<string, string> variableCache = null; 143 IDictionary<string, string> variableCache = null;
107 if (delayedFields.Any()) 144 if (this.DelayedFields.Any())
108 { 145 {
109 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); 146 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
110 } 147 }
@@ -116,8 +153,8 @@ namespace WixToolset.Bind
116 153
117 // Extract files that come from cabinet files (this does not extract files from merge modules). 154 // Extract files that come from cabinet files (this does not extract files from merge modules).
118 { 155 {
119 ExtractEmbeddedFilesCommand extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand(); 156 var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand();
120 extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; 157 extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles;
121 extractEmbeddedFilesCommand.Execute(); 158 extractEmbeddedFilesCommand.Execute();
122 } 159 }
123 160
@@ -198,10 +235,10 @@ namespace WixToolset.Bind
198 235
199 case WixBundlePackageType.Msi: 236 case WixBundlePackageType.Msi:
200 { 237 {
201 ProcessMsiPackageCommand command = new ProcessMsiPackageCommand(); 238 var command = new ProcessMsiPackageCommand();
202 command.AuthoredPayloads = payloads; 239 command.AuthoredPayloads = payloads;
203 command.Facade = facade; 240 command.Facade = facade;
204 command.FileManager = this.FileManagers.First(); 241 command.BackendExtensions = this.BackendExtensions;
205 command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]); 242 command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]);
206 command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]); 243 command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]);
207 command.PayloadTable = this.Output.Tables["WixBundlePayload"]; 244 command.PayloadTable = this.Output.Tables["WixBundlePayload"];
@@ -377,11 +414,11 @@ namespace WixToolset.Bind
377 } 414 }
378 415
379 // Resolve any delayed fields before generating the manifest. 416 // Resolve any delayed fields before generating the manifest.
380 if (delayedFields.Any()) 417 if (this.DelayedFields.Any())
381 { 418 {
382 ResolveDelayedFieldsCommand resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(); 419 var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand();
383 resolveDelayedFieldsCommand.OutputType = this.Output.Type; 420 resolveDelayedFieldsCommand.OutputType = this.Output.Type;
384 resolveDelayedFieldsCommand.DelayedFields = delayedFields; 421 resolveDelayedFieldsCommand.DelayedFields = this.DelayedFields;
385 resolveDelayedFieldsCommand.ModularizationGuid = null; 422 resolveDelayedFieldsCommand.ModularizationGuid = null;
386 resolveDelayedFieldsCommand.VariableCache = variableCache; 423 resolveDelayedFieldsCommand.VariableCache = variableCache;
387 resolveDelayedFieldsCommand.Execute(); 424 resolveDelayedFieldsCommand.Execute();
@@ -406,17 +443,17 @@ namespace WixToolset.Bind
406 command.Output = this.Output; 443 command.Output = this.Output;
407 command.Payloads = payloads; 444 command.Payloads = payloads;
408 command.TableDefinitions = this.TableDefinitions; 445 command.TableDefinitions = this.TableDefinitions;
409 command.TempFilesLocation = this.TempFilesLocation; 446 command.TempFilesLocation = this.IntermediateFolder;
410 command.Execute(); 447 command.Execute();
411 448
412 WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; 449 WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
413 payloads.Add(baManifestPayload); 450 payloads.Add(baManifestPayload);
414 } 451 }
415 452
416 foreach (BinderExtension extension in this.Extensions) 453 //foreach (BinderExtension extension in this.Extensions)
417 { 454 //{
418 extension.Finish(Output); 455 // extension.PostBind(this.Context);
419 } 456 //}
420 457
421 // Create all the containers except the UX container first so the manifest (that goes in the UX container) 458 // Create all the containers except the UX container first so the manifest (that goes in the UX container)
422 // can contain all size and hash information about the non-UX containers. 459 // can contain all size and hash information about the non-UX containers.
@@ -441,7 +478,7 @@ namespace WixToolset.Bind
441 } 478 }
442 else if (Compiler.BurnUXContainerId == container.Id) 479 else if (Compiler.BurnUXContainerId == container.Id)
443 { 480 {
444 container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name); 481 container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
445 container.AttachedContainerIndex = 0; 482 container.AttachedContainerIndex = 0;
446 483
447 // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first 484 // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first
@@ -466,7 +503,7 @@ namespace WixToolset.Bind
466 } 503 }
467 else 504 else
468 { 505 {
469 container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name); 506 container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
470 507
471 // Add detached containers to the list of file transfers. 508 // Add detached containers to the list of file transfers.
472 if (ContainerType.Detached == container.Type) 509 if (ContainerType.Detached == container.Type)
@@ -491,10 +528,10 @@ namespace WixToolset.Bind
491 } 528 }
492 529
493 // Create the bundle manifest then UX container. 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");
495 { 532 {
496 CreateBurnManifestCommand command = new CreateBurnManifestCommand(); 533 var command = new CreateBurnManifestCommand();
497 command.FileManagers = this.FileManagers; 534 command.BackendExtensions = this.BackendExtensions;
498 command.Output = this.Output; 535 command.Output = this.Output;
499 536
500 command.BundleInfo = bundleRow; 537 command.BundleInfo = bundleRow;
@@ -519,7 +556,7 @@ namespace WixToolset.Bind
519 string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString(); 556 string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString();
520 557
521 string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe"); 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));
523 560
524 Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile)); 561 Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile));
525 562
diff --git a/src/WixToolset.Core/ProvidesDependency.cs b/src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs
index ea96b5c8..e64773b4 100644
--- a/src/WixToolset.Core/ProvidesDependency.cs
+++ b/src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Xml; 6 using System.Xml;
diff --git a/src/WixToolset.Core/ProvidesDependencyCollection.cs b/src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs
index a777afb0..668b81d3 100644
--- a/src/WixToolset.Core/ProvidesDependencyCollection.cs
+++ b/src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Collections.ObjectModel; 6 using System.Collections.ObjectModel;
diff --git a/src/WixToolset.Core/WixComponentSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs
index dfd5d8ba..f605d7c7 100644
--- a/src/WixToolset.Core/WixComponentSearchInfo.cs
+++ b/src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Xml; 6 using System.Xml;
diff --git a/src/WixToolset.Core/WixFileSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs
index e53f7bf7..ea955db4 100644
--- a/src/WixToolset.Core/WixFileSearchInfo.cs
+++ b/src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Xml; 6 using System.Xml;
diff --git a/src/WixToolset.Core/WixProductSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs
index 4c57d8be..b3bf5fee 100644
--- a/src/WixToolset.Core/WixProductSearchInfo.cs
+++ b/src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Xml; 6 using System.Xml;
diff --git a/src/WixToolset.Core/WixRegistrySearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs
index e8d7ce9b..e25f25f4 100644
--- a/src/WixToolset.Core/WixRegistrySearchInfo.cs
+++ b/src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Xml; 6 using System.Xml;
diff --git a/src/WixToolset.Core/WixSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs
index 906365a2..9ebca4ae 100644
--- a/src/WixToolset.Core/WixSearchInfo.cs
+++ b/src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Diagnostics; 6 using System.Diagnostics;
diff --git a/src/WixToolset.Core.Burn/BundleBackend.cs b/src/WixToolset.Core.Burn/BundleBackend.cs
new file mode 100644
index 00000000..ef4d362c
--- /dev/null
+++ b/src/WixToolset.Core.Burn/BundleBackend.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs b/src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs
index eb02a983..bac8633b 100644
--- a/src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Bundles
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Rows; 10 using WixToolset.Data.Rows;
11 11
12 internal class AutomaticallySlipstreamPatchesCommand : ICommand 12 internal class AutomaticallySlipstreamPatchesCommand
13 { 13 {
14 public IEnumerable<PackageFacade> PackageFacades { private get; set; } 14 public IEnumerable<PackageFacade> PackageFacades { private get; set; }
15 15
diff --git a/src/WixToolset.Core/Bind/Bundles/BurnCommon.cs b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
index 8cb07791..0baa6094 100644
--- a/src/WixToolset.Core/Bind/Bundles/BurnCommon.cs
+++ b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Diagnostics; 6 using System.Diagnostics;
diff --git a/src/WixToolset.Core/Bind/Bundles/BurnReader.cs b/src/WixToolset.Core.Burn/Bundles/BurnReader.cs
index f6d7a197..261ef7b4 100644
--- a/src/WixToolset.Core/Bind/Bundles/BurnReader.cs
+++ b/src/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
7 using System.Collections.Generic; 7 using System.Collections.Generic;
8 using System.IO; 8 using System.IO;
9 using System.Xml; 9 using System.Xml;
10 using WixToolset.Cab; 10 using WixToolset.Core.Cab;
11 11
12 /// <summary> 12 /// <summary>
13 /// Burn PE reader for the WiX toolset. 13 /// Burn PE reader for the WiX toolset.
@@ -49,6 +49,11 @@ namespace WixToolset.Bind.Bundles
49 } 49 }
50 } 50 }
51 51
52 internal static BurnReader Open(object inputFilePath)
53 {
54 throw new NotImplementedException();
55 }
56
52 /// <summary> 57 /// <summary>
53 /// Opens a Burn reader. 58 /// Opens a Burn reader.
54 /// </summary> 59 /// </summary>
@@ -96,7 +101,7 @@ namespace WixToolset.Bind.Bundles
96 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize); 101 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize);
97 } 102 }
98 103
99 using (WixExtractCab extract = new WixExtractCab()) 104 using (var extract = new WixExtractCab())
100 { 105 {
101 extract.Extract(tempCabPath, outputDirectory); 106 extract.Extract(tempCabPath, outputDirectory);
102 } 107 }
@@ -144,6 +149,11 @@ namespace WixToolset.Bind.Bundles
144 return true; 149 return true;
145 } 150 }
146 151
152 internal void ExtractUXContainer(string uxExtractPath, object intermediateFolder)
153 {
154 throw new NotImplementedException();
155 }
156
147 /// <summary> 157 /// <summary>
148 /// Gets the attached container from the exe and extracts its contents to the output directory. 158 /// Gets the attached container from the exe and extracts its contents to the output directory.
149 /// </summary> 159 /// </summary>
diff --git a/src/WixToolset.Core/Bind/Bundles/BurnWriter.cs b/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index bc0baf46..e7365212 100644
--- a/src/WixToolset.Core/Bind/Bundles/BurnWriter.cs
+++ b/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Diagnostics; 6 using System.Diagnostics;
diff --git a/src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
index 1040b394..58814efc 100644
--- a/src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -12,7 +12,7 @@ namespace WixToolset.Bind.Bundles
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.Rows; 13 using WixToolset.Data.Rows;
14 14
15 internal class CreateBootstrapperApplicationManifestCommand : ICommand 15 internal class CreateBootstrapperApplicationManifestCommand
16 { 16 {
17 public WixBundleRow BundleRow { private get; set; } 17 public WixBundleRow BundleRow { private get; set; }
18 18
diff --git a/src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
index 7bc708a3..772265a0 100644
--- a/src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -13,9 +13,9 @@ namespace WixToolset.Bind.Bundles
13 using WixToolset.Data.Rows; 13 using WixToolset.Data.Rows;
14 using WixToolset.Extensibility; 14 using WixToolset.Extensibility;
15 15
16 internal class CreateBurnManifestCommand : ICommand 16 internal class CreateBurnManifestCommand
17 { 17 {
18 public IEnumerable<IBinderFileManager> FileManagers { private get; set; } 18 public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; }
19 19
20 public Output Output { private get; set; } 20 public Output Output { private get; set; }
21 21
@@ -671,9 +671,9 @@ namespace WixToolset.Bind.Bundles
671 private string ResolveUrl(string url, string fallbackUrl, string packageId, string payloadId, string fileName) 671 private string ResolveUrl(string url, string fallbackUrl, string packageId, string payloadId, string fileName)
672 { 672 {
673 string resolved = null; 673 string resolved = null;
674 foreach (IBinderFileManager fileManager in this.FileManagers) 674 foreach (var extension in this.BackendExtensions)
675 { 675 {
676 resolved = fileManager.ResolveUrl(url, fallbackUrl, packageId, payloadId, fileName); 676 resolved = extension.ResolveUrl(url, fallbackUrl, packageId, payloadId, fileName);
677 if (!String.IsNullOrEmpty(resolved)) 677 if (!String.IsNullOrEmpty(resolved))
678 { 678 {
679 break; 679 break;
diff --git a/src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs
index 1bf987e3..75379713 100644
--- a/src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics; 7 using System.Diagnostics;
8 using System.IO; 8 using System.IO;
9 using System.Linq; 9 using System.Linq;
10 using WixToolset.Cab; 10 using WixToolset.Core.Cab;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Data.Rows; 12 using WixToolset.Data.Rows;
13 13
14 /// <summary> 14 /// <summary>
15 /// Creates cabinet files. 15 /// Creates cabinet files.
16 /// </summary> 16 /// </summary>
17 internal class CreateContainerCommand : ICommand 17 internal class CreateContainerCommand
18 { 18 {
19 public CompressionLevel DefaultCompressionLevel { private get; set; } 19 public CompressionLevel DefaultCompressionLevel { private get; set; }
20 20
@@ -37,7 +37,7 @@ namespace WixToolset.Bind.Bundles
37 ++payloadCount; 37 ++payloadCount;
38 } 38 }
39 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 { 41 {
42 // If a manifest was provided always add it as "payload 0" to the container. 42 // If a manifest was provided always add it as "payload 0" to the container.
43 if (!String.IsNullOrEmpty(this.ManifestFile)) 43 if (!String.IsNullOrEmpty(this.ManifestFile))
diff --git a/src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs b/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
index dc19e380..7485758c 100644
--- a/src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using WixToolset.Data; 6 using WixToolset.Data;
7 using WixToolset.Data.Rows; 7 using WixToolset.Data.Rows;
8 8
9 internal class GetPackageFacadesCommand : ICommand 9 internal class GetPackageFacadesCommand
10 { 10 {
11 public Table PackageTable { private get; set; } 11 public Table PackageTable { private get; set; }
12 12
diff --git a/src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs b/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
index ac3a301d..cb6e2748 100644
--- a/src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Data.Rows; 8 using WixToolset.Data.Rows;
9 9
10 internal class OrderPackagesAndRollbackBoundariesCommand : ICommand 10 internal class OrderPackagesAndRollbackBoundariesCommand
11 { 11 {
12 public Table WixGroupTable { private get; set; } 12 public Table WixGroupTable { private get; set; }
13 13
diff --git a/src/WixToolset.Core/Bind/Bundles/PackageFacade.cs b/src/WixToolset.Core.Burn/Bundles/PackageFacade.cs
index f7e6410f..3f2e184d 100644
--- a/src/WixToolset.Core/Bind/Bundles/PackageFacade.cs
+++ b/src/WixToolset.Core.Burn/Bundles/PackageFacade.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using WixToolset.Data.Rows; 5 using WixToolset.Data.Rows;
6 6
diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs
index a1e7c271..11512c39 100644
--- a/src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Data; 6 using WixToolset.Data;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Bundles
9 /// <summary> 9 /// <summary>
10 /// Initializes package state from the Exe contents. 10 /// Initializes package state from the Exe contents.
11 /// </summary> 11 /// </summary>
12 internal class ProcessExePackageCommand : ICommand 12 internal class ProcessExePackageCommand
13 { 13 {
14 public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; } 14 public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
15 15
diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
index f73776c0..322187f9 100644
--- a/src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
@@ -12,14 +12,15 @@ namespace WixToolset.Bind.Bundles
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.Rows; 13 using WixToolset.Data.Rows;
14 using WixToolset.Extensibility; 14 using WixToolset.Extensibility;
15 using WixToolset.Msi;
16 using WixToolset.Core.Native; 15 using WixToolset.Core.Native;
17 using Dtf = WixToolset.Dtf.WindowsInstaller; 16 using Dtf = WixToolset.Dtf.WindowsInstaller;
17 using WixToolset.Bind;
18 using WixToolset.Data.Bind;
18 19
19 /// <summary> 20 /// <summary>
20 /// Initializes package state from the MSI contents. 21 /// Initializes package state from the MSI contents.
21 /// </summary> 22 /// </summary>
22 internal class ProcessMsiPackageCommand : ICommand 23 internal class ProcessMsiPackageCommand
23 { 24 {
24 private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'"; 25 private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'";
25 26
@@ -27,7 +28,7 @@ namespace WixToolset.Bind.Bundles
27 28
28 public PackageFacade Facade { private get; set; } 29 public PackageFacade Facade { private get; set; }
29 30
30 public IBinderFileManager FileManager { private get; set; } 31 public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; }
31 32
32 public Table MsiFeatureTable { private get; set; } 33 public Table MsiFeatureTable { private get; set; }
33 34
@@ -365,7 +366,7 @@ namespace WixToolset.Bind.Bundles
365 if (!payloadNames.Contains(cabinetName)) 366 if (!payloadNames.Contains(cabinetName))
366 { 367 {
367 string generatedId = Common.GenerateIdentifier("cab", packagePayload.Id, cabinet); 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);
369 370
370 WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); 371 WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers);
371 payload.Id = generatedId; 372 payload.Id = generatedId;
@@ -407,7 +408,7 @@ namespace WixToolset.Bind.Bundles
407 break; 408 break;
408 } 409 }
409 410
410 string sourceName = Installer.GetName(record.GetString(3), true, longNamesInImage); 411 string sourceName = Common.GetName(record.GetString(3), true, longNamesInImage);
411 directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName)); 412 directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName));
412 } 413 }
413 } 414 }
@@ -441,7 +442,7 @@ namespace WixToolset.Bind.Bundles
441 if (!payloadNames.Contains(name)) 442 if (!payloadNames.Contains(name))
442 { 443 {
443 string generatedId = Common.GenerateIdentifier("f", packagePayload.Id, record.GetString(2)); 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);
445 446
446 WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); 447 WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers);
447 payload.Id = generatedId; 448 payload.Id = generatedId;
@@ -510,6 +511,21 @@ namespace WixToolset.Bind.Bundles
510 } 511 }
511 } 512 }
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
513 /// <summary> 529 /// <summary>
514 /// Queries a Windows Installer database for a Property value. 530 /// Queries a Windows Installer database for a Property value.
515 /// </summary> 531 /// </summary>
diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
index 24063221..2d849d03 100644
--- a/src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -16,7 +16,7 @@ namespace WixToolset.Bind.Bundles
16 /// <summary> 16 /// <summary>
17 /// Initializes package state from the Msp contents. 17 /// Initializes package state from the Msp contents.
18 /// </summary> 18 /// </summary>
19 internal class ProcessMspPackageCommand : ICommand 19 internal class ProcessMspPackageCommand
20 { 20 {
21 private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'"; 21 private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'";
22 private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false); 22 private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false);
diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs
index ba59f5f5..fcfc780c 100644
--- a/src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Data; 6 using WixToolset.Data;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Bundles
9 /// <summary> 9 /// <summary>
10 /// Processes the Msu packages to add properties and payloads from the Msu packages. 10 /// Processes the Msu packages to add properties and payloads from the Msu packages.
11 /// </summary> 11 /// </summary>
12 internal class ProcessMsuPackageCommand : ICommand 12 internal class ProcessMsuPackageCommand
13 { 13 {
14 public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; } 14 public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
15 15
diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
index a83a7a4a..5dbd6aaa 100644
--- a/src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,10 +9,12 @@ namespace WixToolset.Bind.Bundles
9 using System.Security.Cryptography; 9 using System.Security.Cryptography;
10 using System.Security.Cryptography.X509Certificates; 10 using System.Security.Cryptography.X509Certificates;
11 using System.Text; 11 using System.Text;
12 using WixToolset.Bind;
12 using WixToolset.Data; 13 using WixToolset.Data;
14 using WixToolset.Data.Bind;
13 using WixToolset.Data.Rows; 15 using WixToolset.Data.Rows;
14 16
15 internal class ProcessPayloadsCommand : ICommand 17 internal class ProcessPayloadsCommand
16 { 18 {
17 private static readonly Version EmptyVersion = new Version(0, 0, 0, 0); 19 private static readonly Version EmptyVersion = new Version(0, 0, 0, 0);
18 20
diff --git a/src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs b/src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs
index 9c614c26..9919f777 100644
--- a/src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -11,7 +11,7 @@ namespace WixToolset.Bind.Bundles
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Data.Rows; 12 using WixToolset.Data.Rows;
13 13
14 internal class VerifyPayloadsWithCatalogCommand : ICommand 14 internal class VerifyPayloadsWithCatalogCommand
15 { 15 {
16 public IEnumerable<WixBundleCatalogRow> Catalogs { private get; set; } 16 public IEnumerable<WixBundleCatalogRow> Catalogs { private get; set; }
17 17
diff --git a/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
new file mode 100644
index 00000000..5eb76479
--- /dev/null
+++ b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
new file mode 100644
index 00000000..26af056b
--- /dev/null
+++ b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/VerifyInterop.cs b/src/WixToolset.Core.Burn/VerifyInterop.cs
index 81fbec65..81fbec65 100644
--- a/src/WixToolset.Core/VerifyInterop.cs
+++ b/src/WixToolset.Core.Burn/VerifyInterop.cs
diff --git a/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj b/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
new file mode 100644
index 00000000..878ac200
--- /dev/null
+++ b/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
@@ -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>
diff --git a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
index 5e2650e9..23c481b7 100644
--- a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Globalization; 7 using System.Globalization;
8 using System.IO; 8 using WixToolset.Core.Bind;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Rows; 10 using WixToolset.Data.Rows;
11 11
12 /// <summary> 12 /// <summary>
13 /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows. 13 /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows.
14 /// </summary> 14 /// </summary>
15 public class AssignMediaCommand : ICommand 15 public class AssignMediaCommand
16 { 16 {
17 public AssignMediaCommand() 17 public AssignMediaCommand()
18 { 18 {
diff --git a/src/WixToolset.Core/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index 93af2e9a..2e2c5417 100644
--- a/src/WixToolset.Core/Bind/BindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
@@ -9,8 +9,11 @@ namespace WixToolset.Bind
9 using System.Globalization; 9 using System.Globalization;
10 using System.IO; 10 using System.IO;
11 using System.Linq; 11 using System.Linq;
12 using WixToolset.Bind.Databases; 12 using WixToolset.Bind;
13 using WixToolset.Core.Bind;
14 using WixToolset.Core.WindowsInstaller.Databases;
13 using WixToolset.Data; 15 using WixToolset.Data;
16 using WixToolset.Data.Bind;
14 using WixToolset.Data.Rows; 17 using WixToolset.Data.Rows;
15 using WixToolset.Extensibility; 18 using WixToolset.Extensibility;
16 using WixToolset.Msi; 19 using WixToolset.Msi;
@@ -18,46 +21,72 @@ namespace WixToolset.Bind
18 /// <summary> 21 /// <summary>
19 /// Binds a databse. 22 /// Binds a databse.
20 /// </summary> 23 /// </summary>
21 internal class BindDatabaseCommand : ICommand 24 internal class BindDatabaseCommand
22 { 25 {
23 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. 26 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
24 private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); 27 private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
25 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 }
27 50
28 public int CabbingThreadCount { private get; set; } 51 private IEnumerable<BindPath> BindPaths { get; }
29 52
30 public CompressionLevel DefaultCompressionLevel { private get; set; } 53 private int Codepage { get; }
31 54
32 public bool DeltaBinaryPatch { get; set; } 55 private int CabbingThreadCount { get; }
33 56
34 public IEnumerable<IBinderExtension> Extensions { private get; set; } 57 private string CabCachePath { get; }
35 58
36 public BinderFileManagerCore FileManagerCore { private get; set; } 59 private CompressionLevel DefaultCompressionLevel { get; }
37 60
38 public IEnumerable<IBinderFileManager> FileManagers { private get; set; } 61 public IEnumerable<IDelayedField> DelayedFields { get; }
39 62
40 public IEnumerable<InspectorExtension> InspectorExtensions { private get; set; } 63 public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
64
65 public bool DeltaBinaryPatch { get; set; }
41 66
42 public Localizer Localizer { private get; set; } 67 private IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { get; }
43 68
44 public string PdbFile { private get; set; } 69 private IEnumerable<IBinderExtension> Extensions { get; }
45 70
46 public Output Output { private get; set; } 71 private IEnumerable<InspectorExtension> InspectorExtensions { get; }
47 72
48 public string OutputPath { private get; set; } 73 private string PdbFile { get; }
49 74
50 public bool SuppressAddingValidationRows { private get; set; } 75 private Output Output { get; }
51 76
52 public bool SuppressLayout { private get; set; } 77 private string OutputPath { get; }
53 78
54 public TableDefinitionCollection TableDefinitions { private get; set; } 79 private bool SuppressAddingValidationRows { get; }
55 80
56 public string TempFilesLocation { private get; set; } 81 private bool SuppressLayout { get; }
57 82
58 public Validator Validator { private get; set; } 83 private TableDefinitionCollection TableDefinitions { get; }
59 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; }
61 90
62 public IEnumerable<FileTransfer> FileTransfers { get; private set; } 91 public IEnumerable<FileTransfer> FileTransfers { get; private set; }
63 92
@@ -69,43 +98,9 @@ namespace WixToolset.Bind
69 98
70 HashSet<string> suppressedTableNames = new HashSet<string>(); 99 HashSet<string> suppressedTableNames = new HashSet<string>();
71 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
106 // If there are any fields to resolve later, create the cache to populate during bind. 101 // If there are any fields to resolve later, create the cache to populate during bind.
107 IDictionary<string, string> variableCache = null; 102 IDictionary<string, string> variableCache = null;
108 if (delayedFields.Any()) 103 if (this.DelayedFields.Any())
109 { 104 {
110 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); 105 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
111 } 106 }
@@ -231,7 +226,7 @@ namespace WixToolset.Bind
231 // Add the property name and value to the variableCache. 226 // Add the property name and value to the variableCache.
232 if (null != variableCache) 227 if (null != variableCache)
233 { 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));
235 variableCache[key] = propertyRow.Value; 230 variableCache[key] = propertyRow.Value;
236 } 231 }
237 } 232 }
@@ -240,7 +235,7 @@ namespace WixToolset.Bind
240 // Extract files that come from cabinet files (this does not extract files from merge modules). 235 // Extract files that come from cabinet files (this does not extract files from merge modules).
241 { 236 {
242 ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand(); 237 ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand();
243 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; 238 command.FilesWithEmbeddedFiles = this.ExpectedEmbeddedFiles;
244 command.Execute(); 239 command.Execute();
245 } 240 }
246 241
@@ -258,7 +253,7 @@ namespace WixToolset.Bind
258 command.WixMergeTable = wixMergeTable; 253 command.WixMergeTable = wixMergeTable;
259 command.OutputInstallerVersion = installerVersion; 254 command.OutputInstallerVersion = installerVersion;
260 command.SuppressLayout = this.SuppressLayout; 255 command.SuppressLayout = this.SuppressLayout;
261 command.TempFilesLocation = this.TempFilesLocation; 256 command.TempFilesLocation = this.IntermediateFolder;
262 command.Execute(); 257 command.Execute();
263 258
264 fileFacades.AddRange(command.MergeModulesFileFacades); 259 fileFacades.AddRange(command.MergeModulesFileFacades);
@@ -303,11 +298,11 @@ namespace WixToolset.Bind
303 298
304 this.UpdateControlText(this.Output); 299 this.UpdateControlText(this.Output);
305 300
306 if (delayedFields.Any()) 301 if (this.DelayedFields.Any())
307 { 302 {
308 ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand(); 303 ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand();
309 command.OutputType = this.Output.Type; 304 command.OutputType = this.Output.Type;
310 command.DelayedFields = delayedFields; 305 command.DelayedFields = this.DelayedFields;
311 command.ModularizationGuid = null; 306 command.ModularizationGuid = null;
312 command.VariableCache = variableCache; 307 command.VariableCache = variableCache;
313 command.Execute(); 308 command.Execute();
@@ -343,7 +338,7 @@ namespace WixToolset.Bind
343 { 338 {
344 Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]); 339 Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);
345 340
346 foreach (BinderExtension extension in this.Extensions) 341 foreach (IBinderExtension extension in this.Extensions)
347 { 342 {
348 extension.AfterResolvedFields(this.Output); 343 extension.AfterResolvedFields(this.Output);
349 } 344 }
@@ -379,14 +374,14 @@ namespace WixToolset.Bind
379 return; 374 return;
380 } 375 }
381 376
382 Directory.CreateDirectory(this.TempFilesLocation); 377 Directory.CreateDirectory(this.IntermediateFolder);
383 378
384 if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch) 379 if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch)
385 { 380 {
386 CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand(); 381 CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand();
387 command.FileFacades = fileFacades; 382 command.FileFacades = fileFacades;
388 command.WixPatchIdTable = this.Output.Tables["WixPatchId"]; 383 command.WixPatchIdTable = this.Output.Tables["WixPatchId"];
389 command.TempFilesLocation = this.TempFilesLocation; 384 command.TempFilesLocation = this.IntermediateFolder;
390 command.Execute(); 385 command.Execute();
391 } 386 }
392 387
@@ -396,17 +391,18 @@ namespace WixToolset.Bind
396 { 391 {
397 Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles()); 392 Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles());
398 393
399 CreateCabinetsCommand command = new CreateCabinetsCommand(); 394 var command = new CreateCabinetsCommand();
400 command.CabbingThreadCount = this.CabbingThreadCount; 395 command.CabbingThreadCount = this.CabbingThreadCount;
396 command.CabCachePath = this.CabCachePath;
401 command.DefaultCompressionLevel = this.DefaultCompressionLevel; 397 command.DefaultCompressionLevel = this.DefaultCompressionLevel;
402 command.Output = this.Output; 398 command.Output = this.Output;
403 command.FileManagers = this.FileManagers; 399 command.BackendExtensions = this.BackendExtensions;
404 command.LayoutDirectory = layoutDirectory; 400 command.LayoutDirectory = layoutDirectory;
405 command.Compressed = compressed; 401 command.Compressed = compressed;
406 command.FileRowsByCabinet = filesByCabinetMedia; 402 command.FileRowsByCabinet = filesByCabinetMedia;
407 command.ResolveMedia = this.ResolveMedia; 403 command.ResolveMedia = this.ResolveMedia;
408 command.TableDefinitions = this.TableDefinitions; 404 command.TableDefinitions = this.TableDefinitions;
409 command.TempFilesLocation = this.TempFilesLocation; 405 command.TempFilesLocation = this.IntermediateFolder;
410 command.WixMediaTable = this.Output.Tables["WixMedia"]; 406 command.WixMediaTable = this.Output.Tables["WixMedia"];
411 command.Execute(); 407 command.Execute();
412 408
@@ -450,14 +446,14 @@ namespace WixToolset.Bind
450 } 446 }
451 } 447 }
452 448
453 foreach (BinderExtension extension in this.Extensions) 449 //foreach (BinderExtension extension in this.Extensions)
454 { 450 //{
455 extension.Finish(this.Output); 451 // extension.PostBind(this.Context);
456 } 452 //}
457 453
458 // generate database file 454 // generate database file
459 Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase()); 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));
461 this.GenerateDatabase(this.Output, tempDatabaseFile, false, false); 457 this.GenerateDatabase(this.Output, tempDatabaseFile, false, false);
462 458
463 FileTransfer transfer; 459 FileTransfer transfer;
@@ -501,14 +497,14 @@ namespace WixToolset.Bind
501 } 497 }
502 498
503 // inspect the MSI prior to running ICEs 499 // inspect the MSI prior to running ICEs
504 InspectorCore inspectorCore = new InspectorCore(); 500 //InspectorCore inspectorCore = new InspectorCore();
505 foreach (InspectorExtension inspectorExtension in this.InspectorExtensions) 501 //foreach (InspectorExtension inspectorExtension in this.InspectorExtensions)
506 { 502 //{
507 inspectorExtension.Core = inspectorCore; 503 // inspectorExtension.Core = inspectorCore;
508 inspectorExtension.InspectDatabase(tempDatabaseFile, pdb); 504 // inspectorExtension.InspectDatabase(tempDatabaseFile, pdb);
509 505
510 inspectorExtension.Core = null; // reset. 506 // inspectorExtension.Core = null; // reset.
511 } 507 //}
512 508
513 if (Messaging.Instance.EncounteredError) 509 if (Messaging.Instance.EncounteredError)
514 { 510 {
@@ -540,7 +536,7 @@ namespace WixToolset.Bind
540 // Process uncompressed files. 536 // Process uncompressed files.
541 if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) 537 if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
542 { 538 {
543 ProcessUncompressedFilesCommand command = new ProcessUncompressedFilesCommand(); 539 var command = new ProcessUncompressedFilesCommand();
544 command.Compressed = compressed; 540 command.Compressed = compressed;
545 command.FileFacades = uncompressedFiles; 541 command.FileFacades = uncompressedFiles;
546 command.LayoutDirectory = layoutDirectory; 542 command.LayoutDirectory = layoutDirectory;
@@ -570,8 +566,8 @@ namespace WixToolset.Bind
570 foreach (Row row in dialogTable.Rows) 566 foreach (Row row in dialogTable.Rows)
571 { 567 {
572 string dialog = (string)row[0]; 568 string dialog = (string)row[0];
573 LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, null); 569
574 if (null != localizedControl) 570 if (this.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
575 { 571 {
576 if (CompilerConstants.IntegerNotSet != localizedControl.X) 572 if (CompilerConstants.IntegerNotSet != localizedControl.X)
577 { 573 {
@@ -610,8 +606,8 @@ namespace WixToolset.Bind
610 { 606 {
611 string dialog = (string)row[0]; 607 string dialog = (string)row[0];
612 string control = (string)row[1]; 608 string control = (string)row[1];
613 LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, control); 609
614 if (null != localizedControl) 610 if (this.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
615 { 611 {
616 if (CompilerConstants.IntegerNotSet != localizedControl.X) 612 if (CompilerConstants.IntegerNotSet != localizedControl.X)
617 { 613 {
@@ -651,10 +647,8 @@ namespace WixToolset.Bind
651 /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param> 647 /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param>
652 private IEnumerable<FileFacade> CopyFromTransformData(Output output) 648 private IEnumerable<FileFacade> CopyFromTransformData(Output output)
653 { 649 {
654 CopyTransformDataCommand command = new CopyTransformDataCommand(); 650 var command = new CopyTransformDataCommand();
655 command.CopyOutFileRows = true; 651 command.CopyOutFileRows = true;
656 command.FileManagerCore = this.FileManagerCore;
657 command.FileManagers = this.FileManagers;
658 command.Output = output; 652 command.Output = output;
659 command.TableDefinitions = this.TableDefinitions; 653 command.TableDefinitions = this.TableDefinitions;
660 command.Execute(); 654 command.Execute();
@@ -669,35 +663,13 @@ namespace WixToolset.Bind
669 /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param> 663 /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param>
670 private void CopyToTransformData(Output output) 664 private void CopyToTransformData(Output output)
671 { 665 {
672 CopyTransformDataCommand command = new CopyTransformDataCommand(); 666 var command = new CopyTransformDataCommand();
673 command.CopyOutFileRows = false; 667 command.CopyOutFileRows = false;
674 command.FileManagerCore = this.FileManagerCore;
675 command.FileManagers = this.FileManagers;
676 command.Output = output; 668 command.Output = output;
677 command.TableDefinitions = this.TableDefinitions; 669 command.TableDefinitions = this.TableDefinitions;
678 command.Execute(); 670 command.Execute();
679 } 671 }
680 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
701 private void UpdateMediaSequences(OutputType outputType, IEnumerable<FileFacade> fileFacades, RowDictionary<MediaRow> mediaRows) 673 private void UpdateMediaSequences(OutputType outputType, IEnumerable<FileFacade> fileFacades, RowDictionary<MediaRow> mediaRows)
702 { 674 {
703 // Calculate sequence numbers and media disk id layout for all file media information objects. 675 // Calculate sequence numbers and media disk id layout for all file media information objects.
@@ -855,7 +827,7 @@ namespace WixToolset.Bind
855 continue; 827 continue;
856 } 828 }
857 829
858 string targetName = Installer.GetName((string)row[2], false, true); 830 string targetName = Common.GetName((string)row[2], false, true);
859 directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName)); 831 directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName));
860 } 832 }
861 } 833 }
@@ -919,7 +891,7 @@ namespace WixToolset.Bind
919 { 891 {
920 // calculate the key file's canonical target path 892 // calculate the key file's canonical target path
921 string directoryPath = Binder.GetDirectoryPath(directories, componentIdGenSeeds, componentRow.Directory, true); 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);
923 path = Path.Combine(directoryPath, fileName); 895 path = Path.Combine(directoryPath, fileName);
924 896
925 // find paths that are not canonicalized 897 // find paths that are not canonicalized
@@ -1256,9 +1228,9 @@ namespace WixToolset.Bind
1256 { 1228 {
1257 string layout = null; 1229 string layout = null;
1258 1230
1259 foreach (IBinderFileManager fileManager in this.FileManagers) 1231 foreach (var extension in this.BackendExtensions)
1260 { 1232 {
1261 layout = fileManager.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory); 1233 layout = extension.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory);
1262 if (!String.IsNullOrEmpty(layout)) 1234 if (!String.IsNullOrEmpty(layout))
1263 { 1235 {
1264 break; 1236 break;
@@ -1294,16 +1266,15 @@ namespace WixToolset.Bind
1294 /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> 1266 /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
1295 private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory) 1267 private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
1296 { 1268 {
1297 GenerateDatabaseCommand command = new GenerateDatabaseCommand(); 1269 var command = new GenerateDatabaseCommand();
1298 command.Extensions = this.Extensions; 1270 command.Extensions = this.Extensions;
1299 command.FileManagers = this.FileManagers;
1300 command.Output = output; 1271 command.Output = output;
1301 command.OutputPath = databaseFile; 1272 command.OutputPath = databaseFile;
1302 command.KeepAddedColumns = keepAddedColumns; 1273 command.KeepAddedColumns = keepAddedColumns;
1303 command.UseSubDirectory = useSubdirectory; 1274 command.UseSubDirectory = useSubdirectory;
1304 command.SuppressAddingValidationRows = this.SuppressAddingValidationRows; 1275 command.SuppressAddingValidationRows = this.SuppressAddingValidationRows;
1305 command.TableDefinitions = this.TableDefinitions; 1276 command.TableDefinitions = this.TableDefinitions;
1306 command.TempFilesLocation = this.TempFilesLocation; 1277 command.TempFilesLocation = this.IntermediateFolder;
1307 command.Codepage = this.Codepage; 1278 command.Codepage = this.Codepage;
1308 command.Execute(); 1279 command.Execute();
1309 } 1280 }
diff --git a/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
index 95bd4cf0..5471792d 100644
--- a/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Globalization; 6 using System.Globalization;
@@ -9,7 +9,7 @@ namespace WixToolset.Bind.Databases
9 /// <summary> 9 /// <summary>
10 /// Binds the summary information table of a database. 10 /// Binds the summary information table of a database.
11 /// </summary> 11 /// </summary>
12 internal class BindSummaryInfoCommand : ICommand 12 internal class BindSummaryInfoCommand
13 { 13 {
14 /// <summary> 14 /// <summary>
15 /// The output to bind. 15 /// The output to bind.
diff --git a/src/WixToolset.Core/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index e909f191..425d1f9c 100644
--- a/src/WixToolset.Core/Bind/BindTransformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -11,12 +11,10 @@ namespace WixToolset.Bind
11 using WixToolset.Msi; 11 using WixToolset.Msi;
12 using WixToolset.Core.Native; 12 using WixToolset.Core.Native;
13 13
14 internal class BindTransformCommand : ICommand 14 internal class BindTransformCommand
15 { 15 {
16 public IEnumerable<IBinderExtension> Extensions { private get; set; } 16 public IEnumerable<IBinderExtension> Extensions { private get; set; }
17 17
18 public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
19
20 public TableDefinitionCollection TableDefinitions { private get; set; } 18 public TableDefinitionCollection TableDefinitions { private get; set; }
21 19
22 public string TempFilesLocation { private get; set; } 20 public string TempFilesLocation { private get; set; }
@@ -379,10 +377,10 @@ namespace WixToolset.Bind
379 } 377 }
380 } 378 }
381 379
382 foreach (BinderExtension extension in this.Extensions) 380 //foreach (BinderExtension extension in this.Extensions)
383 { 381 //{
384 extension.Finish(this.Transform); 382 // extension.PostBind(this.Context);
385 } 383 //}
386 384
387 // Any errors encountered up to this point can cause errors during generation. 385 // Any errors encountered up to this point can cause errors during generation.
388 if (Messaging.Instance.EncounteredError) 386 if (Messaging.Instance.EncounteredError)
@@ -437,9 +435,9 @@ namespace WixToolset.Bind
437 private bool CompareFiles(string targetFile, string updatedFile) 435 private bool CompareFiles(string targetFile, string updatedFile)
438 { 436 {
439 bool? compared = null; 437 bool? compared = null;
440 foreach (IBinderFileManager fileManager in this.FileManagers) 438 foreach (var extension in this.Extensions)
441 { 439 {
442 compared = fileManager.CompareFiles(targetFile, updatedFile); 440 compared = extension.CompareFiles(targetFile, updatedFile);
443 if (compared.HasValue) 441 if (compared.HasValue)
444 { 442 {
445 break; 443 break;
@@ -456,10 +454,9 @@ namespace WixToolset.Bind
456 454
457 private void GenerateDatabase(Output output, string outputPath, bool keepAddedColumns) 455 private void GenerateDatabase(Output output, string outputPath, bool keepAddedColumns)
458 { 456 {
459 GenerateDatabaseCommand command = new GenerateDatabaseCommand(); 457 var command = new GenerateDatabaseCommand();
460 command.Codepage = output.Codepage; 458 command.Codepage = output.Codepage;
461 command.Extensions = this.Extensions; 459 command.Extensions = this.Extensions;
462 command.FileManagers = this.FileManagers;
463 command.KeepAddedColumns = keepAddedColumns; 460 command.KeepAddedColumns = keepAddedColumns;
464 command.Output = output; 461 command.Output = output;
465 command.OutputPath = outputPath; 462 command.OutputPath = outputPath;
diff --git a/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
index 2de6ec25..b2cc76fc 100644
--- a/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using System.Threading; 9 using System.Threading;
10 using WixToolset.Cab; 10 using WixToolset.Core.Bind;
11 using WixToolset.Core.Cab;
11 using WixToolset.Data; 12 using WixToolset.Data;
12 using WixToolset.Data.Rows;
13 13
14 /// <summary> 14 /// <summary>
15 /// Builds cabinets using multiple threads. This implements a thread pool that generates cabinets with multiple 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; 25 private IntPtr newCabNamesCallBackAddress;
26 26
27 public int MaximumCabinetSizeForLargeFileSplitting { get; set; } 27 public int MaximumCabinetSizeForLargeFileSplitting { get; set; }
28
28 public int MaximumUncompressedMediaSize { get; set; } 29 public int MaximumUncompressedMediaSize { get; set; }
29 30
30 /// <summary> 31 /// <summary>
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs
new file mode 100644
index 00000000..df1ccecf
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs
index 20241bc9..dcafcd36 100644
--- a/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using WixToolset.Core.Bind;
6 using WixToolset.Data; 7 using WixToolset.Data;
7 using WixToolset.Data.Rows; 8 using WixToolset.Data.Rows;
8 9
diff --git a/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
index 7cb18e0f..d4d3799f 100644
--- a/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
diff --git a/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
index af1ab3b0..6388a352 100644
--- a/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,15 +9,14 @@ namespace WixToolset.Bind.Databases
9 using WixToolset.Data.Rows; 9 using WixToolset.Data.Rows;
10 using WixToolset.Extensibility; 10 using WixToolset.Extensibility;
11 using WixToolset.Core.Native; 11 using WixToolset.Core.Native;
12 using WixToolset.Core.Bind;
12 13
13 internal class CopyTransformDataCommand : ICommand 14 internal class CopyTransformDataCommand
14 { 15 {
15 public bool CopyOutFileRows { private get; set; } 16 public bool CopyOutFileRows { private get; set; }
16 17
17 public BinderFileManagerCore FileManagerCore { private get; set; } 18 public IEnumerable<IBinderExtension> Extensions { private get; set; }
18 19
19 public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
20
21 public Output Output { private get; set; } 20 public Output Output { private get; set; }
22 21
23 public TableDefinitionCollection TableDefinitions { private get; set; } 22 public TableDefinitionCollection TableDefinitions { private get; set; }
@@ -586,9 +585,10 @@ namespace WixToolset.Bind.Databases
586 private bool CompareFiles(string targetFile, string updatedFile) 585 private bool CompareFiles(string targetFile, string updatedFile)
587 { 586 {
588 bool? compared = null; 587 bool? compared = null;
589 foreach (IBinderFileManager fileManager in this.FileManagers) 588 foreach (var extension in this.Extensions)
590 { 589 {
591 compared = fileManager.CompareFiles(targetFile, updatedFile); 590 compared = extension.CompareFiles(targetFile, updatedFile);
591
592 if (compared.HasValue) 592 if (compared.HasValue)
593 { 593 {
594 break; 594 break;
diff --git a/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
index 35c8abb4..02015744 100644
--- a/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,15 +9,21 @@ namespace WixToolset.Bind.Databases
9 using System.Linq; 9 using System.Linq;
10 using System.Runtime.InteropServices; 10 using System.Runtime.InteropServices;
11 using System.Threading; 11 using System.Threading;
12 using WixToolset.Core.Bind;
13 using WixToolset.Core.WindowsInstaller.Bind;
12 using WixToolset.Data; 14 using WixToolset.Data;
15 using WixToolset.Data.Bind;
13 using WixToolset.Data.Rows; 16 using WixToolset.Data.Rows;
14 using WixToolset.Extensibility; 17 using WixToolset.Extensibility;
15 18
16 /// <summary> 19 /// <summary>
17 /// Creates cabinet files. 20 /// Creates cabinet files.
18 /// </summary> 21 /// </summary>
19 internal class CreateCabinetsCommand : ICommand 22 internal class CreateCabinetsCommand
20 { 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
21 private List<FileTransfer> fileTransfers; 27 private List<FileTransfer> fileTransfers;
22 28
23 private FileSplitCabNamesCallback newCabNamesCallBack; 29 private FileSplitCabNamesCallback newCabNamesCallBack;
@@ -28,7 +34,7 @@ namespace WixToolset.Bind.Databases
28 { 34 {
29 this.fileTransfers = new List<FileTransfer>(); 35 this.fileTransfers = new List<FileTransfer>();
30 36
31 this.newCabNamesCallBack = NewCabNamesCallBack; 37 this.newCabNamesCallBack = this.NewCabNamesCallBack;
32 } 38 }
33 39
34 /// <summary> 40 /// <summary>
@@ -36,6 +42,8 @@ namespace WixToolset.Bind.Databases
36 /// </summary> 42 /// </summary>
37 public int CabbingThreadCount { private get; set; } 43 public int CabbingThreadCount { private get; set; }
38 44
45 public string CabCachePath { private get; set; }
46
39 public string TempFilesLocation { private get; set; } 47 public string TempFilesLocation { private get; set; }
40 48
41 /// <summary> 49 /// <summary>
@@ -44,9 +52,9 @@ namespace WixToolset.Bind.Databases
44 /// </summary> 52 /// </summary>
45 public CompressionLevel DefaultCompressionLevel { private get; set; } 53 public CompressionLevel DefaultCompressionLevel { private get; set; }
46 54
47 public Output Output { private get; set; } 55 public IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { private get; set; }
48 56
49 public IEnumerable<IBinderFileManager> FileManagers { private get; set; } 57 public Output Output { private get; set; }
50 58
51 public string LayoutDirectory { private get; set; } 59 public string LayoutDirectory { private get; set; }
52 60
@@ -60,7 +68,7 @@ namespace WixToolset.Bind.Databases
60 68
61 public Table WixMediaTable { private get; set; } 69 public Table WixMediaTable { private get; set; }
62 70
63 public IEnumerable<FileTransfer> FileTransfers { get { return this.fileTransfers; } } 71 public IEnumerable<FileTransfer> FileTransfers => this.fileTransfers;
64 72
65 /// <param name="output">Output to generate image for.</param> 73 /// <param name="output">Output to generate image for.</param>
66 /// <param name="fileTransfers">Array of files to be transfered.</param> 74 /// <param name="fileTransfers">Array of files to be transfered.</param>
@@ -204,7 +212,9 @@ namespace WixToolset.Bind.Databases
204 } 212 }
205 } 213 }
206 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);
208 218
209 // create a cabinet work item if it's not being skipped 219 // create a cabinet work item if it's not being skipped
210 if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) 220 if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption)
@@ -255,23 +265,23 @@ namespace WixToolset.Bind.Databases
255 return cabinetWorkItem; 265 return cabinetWorkItem;
256 } 266 }
257 267
258 private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades) 268 //private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades)
259 { 269 //{
260 ResolvedCabinet resolved = null; 270 // ResolvedCabinet resolved = null;
261 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();
263 273
264 foreach (IBinderFileManager fileManager in this.FileManagers) 274 // foreach (var extension in this.BackendExtensions)
265 { 275 // {
266 resolved = fileManager.ResolveCabinet(cabinetPath, filesWithPath); 276 // resolved = extension.ResolveCabinet(cabinetPath, filesWithPath);
267 if (null != resolved) 277 // if (null != resolved)
268 { 278 // {
269 break; 279 // break;
270 } 280 // }
271 } 281 // }
272 282
273 return resolved; 283 // return resolved;
274 } 284 //}
275 285
276 /// <summary> 286 /// <summary>
277 /// Delegate for Cabinet Split Callback 287 /// Delegate for Cabinet Split Callback
@@ -451,7 +461,7 @@ namespace WixToolset.Bind.Databases
451 } 461 }
452 catch (OverflowException) 462 catch (OverflowException)
453 { 463 {
454 throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, CompilerCore.MaxValueOfMaxCabSizeForLargeFileSplitting)); 464 throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, MaxValueOfMaxCabSizeForLargeFileSplitting));
455 } 465 }
456 466
457 try 467 try
@@ -482,7 +492,7 @@ namespace WixToolset.Bind.Databases
482 else 492 else
483 { 493 {
484 maxCabSizeForLargeFileSplitting = 0; 494 maxCabSizeForLargeFileSplitting = 0;
485 maxUncompressedMediaSize = CompilerCore.DefaultMaximumUncompressedMediaSize; 495 maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize;
486 } 496 }
487 } 497 }
488 } 498 }
diff --git a/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
index 933a1ea8..767671b8 100644
--- a/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Globalization; 7 using System.Globalization;
8 using System.IO; 8 using System.IO;
9 using WixToolset.Core.Bind;
9 using WixToolset.Data; 10 using WixToolset.Data;
10 using WixToolset.Data.Rows; 11 using WixToolset.Data.Rows;
11 12
12 /// <summary> 13 /// <summary>
13 /// Creates delta patches and updates the appropriate rows to point to the newly generated patches. 14 /// Creates delta patches and updates the appropriate rows to point to the newly generated patches.
14 /// </summary> 15 /// </summary>
15 internal class CreateDeltaPatchesCommand : ICommand 16 internal class CreateDeltaPatchesCommand
16 { 17 {
17 public IEnumerable<FileFacade> FileFacades { private get; set; } 18 public IEnumerable<FileFacade> FileFacades { private get; set; }
18 19
@@ -23,7 +24,7 @@ namespace WixToolset.Bind.Databases
23 public void Execute() 24 public void Execute()
24 { 25 {
25 bool optimizePatchSizeForLargeFiles = false; 26 bool optimizePatchSizeForLargeFiles = false;
26 PatchAPI.PatchInterop.PatchSymbolFlagsType apiPatchingSymbolFlags = 0; 27 PatchSymbolFlagsType apiPatchingSymbolFlags = 0;
27 28
28 if (null != this.WixPatchIdTable) 29 if (null != this.WixPatchIdTable)
29 { 30 {
@@ -37,7 +38,7 @@ namespace WixToolset.Bind.Databases
37 38
38 if (null != row[3]) 39 if (null != row[3])
39 { 40 {
40 apiPatchingSymbolFlags = (PatchAPI.PatchInterop.PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture); 41 apiPatchingSymbolFlags = (PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture);
41 } 42 }
42 } 43 }
43 } 44 }
diff --git a/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs
index 5db2768b..aef130b0 100644
--- a/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Data.Rows; 8 using WixToolset.Data.Rows;
9 9
10 internal class CreateSpecialPropertiesCommand : ICommand 10 internal class CreateSpecialPropertiesCommand
11 { 11 {
12 public Table PropertyTable { private get; set; } 12 public Table PropertyTable { private get; set; }
13 13
diff --git a/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
index bee1488b..ae76037d 100644
--- a/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,17 +9,18 @@ namespace WixToolset.Bind.Databases
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using System.Runtime.InteropServices; 11 using System.Runtime.InteropServices;
12 using WixToolset.Cab;
13 using WixToolset.Data; 12 using WixToolset.Data;
14 using WixToolset.Data.Rows; 13 using WixToolset.Data.Rows;
15 using WixToolset.MergeMod; 14 using WixToolset.MergeMod;
16 using WixToolset.Msi; 15 using WixToolset.Msi;
17 using WixToolset.Core.Native; 16 using WixToolset.Core.Native;
17 using WixToolset.Core.Bind;
18 using WixToolset.Core.Cab;
18 19
19 /// <summary> 20 /// <summary>
20 /// Retrieve files information and extract them from merge modules. 21 /// Retrieve files information and extract them from merge modules.
21 /// </summary> 22 /// </summary>
22 internal class ExtractMergeModuleFilesCommand : ICommand 23 internal class ExtractMergeModuleFilesCommand
23 { 24 {
24 public IEnumerable<FileFacade> FileFacades { private get; set; } 25 public IEnumerable<FileFacade> FileFacades { private get; set; }
25 26
@@ -193,7 +194,7 @@ namespace WixToolset.Bind.Databases
193 string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId); 194 string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId);
194 Directory.CreateDirectory(mergeIdPath); 195 Directory.CreateDirectory(mergeIdPath);
195 196
196 using (WixExtractCab extractCab = new WixExtractCab()) 197 using (var extractCab = new WixExtractCab())
197 { 198 {
198 try 199 try
199 { 200 {
diff --git a/src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index fdf1ab32..26d254f2 100644
--- a/src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -13,14 +13,12 @@ namespace WixToolset.Bind
13 using WixToolset.Msi; 13 using WixToolset.Msi;
14 using WixToolset.Core.Native; 14 using WixToolset.Core.Native;
15 15
16 internal class GenerateDatabaseCommand : ICommand 16 internal class GenerateDatabaseCommand
17 { 17 {
18 public int Codepage { private get; set; } 18 public int Codepage { private get; set; }
19 19
20 public IEnumerable<IBinderExtension> Extensions { private get; set; } 20 public IEnumerable<IBinderExtension> Extensions { private get; set; }
21 21
22 public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
23
24 /// <summary> 22 /// <summary>
25 /// Whether to keep columns added in a transform. 23 /// Whether to keep columns added in a transform.
26 /// </summary> 24 /// </summary>
@@ -295,7 +293,6 @@ namespace WixToolset.Bind
295 { 293 {
296 BindTransformCommand command = new BindTransformCommand(); 294 BindTransformCommand command = new BindTransformCommand();
297 command.Extensions = this.Extensions; 295 command.Extensions = this.Extensions;
298 command.FileManagers = this.FileManagers;
299 command.TempFilesLocation = this.TempFilesLocation; 296 command.TempFilesLocation = this.TempFilesLocation;
300 command.Transform = transform; 297 command.Transform = transform;
301 command.OutputPath = outputPath; 298 command.OutputPath = outputPath;
diff --git a/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
index b6bcd3af..caf8b7a7 100644
--- a/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Globalization; 7 using System.Globalization;
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Core.Bind;
9 using WixToolset.Data; 10 using WixToolset.Data;
10 using WixToolset.Data.Rows; 11 using WixToolset.Data.Rows;
11 12
12 internal class GetFileFacadesCommand : ICommand 13 internal class GetFileFacadesCommand
13 { 14 {
14 public Table FileTable { private get; set; } 15 public Table FileTable { private get; set; }
15 16
diff --git a/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
index 035ef059..624cbb43 100644
--- a/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -20,11 +20,12 @@ namespace WixToolset.Bind.Databases
20 using WixToolset.MergeMod; 20 using WixToolset.MergeMod;
21 using WixToolset.Msi; 21 using WixToolset.Msi;
22 using WixToolset.Core.Native; 22 using WixToolset.Core.Native;
23 using WixToolset.Core.Bind;
23 24
24 /// <summary> 25 /// <summary>
25 /// Update file information. 26 /// Update file information.
26 /// </summary> 27 /// </summary>
27 internal class MergeModulesCommand : ICommand 28 internal class MergeModulesCommand
28 { 29 {
29 public IEnumerable<FileFacade> FileFacades { private get; set; } 30 public IEnumerable<FileFacade> FileFacades { private get; set; }
30 31
diff --git a/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
index dd7b85b7..b3c09b9e 100644
--- a/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
@@ -10,11 +10,14 @@ namespace WixToolset.Bind.Databases
10 using WixToolset.Data.Rows; 10 using WixToolset.Data.Rows;
11 using WixToolset.Msi; 11 using WixToolset.Msi;
12 using WixToolset.Core.Native; 12 using WixToolset.Core.Native;
13 using WixToolset.Bind;
14 using WixToolset.Core.Bind;
15 using WixToolset.Data.Bind;
13 16
14 /// <summary> 17 /// <summary>
15 /// Defines the file transfers necessary to layout the uncompressed files. 18 /// Defines the file transfers necessary to layout the uncompressed files.
16 /// </summary> 19 /// </summary>
17 internal class ProcessUncompressedFilesCommand : ICommand 20 internal class ProcessUncompressedFilesCommand
18 { 21 {
19 public string DatabasePath { private get; set; } 22 public string DatabasePath { private get; set; }
20 23
@@ -55,7 +58,7 @@ namespace WixToolset.Bind.Databases
55 break; 58 break;
56 } 59 }
57 60
58 string sourceName = Installer.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage); 61 string sourceName = Common.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage);
59 62
60 directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName)); 63 directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName));
61 } 64 }
diff --git a/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs
index 9e17ee02..7da32206 100644
--- a/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Data.Rows; 8 using WixToolset.Data.Rows;
9 9
10 internal class UpdateControlTextCommand : ICommand 10 internal class UpdateControlTextCommand
11 { 11 {
12 public Table BBControlTable { private get; set; } 12 public Table BBControlTable { private get; set; }
13 13
diff --git a/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
index 36818afa..cd9444ee 100644
--- a/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.WindowsInstaller.Databases
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -12,6 +12,7 @@ namespace WixToolset.Bind.Databases
12 using System.Xml; 12 using System.Xml;
13 using System.Xml.XPath; 13 using System.Xml.XPath;
14 using WixToolset.Clr.Interop; 14 using WixToolset.Clr.Interop;
15 using WixToolset.Core.Bind;
15 using WixToolset.Data; 16 using WixToolset.Data;
16 using WixToolset.Data.Rows; 17 using WixToolset.Data.Rows;
17 using WixToolset.Msi; 18 using WixToolset.Msi;
@@ -19,7 +20,7 @@ namespace WixToolset.Bind.Databases
19 /// <summary> 20 /// <summary>
20 /// Update file information. 21 /// Update file information.
21 /// </summary> 22 /// </summary>
22 internal class UpdateFileFacadesCommand : ICommand 23 internal class UpdateFileFacadesCommand
23 { 24 {
24 public IEnumerable<FileFacade> FileFacades { private get; set; } 25 public IEnumerable<FileFacade> FileFacades { private get; set; }
25 26
@@ -52,17 +53,17 @@ namespace WixToolset.Bind.Databases
52 } 53 }
53 catch (ArgumentException) 54 catch (ArgumentException)
54 { 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));
56 return; 57 return;
57 } 58 }
58 catch (PathTooLongException) 59 catch (PathTooLongException)
59 { 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));
61 return; 62 return;
62 } 63 }
63 catch (NotSupportedException) 64 catch (NotSupportedException)
64 { 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));
66 return; 67 return;
67 } 68 }
68 69
@@ -195,13 +196,13 @@ namespace WixToolset.Bind.Databases
195 { 196 {
196 if (!String.IsNullOrEmpty(file.File.Version)) 197 if (!String.IsNullOrEmpty(file.File.Version))
197 { 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));
199 this.VariableCache[key] = file.File.Version; 200 this.VariableCache[key] = file.File.Version;
200 } 201 }
201 202
202 if (!String.IsNullOrEmpty(file.File.Language)) 203 if (!String.IsNullOrEmpty(file.File.Language))
203 { 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));
205 this.VariableCache[key] = file.File.Language; 206 this.VariableCache[key] = file.File.Language;
206 } 207 }
207 } 208 }
@@ -326,7 +327,7 @@ namespace WixToolset.Bind.Databases
326 // add the assembly name to the information cache 327 // add the assembly name to the information cache
327 if (null != this.VariableCache) 328 if (null != this.VariableCache)
328 { 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);
330 string key = String.Concat("assemblyfullname.", fileId); 331 string key = String.Concat("assemblyfullname.", fileId);
331 string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]); 332 string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]);
332 if (assemblyNameValues.ContainsKey("processorArchitecture")) 333 if (assemblyNameValues.ContainsKey("processorArchitecture"))
@@ -523,7 +524,7 @@ namespace WixToolset.Bind.Databases
523 524
524 if (this.VariableCache != null) 525 if (this.VariableCache != null)
525 { 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();
527 this.VariableCache[key] = (string)assemblyNameRow[2]; 528 this.VariableCache[key] = (string)assemblyNameRow[2];
528 } 529 }
529 } 530 }
diff --git a/src/WixToolset.Core/CLR/Interop/CLRInterop.cs b/src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs
index 4157f23a..4157f23a 100644
--- a/src/WixToolset.Core/CLR/Interop/CLRInterop.cs
+++ b/src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs
diff --git a/src/WixToolset.Core/Differ.cs b/src/WixToolset.Core.WindowsInstaller/Differ.cs
index 71a64327..bdd06d32 100644
--- a/src/WixToolset.Core/Differ.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Differ.cs
@@ -6,6 +6,7 @@ namespace WixToolset
6 using System.Collections; 6 using System.Collections;
7 using System.Collections.Generic; 7 using System.Collections.Generic;
8 using System.Globalization; 8 using System.Globalization;
9 using WixToolset.Core;
9 using WixToolset.Data; 10 using WixToolset.Data;
10 using WixToolset.Data.Rows; 11 using WixToolset.Data.Rows;
11 using WixToolset.Extensibility; 12 using WixToolset.Extensibility;
@@ -155,17 +156,6 @@ namespace WixToolset
155 this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags); 156 this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags);
156 } 157 }
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
169 return transform; 159 return transform;
170 } 160 }
171 161
diff --git a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
new file mode 100644
index 00000000..40901d7c
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/MergeMod/NativeMethods.cs b/src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs
index daf259b4..daf259b4 100644
--- a/src/WixToolset.Core/MergeMod/NativeMethods.cs
+++ b/src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs
diff --git a/src/WixToolset.Core/Msi/Database.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
index 801ebdde..801ebdde 100644
--- a/src/WixToolset.Core/Msi/Database.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
diff --git a/src/WixToolset.Core/Msi/Installer.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
index 3beb26f4..f8bce602 100644
--- a/src/WixToolset.Core/Msi/Installer.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
@@ -359,126 +359,5 @@ namespace WixToolset.Msi
359 { 359 {
360 return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd); 360 return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd);
361 } 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 }
483 } 362 }
484} 363}
diff --git a/src/WixToolset.Core/Msi/Interop/MsiInterop.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs
index 054289ee..054289ee 100644
--- a/src/WixToolset.Core/Msi/Interop/MsiInterop.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs
diff --git a/src/WixToolset.Core/Msi/MsiException.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
index b33bf27a..b33bf27a 100644
--- a/src/WixToolset.Core/Msi/MsiException.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
diff --git a/src/WixToolset.Core/Msi/MsiHandle.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
index 6d2dc984..6d2dc984 100644
--- a/src/WixToolset.Core/Msi/MsiHandle.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
diff --git a/src/WixToolset.Core/Msi/Record.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
index 438aa3b0..438aa3b0 100644
--- a/src/WixToolset.Core/Msi/Record.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
diff --git a/src/WixToolset.Core/Msi/Session.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
index d3a19711..d3a19711 100644
--- a/src/WixToolset.Core/Msi/Session.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
diff --git a/src/WixToolset.Core/Msi/SummaryInformation.cs b/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
index 39949db6..26831731 100644
--- a/src/WixToolset.Core/Msi/SummaryInformation.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
@@ -3,11 +3,8 @@
3namespace WixToolset.Msi 3namespace WixToolset.Msi
4{ 4{
5 using System; 5 using System;
6 using System.ComponentModel;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization; 6 using System.Globalization;
9 using System.Text; 7 using System.Text;
10 using System.Runtime.InteropServices;
11 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; 8 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
12 using WixToolset.Core.Native; 9 using WixToolset.Core.Native;
13 10
@@ -245,79 +242,4 @@ namespace WixToolset.Msi
245 } 242 }
246 } 243 }
247 } 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
323} 245}
diff --git a/src/WixToolset.Core/Msi/View.cs b/src/WixToolset.Core.WindowsInstaller/Msi/View.cs
index d6542824..d6542824 100644
--- a/src/WixToolset.Core/Msi/View.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/View.cs
diff --git a/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
new file mode 100644
index 00000000..716ea000
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs b/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
new file mode 100644
index 00000000..268213d7
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
new file mode 100644
index 00000000..4b13258b
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
@@ -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
3namespace 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
diff --git a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs
new file mode 100644
index 00000000..2cb7da89
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs
@@ -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
3namespace 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
diff --git a/src/WixToolset.Core/Ole32/Storage.cs b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs
index c6a43bc4..c6a43bc4 100644
--- a/src/WixToolset.Core/Ole32/Storage.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs
diff --git a/src/WixToolset.Core/Patch.cs b/src/WixToolset.Core.WindowsInstaller/Patch.cs
index e3e6c27f..67150e32 100644
--- a/src/WixToolset.Core/Patch.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Patch.cs
@@ -9,35 +9,8 @@ namespace WixToolset.Data
9 using System.Globalization; 9 using System.Globalization;
10 using WixToolset.Data.Rows; 10 using WixToolset.Data.Rows;
11 using WixToolset.Extensibility; 11 using WixToolset.Extensibility;
12 using WixToolset.Msi;
13 using WixToolset.Core.Native; 12 using WixToolset.Core.Native;
14 13 using WixToolset.Msi;
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 }
41 14
42 /// <summary> 15 /// <summary>
43 /// Contains output tables and logic for building an MSP package. 16 /// Contains output tables and logic for building an MSP package.
@@ -80,8 +53,6 @@ namespace WixToolset.Data
80 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] 53 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
81 public void AttachTransforms(List<PatchTransform> transforms) 54 public void AttachTransforms(List<PatchTransform> transforms)
82 { 55 {
83 InspectorCore inspectorCore = new InspectorCore();
84
85 // Track if at least one transform gets attached. 56 // Track if at least one transform gets attached.
86 bool attachedTransform = false; 57 bool attachedTransform = false;
87 58
@@ -429,16 +400,6 @@ namespace WixToolset.Data
429 Row savedbyRow = patchSummaryInfo.CreateRow(null); 400 Row savedbyRow = patchSummaryInfo.CreateRow(null);
430 savedbyRow[0] = (int)SummaryInformation.Patch.TransformNames; 401 savedbyRow[0] = (int)SummaryInformation.Patch.TransformNames;
431 savedbyRow[1] = String.Join(";", (string[])transformNames.ToArray(typeof(string))); 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 }
442 } 403 }
443 404
444 /// <summary> 405 /// <summary>
diff --git a/src/WixToolset.Core/PatchAPI/PatchInterop.cs b/src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs
index ce749a33..fcd749d2 100644
--- a/src/WixToolset.Core/PatchAPI/PatchInterop.cs
+++ b/src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs
@@ -7,6 +7,7 @@ namespace WixToolset.PatchAPI
7 using System.Diagnostics.CodeAnalysis; 7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization; 8 using System.Globalization;
9 using System.Runtime.InteropServices; 9 using System.Runtime.InteropServices;
10 using WixToolset.Core;
10 11
11 /// <summary> 12 /// <summary>
12 /// Interop class for the mspatchc.dll. 13 /// Interop class for the mspatchc.dll.
@@ -323,20 +324,6 @@ namespace WixToolset.PatchAPI
323 internal const uint PATCH_OPTION_VALID_FLAGS = 0xC0FF0007; 324 internal const uint PATCH_OPTION_VALID_FLAGS = 0xC0FF0007;
324 325
325 // 326 //
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
339 //
340 // The following flags are used with PATCH_OPTION_DATA ExtendedOptionFlags: 327 // The following flags are used with PATCH_OPTION_DATA ExtendedOptionFlags:
341 // 328 //
342 329
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
new file mode 100644
index 00000000..229e75b4
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
new file mode 100644
index 00000000..208be874
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
new file mode 100644
index 00000000..f04dcefe
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
new file mode 100644
index 00000000..c0eda9c7
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Validator.cs b/src/WixToolset.Core.WindowsInstaller/Validator.cs
index 6420b9b7..db66f600 100644
--- a/src/WixToolset.Core/Validator.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Validator.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -12,8 +12,10 @@ namespace WixToolset
12 using System.Threading; 12 using System.Threading;
13 using WixToolset.Data; 13 using WixToolset.Data;
14 using WixToolset.Extensibility; 14 using WixToolset.Extensibility;
15 using WixToolset.Msi;
16 using WixToolset.Core.Native; 15 using WixToolset.Core.Native;
16 using WixToolset.Msi;
17 using System.Linq;
18 using System.Reflection;
17 19
18 /// <summary> 20 /// <summary>
19 /// Runs internal consistency evaluators (ICEs) from cub files against a database. 21 /// Runs internal consistency evaluators (ICEs) from cub files against a database.
@@ -23,9 +25,7 @@ namespace WixToolset
23 private string actionName; 25 private string actionName;
24 private StringCollection cubeFiles; 26 private StringCollection cubeFiles;
25 private ValidatorExtension extension; 27 private ValidatorExtension extension;
26 private string[] ices;
27 private Output output; 28 private Output output;
28 private string[] suppressedICEs;
29 private InstallUIHandler validationUIHandler; 29 private InstallUIHandler validationUIHandler;
30 private bool validationSessionComplete; 30 private bool validationSessionComplete;
31 31
@@ -54,11 +54,7 @@ namespace WixToolset
54 /// </summary> 54 /// </summary>
55 /// <value>The list of ICEs.</value> 55 /// <value>The list of ICEs.</value>
56 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] 56 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
57 public string[] ICEs 57 public ISet<string> ICEs { get; set; }
58 {
59 get { return this.ices; }
60 set { this.ices = value; }
61 }
62 58
63 /// <summary> 59 /// <summary>
64 /// Gets or sets the output used for finding source line information. 60 /// Gets or sets the output used for finding source line information.
@@ -76,16 +72,12 @@ namespace WixToolset
76 /// </summary> 72 /// </summary>
77 /// <value>The suppressed ICEs.</value> 73 /// <value>The suppressed ICEs.</value>
78 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] 74 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
79 public string[] SuppressedICEs 75 public ISet<string> SuppressedICEs { get; set; }
80 {
81 get { return this.suppressedICEs; }
82 set { this.suppressedICEs = value; }
83 }
84 76
85 /// <summary> 77 /// <summary>
86 /// Sets the temporary path for the Binder. 78 /// Sets the temporary path for the Binder.
87 /// </summary> 79 /// </summary>
88 public string TempFilesLocation { private get; set; } 80 public string IntermediateFolder { private get; set; }
89 81
90 /// <summary> 82 /// <summary>
91 /// Add a cube file to the validation run. 83 /// Add a cube file to the validation run.
@@ -103,8 +95,6 @@ namespace WixToolset
103 /// <returns>true if validation succeeded; false otherwise.</returns> 95 /// <returns>true if validation succeeded; false otherwise.</returns>
104 public void Validate(string databaseFile) 96 public void Validate(string databaseFile)
105 { 97 {
106 Dictionary<string, string> indexedICEs = new Dictionary<string, string>();
107 Dictionary<string, string> indexedSuppressedICEs = new Dictionary<string, string>();
108 int previousUILevel = (int)InstallUILevels.Basic; 98 int previousUILevel = (int)InstallUILevels.Basic;
109 IntPtr previousHwnd = IntPtr.Zero; 99 IntPtr previousHwnd = IntPtr.Zero;
110 InstallUIHandler previousUIHandler = null; 100 InstallUIHandler previousUIHandler = null;
@@ -120,28 +110,10 @@ namespace WixToolset
120 this.extension.InitializeValidator(); 110 this.extension.InitializeValidator();
121 111
122 // Ensure the temporary files can be created. 112 // Ensure the temporary files can be created.
123 Directory.CreateDirectory(this.TempFilesLocation); 113 Directory.CreateDirectory(this.IntermediateFolder);
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 }
142 114
143 // copy the database to a temporary location so it can be manipulated 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));
145 File.Copy(databaseFile, tempDatabaseFile); 117 File.Copy(databaseFile, tempDatabaseFile);
146 118
147 // remove the read-only property from the temporary database 119 // remove the read-only property from the temporary database
@@ -236,7 +208,7 @@ namespace WixToolset
236 208
237 string action = record.GetString(1); 209 string action = record.GetString(1);
238 210
239 if (!indexedSuppressedICEs.ContainsKey(action)) 211 if ((this.SuppressedICEs == null || !this.SuppressedICEs.Contains(action)) && (this.ICEs == null || this.ICEs.Contains(action)))
240 { 212 {
241 actions.Add(action); 213 actions.Add(action);
242 } 214 }
@@ -244,18 +216,6 @@ namespace WixToolset
244 } 216 }
245 } 217 }
246 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
259 // disable the internal UI handler and set an external UI handler 219 // disable the internal UI handler and set an external UI handler
260 previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd); 220 previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd);
261 previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero); 221 previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero);
@@ -370,6 +330,30 @@ namespace WixToolset
370 this.extension.OnMessage(e); 330 this.extension.OnMessage(e);
371 } 331 }
372 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
373 /// <summary> 357 /// <summary>
374 /// The validation external UI handler. 358 /// The validation external UI handler.
375 /// </summary> 359 /// </summary>
diff --git a/src/WixToolset.Core/Extensibility/ValidatorExtension.cs b/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
index 44ec3106..44ec3106 100644
--- a/src/WixToolset.Core/Extensibility/ValidatorExtension.cs
+++ b/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
diff --git a/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs b/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs
new file mode 100644
index 00000000..b66a4617
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj b/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
new file mode 100644
index 00000000..d74cb1e8
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
@@ -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>
diff --git a/src/WixToolset.Core/BackendContext.cs b/src/WixToolset.Core/BackendContext.cs
new file mode 100644
index 00000000..7166a3a3
--- /dev/null
+++ b/src/WixToolset.Core/BackendContext.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/DelayedField.cs b/src/WixToolset.Core/Bind/DelayedField.cs
index 181ac3e3..6c56f27c 100644
--- a/src/WixToolset.Core/Bind/DelayedField.cs
+++ b/src/WixToolset.Core/Bind/DelayedField.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Bind
4{ 4{
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using System.Text;
9 using WixToolset.Data; 5 using WixToolset.Data;
6 using WixToolset.Extensibility;
10 7
11 /// <summary> 8 /// <summary>
12 /// Structure used to hold a row and field that contain binder variables, which need to be resolved 9 /// Structure used to hold a row and field that contain binder variables, which need to be resolved
13 /// later, once the files have been resolved. 10 /// later, once the files have been resolved.
14 /// </summary> 11 /// </summary>
15 internal class DelayedField 12 internal class DelayedField : IDelayedField
16 { 13 {
17 /// <summary> 14 /// <summary>
18 /// Basic constructor for struct 15 /// Basic constructor for struct
@@ -28,11 +25,11 @@ namespace WixToolset.Bind
28 /// <summary> 25 /// <summary>
29 /// The row containing the field. 26 /// The row containing the field.
30 /// </summary> 27 /// </summary>
31 public Row Row { get; private set; } 28 public Row Row { get; }
32 29
33 /// <summary> 30 /// <summary>
34 /// The field needing further resolving. 31 /// The field needing further resolving.
35 /// </summary> 32 /// </summary>
36 public Field Field { get; private set; } 33 public Field Field { get; }
37 } 34 }
38} 35}
diff --git a/src/WixToolset.Core/Bind/ExpectedExtractFile.cs b/src/WixToolset.Core/Bind/ExpectedExtractFile.cs
new file mode 100644
index 00000000..fc2b43c7
--- /dev/null
+++ b/src/WixToolset.Core/Bind/ExpectedExtractFile.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs b/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs
index 0ecd0096..28fc4817 100644
--- a/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs
+++ b/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -13,11 +13,11 @@ namespace WixToolset.Bind
13 /// <summary> 13 /// <summary>
14 /// Internal helper class used to extract embedded files. 14 /// Internal helper class used to extract embedded files.
15 /// </summary> 15 /// </summary>
16 internal sealed class ExtractEmbeddedFiles 16 internal class ExtractEmbeddedFiles
17 { 17 {
18 private Dictionary<Uri, SortedList<int, string>> filesWithEmbeddedFiles = new Dictionary<Uri, SortedList<int, string>>(); 18 private Dictionary<Uri, SortedList<int, string>> filesWithEmbeddedFiles = new Dictionary<Uri, SortedList<int, string>>();
19 19
20 public IEnumerable<Uri> Uris { get { return this.filesWithEmbeddedFiles.Keys; } } 20 public IEnumerable<Uri> Uris => this.filesWithEmbeddedFiles.Keys;
21 21
22 /// <summary> 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. 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; 53 return extractPath;
54 } 54 }
55 55
56 public IEnumerable<ExtractFile> GetExtractFilesForUri(Uri uri) 56 public IEnumerable<ExpectedExtractFile> GetExpectedEmbeddedFiles()
57 { 57 {
58 SortedList<int, string> extracts; 58 foreach (var uriWithExtracts in filesWithEmbeddedFiles)
59 if (!filesWithEmbeddedFiles.TryGetValue(uri, out extracts)) 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))
60 { 75 {
61 extracts = new SortedList<int, string>(); 76 extracts = new SortedList<int, string>();
62 } 77 }
63 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 });
65 } 80 }
66 81
67 private string HashUri(string uri) 82 private string HashUri(string uri)
@@ -72,12 +87,5 @@ namespace WixToolset.Bind
72 return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_'); 87 return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_');
73 } 88 }
74 } 89 }
75
76 internal struct ExtractFile
77 {
78 public int EmbeddedFileIndex { get; set; }
79
80 public string OutputPath { get; set; }
81 }
82 } 90 }
83} 91}
diff --git a/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs b/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
index 68bfd8d7..7de40fb8 100644
--- a/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
+++ b/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Bind
4{ 4{
5 using System.Collections.Generic;
5 using System.IO; 6 using System.IO;
7 using System.Linq;
6 using System.Reflection; 8 using System.Reflection;
7 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Extensibility;
8 11
9 internal class ExtractEmbeddedFilesCommand : ICommand 12 public class ExtractEmbeddedFilesCommand
10 { 13 {
11 public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; } 14 public IEnumerable<IExpectedExtractFile> FilesWithEmbeddedFiles { private get; set; }
12 15
13 public void Execute() 16 public void Execute()
14 { 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)
16 { 21 {
22 var baseUri = expectedEmbeddedFileByUri.Key;
23
17 Stream stream = null; 24 Stream stream = null;
18 try 25 try
19 { 26 {
@@ -34,18 +41,20 @@ namespace WixToolset.Bind
34 41
35 using (FileStructure fs = FileStructure.Read(stream)) 42 using (FileStructure fs = FileStructure.Read(stream))
36 { 43 {
37 foreach (var embeddedFile in this.FilesWithEmbeddedFiles.GetExtractFilesForUri(baseUri)) 44 var uniqueIndicies = new SortedSet<int>();
45
46 foreach (var embeddedFile in expectedEmbeddedFileByUri)
38 { 47 {
39 fs.ExtractEmbeddedFile(embeddedFile.EmbeddedFileIndex, embeddedFile.OutputPath); 48 if (uniqueIndicies.Add(embeddedFile.EmbeddedFileIndex))
49 {
50 fs.ExtractEmbeddedFile(embeddedFile.EmbeddedFileIndex, embeddedFile.OutputPath);
51 }
40 } 52 }
41 } 53 }
42 } 54 }
43 finally 55 finally
44 { 56 {
45 if (null != stream) 57 stream?.Close();
46 {
47 stream.Close();
48 }
49 } 58 }
50 } 59 }
51 } 60 }
diff --git a/src/WixToolset.Core/Bind/Databases/FileFacade.cs b/src/WixToolset.Core/Bind/FileFacade.cs
index 37115c97..aaa6b7d3 100644
--- a/src/WixToolset.Core/Bind/Databases/FileFacade.cs
+++ b/src/WixToolset.Core/Bind/FileFacade.cs
@@ -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. 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 2
3namespace WixToolset.Bind.Databases 3namespace WixToolset.Core.Bind
4{ 4{
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using WixToolset.Data; 6 using WixToolset.Data;
diff --git a/src/WixToolset.Core/Bind/FileResolver.cs b/src/WixToolset.Core/Bind/FileResolver.cs
new file mode 100644
index 00000000..8d624e6f
--- /dev/null
+++ b/src/WixToolset.Core/Bind/FileResolver.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/FileTransfer.cs b/src/WixToolset.Core/Bind/FileTransfer.cs
deleted file mode 100644
index 64bbc5f1..00000000
--- a/src/WixToolset.Core/Bind/FileTransfer.cs
+++ /dev/null
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
index 4ffe9e82..15365c2a 100644
--- a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
+++ b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Globalization; 7 using System.Globalization;
8 using System.Linq;
9 using System.Text;
10 using WixToolset.Data; 8 using WixToolset.Data;
9 using WixToolset.Extensibility;
11 10
12 /// <summary> 11 /// <summary>
13 /// Resolves the fields which had variables that needed to be resolved after the file information 12 /// Resolves the fields which had variables that needed to be resolved after the file information
14 /// was loaded. 13 /// was loaded.
15 /// </summary> 14 /// </summary>
16 internal class ResolveDelayedFieldsCommand : ICommand 15 public class ResolveDelayedFieldsCommand : ICommand
17 { 16 {
18 public OutputType OutputType { private get; set;} 17 public OutputType OutputType { private get; set;}
19 18
20 public IEnumerable<DelayedField> DelayedFields { private get; set;} 19 public IEnumerable<IDelayedField> DelayedFields { private get; set;}
21 20
22 public IDictionary<string, string> VariableCache { private get; set; } 21 public IDictionary<string, string> VariableCache { private get; set; }
23 22
@@ -29,9 +28,9 @@ namespace WixToolset.Bind
29 /// <param name="modularizationGuid">The modularization guid (used in case of a merge module).</param> 28 /// <param name="modularizationGuid">The modularization guid (used in case of a merge module).</param>
30 public void Execute() 29 public void Execute()
31 { 30 {
32 List<DelayedField> deferredFields = new List<DelayedField>(); 31 var deferredFields = new List<IDelayedField>();
33 32
34 foreach (DelayedField delayedField in this.DelayedFields) 33 foreach (IDelayedField delayedField in this.DelayedFields)
35 { 34 {
36 try 35 try
37 { 36 {
@@ -43,7 +42,7 @@ namespace WixToolset.Bind
43 string value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, (string)delayedField.Field.Data, this.VariableCache); 42 string value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, (string)delayedField.Field.Data, this.VariableCache);
44 43
45 // update the variable cache with the new value 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]));
47 this.VariableCache[key] = value; 46 this.VariableCache[key] = value;
48 47
49 // update the field data 48 // update the field data
diff --git a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
index 4caec9b4..f4f4f9e8 100644
--- a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
+++ b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Bind
4{ 4{
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using WixToolset.Data; 6 using WixToolset.Data;
7 using WixToolset.Data.Bind;
7 using WixToolset.Extensibility; 8 using WixToolset.Extensibility;
8 9
9 /// <summary> 10 /// <summary>
10 /// Resolve source fields in the tables included in the output 11 /// Resolve source fields in the tables included in the output
11 /// </summary> 12 /// </summary>
12 internal class ResolveFieldsCommand : ICommand 13 internal class ResolveFieldsCommand
13 { 14 {
14 public TableIndexedCollection Tables { private get; set; } 15 public bool BuildingPatch { private get; set; }
15 16
16 public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; } 17 public IBindVariableResolver BindVariableResolver { private get; set; }
17 18
18 public BinderFileManagerCore FileManagerCore { private get; set; } 19 public IEnumerable<BindPath> BindPaths { private get; set; }
19 20
20 public IEnumerable<IBinderFileManager> FileManagers { private get; set; } 21 public IEnumerable<IBinderExtension> Extensions { private get; set; }
21 22
22 public bool SupportDelayedResolution { private get; set; } 23 public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; }
24
25 public string IntermediateFolder { private get; set; }
23 26
24 public string TempFilesLocation { private get; set; } 27 public TableIndexedCollection Tables { private get; set; }
25 28
26 public WixVariableResolver WixVariableResolver { private get; set; } 29 public bool SupportDelayedResolution { private get; set; }
27 30
28 public IEnumerable<DelayedField> DelayedFields { get; private set; } 31 public IEnumerable<DelayedField> DelayedFields { get; private set; }
29 32
@@ -31,6 +34,8 @@ namespace WixToolset.Bind
31 { 34 {
32 List<DelayedField> delayedFields = this.SupportDelayedResolution ? new List<DelayedField>() : null; 35 List<DelayedField> delayedFields = this.SupportDelayedResolution ? new List<DelayedField>() : null;
33 36
37 var fileResolver = new FileResolver(this.BindPaths, this.Extensions);
38
34 foreach (Table table in this.Tables) 39 foreach (Table table in this.Tables)
35 { 40 {
36 foreach (Row row in table.Rows) 41 foreach (Row row in table.Rows)
@@ -46,7 +51,7 @@ namespace WixToolset.Bind
46 // resolve localization and wix variables 51 // resolve localization and wix variables
47 if (field.Data is string) 52 if (field.Data is string)
48 { 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);
50 if (delayedResolve) 55 if (delayedResolve)
51 { 56 {
52 delayedFields.Add(new DelayedField(row, field)); 57 delayedFields.Add(new DelayedField(row, field));
@@ -74,7 +79,7 @@ namespace WixToolset.Bind
74 // File is embedded and path to it was not modified above. 79 // File is embedded and path to it was not modified above.
75 if (objectField.EmbeddedFileIndex.HasValue && isDefault) 80 if (objectField.EmbeddedFileIndex.HasValue && isDefault)
76 { 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);
78 83
79 // Set the path to the embedded file once where it will be extracted. 84 // Set the path to the embedded file once where it will be extracted.
80 objectField.Data = extractPath; 85 objectField.Data = extractPath;
@@ -83,7 +88,7 @@ namespace WixToolset.Bind
83 { 88 {
84 try 89 try
85 { 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)
87 { 92 {
88 // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file 93 // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file
89 if (null == objectField.UnresolvedData) 94 if (null == objectField.UnresolvedData)
@@ -92,12 +97,12 @@ namespace WixToolset.Bind
92 } 97 }
93 98
94 // resolve the path to the file 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);
96 } 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)
98 { 103 {
99 // resolve the path to the file 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);
101 } 106 }
102 else // Re-base binding path scenario caused by pyro.exe -bt -bu 107 else // Re-base binding path scenario caused by pyro.exe -bt -bu
103 { 108 {
@@ -106,7 +111,7 @@ namespace WixToolset.Bind
106 111
107 // if -bu is used in pyro command, this condition holds true and the tool 112 // if -bu is used in pyro command, this condition holds true and the tool
108 // will use pre-resolved source for new wixpdb file 113 // will use pre-resolved source for new wixpdb file
109 if (this.FileManagerCore.RebaseUpdated) 114 if (fileResolver.RebaseUpdated)
110 { 115 {
111 // try to use the unResolved Source if it exists. 116 // try to use the unResolved Source if it exists.
112 // New version of wixpdb file keeps a copy of pre-resolved Source. i.e. !(bindpath.test)\foo.dll 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
117 } 122 }
118 } 123 }
119 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);
121 } 126 }
122 } 127 }
123 catch (WixFileNotFoundException) 128 catch (WixFileNotFoundException)
@@ -127,10 +132,10 @@ namespace WixToolset.Bind
127 } 132 }
128 } 133 }
129 134
130 isDefault = true;
131 if (null != objectField.PreviousData) 135 if (null != objectField.PreviousData)
132 { 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
134 if (!Messaging.Instance.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field. 139 if (!Messaging.Instance.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field.
135 { 140 {
136 // file is compressed in a cabinet (and not modified above) 141 // file is compressed in a cabinet (and not modified above)
@@ -142,7 +147,7 @@ namespace WixToolset.Bind
142 objectField.PreviousBaseUri = objectField.BaseUri; 147 objectField.PreviousBaseUri = objectField.BaseUri;
143 } 148 }
144 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);
146 151
147 // set the path to the file once its extracted from the cabinet 152 // set the path to the file once its extracted from the cabinet
148 objectField.PreviousData = extractPath; 153 objectField.PreviousData = extractPath;
@@ -151,14 +156,14 @@ namespace WixToolset.Bind
151 { 156 {
152 try 157 try
153 { 158 {
154 if (!this.FileManagerCore.RebaseTarget && !this.FileManagerCore.RebaseUpdated) 159 if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated)
155 { 160 {
156 // resolve the path to the file 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);
158 } 163 }
159 else 164 else
160 { 165 {
161 if (this.FileManagerCore.RebaseTarget) 166 if (fileResolver.RebaseTarget)
162 { 167 {
163 // if -bt is used, it come here 168 // if -bt is used, it come here
164 // Try to use the original unresolved source from either target build or update build 169 // Try to use the original unresolved source from either target build or update build
@@ -172,7 +177,7 @@ namespace WixToolset.Bind
172 } 177 }
173 178
174 // resolve the path to the file 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);
176 181
177 } 182 }
178 } 183 }
@@ -192,24 +197,28 @@ namespace WixToolset.Bind
192 this.DelayedFields = delayedFields; 197 this.DelayedFields = delayedFields;
193 } 198 }
194 199
200#if false
195 private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage = BindStage.Normal) 201 private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage = BindStage.Normal)
196 { 202 {
197 string path = null; 203 string path = null;
198 foreach (IBinderFileManager fileManager in this.FileManagers) 204 foreach (var extension in this.Extensions)
199 { 205 {
200 path = fileManager.ResolveFile(source, type, sourceLineNumbers, bindStage); 206 path = extension.ResolveFile(source, type, sourceLineNumbers, bindStage);
201 if (null != path) 207 if (null != path)
202 { 208 {
203 break; 209 break;
204 } 210 }
205 } 211 }
206 212
207 if (null == path) 213 throw new NotImplementedException(); // need to do default binder stuff
208 { 214
209 throw new WixFileNotFoundException(sourceLineNumbers, source, type); 215 //if (null == path)
210 } 216 //{
217 // throw new WixFileNotFoundException(sourceLineNumbers, source, type);
218 //}
211 219
212 return path; 220 //return path;
213 } 221 }
222#endif
214 } 223 }
215} 224}
diff --git a/src/WixToolset.Core/Bind/ResolveResult.cs b/src/WixToolset.Core/Bind/ResolveResult.cs
new file mode 100644
index 00000000..13f25054
--- /dev/null
+++ b/src/WixToolset.Core/Bind/ResolveResult.cs
@@ -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
3namespace 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
diff --git a/src/WixToolset.Core/Bind/ResolvedDirectory.cs b/src/WixToolset.Core/Bind/ResolvedDirectory.cs
index 6985f95d..fca706d8 100644
--- a/src/WixToolset.Core/Bind/ResolvedDirectory.cs
+++ b/src/WixToolset.Core/Bind/ResolvedDirectory.cs
@@ -5,7 +5,7 @@ namespace WixToolset.Bind
5 /// <summary> 5 /// <summary>
6 /// Structure used for resolved directory information. 6 /// Structure used for resolved directory information.
7 /// </summary> 7 /// </summary>
8 internal struct ResolvedDirectory 8 public struct ResolvedDirectory
9 { 9 {
10 /// <summary>The directory parent.</summary> 10 /// <summary>The directory parent.</summary>
11 public string DirectoryParent; 11 public string DirectoryParent;
diff --git a/src/WixToolset.Core/Bind/TransferFilesCommand.cs b/src/WixToolset.Core/Bind/TransferFilesCommand.cs
index 719b8b20..f116569c 100644
--- a/src/WixToolset.Core/Bind/TransferFilesCommand.cs
+++ b/src/WixToolset.Core/Bind/TransferFilesCommand.cs
@@ -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. 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 2
3namespace WixToolset.Bind 3namespace WixToolset.Core.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Security.AccessControl; 8 using System.Security.AccessControl;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Bind;
10 using WixToolset.Extensibility; 11 using WixToolset.Extensibility;
11 12
12 internal class TransferFilesCommand : ICommand 13 internal class TransferFilesCommand
13 { 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; }
15 23
16 public IEnumerable<FileTransfer> FileTransfers { private get; set; } 24 private IEnumerable<FileTransfer> FileTransfers { get; }
17 25
18 public bool SuppressAclReset { private get; set; } 26 private bool SuppressAclReset { get; }
19 27
20 public void Execute() 28 public void Execute()
21 { 29 {
22 List<string> destinationFiles = new List<string>(); 30 List<string> destinationFiles = new List<string>();
23 31
24 foreach (FileTransfer fileTransfer in this.FileTransfers) 32 foreach (var fileTransfer in this.FileTransfers)
25 { 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);
27 35
28 // If the source and destination are identical, then there's nothing to do here 36 // If the source and destination are identical, then there's nothing to do here
29 if (0 == String.Compare(fileSource, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase)) 37 if (0 == String.Compare(fileSource, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase))
@@ -165,44 +173,17 @@ namespace WixToolset.Bind
165 } 173 }
166 } 174 }
167 175
168 private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) 176 private void TransferFile(bool move, string source, string destination)
169 { 177 {
170 string path = null; 178 bool complete = false;
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 }
179 179
180 if (null == path) 180 if (move)
181 { 181 {
182 throw new WixFileNotFoundException(sourceLineNumbers, source, type); 182 complete = this.FileResolver.MoveFile(source, destination, true);
183 } 183 }
184 184 else
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)
192 { 185 {
193 if (move) 186 complete = this.FileResolver.CopyFile(source, destination, true);
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 }
206 } 187 }
207 188
208 if (!complete) 189 if (!complete)
diff --git a/src/WixToolset.Core/BindContext.cs b/src/WixToolset.Core/BindContext.cs
new file mode 100644
index 00000000..499f3245
--- /dev/null
+++ b/src/WixToolset.Core/BindContext.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Binder.cs b/src/WixToolset.Core/Binder.cs
index 18ad2d62..43c15634 100644
--- a/src/WixToolset.Core/Binder.cs
+++ b/src/WixToolset.Core/Binder.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
@@ -11,105 +11,72 @@ namespace WixToolset
11 using System.Linq; 11 using System.Linq;
12 using System.Reflection; 12 using System.Reflection;
13 using WixToolset.Bind; 13 using WixToolset.Bind;
14 using WixToolset.Core.Bind;
14 using WixToolset.Data; 15 using WixToolset.Data;
16 using WixToolset.Data.Bind;
15 using WixToolset.Data.Rows; 17 using WixToolset.Data.Rows;
16 using WixToolset.Extensibility; 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 }
63 19
64 /// <summary> 20 /// <summary>
65 /// Binder of the WiX toolset. 21 /// Binder of the WiX toolset.
66 /// </summary> 22 /// </summary>
67 public sealed class Binder 23 public sealed class Binder
68 { 24 {
69 private BinderCore core; 25 //private BinderCore core;
70 private BinderFileManagerCore fileManagerCore; 26 //private List<IBinderExtension> extensions;
71 private List<IBinderExtension> extensions; 27 //private List<IBinderFileManager> fileManagers;
72 private List<IBinderFileManager> fileManagers;
73 private List<InspectorExtension> inspectorExtensions;
74 28
75 public Binder() 29 public Binder()
76 { 30 {
77 this.DefaultCompressionLevel = CompressionLevel.High; 31 //this.DefaultCompressionLevel = CompressionLevel.High;
78 32
79 this.BindPaths = new List<BindPath>(); 33 //this.BindPaths = new List<BindPath>();
80 this.TargetBindPaths = new List<BindPath>(); 34 //this.TargetBindPaths = new List<BindPath>();
81 this.UpdatedBindPaths = new List<BindPath>(); 35 //this.UpdatedBindPaths = new List<BindPath>();
82 36
83 this.extensions = new List<IBinderExtension>(); 37 //this.extensions = new List<IBinderExtension>();
84 this.fileManagers = new List<IBinderFileManager>(); 38 //this.fileManagers = new List<IBinderFileManager>();
85 this.inspectorExtensions = new List<InspectorExtension>(); 39 //this.inspectorExtensions = new List<InspectorExtension>();
86 40
87 this.Ices = new List<string>(); 41 //this.Ices = new List<string>();
88 this.SuppressIces = new List<string>(); 42 //this.SuppressIces = new List<string>();
89 } 43 }
90 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; }
92 57
93 public string OutputsFile { private get; set; } 58 //public string ContentsFile { private get; set; }
94 59
95 public string BuiltOutputsFile { private get; set; } 60 //public string OutputsFile { private get; set; }
96 61
97 public string WixprojectFile { private get; set; } 62 //public string BuiltOutputsFile { private get; set; }
63
64 //public string WixprojectFile { private get; set; }
98 65
99 /// <summary> 66 /// <summary>
100 /// Gets the list of bindpaths. 67 /// Gets the list of bindpaths.
101 /// </summary> 68 /// </summary>
102 public List<BindPath> BindPaths { get; private set; } 69 //public List<BindPath> BindPaths { get; private set; }
103 70
104 /// <summary> 71 /// <summary>
105 /// Gets the list of target bindpaths. 72 /// Gets the list of target bindpaths.
106 /// </summary> 73 /// </summary>
107 public List<BindPath> TargetBindPaths { get; private set; } 74 //public List<BindPath> TargetBindPaths { get; private set; }
108 75
109 /// <summary> 76 /// <summary>
110 /// Gets the list of updated bindpaths. 77 /// Gets the list of updated bindpaths.
111 /// </summary> 78 /// </summary>
112 public List<BindPath> UpdatedBindPaths { get; private set; } 79 //public List<BindPath> UpdatedBindPaths { get; private set; }
113 80
114 /// <summary> 81 /// <summary>
115 /// Gets or sets the option to enable building binary delta patches. 82 /// Gets or sets the option to enable building binary delta patches.
@@ -132,17 +99,17 @@ namespace WixToolset
132 /// Gets or sets the default compression level to use for cabinets 99 /// Gets or sets the default compression level to use for cabinets
133 /// that don't have their compression level explicitly set. 100 /// that don't have their compression level explicitly set.
134 /// </summary> 101 /// </summary>
135 public CompressionLevel DefaultCompressionLevel { get; set; } 102 //public CompressionLevel DefaultCompressionLevel { get; set; }
136 103
137 /// <summary> 104 /// <summary>
138 /// Gets and sets the location to save the WixPdb. 105 /// Gets and sets the location to save the WixPdb.
139 /// </summary> 106 /// </summary>
140 /// <value>The location in which to save the WixPdb. Null if the the WixPdb should not be output.</value> 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; }
142 109
143 public List<string> Ices { get; private set; } 110 //public List<string> Ices { get; private set; }
144 111
145 public List<string> SuppressIces { get; private set; } 112 //public List<string> SuppressIces { get; private set; }
146 113
147 /// <summary> 114 /// <summary>
148 /// Gets and sets the option to suppress resetting ACLs by the binder. 115 /// Gets and sets the option to suppress resetting ACLs by the binder.
@@ -185,24 +152,164 @@ namespace WixToolset
185 /// Gets or sets the Wix variable resolver. 152 /// Gets or sets the Wix variable resolver.
186 /// </summary> 153 /// </summary>
187 /// <value>The Wix variable resolver.</value> 154 /// <value>The Wix variable resolver.</value>
188 public WixVariableResolver WixVariableResolver { get; set; } 155 internal WixVariableResolver WixVariableResolver { get; set; }
189 156
190 /// <summary> 157 /// <summary>
191 /// Add a binder extension. 158 /// Add a binder extension.
192 /// </summary> 159 /// </summary>
193 /// <param name="extension">New extension.</param> 160 /// <param name="extension">New extension.</param>
194 public void AddExtension(IBinderExtension extension) 161 //public void AddExtension(IBinderExtension extension)
195 { 162 //{
196 this.extensions.Add(extension); 163 // this.extensions.Add(extension);
197 } 164 //}
198 165
199 /// <summary> 166 /// <summary>
200 /// Add a file manager extension. 167 /// Add a file manager extension.
201 /// </summary> 168 /// </summary>
202 /// <param name="extension">New file manager.</param> 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)
204 { 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 }
206 } 313 }
207 314
208 /// <summary> 315 /// <summary>
@@ -212,6 +319,7 @@ namespace WixToolset
212 /// <param name="file">The Windows Installer file to create.</param> 319 /// <param name="file">The Windows Installer file to create.</param>
213 /// <remarks>The Binder.DeleteTempFiles method should be called after calling this method.</remarks> 320 /// <remarks>The Binder.DeleteTempFiles method should be called after calling this method.</remarks>
214 /// <returns>true if binding completed successfully; false otherwise</returns> 321 /// <returns>true if binding completed successfully; false otherwise</returns>
322#if false
215 public bool Bind(Output output, string file) 323 public bool Bind(Output output, string file)
216 { 324 {
217 // Ensure the cabinet cache path exists if we are going to use it. 325 // Ensure the cabinet cache path exists if we are going to use it.
@@ -220,20 +328,20 @@ namespace WixToolset
220 Directory.CreateDirectory(this.CabCachePath); 328 Directory.CreateDirectory(this.CabCachePath);
221 } 329 }
222 330
223 this.fileManagerCore = new BinderFileManagerCore(); 331 //var fileManagerCore = new BinderFileManagerCore();
224 this.fileManagerCore.CabCachePath = this.CabCachePath; 332 //fileManagerCore.CabCachePath = this.CabCachePath;
225 this.fileManagerCore.Output = output; 333 //fileManagerCore.Output = output;
226 this.fileManagerCore.TempFilesLocation = this.TempFilesLocation; 334 //fileManagerCore.TempFilesLocation = this.TempFilesLocation;
227 this.fileManagerCore.AddBindPaths(this.BindPaths, BindStage.Normal); 335 //fileManagerCore.AddBindPaths(this.BindPaths, BindStage.Normal);
228 this.fileManagerCore.AddBindPaths(this.TargetBindPaths, BindStage.Target); 336 //fileManagerCore.AddBindPaths(this.TargetBindPaths, BindStage.Target);
229 this.fileManagerCore.AddBindPaths(this.UpdatedBindPaths, BindStage.Updated); 337 //fileManagerCore.AddBindPaths(this.UpdatedBindPaths, BindStage.Updated);
230 foreach (IBinderFileManager fileManager in this.fileManagers) 338 //foreach (IBinderFileManager fileManager in this.fileManagers)
231 { 339 //{
232 fileManager.Core = this.fileManagerCore; 340 // fileManager.Core = fileManagerCore;
233 } 341 //}
234 342
235 this.core = new BinderCore(); 343 this.core = new BinderCore();
236 this.core.FileManagerCore = this.fileManagerCore; 344 this.core.FileManagerCore = fileManagerCore;
237 345
238 this.WriteBuildInfoTable(output, file); 346 this.WriteBuildInfoTable(output, file);
239 347
@@ -246,54 +354,69 @@ namespace WixToolset
246 } 354 }
247 355
248 // Gather all the wix variables. 356 // Gather all the wix variables.
249 Table wixVariableTable = output.Tables["WixVariable"]; 357 //Table wixVariableTable = output.Tables["WixVariable"];
250 if (null != wixVariableTable) 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)
251 { 385 {
252 foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows) 386 if (factory.TryCreateBackend(output.Type.ToString(), file, null, out var backend))
253 { 387 {
254 this.WixVariableResolver.AddVariable(wixVariableRow); 388 result = backend.Bind(context);
389 break;
255 } 390 }
256 } 391 }
257 392
258 IEnumerable<FileTransfer> fileTransfers = null; 393 if (result == null)
259 IEnumerable<string> contentPaths = null;
260
261 switch (output.Type)
262 { 394 {
263 case OutputType.Bundle: 395 // TODO: messaging that a backend could not be found to bind the output type?
264 this.BindBundle(output, file, out fileTransfers, out contentPaths);
265 break;
266
267 case OutputType.Transform:
268 this.BindTransform(output, file);
269 break;
270 396
271 default: 397 return false;
272 this.BindDatabase(output, file, out fileTransfers, out contentPaths);
273 break;
274 } 398 }
275 399
276
277 // Layout media 400 // Layout media
278 try 401 try
279 { 402 {
280 this.LayoutMedia(fileTransfers); 403 this.LayoutMedia(result.FileTransfers);
281 } 404 }
282 finally 405 finally
283 { 406 {
284 if (!String.IsNullOrEmpty(this.ContentsFile) && contentPaths != null) 407 if (!String.IsNullOrEmpty(this.ContentsFile) && result.ContentFilePaths != null)
285 { 408 {
286 this.CreateContentsFile(this.ContentsFile, contentPaths); 409 this.CreateContentsFile(this.ContentsFile, result.ContentFilePaths);
287 } 410 }
288 411
289 if (!String.IsNullOrEmpty(this.OutputsFile) && fileTransfers != null) 412 if (!String.IsNullOrEmpty(this.OutputsFile) && result.FileTransfers != null)
290 { 413 {
291 this.CreateOutputsFile(this.OutputsFile, fileTransfers, this.PdbFile); 414 this.CreateOutputsFile(this.OutputsFile, result.FileTransfers, this.PdbFile);
292 } 415 }
293 416
294 if (!String.IsNullOrEmpty(this.BuiltOutputsFile) && fileTransfers != null) 417 if (!String.IsNullOrEmpty(this.BuiltOutputsFile) && result.FileTransfers != null)
295 { 418 {
296 this.CreateBuiltOutputsFile(this.BuiltOutputsFile, fileTransfers, this.PdbFile); 419 this.CreateBuiltOutputsFile(this.BuiltOutputsFile, result.FileTransfers, this.PdbFile);
297 } 420 }
298 } 421 }
299 422
@@ -301,6 +424,7 @@ namespace WixToolset
301 424
302 return Messaging.Instance.EncounteredError; 425 return Messaging.Instance.EncounteredError;
303 } 426 }
427#endif
304 428
305 /// <summary> 429 /// <summary>
306 /// Does any housekeeping after Bind. 430 /// Does any housekeeping after Bind.
@@ -312,12 +436,12 @@ namespace WixToolset
312 { 436 {
313 if (!this.DeleteTempFiles()) 437 if (!this.DeleteTempFiles())
314 { 438 {
315 this.core.OnMessage(WixWarnings.FailedToDeleteTempDir(this.TempFilesLocation)); 439 this.Context.Messaging.OnMessage(WixWarnings.FailedToDeleteTempDir(this.TempFilesLocation));
316 } 440 }
317 } 441 }
318 else 442 else
319 { 443 {
320 this.core.OnMessage(WixVerboses.BinderTempDirLocatedAt(this.TempFilesLocation)); 444 this.Context.Messaging.OnMessage(WixVerboses.BinderTempDirLocatedAt(this.TempFilesLocation));
321 } 445 }
322 } 446 }
323 447
@@ -327,7 +451,7 @@ namespace WixToolset
327 /// <returns>True if all files were deleted, false otherwise.</returns> 451 /// <returns>True if all files were deleted, false otherwise.</returns>
328 private bool DeleteTempFiles() 452 private bool DeleteTempFiles()
329 { 453 {
330 bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this.core); 454 bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this.Context.Messaging);
331 return deleted; 455 return deleted;
332 } 456 }
333 457
@@ -338,7 +462,7 @@ namespace WixToolset
338 /// <param name="databaseFile">The output file if OutputFile not set.</param> 462 /// <param name="databaseFile">The output file if OutputFile not set.</param>
339 private void WriteBuildInfoTable(Output output, string outputFile) 463 private void WriteBuildInfoTable(Output output, string outputFile)
340 { 464 {
341 Table buildInfoTable = output.EnsureTable(this.core.TableDefinitions["WixBuildInfo"]); 465 Table buildInfoTable = output.EnsureTable(this.TableDefinitions["WixBuildInfo"]);
342 Row buildInfoRow = buildInfoTable.CreateRow(null); 466 Row buildInfoRow = buildInfoTable.CreateRow(null);
343 467
344 Assembly executingAssembly = Assembly.GetExecutingAssembly(); 468 Assembly executingAssembly = Assembly.GetExecutingAssembly();
@@ -346,17 +470,18 @@ namespace WixToolset
346 buildInfoRow[0] = fileVersion.FileVersion; 470 buildInfoRow[0] = fileVersion.FileVersion;
347 buildInfoRow[1] = outputFile; 471 buildInfoRow[1] = outputFile;
348 472
349 if (!String.IsNullOrEmpty(this.WixprojectFile)) 473 if (!String.IsNullOrEmpty(this.Context.WixprojectFile))
350 { 474 {
351 buildInfoRow[2] = this.WixprojectFile; 475 buildInfoRow[2] = this.Context.WixprojectFile;
352 } 476 }
353 477
354 if (!String.IsNullOrEmpty(this.PdbFile)) 478 if (!String.IsNullOrEmpty(this.Context.OutputPdbPath))
355 { 479 {
356 buildInfoRow[3] = this.PdbFile; 480 buildInfoRow[3] = this.Context.OutputPdbPath;
357 } 481 }
358 } 482 }
359 483
484#if DELETE_THIS_CODE
360 /// <summary> 485 /// <summary>
361 /// Binds a bundle. 486 /// Binds a bundle.
362 /// </summary> 487 /// </summary>
@@ -454,6 +579,7 @@ namespace WixToolset
454 command.OutputPath = outputPath; 579 command.OutputPath = outputPath;
455 command.Execute(); 580 command.Execute();
456 } 581 }
582#endif
457 583
458 /// <summary> 584 /// <summary>
459 /// Final step in binding that transfers (moves/copies) all files generated into the appropriate 585 /// Final step in binding that transfers (moves/copies) all files generated into the appropriate
@@ -464,12 +590,9 @@ namespace WixToolset
464 { 590 {
465 if (null != transfers && transfers.Any()) 591 if (null != transfers && transfers.Any())
466 { 592 {
467 this.core.OnMessage(WixVerboses.LayingOutMedia()); 593 this.Context.Messaging.OnMessage(WixVerboses.LayingOutMedia());
468 594
469 TransferFilesCommand command = new TransferFilesCommand(); 595 var command = new TransferFilesCommand(this.Context.BindPaths, this.Context.Extensions, transfers, this.Context.SuppressAclReset);
470 command.FileManagers = this.fileManagers;
471 command.FileTransfers = transfers;
472 command.SuppressAclReset = this.SuppressAclReset;
473 command.Execute(); 596 command.Execute();
474 } 597 }
475 } 598 }
@@ -482,7 +605,7 @@ namespace WixToolset
482 /// <param name="directory">Directory identifier.</param> 605 /// <param name="directory">Directory identifier.</param>
483 /// <param name="canonicalize">Canonicalize the path for standard directories.</param> 606 /// <param name="canonicalize">Canonicalize the path for standard directories.</param>
484 /// <returns>Source path of a directory.</returns> 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)
486 { 609 {
487 if (!directories.Contains(directory)) 610 if (!directories.Contains(directory))
488 { 611 {
@@ -543,9 +666,9 @@ namespace WixToolset
543 /// <param name="compressed">Specifies the package is compressed.</param> 666 /// <param name="compressed">Specifies the package is compressed.</param>
544 /// <param name="useLongName">Specifies the package uses long file names.</param> 667 /// <param name="useLongName">Specifies the package uses long file names.</param>
545 /// <returns>Source path of file relative to package directory.</returns> 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)
547 { 670 {
548 string fileSourcePath = Installer.GetName(fileName, true, useLongName); 671 string fileSourcePath = Common.GetName(fileName, true, useLongName);
549 672
550 if (compressed) 673 if (compressed)
551 { 674 {
diff --git a/src/WixToolset.Core/BinderFileManager.cs b/src/WixToolset.Core/BinderFileManager.cs
index 0da54002..1527d93d 100644
--- a/src/WixToolset.Core/BinderFileManager.cs
+++ b/src/WixToolset.Core/BinderFileManager.cs
@@ -12,6 +12,7 @@ namespace WixToolset
12 using WixToolset.Data.Rows; 12 using WixToolset.Data.Rows;
13 using WixToolset.Extensibility; 13 using WixToolset.Extensibility;
14 14
15#if false
15 /// <summary> 16 /// <summary>
16 /// Base class for creating a binder file manager. 17 /// Base class for creating a binder file manager.
17 /// </summary> 18 /// </summary>
@@ -367,4 +368,5 @@ namespace WixToolset
367 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 368 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
368 private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes); 369 private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
369 } 370 }
371#endif
370} 372}
diff --git a/src/WixToolset.Core/BinderFileManagerCore.cs b/src/WixToolset.Core/BinderFileManagerCore.cs
index 6a5e1d5e..f1a78880 100644
--- a/src/WixToolset.Core/BinderFileManagerCore.cs
+++ b/src/WixToolset.Core/BinderFileManagerCore.cs
@@ -6,6 +6,7 @@ namespace WixToolset
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq; 7 using System.Linq;
8 using WixToolset.Data; 8 using WixToolset.Data;
9 using WixToolset.Data.Bind;
9 using WixToolset.Extensibility; 10 using WixToolset.Extensibility;
10 11
11 public class BinderFileManagerCore : IBinderFileManagerCore 12 public class BinderFileManagerCore : IBinderFileManagerCore
diff --git a/src/WixToolset.Core/Cab/CabinetFileInfo.cs b/src/WixToolset.Core/Cab/CabinetFileInfo.cs
index 849bb3bb..816f9e3e 100644
--- a/src/WixToolset.Core/Cab/CabinetFileInfo.cs
+++ b/src/WixToolset.Core/Cab/CabinetFileInfo.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core.Cab
4{ 4{
5 using System;
6
7 /// <summary> 5 /// <summary>
8 /// Properties of a file in a cabinet. 6 /// Properties of a file in a cabinet.
9 /// </summary> 7 /// </summary>
10 internal sealed class CabinetFileInfo 8 public sealed class CabinetFileInfo
11 { 9 {
12 private string fileId;
13 private ushort date;
14 private ushort time;
15 private int size;
16
17 /// <summary> 10 /// <summary>
18 /// Constructs CabinetFileInfo 11 /// Constructs CabinetFileInfo
19 /// </summary> 12 /// </summary>
@@ -22,43 +15,31 @@ namespace WixToolset
22 /// <param name="time">Last modified time (MS-DOS time)</param> 15 /// <param name="time">Last modified time (MS-DOS time)</param>
23 public CabinetFileInfo(string fileId, ushort date, ushort time, int size) 16 public CabinetFileInfo(string fileId, ushort date, ushort time, int size)
24 { 17 {
25 this.fileId = fileId; 18 this.FileId = fileId;
26 this.date = date; 19 this.Date = date;
27 this.time = time; 20 this.Time = time;
28 this.size = size; 21 this.Size = size;
29 } 22 }
30 23
31 /// <summary> 24 /// <summary>
32 /// Gets the file Id of the file. 25 /// Gets the file Id of the file.
33 /// </summary> 26 /// </summary>
34 /// <value>file Id</value> 27 /// <value>file Id</value>
35 public string FileId 28 public string FileId { get; }
36 {
37 get { return this.fileId; }
38 }
39 29
40 /// <summary> 30 /// <summary>
41 /// Gets modified date (DOS format). 31 /// Gets modified date (DOS format).
42 /// </summary> 32 /// </summary>
43 public ushort Date 33 public ushort Date { get; }
44 {
45 get { return this.date; }
46 }
47 34
48 /// <summary> 35 /// <summary>
49 /// Gets modified time (DOS format). 36 /// Gets modified time (DOS format).
50 /// </summary> 37 /// </summary>
51 public ushort Time 38 public ushort Time { get; }
52 {
53 get { return this.time; }
54 }
55 39
56 /// <summary> 40 /// <summary>
57 /// Gets the size of the file in bytes. 41 /// Gets the size of the file in bytes.
58 /// </summary> 42 /// </summary>
59 public int Size 43 public int Size { get; }
60 {
61 get { return this.size; }
62 }
63 } 44 }
64} 45}
diff --git a/src/WixToolset.Core/Cab/WixCreateCab.cs b/src/WixToolset.Core/Cab/WixCreateCab.cs
index 8f985a43..4ebdd1c0 100644
--- a/src/WixToolset.Core/Cab/WixCreateCab.cs
+++ b/src/WixToolset.Core/Cab/WixCreateCab.cs
@@ -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. 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 2
3namespace WixToolset.Cab 3namespace WixToolset.Core.Cab
4{ 4{
5 using System; 5 using System;
6 using System.Globalization; 6 using System.Globalization;
7 using System.IO; 7 using System.IO;
8 using System.Runtime.InteropServices; 8 using System.Runtime.InteropServices;
9 using WixToolset.Bind.Databases; 9 using WixToolset.Core.Bind;
10 using WixToolset.Core.Native; 10 using WixToolset.Core.Native;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 12
diff --git a/src/WixToolset.Core/Cab/WixEnumerateCab.cs b/src/WixToolset.Core/Cab/WixEnumerateCab.cs
index 017eeffb..0b4055d6 100644
--- a/src/WixToolset.Core/Cab/WixEnumerateCab.cs
+++ b/src/WixToolset.Core/Cab/WixEnumerateCab.cs
@@ -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. 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 2
3namespace WixToolset.Cab 3namespace WixToolset.Core.Cab
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -10,7 +10,7 @@ namespace WixToolset.Cab
10 /// <summary> 10 /// <summary>
11 /// Wrapper class around interop with wixcab.dll to enumerate files from a cabinet. 11 /// Wrapper class around interop with wixcab.dll to enumerate files from a cabinet.
12 /// </summary> 12 /// </summary>
13 internal sealed class WixEnumerateCab : IDisposable 13 public sealed class WixEnumerateCab : IDisposable
14 { 14 {
15 private bool disposed; 15 private bool disposed;
16 private List<CabinetFileInfo> fileInfoList; 16 private List<CabinetFileInfo> fileInfoList;
@@ -38,7 +38,7 @@ namespace WixToolset.Cab
38 /// </summary> 38 /// </summary>
39 /// <param name="cabinetFile">path to cabinet</param> 39 /// <param name="cabinetFile">path to cabinet</param>
40 /// <returns>list of CabinetFileInfo</returns> 40 /// <returns>list of CabinetFileInfo</returns>
41 internal List<CabinetFileInfo> Enumerate(string cabinetFile) 41 public List<CabinetFileInfo> Enumerate(string cabinetFile)
42 { 42 {
43 this.fileInfoList = new List<CabinetFileInfo>(); 43 this.fileInfoList = new List<CabinetFileInfo>();
44 44
diff --git a/src/WixToolset.Core/Cab/WixExtractCab.cs b/src/WixToolset.Core/Cab/WixExtractCab.cs
index debdaf15..e776b08e 100644
--- a/src/WixToolset.Core/Cab/WixExtractCab.cs
+++ b/src/WixToolset.Core/Cab/WixExtractCab.cs
@@ -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. 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 2
3namespace WixToolset.Cab 3namespace WixToolset.Core.Cab
4{ 4{
5 using System; 5 using System;
6 using System.Runtime.InteropServices;
7 using WixToolset.Core.Native; 6 using WixToolset.Core.Native;
8 7
9 /// <summary> 8 /// <summary>
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs
index afb9e829..32da5bcf 100644
--- a/src/WixToolset.Core/CommandLine/BuildCommand.cs
+++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs
@@ -7,13 +7,14 @@ namespace WixToolset.Core
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Rows;
10 using WixToolset.Extensibility; 11 using WixToolset.Extensibility;
11 12
12 internal class BuildCommand : ICommandLineCommand 13 internal class BuildCommand : ICommandLineCommand
13 { 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)
15 { 16 {
16 this.Extensions = extensions; 17 this.ExtensionManager = extensions;
17 this.LocFiles = locFiles; 18 this.LocFiles = locFiles;
18 this.LibraryFiles = libraryFiles; 19 this.LibraryFiles = libraryFiles;
19 this.PreprocessorVariables = preprocessorVariables; 20 this.PreprocessorVariables = preprocessorVariables;
@@ -21,6 +22,7 @@ namespace WixToolset.Core
21 this.OutputPath = outputPath; 22 this.OutputPath = outputPath;
22 this.OutputType = outputType; 23 this.OutputType = outputType;
23 24
25 this.CabCachePath = cabCachePath;
24 this.Cultures = cultures; 26 this.Cultures = cultures;
25 this.BindFiles = bindFiles; 27 this.BindFiles = bindFiles;
26 this.BindPaths = bindPaths; 28 this.BindPaths = bindPaths;
@@ -32,7 +34,7 @@ namespace WixToolset.Core
32 this.WixProjectFile = wixProjectFile; 34 this.WixProjectFile = wixProjectFile;
33 } 35 }
34 36
35 public ExtensionManager Extensions { get; } 37 public ExtensionManager ExtensionManager { get; }
36 38
37 public IEnumerable<string> LocFiles { get; } 39 public IEnumerable<string> LocFiles { get; }
38 40
@@ -46,6 +48,8 @@ namespace WixToolset.Core
46 48
47 private OutputType OutputType { get; } 49 private OutputType OutputType { get; }
48 50
51 public string CabCachePath { get; }
52
49 public IEnumerable<string> Cultures { get; } 53 public IEnumerable<string> Cultures { get; }
50 54
51 public bool BindFiles { get; } 55 public bool BindFiles { get; }
@@ -70,7 +74,9 @@ namespace WixToolset.Core
70 74
71 if (this.OutputType == OutputType.Library) 75 if (this.OutputType == OutputType.Library)
72 { 76 {
73 this.LibraryPhase(intermediates, tableDefinitions); 77 var library = this.LibraryPhase(intermediates, tableDefinitions);
78
79 library?.Save(this.OutputPath);
74 } 80 }
75 else 81 else
76 { 82 {
@@ -105,51 +111,40 @@ namespace WixToolset.Core
105 return intermediates; 111 return intermediates;
106 } 112 }
107 113
108 private void LibraryPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions) 114 private Library LibraryPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
109 { 115 {
110 var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList(); 116 var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList();
111 117
112 // If there was an error adding localization files, then bail. 118 // If there was an error adding localization files, then bail.
113 if (Messaging.Instance.EncounteredError) 119 if (Messaging.Instance.EncounteredError)
114 { 120 {
115 return; 121 return null;
116 } 122 }
117 123
118 var sections = intermediates.SelectMany(i => i.Sections).ToList(); 124 var resolver = CreateWixResolverWithVariables(null, null);
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 }
136 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;
138 133
139 var library = librarian.Combine(sections, localizations, resolver); 134 var librarian = new Librarian(context);
140 135
141 library?.Save(this.OutputPath); 136 return librarian.Combine();
142 } 137 }
143 138
144 private Output LinkPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions) 139 private Output LinkPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
145 { 140 {
146 var sections = intermediates.SelectMany(i => i.Sections).ToList(); 141 var sections = intermediates.SelectMany(i => i.Sections).ToList();
147 142
148 sections.AddRange(SectionsFromLibraries(tableDefinitions)); 143 sections.AddRange(this.SectionsFromLibraries(tableDefinitions));
149 144
150 var linker = new Linker(); 145 var linker = new Linker();
151 146
152 foreach (var data in this.Extensions.Create<IExtensionData>()) 147 foreach (var data in this.ExtensionManager.Create<IExtensionData>())
153 { 148 {
154 linker.AddExtensionData(data); 149 linker.AddExtensionData(data);
155 } 150 }
@@ -159,6 +154,40 @@ namespace WixToolset.Core
159 return output; 154 return output;
160 } 155 }
161 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
162 private IEnumerable<Section> SectionsFromLibraries(TableDefinitionCollection tableDefinitions) 191 private IEnumerable<Section> SectionsFromLibraries(TableDefinitionCollection tableDefinitions)
163 { 192 {
164 var sections = new List<Section>(); 193 var sections = new List<Section>();
@@ -187,34 +216,6 @@ namespace WixToolset.Core
187 return sections; 216 return sections;
188 } 217 }
189 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
218 private IEnumerable<Localization> LoadLocalizationFiles(TableDefinitionCollection tableDefinitions) 219 private IEnumerable<Localization> LoadLocalizationFiles(TableDefinitionCollection tableDefinitions)
219 { 220 {
220 foreach (var loc in this.LocFiles) 221 foreach (var loc in this.LocFiles)
@@ -225,30 +226,21 @@ namespace WixToolset.Core
225 } 226 }
226 } 227 }
227 228
228 /// <summary> 229 private static WixVariableResolver CreateWixResolverWithVariables(Localizer localizer, Output output)
229 /// File resolution mechanism to create binary library.
230 /// </summary>
231 private class LibraryBinaryFileResolver : ILibraryBinaryFileResolver
232 { 230 {
233 public IEnumerable<IBinderFileManager> FileManagers { get; set; } 231 var resolver = new WixVariableResolver(localizer);
234
235 public WixVariableResolver VariableResolver { get; set; }
236 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)
238 { 236 {
239 string resolvedPath = this.VariableResolver.ResolveVariables(sourceLineNumber, path, false); 237 foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows)
240
241 foreach (IBinderFileManager fileManager in this.FileManagers)
242 { 238 {
243 string finalPath = fileManager.ResolveFile(resolvedPath, table, sourceLineNumber, BindStage.Normal); 239 resolver.AddVariable(wixVariableRow);
244 if (!String.IsNullOrEmpty(finalPath))
245 {
246 return finalPath;
247 }
248 } 240 }
249
250 return null;
251 } 241 }
242
243 return resolver;
252 } 244 }
253 } 245 }
254} 246}
diff --git a/src/WixToolset.Core/CommandLine/CommandLine.cs b/src/WixToolset.Core/CommandLine/CommandLine.cs
index a3a6831c..2f203ecb 100644
--- a/src/WixToolset.Core/CommandLine/CommandLine.cs
+++ b/src/WixToolset.Core/CommandLine/CommandLine.cs
@@ -6,6 +6,7 @@ namespace WixToolset.Core
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using System.Reflection;
9 using System.Text; 10 using System.Text;
10 using System.Text.RegularExpressions; 11 using System.Text.RegularExpressions;
11 using WixToolset.Data; 12 using WixToolset.Data;
@@ -71,6 +72,7 @@ namespace WixToolset.Core
71 72
72 var intermediateFolder = String.Empty; 73 var intermediateFolder = String.Empty;
73 74
75 var cabCachePath = String.Empty;
74 var cultures = new List<string>(); 76 var cultures = new List<string>();
75 var contentsFile = String.Empty; 77 var contentsFile = String.Empty;
76 var outputsFile = String.Empty; 78 var outputsFile = String.Empty;
@@ -98,6 +100,10 @@ namespace WixToolset.Core
98 cmdline.GetNextArgumentOrError(bindPaths); 100 cmdline.GetNextArgumentOrError(bindPaths);
99 return true; 101 return true;
100 102
103 case "cc":
104 cmdline.GetNextArgumentOrError(ref cabCachePath);
105 return true;
106
101 case "cultures": 107 case "cultures":
102 cmdline.GetNextArgumentOrError(cultures); 108 cmdline.GetNextArgumentOrError(cultures);
103 return true; 109 return true;
@@ -190,12 +196,14 @@ namespace WixToolset.Core
190 { 196 {
191 case Commands.Build: 197 case Commands.Build:
192 { 198 {
199 LoadStandardBackends(cli.ExtensionManager);
200
193 var sourceFiles = GatherSourceFiles(files, outputFolder); 201 var sourceFiles = GatherSourceFiles(files, outputFolder);
194 var variables = GatherPreprocessorVariables(defines); 202 var variables = GatherPreprocessorVariables(defines);
195 var bindPathList = GatherBindPaths(bindPaths); 203 var bindPathList = GatherBindPaths(bindPaths);
196 var extensions = cli.ExtensionManager; 204 var extensions = cli.ExtensionManager;
197 var type = CalculateOutputType(outputType, outputFile); 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);
199 } 207 }
200 208
201 case Commands.Compile: 209 case Commands.Compile:
@@ -209,6 +217,18 @@ namespace WixToolset.Core
209 return null; 217 return null;
210 } 218 }
211 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
212 private static OutputType CalculateOutputType(string outputType, string outputFile) 232 private static OutputType CalculateOutputType(string outputType, string outputFile)
213 { 233 {
214 if (String.IsNullOrEmpty(outputType)) 234 if (String.IsNullOrEmpty(outputType))
diff --git a/src/WixToolset.Core/Common.cs b/src/WixToolset.Core/Common.cs
index a2881984..28e7ee7b 100644
--- a/src/WixToolset.Core/Common.cs
+++ b/src/WixToolset.Core/Common.cs
@@ -17,7 +17,7 @@ namespace WixToolset
17 /// <summary> 17 /// <summary>
18 /// Common Wix utility methods and types. 18 /// Common Wix utility methods and types.
19 /// </summary> 19 /// </summary>
20 internal static class Common 20 public static class Common
21 { 21 {
22 //------------------------------------------------------------------------------------------------- 22 //-------------------------------------------------------------------------------------------------
23 // Layout of an Access Mask (from http://technet.microsoft.com/en-us/library/cc783530(WS.10).aspx) 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) 89 // FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
90 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", "FileAllRights", "ReadAttributes", "WriteAttributes" }; 90 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", "FileAllRights", "ReadAttributes", "WriteAttributes" };
91 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" }; 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 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);
95 93
96 internal const char CustomRowFieldSeparator = '\x85'; 94 internal const char CustomRowFieldSeparator = '\x85';
97 95
@@ -170,15 +168,14 @@ namespace WixToolset
170 /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception> 168 /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
171 /// <exception cref="NotSupportedException">The value doesn't not represent a valid code page name or integer value.</exception> 169 /// <exception cref="NotSupportedException">The value doesn't not represent a valid code page name or integer value.</exception>
172 /// <exception cref="WixException">The code page is invalid for summary information.</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)
174 { 172 {
175 int codePage;
176 Encoding encoding;
177
178 try 173 try
179 { 174 {
175 Encoding encoding;
176
180 // check if a integer as a string was passed 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))
182 { 179 {
183 if (0 == codePage) 180 if (0 == codePage)
184 { 181 {
@@ -366,9 +363,9 @@ namespace WixToolset
366 /// Generate a new Windows Installer-friendly guid. 363 /// Generate a new Windows Installer-friendly guid.
367 /// </summary> 364 /// </summary>
368 /// <returns>A new guid.</returns> 365 /// <returns>A new guid.</returns>
369 internal static string GenerateGuid() 366 public static string GenerateGuid()
370 { 367 {
371 return Guid.NewGuid().ToString("B").ToUpper(CultureInfo.InvariantCulture); 368 return Guid.NewGuid().ToString("B").ToUpperInvariant();
372 } 369 }
373 370
374 /// <summary> 371 /// <summary>
@@ -465,7 +462,7 @@ namespace WixToolset
465 } 462 }
466 } 463 }
467 464
468 internal static string GetFileHash(string path) 465 public static string GetFileHash(string path)
469 { 466 {
470 using (SHA1Managed managed = new SHA1Managed()) 467 using (SHA1Managed managed = new SHA1Managed())
471 { 468 {
@@ -478,6 +475,147 @@ namespace WixToolset
478 } 475 }
479 476
480 /// <summary> 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>
481 /// Get an attribute value. 619 /// Get an attribute value.
482 /// </summary> 620 /// </summary>
483 /// <param name="sourceLineNumbers">Source line information about the owner element.</param> 621 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs
index ed7cb60e..d085e788 100644
--- a/src/WixToolset.Core/Compiler.cs
+++ b/src/WixToolset.Core/Compiler.cs
@@ -11,11 +11,11 @@ namespace WixToolset
11 using System.IO; 11 using System.IO;
12 using System.Text.RegularExpressions; 12 using System.Text.RegularExpressions;
13 using System.Xml.Linq; 13 using System.Xml.Linq;
14 using WixToolset.Core;
15 using WixToolset.Core.Native;
14 using WixToolset.Data; 16 using WixToolset.Data;
15 using WixToolset.Data.Rows; 17 using WixToolset.Data.Rows;
16 using WixToolset.Extensibility; 18 using WixToolset.Extensibility;
17 using WixToolset.Msi;
18 using WixToolset.Core.Native;
19 using Wix = WixToolset.Data.Serialize; 19 using Wix = WixToolset.Data.Serialize;
20 20
21 /// <summary> 21 /// <summary>
@@ -158,10 +158,7 @@ namespace WixToolset
158 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] 158 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
159 public Intermediate Compile(XDocument source) 159 public Intermediate Compile(XDocument source)
160 { 160 {
161 if (null == source) 161 if (null == source) throw new ArgumentNullException(nameof(source));
162 {
163 throw new ArgumentNullException("source");
164 }
165 162
166 bool encounteredError = false; 163 bool encounteredError = false;
167 164
@@ -220,9 +217,7 @@ namespace WixToolset
220 { 217 {
221 if (field.Data is string) 218 if (field.Data is string)
222 { 219 {
223 bool isDefault = false; 220 field.Data = this.componentIdPlaceholdersResolver.ResolveVariables(row.SourceLineNumbers, (string)field.Data, false, false, out var defaultIgnored, out var delayedIgnored);
224 bool delayedResolve = false;
225 field.Data = this.componentIdPlaceholdersResolver.ResolveVariables(row.SourceLineNumbers, (string)field.Data, false, false, ref isDefault, ref delayedResolve);
226 } 221 }
227 } 222 }
228 } 223 }
@@ -470,7 +465,8 @@ namespace WixToolset
470 case "Advertise": 465 case "Advertise":
471 appIdAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib); 466 appIdAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
472 break; 467 break;
473 case "Description": description = this.core.GetAttributeValue(sourceLineNumbers, attrib); 468 case "Description":
469 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
474 break; 470 break;
475 case "DllSurrogate": 471 case "DllSurrogate":
476 dllSurrogate = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); 472 dllSurrogate = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
@@ -9471,13 +9467,13 @@ namespace WixToolset
9471 targetProductName = this.core.GetAttributeValue(sourceLineNumbers, attrib); 9467 targetProductName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9472 break; 9468 break;
9473 case "ApiPatchingSymbolNoImagehlpFlag": 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;
9475 break; 9471 break;
9476 case "ApiPatchingSymbolNoFailuresFlag": 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;
9478 break; 9474 break;
9479 case "ApiPatchingSymbolUndecoratedTooFlag": 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;
9481 break; 9477 break;
9482 case "OptimizePatchSizeForLargeFiles": 9478 case "OptimizePatchSizeForLargeFiles":
9483 optimizePatchSizeForLargeFiles = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); 9479 optimizePatchSizeForLargeFiles = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
@@ -11802,7 +11798,7 @@ namespace WixToolset
11802 private void ParseProductElement(XElement node) 11798 private void ParseProductElement(XElement node)
11803 { 11799 {
11804 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 11800 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11805 int codepage = 0; 11801 int codepage = 65001;
11806 string productCode = null; 11802 string productCode = null;
11807 string upgradeCode = null; 11803 string upgradeCode = null;
11808 string manufacturer = null; 11804 string manufacturer = null;
diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs
index 8640a2da..8f4703f7 100644
--- a/src/WixToolset.Core/CompilerCore.cs
+++ b/src/WixToolset.Core/CompilerCore.cs
@@ -45,10 +45,6 @@ namespace WixToolset
45 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/"; 45 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/";
46 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; 46 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
47 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
52 private static readonly Regex AmbiguousFilename = new Regex(@"^.{6}\~\d", RegexOptions.Compiled); 48 private static readonly Regex AmbiguousFilename = new Regex(@"^.{6}\~\d", RegexOptions.Compiled);
53 49
54 private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * " 50 private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * "
@@ -67,6 +63,11 @@ namespace WixToolset
67 63
68 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); 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);
69 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
70 // Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 113) 71 // Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 113)
71 private static readonly List<String> BuiltinBundleVariables = new List<string>( 72 private static readonly List<String> BuiltinBundleVariables = new List<string>(
72 new string[] { 73 new string[] {
diff --git a/src/WixToolset.Core/Data/messages.xml b/src/WixToolset.Core/Data/messages.xml
index edc98147..d981e2d1 100644
--- a/src/WixToolset.Core/Data/messages.xml
+++ b/src/WixToolset.Core/Data/messages.xml
@@ -957,12 +957,6 @@
957 <Parameter Type="System.String" Name="exceptionMessage" /> 957 <Parameter Type="System.String" Name="exceptionMessage" />
958 </Instance> 958 </Instance>
959 </Message> 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>
966 <Message Id="ReferenceLoopDetected" Number="86"> 960 <Message Id="ReferenceLoopDetected" Number="86">
967 <Instance> 961 <Instance>
968 A circular reference of groups was detected. The infinite loop includes: {0}. Group references must form a directed acyclic graph. 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 @@
2138 This patch is not uninstallable. The 'Patch' element's attribute 'AllowRemoval' should be set to 'no'. 2132 This patch is not uninstallable. The 'Patch' element's attribute 'AllowRemoval' should be set to 'no'.
2139 </Instance> 2133 </Instance>
2140 </Message> 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>
2147 <Message Id="FileTooLarge" Number="263"> 2135 <Message Id="FileTooLarge" Number="263">
2148 <Instance> 2136 <Instance>
2149 '{0}' is too large, file size must be less than 2147483648. 2137 '{0}' is too large, file size must be less than 2147483648.
diff --git a/src/WixToolset.Core/Decompiler.cs b/src/WixToolset.Core/Decompiler.cs
index 249b5788..e72b0104 100644
--- a/src/WixToolset.Core/Decompiler.cs
+++ b/src/WixToolset.Core/Decompiler.cs
@@ -3,7 +3,6 @@
3namespace WixToolset 3namespace WixToolset
4{ 4{
5 using System; 5 using System;
6 using System.CodeDom.Compiler;
7 using System.Collections; 6 using System.Collections;
8 using System.Collections.Generic; 7 using System.Collections.Generic;
9 using System.Collections.Specialized; 8 using System.Collections.Specialized;
@@ -15,9 +14,9 @@ namespace WixToolset
15 using WixToolset.Data; 14 using WixToolset.Data;
16 using WixToolset.Data.Rows; 15 using WixToolset.Data.Rows;
17 using WixToolset.Extensibility; 16 using WixToolset.Extensibility;
18 using WixToolset.Msi;
19 using WixToolset.Core.Native; 17 using WixToolset.Core.Native;
20 using Wix = WixToolset.Data.Serialize; 18 using Wix = WixToolset.Data.Serialize;
19 using WixToolset.Core;
21 20
22 /// <summary> 21 /// <summary>
23 /// Decompiles an msi database into WiX source. 22 /// Decompiles an msi database into WiX source.
@@ -5201,7 +5200,7 @@ namespace WixToolset
5201 5200
5202 directory.Id = Convert.ToString(row[0]); 5201 directory.Id = Convert.ToString(row[0]);
5203 5202
5204 string[] names = Installer.GetNames(Convert.ToString(row[2])); 5203 string[] names = Common.GetNames(Convert.ToString(row[2]));
5205 5204
5206 if (String.Equals(directory.Id, "TARGETDIR", StringComparison.Ordinal) && !String.Equals(names[0], "SourceDir", StringComparison.Ordinal)) 5205 if (String.Equals(directory.Id, "TARGETDIR", StringComparison.Ordinal) && !String.Equals(names[0], "SourceDir", StringComparison.Ordinal))
5207 { 5206 {
@@ -5319,7 +5318,7 @@ namespace WixToolset
5319 5318
5320 if (null != row[3]) 5319 if (null != row[3])
5321 { 5320 {
5322 string[] names = Installer.GetNames(Convert.ToString(row[3])); 5321 string[] names = Common.GetNames(Convert.ToString(row[3]));
5323 if (null != names[0] && null != names[1]) 5322 if (null != names[0] && null != names[1])
5324 { 5323 {
5325 copyFile.DestinationShortName = names[0]; 5324 copyFile.DestinationShortName = names[0];
@@ -5788,7 +5787,7 @@ namespace WixToolset
5788 5787
5789 file.Id = fileRow.File; 5788 file.Id = fileRow.File;
5790 5789
5791 string[] names = Installer.GetNames(fileRow.FileName); 5790 string[] names = Common.GetNames(fileRow.FileName);
5792 if (null != names[0] && null != names[1]) 5791 if (null != names[0] && null != names[1])
5793 { 5792 {
5794 file.ShortName = names[0]; 5793 file.ShortName = names[0];
@@ -5974,7 +5973,7 @@ namespace WixToolset
5974 5973
5975 iniFile.Id = Convert.ToString(row[0]); 5974 iniFile.Id = Convert.ToString(row[0]);
5976 5975
5977 string[] names = Installer.GetNames(Convert.ToString(row[1])); 5976 string[] names = Common.GetNames(Convert.ToString(row[1]));
5978 5977
5979 if (null != names[0]) 5978 if (null != names[0])
5980 { 5979 {
@@ -6044,7 +6043,7 @@ namespace WixToolset
6044 6043
6045 iniFileSearch.Id = Convert.ToString(row[0]); 6044 iniFileSearch.Id = Convert.ToString(row[0]);
6046 6045
6047 string[] names = Installer.GetNames(Convert.ToString(row[1])); 6046 string[] names = Common.GetNames(Convert.ToString(row[1]));
6048 if (null != names[0] && null != names[1]) 6047 if (null != names[0] && null != names[1])
6049 { 6048 {
6050 iniFileSearch.ShortName = names[0]; 6049 iniFileSearch.ShortName = names[0];
@@ -6681,7 +6680,7 @@ namespace WixToolset
6681 6680
6682 if (null != row[3]) 6681 if (null != row[3])
6683 { 6682 {
6684 string[] names = Installer.GetNames(Convert.ToString(row[3])); 6683 string[] names = Common.GetNames(Convert.ToString(row[3]));
6685 if (null != names[0] && null != names[1]) 6684 if (null != names[0] && null != names[1])
6686 { 6685 {
6687 copyFile.DestinationShortName = names[0]; 6686 copyFile.DestinationShortName = names[0];
@@ -8007,7 +8006,7 @@ namespace WixToolset
8007 8006
8008 removeFile.Id = Convert.ToString(row[0]); 8007 removeFile.Id = Convert.ToString(row[0]);
8009 8008
8010 string[] names = Installer.GetNames(Convert.ToString(row[2])); 8009 string[] names = Common.GetNames(Convert.ToString(row[2]));
8011 if (null != names[0] && null != names[1]) 8010 if (null != names[0] && null != names[1])
8012 { 8011 {
8013 removeFile.ShortName = names[0]; 8012 removeFile.ShortName = names[0];
@@ -8062,7 +8061,7 @@ namespace WixToolset
8062 8061
8063 iniFile.Id = Convert.ToString(row[0]); 8062 iniFile.Id = Convert.ToString(row[0]);
8064 8063
8065 string[] names = Installer.GetNames(Convert.ToString(row[1])); 8064 string[] names = Common.GetNames(Convert.ToString(row[1]));
8066 if (null != names[0] && null != names[1]) 8065 if (null != names[0] && null != names[1])
8067 { 8066 {
8068 iniFile.ShortName = names[0]; 8067 iniFile.ShortName = names[0];
@@ -8531,7 +8530,7 @@ namespace WixToolset
8531 8530
8532 shortcut.Directory = Convert.ToString(row[1]); 8531 shortcut.Directory = Convert.ToString(row[1]);
8533 8532
8534 string[] names = Installer.GetNames(Convert.ToString(row[2])); 8533 string[] names = Common.GetNames(Convert.ToString(row[2]));
8535 if (null != names[0] && null != names[1]) 8534 if (null != names[0] && null != names[1])
8536 { 8535 {
8537 shortcut.ShortName = names[0]; 8536 shortcut.ShortName = names[0];
@@ -8654,7 +8653,7 @@ namespace WixToolset
8654 8653
8655 fileSearch.Id = Convert.ToString(row[0]); 8654 fileSearch.Id = Convert.ToString(row[0]);
8656 8655
8657 string[] names = Installer.GetNames(Convert.ToString(row[1])); 8656 string[] names = Common.GetNames(Convert.ToString(row[1]));
8658 if (null != names[0]) 8657 if (null != names[0])
8659 { 8658 {
8660 // it is permissable to just have a long name 8659 // it is permissable to just have a long name
diff --git a/src/WixToolset.Core/Extensibility/HeatExtension.cs b/src/WixToolset.Core/Extensibility/HeatExtension.cs
index 5e292220..48e1a93b 100644
--- a/src/WixToolset.Core/Extensibility/HeatExtension.cs
+++ b/src/WixToolset.Core/Extensibility/HeatExtension.cs
@@ -3,14 +3,10 @@
3namespace WixToolset.Extensibility 3namespace WixToolset.Extensibility
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using System.IO; 6 using System.IO;
8 using System.Reflection; 7 using System.Reflection;
9 using WixToolset;
10 using WixToolset.Data; 8 using WixToolset.Data;
11 using WixToolset.Extensibilty;
12 using WixToolset.Tools; 9 using WixToolset.Tools;
13 using Wix = WixToolset.Data.Serialize;
14 10
15 /// <summary> 11 /// <summary>
16 /// A command line option. 12 /// A command line option.
diff --git a/src/WixToolset.Core/Extensibility/IHeatCore.cs b/src/WixToolset.Core/Extensibility/IHeatCore.cs
index bc853b24..dbfc8929 100644
--- a/src/WixToolset.Core/Extensibility/IHeatCore.cs
+++ b/src/WixToolset.Core/Extensibility/IHeatCore.cs
@@ -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. 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 2
3namespace WixToolset.Extensibilty 3namespace WixToolset.Extensibility
4{ 4{
5 using WixToolset.Data; 5 using WixToolset.Data;
6 6
diff --git a/src/WixToolset.Core/ExtensionManager.cs b/src/WixToolset.Core/ExtensionManager.cs
index 45cb65ec..7e40571b 100644
--- a/src/WixToolset.Core/ExtensionManager.cs
+++ b/src/WixToolset.Core/ExtensionManager.cs
@@ -8,8 +8,9 @@ namespace WixToolset
8 using System.Linq; 8 using System.Linq;
9 using System.Reflection; 9 using System.Reflection;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Extensibility;
11 12
12 public class ExtensionManager 13 public class ExtensionManager : IExtensionManager
13 { 14 {
14 private List<Assembly> extensionAssemblies = new List<Assembly>(); 15 private List<Assembly> extensionAssemblies = new List<Assembly>();
15 16
@@ -67,8 +68,7 @@ namespace WixToolset
67 /// <returns>Extensions created of the specified type.</returns> 68 /// <returns>Extensions created of the specified type.</returns>
68 public IEnumerable<T> Create<T>() where T : class 69 public IEnumerable<T> Create<T>() where T : class
69 { 70 {
70 var extensionType = typeof(T); 71 var types = this.extensionAssemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && typeof(T).IsAssignableFrom(t)));
71 var types = this.extensionAssemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && extensionType.IsAssignableFrom(t)));
72 return types.Select(t => (T)Activator.CreateInstance(t)).ToList(); 72 return types.Select(t => (T)Activator.CreateInstance(t)).ToList();
73 } 73 }
74 74
diff --git a/src/WixToolset.Core/HeatCore.cs b/src/WixToolset.Core/HeatCore.cs
index 5c5defe8..01233c40 100644
--- a/src/WixToolset.Core/HeatCore.cs
+++ b/src/WixToolset.Core/HeatCore.cs
@@ -2,11 +2,8 @@
2 2
3namespace WixToolset.Tools 3namespace WixToolset.Tools
4{ 4{
5 using System;
6 using System.Reflection;
7 using WixToolset.Data; 5 using WixToolset.Data;
8 using WixToolset.Extensibilty; 6 using WixToolset.Extensibility;
9 using Wix = WixToolset.Data.Serialize;
10 7
11 /// <summary> 8 /// <summary>
12 /// The WiX Toolset Harvester application core. 9 /// The WiX Toolset Harvester application core.
diff --git a/src/WixToolset.Core/IncribeContext.cs b/src/WixToolset.Core/IncribeContext.cs
new file mode 100644
index 00000000..604ba5d1
--- /dev/null
+++ b/src/WixToolset.Core/IncribeContext.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Inscriber.cs b/src/WixToolset.Core/Inscriber.cs
index 5b467ec1..f01e0629 100644
--- a/src/WixToolset.Core/Inscriber.cs
+++ b/src/WixToolset.Core/Inscriber.cs
@@ -2,17 +2,8 @@
2 2
3namespace WixToolset 3namespace WixToolset
4{ 4{
5 using System;
6 using System.CodeDom.Compiler;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using System.IO; 5 using System.IO;
10 using System.Runtime.InteropServices;
11 using System.Security.Cryptography.X509Certificates;
12 using WixToolset.Bind.Bundles;
13 using WixToolset.Data; 6 using WixToolset.Data;
14 using WixToolset.Msi;
15 using WixToolset.Core.Native;
16 7
17 /// <summary> 8 /// <summary>
18 /// Converts a wixout representation of an MSM database into a ComponentGroup the form of WiX source. 9 /// Converts a wixout representation of an MSM database into a ComponentGroup the form of WiX source.
@@ -81,41 +72,41 @@ namespace WixToolset
81 /// <returns>True if bundle was updated.</returns> 72 /// <returns>True if bundle was updated.</returns>
82 public bool InscribeBundleEngine(string bundleFile, string outputFile) 73 public bool InscribeBundleEngine(string bundleFile, string outputFile)
83 { 74 {
84 string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_unsigned.exe"); 75 //string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_unsigned.exe");
85 76
86 using (BurnReader reader = BurnReader.Open(bundleFile)) 77 //using (BurnReader reader = BurnReader.Open(bundleFile))
87 using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete)) 78 //using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
88 { 79 //{
89 reader.Stream.Seek(0, SeekOrigin.Begin); 80 // reader.Stream.Seek(0, SeekOrigin.Begin);
90 81
91 byte[] buffer = new byte[4 * 1024]; 82 // byte[] buffer = new byte[4 * 1024];
92 int total = 0; 83 // int total = 0;
93 int read = 0; 84 // int read = 0;
94 do 85 // do
95 { 86 // {
96 read = Math.Min(buffer.Length, (int)reader.EngineSize - total); 87 // read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
97 88
98 read = reader.Stream.Read(buffer, 0, read); 89 // read = reader.Stream.Read(buffer, 0, read);
99 writer.Write(buffer, 0, read); 90 // writer.Write(buffer, 0, read);
100 91
101 total += read; 92 // total += read;
102 } while (total < reader.EngineSize && 0 < read); 93 // } while (total < reader.EngineSize && 0 < read);
103 94
104 if (total != reader.EngineSize) 95 // if (total != reader.EngineSize)
105 { 96 // {
106 throw new InvalidOperationException("Failed to copy engine out of bundle."); 97 // throw new InvalidOperationException("Failed to copy engine out of bundle.");
107 } 98 // }
108 99
109 // TODO: update writer with detached container signatures. 100 // // TODO: update writer with detached container signatures.
110 } 101 //}
111 102
112 Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); 103 //Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
113 if (File.Exists(outputFile)) 104 //if (File.Exists(outputFile))
114 { 105 //{
115 File.Delete(outputFile); 106 // File.Delete(outputFile);
116 } 107 //}
117 File.Move(tempFile, outputFile); 108 //File.Move(tempFile, outputFile);
118 WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1); 109 //WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
119 110
120 return true; 111 return true;
121 } 112 }
@@ -129,36 +120,37 @@ namespace WixToolset
129 /// <returns>True if bundle was updated.</returns> 120 /// <returns>True if bundle was updated.</returns>
130 public bool InscribeBundle(string bundleFile, string signedEngineFile, string outputFile) 121 public bool InscribeBundle(string bundleFile, string signedEngineFile, string outputFile)
131 { 122 {
132 bool inscribed = false; 123 //bool inscribed = false;
133 string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_signed.exe"); 124 //string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_signed.exe");
134 125
135 using (BurnReader reader = BurnReader.Open(bundleFile)) 126 //using (BurnReader reader = BurnReader.Open(bundleFile))
136 { 127 //{
137 File.Copy(signedEngineFile, tempFile, true); 128 // File.Copy(signedEngineFile, tempFile, true);
138 129
139 // If there was an attached container on the original (unsigned) bundle, put it back. 130 // // If there was an attached container on the original (unsigned) bundle, put it back.
140 if (reader.AttachedContainerSize > 0) 131 // if (reader.AttachedContainerSize > 0)
141 { 132 // {
142 reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin); 133 // reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
143 134
144 using (BurnWriter writer = BurnWriter.Open(tempFile)) 135 // using (BurnWriter writer = BurnWriter.Open(tempFile))
145 { 136 // {
146 writer.RememberThenResetSignature(); 137 // writer.RememberThenResetSignature();
147 writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached); 138 // writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
148 inscribed = true; 139 // inscribed = true;
149 } 140 // }
150 } 141 // }
151 } 142 //}
152 143
153 Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); 144 //Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
154 if (File.Exists(outputFile)) 145 //if (File.Exists(outputFile))
155 { 146 //{
156 File.Delete(outputFile); 147 // File.Delete(outputFile);
157 } 148 //}
158 File.Move(tempFile, outputFile); 149 //File.Move(tempFile, outputFile);
159 WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1); 150 //WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
160 151
161 return inscribed; 152 //return inscribed;
153 return false;
162 } 154 }
163 155
164 /// <summary> 156 /// <summary>
@@ -170,256 +162,257 @@ namespace WixToolset
170 /// <returns>True if database is updated.</returns> 162 /// <returns>True if database is updated.</returns>
171 public bool InscribeDatabase(string databaseFile, string outputFile, bool tidy) 163 public bool InscribeDatabase(string databaseFile, string outputFile, bool tidy)
172 { 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 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
174 bool foundUnsignedExternals = false; 166 //bool foundUnsignedExternals = false;
175 bool shouldCommit = false; 167 //bool shouldCommit = false;
176 168
177 FileAttributes attributes = File.GetAttributes(databaseFile); 169 //FileAttributes attributes = File.GetAttributes(databaseFile);
178 if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) 170 //if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
179 { 171 //{
180 this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile)); 172 // this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile));
181 return shouldCommit; 173 // return shouldCommit;
182 } 174 //}
183 175
184 using (Database database = new Database(databaseFile, OpenDatabase.Transact)) 176 //using (Database database = new Database(databaseFile, OpenDatabase.Transact))
185 { 177 //{
186 // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content 178 // // 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; 179 // int codepage = 1252;
188 180
189 // list of certificates for this database (hash/identifier) 181 // // list of certificates for this database (hash/identifier)
190 Dictionary<string, string> certificates = new Dictionary<string, string>(); 182 // Dictionary<string, string> certificates = new Dictionary<string, string>();
191 183
192 // Reset the in-memory tables for this new database 184 // // Reset the in-memory tables for this new database
193 Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]); 185 // Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]);
194 Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]); 186 // Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]);
195 187
196 // If any digital signature records exist that are not of the media type, preserve them 188 // // If any digital signature records exist that are not of the media type, preserve them
197 if (database.TableExists("MsiDigitalSignature")) 189 // if (database.TableExists("MsiDigitalSignature"))
198 { 190 // {
199 using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'")) 191 // using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
200 { 192 // {
201 while (true) 193 // while (true)
202 { 194 // {
203 using (Record digitalSignatureRecord = digitalSignatureView.Fetch()) 195 // using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
204 { 196 // {
205 if (null == digitalSignatureRecord) 197 // if (null == digitalSignatureRecord)
206 { 198 // {
207 break; 199 // break;
208 } 200 // }
209 201
210 Row digitalSignatureRow = null; 202 // Row digitalSignatureRow = null;
211 digitalSignatureRow = digitalSignatureTable.CreateRow(null); 203 // digitalSignatureRow = digitalSignatureTable.CreateRow(null);
212 204
213 string table = digitalSignatureRecord.GetString(0); 205 // string table = digitalSignatureRecord.GetString(0);
214 string signObject = digitalSignatureRecord.GetString(1); 206 // string signObject = digitalSignatureRecord.GetString(1);
215 207
216 digitalSignatureRow[0] = table; 208 // digitalSignatureRow[0] = table;
217 digitalSignatureRow[1] = signObject; 209 // digitalSignatureRow[1] = signObject;
218 digitalSignatureRow[2] = digitalSignatureRecord.GetString(2); 210 // digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
219 211
220 if (false == digitalSignatureRecord.IsNull(3)) 212 // if (false == digitalSignatureRecord.IsNull(3))
221 { 213 // {
222 // Export to a file, because the MSI API's require us to provide a file path on disk 214 // // 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"); 215 // string hashPath = Path.Combine(this.TempFilesLocation, "MsiDigitalSignature");
224 string hashFileName = string.Concat(table, ".", signObject, ".bin"); 216 // string hashFileName = string.Concat(table, ".", signObject, ".bin");
225 217
226 Directory.CreateDirectory(hashPath); 218 // Directory.CreateDirectory(hashPath);
227 hashPath = Path.Combine(hashPath, hashFileName); 219 // hashPath = Path.Combine(hashPath, hashFileName);
228 220
229 using (FileStream fs = File.Create(hashPath)) 221 // using (FileStream fs = File.Create(hashPath))
230 { 222 // {
231 int bytesRead; 223 // int bytesRead;
232 byte[] buffer = new byte[1024 * 4]; 224 // byte[] buffer = new byte[1024 * 4];
233 225
234 while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length))) 226 // while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
235 { 227 // {
236 fs.Write(buffer, 0, bytesRead); 228 // fs.Write(buffer, 0, bytesRead);
237 } 229 // }
238 } 230 // }
239 231
240 digitalSignatureRow[3] = hashFileName; 232 // digitalSignatureRow[3] = hashFileName;
241 } 233 // }
242 } 234 // }
243 } 235 // }
244 } 236 // }
245 } 237 // }
246 238
247 // If any digital certificates exist, extract and preserve them 239 // // If any digital certificates exist, extract and preserve them
248 if (database.TableExists("MsiDigitalCertificate")) 240 // if (database.TableExists("MsiDigitalCertificate"))
249 { 241 // {
250 using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`")) 242 // using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
251 { 243 // {
252 while (true) 244 // while (true)
253 { 245 // {
254 using (Record digitalCertificateRecord = digitalCertificateView.Fetch()) 246 // using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
255 { 247 // {
256 if (null == digitalCertificateRecord) 248 // if (null == digitalCertificateRecord)
257 { 249 // {
258 break; 250 // break;
259 } 251 // }
260 252
261 string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate 253 // string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
262 254
263 // Export to a file, because the MSI API's require us to provide a file path on disk 255 // // 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"); 256 // string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
265 Directory.CreateDirectory(certPath); 257 // Directory.CreateDirectory(certPath);
266 certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer")); 258 // certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
267 259
268 using (FileStream fs = File.Create(certPath)) 260 // using (FileStream fs = File.Create(certPath))
269 { 261 // {
270 int bytesRead; 262 // int bytesRead;
271 byte[] buffer = new byte[1024 * 4]; 263 // byte[] buffer = new byte[1024 * 4];
272 264
273 while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length))) 265 // while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
274 { 266 // {
275 fs.Write(buffer, 0, bytesRead); 267 // fs.Write(buffer, 0, bytesRead);
276 } 268 // }
277 } 269 // }
278 270
279 // Add it to our "add to MsiDigitalCertificate" table dictionary 271 // // Add it to our "add to MsiDigitalCertificate" table dictionary
280 Row digitalCertificateRow = digitalCertificateTable.CreateRow(null); 272 // Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
281 digitalCertificateRow[0] = certificateId; 273 // digitalCertificateRow[0] = certificateId;
282 274
283 // Now set the file path on disk where this binary stream will be picked up at import time 275 // // 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"); 276 // digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
285 277
286 // Load the cert to get it's thumbprint 278 // // Load the cert to get it's thumbprint
287 X509Certificate cert = X509Certificate.CreateFromCertFile(certPath); 279 // X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
288 X509Certificate2 cert2 = new X509Certificate2(cert); 280 // X509Certificate2 cert2 = new X509Certificate2(cert);
289 281
290 certificates.Add(cert2.Thumbprint, certificateId); 282 // certificates.Add(cert2.Thumbprint, certificateId);
291 } 283 // }
292 } 284 // }
293 } 285 // }
294 } 286 // }
295 287
296 using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`")) 288 // using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
297 { 289 // {
298 while (true) 290 // while (true)
299 { 291 // {
300 using (Record mediaRecord = mediaView.Fetch()) 292 // using (Record mediaRecord = mediaView.Fetch())
301 { 293 // {
302 if (null == mediaRecord) 294 // if (null == mediaRecord)
303 { 295 // {
304 break; 296 // break;
305 } 297 // }
306 298
307 X509Certificate2 cert2 = null; 299 // X509Certificate2 cert2 = null;
308 Row digitalSignatureRow = null; 300 // Row digitalSignatureRow = null;
309 301
310 string cabName = mediaRecord.GetString(4); // get the name of the cab 302 // 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. 303 // // If there is no cabinet or it's an internal cab, skip it.
312 if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal)) 304 // if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
313 { 305 // {
314 continue; 306 // continue;
315 } 307 // }
316 308
317 string cabId = mediaRecord.GetString(1); // get the ID of the cab 309 // string cabId = mediaRecord.GetString(1); // get the ID of the cab
318 string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName); 310 // string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName);
319 311
320 // If the cabs aren't there, throw an error but continue to catch the other errors 312 // // If the cabs aren't there, throw an error but continue to catch the other errors
321 if (!File.Exists(cabPath)) 313 // if (!File.Exists(cabPath))
322 { 314 // {
323 this.OnMessage(WixErrors.WixFileNotFound(cabPath)); 315 // this.OnMessage(WixErrors.WixFileNotFound(cabPath));
324 continue; 316 // continue;
325 } 317 // }
326 318
327 try 319 // try
328 { 320 // {
329 // Get the certificate from the cab 321 // // Get the certificate from the cab
330 X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath); 322 // X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
331 cert2 = new X509Certificate2(signedFileCert); 323 // cert2 = new X509Certificate2(signedFileCert);
332 } 324 // }
333 catch (System.Security.Cryptography.CryptographicException e) 325 // catch (System.Security.Cryptography.CryptographicException e)
334 { 326 // {
335 uint HResult = unchecked((uint)Marshal.GetHRForException(e)); 327 // uint HResult = unchecked((uint)Marshal.GetHRForException(e));
336 328
337 // If the file has no cert, continue, but flag that we found at least one so we can later give a warning 329 // // 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 330 // if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
339 { 331 // {
340 foundUnsignedExternals = true; 332 // foundUnsignedExternals = true;
341 continue; 333 // continue;
342 } 334 // }
343 335
344 // todo: exactly which HRESULT corresponds to this issue? 336 // // 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. 337 // // 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 338 // if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
347 (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP 339 // (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
348 { 340 // {
349 this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult))); 341 // this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
350 } 342 // }
351 else // otherwise, generic error 343 // else // otherwise, generic error
352 { 344 // {
353 this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult))); 345 // this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
354 } 346 // }
355 } 347 // }
356 348
357 // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added 349 // // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
358 if (!certificates.ContainsKey(cert2.Thumbprint)) 350 // if (!certificates.ContainsKey(cert2.Thumbprint))
359 { 351 // {
360 // generate a stable identifier 352 // // generate a stable identifier
361 string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint); 353 // string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
362 354
363 // Add it to our "add to MsiDigitalCertificate" table dictionary 355 // // Add it to our "add to MsiDigitalCertificate" table dictionary
364 Row digitalCertificateRow = digitalCertificateTable.CreateRow(null); 356 // Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
365 digitalCertificateRow[0] = certificateGeneratedId; 357 // digitalCertificateRow[0] = certificateGeneratedId;
366 358
367 // Export to a file, because the MSI API's require us to provide a file path on disk 359 // // 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"); 360 // string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
369 Directory.CreateDirectory(certPath); 361 // Directory.CreateDirectory(certPath);
370 certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer")); 362 // certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
371 File.Delete(certPath); 363 // File.Delete(certPath);
372 364
373 using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create))) 365 // using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
374 { 366 // {
375 writer.Write(cert2.RawData); 367 // writer.Write(cert2.RawData);
376 writer.Close(); 368 // writer.Close();
377 } 369 // }
378 370
379 // Now set the file path on disk where this binary stream will be picked up at import time 371 // // 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"); 372 // digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
381 373
382 certificates.Add(cert2.Thumbprint, certificateGeneratedId); 374 // certificates.Add(cert2.Thumbprint, certificateGeneratedId);
383 } 375 // }
384 376
385 digitalSignatureRow = digitalSignatureTable.CreateRow(null); 377 // digitalSignatureRow = digitalSignatureTable.CreateRow(null);
386 378
387 digitalSignatureRow[0] = "Media"; 379 // digitalSignatureRow[0] = "Media";
388 digitalSignatureRow[1] = cabId; 380 // digitalSignatureRow[1] = cabId;
389 digitalSignatureRow[2] = certificates[cert2.Thumbprint]; 381 // digitalSignatureRow[2] = certificates[cert2.Thumbprint];
390 } 382 // }
391 } 383 // }
392 } 384 // }
393 385
394 if (digitalCertificateTable.Rows.Count > 0) 386 // if (digitalCertificateTable.Rows.Count > 0)
395 { 387 // {
396 database.ImportTable(codepage, digitalCertificateTable, this.TempFilesLocation, true); 388 // database.ImportTable(codepage, digitalCertificateTable, this.TempFilesLocation, true);
397 shouldCommit = true; 389 // shouldCommit = true;
398 } 390 // }
399 391
400 if (digitalSignatureTable.Rows.Count > 0) 392 // if (digitalSignatureTable.Rows.Count > 0)
401 { 393 // {
402 database.ImportTable(codepage, digitalSignatureTable, this.TempFilesLocation, true); 394 // database.ImportTable(codepage, digitalSignatureTable, this.TempFilesLocation, true);
403 shouldCommit = true; 395 // shouldCommit = true;
404 } 396 // }
405 397
406 // TODO: if we created the table(s), then we should add the _Validation records for them. 398 // // TODO: if we created the table(s), then we should add the _Validation records for them.
407 399
408 certificates = null; 400 // certificates = null;
409 401
410 // If we did find external cabs but none of them were signed, give a warning 402 // // If we did find external cabs but none of them were signed, give a warning
411 if (foundUnsignedExternals) 403 // if (foundUnsignedExternals)
412 { 404 // {
413 this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile)); 405 // this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile));
414 } 406 // }
415 407
416 if (shouldCommit) 408 // if (shouldCommit)
417 { 409 // {
418 database.Commit(); 410 // database.Commit();
419 } 411 // }
420 } 412 //}
421 413
422 return shouldCommit; 414 //return shouldCommit;
415 return false;
423 } 416 }
424 417
425 /// <summary> 418 /// <summary>
diff --git a/src/WixToolset.Core/Librarian.cs b/src/WixToolset.Core/Librarian.cs
index 66a8c32d..092d81dc 100644
--- a/src/WixToolset.Core/Librarian.cs
+++ b/src/WixToolset.Core/Librarian.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq; 7 using System.Linq;
8 using WixToolset.Core.Bind;
8 using WixToolset.Data; 9 using WixToolset.Data;
9 using WixToolset.Link; 10 using WixToolset.Link;
10 11
@@ -13,20 +14,41 @@ namespace WixToolset
13 /// </summary> 14 /// </summary>
14 public sealed class Librarian 15 public sealed class Librarian
15 { 16 {
17 public Librarian(LibraryContext context)
18 {
19 this.Context = context;
20 }
21
22 private LibraryContext Context { get; }
23
16 /// <summary> 24 /// <summary>
17 /// Create a library by combining several intermediates (objects). 25 /// Create a library by combining several intermediates (objects).
18 /// </summary> 26 /// </summary>
19 /// <param name="sections">The sections to combine into a library.</param> 27 /// <param name="sections">The sections to combine into a library.</param>
20 /// <returns>Returns the new library.</returns> 28 /// <returns>Returns the new library.</returns>
21 public Library Combine(IEnumerable<Section> sections, IEnumerable<Localization> localizations, ILibraryBinaryFileResolver resolver) 29 public Library Combine()
22 { 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);
24 37
25 var embedFilePaths = ResolveFilePathsToEmbed(sections, resolver); 38 var localizationsByCulture = CollateLocalizations(this.Context.Localizations);
26 39
27 var library = new Library(sections, localizationsByCulture, embedFilePaths); 40 var embedFilePaths = ResolveFilePathsToEmbed(this.Context.Sections, fileResolver);
28 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;
30 } 52 }
31 53
32 /// <summary> 54 /// <summary>
@@ -70,12 +92,12 @@ namespace WixToolset
70 return localizationsByCulture; 92 return localizationsByCulture;
71 } 93 }
72 94
73 private static List<string> ResolveFilePathsToEmbed(IEnumerable<Section> sections, ILibraryBinaryFileResolver resolver) 95 private List<string> ResolveFilePathsToEmbed(IEnumerable<Section> sections, FileResolver fileResolver)
74 { 96 {
75 var embedFilePaths = new List<string>(); 97 var embedFilePaths = new List<string>();
76 98
77 // Resolve paths to files that are to be embedded in the library. 99 // Resolve paths to files that are to be embedded in the library.
78 if (null != resolver) 100 if (this.Context.BindFiles)
79 { 101 {
80 foreach (Table table in sections.SelectMany(s => s.Tables)) 102 foreach (Table table in sections.SelectMany(s => s.Tables))
81 { 103 {
@@ -85,7 +107,10 @@ namespace WixToolset
85 { 107 {
86 if (null != objectField.Data) 108 if (null != objectField.Data)
87 { 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
89 if (!String.IsNullOrEmpty(file)) 114 if (!String.IsNullOrEmpty(file))
90 { 115 {
91 // File was successfully resolved so track the embedded index as the embedded file index. 116 // File was successfully resolved so track the embedded index as the embedded file index.
diff --git a/src/WixToolset.Core/LibraryContext.cs b/src/WixToolset.Core/LibraryContext.cs
new file mode 100644
index 00000000..36e38739
--- /dev/null
+++ b/src/WixToolset.Core/LibraryContext.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs
index 1e5b6e96..c1c9f848 100644
--- a/src/WixToolset.Core/Linker.cs
+++ b/src/WixToolset.Core/Linker.cs
@@ -78,7 +78,7 @@ namespace WixToolset
78 /// Gets or sets the Wix variable resolver. 78 /// Gets or sets the Wix variable resolver.
79 /// </summary> 79 /// </summary>
80 /// <value>The Wix variable resolver.</value> 80 /// <value>The Wix variable resolver.</value>
81 public WixVariableResolver WixVariableResolver { get; set; } 81 internal IBindVariableResolver WixVariableResolver { get; set; }
82 82
83 /// <summary> 83 /// <summary>
84 /// Adds an extension. 84 /// Adds an extension.
diff --git a/src/WixToolset.Core/Localizer.cs b/src/WixToolset.Core/Localizer.cs
index 63ead24a..72d0955b 100644
--- a/src/WixToolset.Core/Localizer.cs
+++ b/src/WixToolset.Core/Localizer.cs
@@ -8,11 +8,12 @@ namespace WixToolset
8 using WixToolset.Data; 8 using WixToolset.Data;
9 using WixToolset.Data.Rows; 9 using WixToolset.Data.Rows;
10 using WixToolset.Core.Native; 10 using WixToolset.Core.Native;
11 using WixToolset.Extensibility;
11 12
12 /// <summary> 13 /// <summary>
13 /// Parses localization files and localizes database values. 14 /// Parses localization files and localizes database values.
14 /// </summary> 15 /// </summary>
15 public sealed class Localizer 16 public sealed class Localizer : ILocalizer
16 { 17 {
17 public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl"; 18 public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl";
18 private static string XmlElementName = "WixLocalization"; 19 private static string XmlElementName = "WixLocalization";
@@ -55,7 +56,28 @@ namespace WixToolset
55 /// Gets the codepage. 56 /// Gets the codepage.
56 /// </summary> 57 /// </summary>
57 /// <value>The codepage.</value> 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 }
59 81
60 /// <summary> 82 /// <summary>
61 /// Loads a localization file from a path on disk. 83 /// Loads a localization file from a path on disk.
@@ -97,36 +119,13 @@ namespace WixToolset
97 } 119 }
98 120
99 /// <summary> 121 /// <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. 122 /// Adds a WixVariableRow to a dictionary while performing the expected override checks.
123 /// </summary> 123 /// </summary>
124 /// <param name="variables">Dictionary of variable rows.</param> 124 /// <param name="variables">Dictionary of variable rows.</param>
125 /// <param name="wixVariableRow">Row to add to the variables dictionary.</param> 125 /// <param name="wixVariableRow">Row to add to the variables dictionary.</param>
126 private static void AddWixVariable(IDictionary<string, WixVariableRow> variables, WixVariableRow wixVariableRow) 126 private static void AddWixVariable(IDictionary<string, WixVariableRow> variables, WixVariableRow wixVariableRow)
127 { 127 {
128 WixVariableRow existingWixVariableRow; 128 if (!variables.TryGetValue(wixVariableRow.Id, out var existingWixVariableRow) || (existingWixVariableRow.Overridable && !wixVariableRow.Overridable))
129 if (!variables.TryGetValue(wixVariableRow.Id, out existingWixVariableRow) || (existingWixVariableRow.Overridable && !wixVariableRow.Overridable))
130 { 129 {
131 variables[wixVariableRow.Id] = wixVariableRow; 130 variables[wixVariableRow.Id] = wixVariableRow;
132 } 131 }
diff --git a/src/WixToolset.Core/OptimizeCA.cs b/src/WixToolset.Core/OptimizeCA.cs
new file mode 100644
index 00000000..efd07299
--- /dev/null
+++ b/src/WixToolset.Core/OptimizeCA.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/PatchSymbolFlagsType.cs b/src/WixToolset.Core/PatchSymbolFlagsType.cs
new file mode 100644
index 00000000..eeb5c798
--- /dev/null
+++ b/src/WixToolset.Core/PatchSymbolFlagsType.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/PatchTransform.cs b/src/WixToolset.Core/PatchTransform.cs
index c87b1a21..46e4e6d7 100644
--- a/src/WixToolset.Core/PatchTransform.cs
+++ b/src/WixToolset.Core/PatchTransform.cs
@@ -155,7 +155,7 @@ namespace WixToolset
155 if (!deletedComponent.ContainsKey(componentId)) 155 if (!deletedComponent.ContainsKey(componentId))
156 { 156 {
157 bool foundRemoveFileEntry = false; 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 159
160 Table removeFileTable = this.Transform.Tables["RemoveFile"]; 160 Table removeFileTable = this.Transform.Tables["RemoveFile"];
161 if (null != removeFileTable) 161 if (null != removeFileTable)
@@ -172,7 +172,7 @@ namespace WixToolset
172 // Check if there is a RemoveFile entry for this file 172 // Check if there is a RemoveFile entry for this file
173 if (null != removeFileRow[2]) 173 if (null != removeFileRow[2])
174 { 174 {
175 string removeFileName = Msi.Installer.GetName((string)removeFileRow[2], false, true); 175 string removeFileName = Common.GetName((string)removeFileRow[2], false, true);
176 176
177 // Convert the MSI format for a wildcard string to Regex format. 177 // Convert the MSI format for a wildcard string to Regex format.
178 removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\."); 178 removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\.");
diff --git a/src/WixToolset.Core/Properties/AssemblyInfo.cs b/src/WixToolset.Core/Properties/AssemblyInfo.cs
index b3740b2a..81274e3f 100644
--- a/src/WixToolset.Core/Properties/AssemblyInfo.cs
+++ b/src/WixToolset.Core/Properties/AssemblyInfo.cs
@@ -5,5 +5,5 @@ using System.Reflection;
5using System.Runtime.InteropServices; 5using System.Runtime.InteropServices;
6 6
7[assembly: AssemblyCulture("")] 7[assembly: AssemblyCulture("")]
8[assembly: CLSCompliant(true)] 8[assembly: CLSCompliant(false)]
9[assembly: ComVisible(false)] 9[assembly: ComVisible(false)]
diff --git a/src/WixToolset.Core/TransformsFlags.cs b/src/WixToolset.Core/TransformsFlags.cs
new file mode 100644
index 00000000..d9ec94ac
--- /dev/null
+++ b/src/WixToolset.Core/TransformsFlags.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/UnbindContext.cs b/src/WixToolset.Core/UnbindContext.cs
new file mode 100644
index 00000000..ed55f312
--- /dev/null
+++ b/src/WixToolset.Core/UnbindContext.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core/Unbinder.cs b/src/WixToolset.Core/Unbinder.cs
index 744d5536..2ff51997 100644
--- a/src/WixToolset.Core/Unbinder.cs
+++ b/src/WixToolset.Core/Unbinder.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System;
6 using System.CodeDom.Compiler;
7 using System.Collections; 5 using System.Collections;
8 using System.Collections.Generic;
9 using System.Collections.Specialized;
10 using System.ComponentModel;
11 using System.Globalization;
12 using System.IO; 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;
18 using WixToolset.Data; 7 using WixToolset.Data;
19 using WixToolset.Data.Rows;
20 using WixToolset.Extensibility; 8 using WixToolset.Extensibility;
21 using WixToolset.Msi; 9 using System.Collections.Generic;
22 using WixToolset.Core.Native;
23 using WixToolset.Ole32;
24 10
25 /// <summary> 11 /// <summary>
26 /// Unbinder core of the WiX toolset. 12 /// Unbinder core of the WiX toolset.
27 /// </summary> 13 /// </summary>
28 public sealed class Unbinder : IMessageHandler 14 public sealed class Unbinder
29 { 15 {
30 private string emptyFile;
31 private bool isAdminImage;
32 private int sectionCount;
33 private bool suppressDemodularization;
34 private bool suppressExtractCabinets;
35 private TableDefinitionCollection tableDefinitions; 16 private TableDefinitionCollection tableDefinitions;
36 private ArrayList unbinderExtensions; 17 private ArrayList unbinderExtensions;
37 // private TempFileCollection tempFiles; 18 // private TempFileCollection tempFiles;
@@ -45,61 +26,32 @@ namespace WixToolset
45 this.unbinderExtensions = new ArrayList(); 26 this.unbinderExtensions = new ArrayList();
46 } 27 }
47 28
29 public IEnumerable<IBackendFactory> BackendFactories { get; }
30
48 /// <summary> 31 /// <summary>
49 /// Gets or sets whether the input msi is an admin image. 32 /// Gets or sets whether the input msi is an admin image.
50 /// </summary> 33 /// </summary>
51 /// <value>Set to true if the input msi is part of an admin image.</value> 34 /// <value>Set to true if the input msi is part of an admin image.</value>
52 public bool IsAdminImage 35 public bool IsAdminImage { get; set; }
53 {
54 get { return this.isAdminImage; }
55 set { this.isAdminImage = value; }
56 }
57 36
58 /// <summary> 37 /// <summary>
59 /// Gets or sets the option to suppress demodularizing values. 38 /// Gets or sets the option to suppress demodularizing values.
60 /// </summary> 39 /// </summary>
61 /// <value>The option to suppress demodularizing values.</value> 40 /// <value>The option to suppress demodularizing values.</value>
62 public bool SuppressDemodularization 41 public bool SuppressDemodularization { get; set; }
63 {
64 get { return this.suppressDemodularization; }
65 set { this.suppressDemodularization = value; }
66 }
67 42
68 /// <summary> 43 /// <summary>
69 /// Gets or sets the option to suppress extracting cabinets. 44 /// Gets or sets the option to suppress extracting cabinets.
70 /// </summary> 45 /// </summary>
71 /// <value>The option to suppress extracting cabinets.</value> 46 /// <value>The option to suppress extracting cabinets.</value>
72 public bool SuppressExtractCabinets 47 public bool SuppressExtractCabinets { get; set; }
73 {
74 get { return this.suppressExtractCabinets; }
75 set { this.suppressExtractCabinets = value; }
76 }
77 48
78 /// <summary> 49 /// <summary>
79 /// Gets or sets the temporary path for the Binder. If left null, the binder 50 /// Gets or sets the temporary path for the Binder. If left null, the binder
80 /// will use %TEMP% environment variable. 51 /// will use %TEMP% environment variable.
81 /// </summary> 52 /// </summary>
82 /// <value>Path to temp files.</value> 53 /// <value>Path to temp files.</value>
83 public string TempFilesLocation 54 public string TempFilesLocation => Path.GetTempPath();
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 }
103 55
104 /// <summary> 56 /// <summary>
105 /// Adds extension data. 57 /// Adds extension data.
@@ -156,1336 +108,25 @@ namespace WixToolset
156 // if we don't have the temporary files object yet, get one 108 // if we don't have the temporary files object yet, get one
157 Directory.CreateDirectory(this.TempFilesLocation); // ensure the base path is there 109 Directory.CreateDirectory(this.TempFilesLocation); // ensure the base path is there
158 110
159 if (OutputType.Patch == outputType) 111 var context = new UnbindContext();
160 { 112 context.InputFilePath = file;
161 return this.UnbindPatch(file, exportBasePath); 113 context.ExportBasePath = exportBasePath;
162 } 114 context.IntermediateFolder = this.TempFilesLocation;
163 else if (OutputType.Transform == outputType) 115 context.IsAdminImage = this.IsAdminImage;
164 { 116 context.SuppressDemodularization = this.SuppressDemodularization;
165 return this.UnbindTransform(file, exportBasePath); 117 context.SuppressExtractCabinets = this.SuppressExtractCabinets;
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);
554 118
555 switch (row.Fields[i].Column.Category) 119 foreach (var factory in this.BackendFactories)
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)
614 { 120 {
615 Table table = output.Tables["_SummaryInformation"]; 121 if (factory.TryCreateBackend(outputType.ToString(), file, null, out var backend))
616
617 foreach (Row row in table.Rows)
618 { 122 {
619 if (9 == (int)row[0]) // PID_REVNUMBER 123 return backend.Unbind(context);
620 {
621 row[1] = modularizationGuid;
622 }
623 } 124 }
624 } 125 }
625 126
626 if (this.isAdminImage) 127 // TODO: Display message that could not find a unbinder for output type?
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 }
1313 128
1314 return null; 129 return null;
1315 } 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 }
1490 } 131 }
1491} 132}
diff --git a/src/WixToolset.Core/Uuid.cs b/src/WixToolset.Core/Uuid.cs
index 2e599793..d512d92f 100644
--- a/src/WixToolset.Core/Uuid.cs
+++ b/src/WixToolset.Core/Uuid.cs
@@ -10,7 +10,7 @@ namespace WixToolset
10 /// <summary> 10 /// <summary>
11 /// Implementation of RFC 4122 - A Universally Unique Identifier (UUID) URN Namespace. 11 /// Implementation of RFC 4122 - A Universally Unique Identifier (UUID) URN Namespace.
12 /// </summary> 12 /// </summary>
13 internal sealed class Uuid 13 public sealed class Uuid
14 { 14 {
15 /// <summary> 15 /// <summary>
16 /// Protect the constructor. 16 /// Protect the constructor.
diff --git a/src/WixToolset.Core/WixStrings.Designer.cs b/src/WixToolset.Core/WixStrings.Designer.cs
index 4ba9381a..75e2b908 100644
--- a/src/WixToolset.Core/WixStrings.Designer.cs
+++ b/src/WixToolset.Core/WixStrings.Designer.cs
@@ -14,7 +14,7 @@ namespace WixToolset {
14 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 14 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
15 [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 15 [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
16 [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
17 internal class WixStrings { 17 public class WixStrings {
18 18
19 private static global::System.Resources.ResourceManager resourceMan; 19 private static global::System.Resources.ResourceManager resourceMan;
20 20
@@ -127,7 +127,7 @@ namespace WixToolset {
127 /// <summary> 127 /// <summary>
128 /// Looks up a localized string similar to Could not determine ProductCode from transform summary information. 128 /// Looks up a localized string similar to Could not determine ProductCode from transform summary information.
129 /// </summary> 129 /// </summary>
130 internal static string EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo { 130 public static string EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo {
131 get { 131 get {
132 return ResourceManager.GetString("EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo", resourceCulture); 132 return ResourceManager.GetString("EXP_CouldnotDetermineProductCodeFromTransformSummaryInfo", resourceCulture);
133 } 133 }
@@ -226,7 +226,7 @@ namespace WixToolset {
226 /// <summary> 226 /// <summary>
227 /// Looks up a localized string similar to Transform authored into multiple Media &apos;{0}&apos; and &apos;{1}&apos;.. 227 /// Looks up a localized string similar to Transform authored into multiple Media &apos;{0}&apos; and &apos;{1}&apos;..
228 /// </summary> 228 /// </summary>
229 internal static string EXP_TransformAuthoredIntoMultipleMedia { 229 public static string EXP_TransformAuthoredIntoMultipleMedia {
230 get { 230 get {
231 return ResourceManager.GetString("EXP_TransformAuthoredIntoMultipleMedia", resourceCulture); 231 return ResourceManager.GetString("EXP_TransformAuthoredIntoMultipleMedia", resourceCulture);
232 } 232 }
@@ -253,7 +253,7 @@ namespace WixToolset {
253 /// <summary> 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;. 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> 255 /// </summary>
256 internal static string EXP_UnexpectedMergerErrorInSourceFile { 256 public static string EXP_UnexpectedMergerErrorInSourceFile {
257 get { 257 get {
258 return ResourceManager.GetString("EXP_UnexpectedMergerErrorInSourceFile", resourceCulture); 258 return ResourceManager.GetString("EXP_UnexpectedMergerErrorInSourceFile", resourceCulture);
259 } 259 }
@@ -262,7 +262,7 @@ namespace WixToolset {
262 /// <summary> 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;. 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> 264 /// </summary>
265 internal static string EXP_UnexpectedMergerErrorWithType { 265 public static string EXP_UnexpectedMergerErrorWithType {
266 get { 266 get {
267 return ResourceManager.GetString("EXP_UnexpectedMergerErrorWithType", resourceCulture); 267 return ResourceManager.GetString("EXP_UnexpectedMergerErrorWithType", resourceCulture);
268 } 268 }
diff --git a/src/WixToolset.Core/WixVariableResolver.cs b/src/WixToolset.Core/WixVariableResolver.cs
index d437423c..357ff700 100644
--- a/src/WixToolset.Core/WixVariableResolver.cs
+++ b/src/WixToolset.Core/WixVariableResolver.cs
@@ -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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -10,11 +10,12 @@ namespace WixToolset
10 using System.Text.RegularExpressions; 10 using System.Text.RegularExpressions;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Data.Rows; 12 using WixToolset.Data.Rows;
13 using WixToolset.Extensibility;
13 14
14 /// <summary> 15 /// <summary>
15 /// WiX variable resolver. 16 /// WiX variable resolver.
16 /// </summary> 17 /// </summary>
17 public sealed class WixVariableResolver 18 internal sealed class WixVariableResolver : IBindVariableResolver
18 { 19 {
19 private Dictionary<string, string> wixVariables; 20 private Dictionary<string, string> wixVariables;
20 21
@@ -31,7 +32,7 @@ namespace WixToolset
31 /// Gets or sets the localizer. 32 /// Gets or sets the localizer.
32 /// </summary> 33 /// </summary>
33 /// <value>The localizer.</value> 34 /// <value>The localizer.</value>
34 public Localizer Localizer { get; private set; } 35 private Localizer Localizer { get; }
35 36
36 /// <summary> 37 /// <summary>
37 /// Gets the count of variables added to the resolver. 38 /// Gets the count of variables added to the resolver.
@@ -83,10 +84,7 @@ namespace WixToolset
83 /// <returns>The resolved value.</returns> 84 /// <returns>The resolved value.</returns>
84 public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly) 85 public string ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly)
85 { 86 {
86 bool isDefault = false; 87 return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, out var defaultIgnored, out var delayedIgnored);
87 bool delayedResolve = false;
88
89 return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, ref isDefault, ref delayedResolve);
90 } 88 }
91 89
92 /// <summary> 90 /// <summary>
@@ -97,11 +95,9 @@ namespace WixToolset
97 /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param> 95 /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param>
98 /// <param name="isDefault">true if the resolved value was the default.</param> 96 /// <param name="isDefault">true if the resolved value was the default.</param>
99 /// <returns>The resolved value.</returns> 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)
101 { 99 {
102 bool delayedResolve = false; 100 return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, out isDefault, out var ignored);
103
104 return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, ref isDefault, ref delayedResolve);
105 } 101 }
106 102
107 /// <summary> 103 /// <summary>
@@ -114,9 +110,9 @@ namespace WixToolset
114 /// <param name="isDefault">true if the resolved value was the default.</param> 110 /// <param name="isDefault">true if the resolved value was the default.</param>
115 /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param> 111 /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
116 /// <returns>The resolved value.</returns> 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)
118 { 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);
120 } 116 }
121 117
122 /// <summary> 118 /// <summary>
@@ -129,7 +125,7 @@ namespace WixToolset
129 /// <param name="isDefault">true if the resolved value was the default.</param> 125 /// <param name="isDefault">true if the resolved value was the default.</param>
130 /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param> 126 /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
131 /// <returns>The resolved value.</returns> 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)
133 { 129 {
134 MatchCollection matches = Common.WixVariableRegex.Matches(value); 130 MatchCollection matches = Common.WixVariableRegex.Matches(value);
135 131
@@ -190,10 +186,7 @@ namespace WixToolset
190 Messaging.Instance.OnMessage(WixWarnings.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId)); 186 Messaging.Instance.OnMessage(WixWarnings.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId));
191 } 187 }
192 188
193 if (null != this.Localizer) 189 resolvedValue = this.Localizer?.GetLocalizedValue(variableId);
194 {
195 resolvedValue = this.Localizer.GetLocalizedValue(variableId);
196 }
197 } 190 }
198 else if (!localizationOnly && "wix" == variableNamespace) 191 else if (!localizationOnly && "wix" == variableNamespace)
199 { 192 {
@@ -223,6 +216,7 @@ namespace WixToolset
223 } 216 }
224 else 217 else
225 { 218 {
219
226 // insert the resolved value if it was found or display an error 220 // insert the resolved value if it was found or display an error
227 if (null != resolvedValue) 221 if (null != resolvedValue)
228 { 222 {
@@ -248,6 +242,19 @@ namespace WixToolset
248 } 242 }
249 243
250 /// <summary> 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>
251 /// Resolve the delay variables in a value. 258 /// Resolve the delay variables in a value.
252 /// </summary> 259 /// </summary>
253 /// <param name="sourceLineNumbers">The source line information for the value.</param> 260 /// <param name="sourceLineNumbers">The source line information for the value.</param>
diff --git a/src/wix/wix.csproj b/src/wix/wix.csproj
index f183be39..828793ae 100644
--- a/src/wix/wix.csproj
+++ b/src/wix/wix.csproj
@@ -21,6 +21,8 @@
21 21
22 <ItemGroup> 22 <ItemGroup>
23 <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" /> 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" />
24 26
25 <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " /> 27 <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj') " />
26 <PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !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') " />