aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj1
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs26
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs127
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs61
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs4
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs42
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs32
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs11
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs1
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs101
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj1
-rw-r--r--src/WixToolset.Core/BindContext.cs8
-rw-r--r--src/WixToolset.Core/CommandLine/BuildCommand.cs4
-rw-r--r--src/WixToolset.Core/Compiler.cs4
-rw-r--r--src/WixToolset.Core/CompilerCore.cs18
-rw-r--r--src/WixToolset.Core/Compiler_Bundle.cs2
-rw-r--r--src/WixToolset.Core/Compiler_Module.cs20
-rw-r--r--src/WixToolset.Core/Compiler_Package.cs46
-rw-r--r--src/WixToolset.Core/Compiler_Patch.cs9
-rw-r--r--src/WixToolset.Core/Compiler_PatchCreation.cs2
-rw-r--r--src/WixToolset.Core/IResolver.cs9
-rw-r--r--src/WixToolset.Core/Link/CollateLocalizationsCommand.cs4
-rw-r--r--src/WixToolset.Core/Linker.cs2
-rw-r--r--src/WixToolset.Core/LocalizationParser.cs20
-rw-r--r--src/WixToolset.Core/ResolveResult.cs6
-rw-r--r--src/WixToolset.Core/Resolver.cs81
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/LanguageFixture.cs155
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/LinkerFixture.cs4
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/ParseFixture.cs2
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl7
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl7
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxl7
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxs16
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/Language/PackageWithEnSummaryInfo.ja-jp.wxl7
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/Language/data/test.txt1
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/VariableResolverFixture.cs2
38 files changed, 619 insertions, 235 deletions
diff --git a/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj b/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
index 2e828eae..85bfae69 100644
--- a/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
+++ b/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
@@ -22,7 +22,6 @@
22 </ItemGroup> 22 </ItemGroup>
23 23
24 <ItemGroup> 24 <ItemGroup>
25 <PackageReference Include="System.Security.Permissions" Version="4.6.0" />
26 <PackageReference Include="WixToolset.Burn" Version="4.0.*" /> 25 <PackageReference Include="WixToolset.Burn" Version="4.0.*" />
27 <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" /> 26 <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" />
28 <PackageReference Include="WixToolset.Data" Version="4.0.*" /> 27 <PackageReference Include="WixToolset.Data" Version="4.0.*" />
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
index 5f8df92c..76bcd532 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
@@ -91,15 +91,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
91 var symbols = this.Intermediate.Sections.SelectMany(s => s.Symbols).ToList(); 91 var symbols = this.Intermediate.Sections.SelectMany(s => s.Symbols).ToList();
92 92
93 // Get the patch id from the WixPatchId symbol. 93 // Get the patch id from the WixPatchId symbol.
94 var patchIdSymbol = symbols.OfType<WixPatchIdSymbol>().FirstOrDefault(); 94 var patchSymbol = symbols.OfType<WixPatchSymbol>().FirstOrDefault();
95 95
96 if (String.IsNullOrEmpty(patchIdSymbol.Id?.Id)) 96 if (String.IsNullOrEmpty(patchSymbol.Id?.Id))
97 { 97 {
98 this.Messaging.Write(ErrorMessages.ExpectedPatchIdInWixMsp()); 98 this.Messaging.Write(ErrorMessages.ExpectedPatchIdInWixMsp());
99 return subStorages; 99 return subStorages;
100 } 100 }
101 101
102 if (String.IsNullOrEmpty(patchIdSymbol.ClientPatchId)) 102 if (String.IsNullOrEmpty(patchSymbol.ClientPatchId))
103 { 103 {
104 this.Messaging.Write(ErrorMessages.ExpectedClientPatchIdInWixMsp()); 104 this.Messaging.Write(ErrorMessages.ExpectedClientPatchIdInWixMsp());
105 return subStorages; 105 return subStorages;
@@ -115,7 +115,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
115 } 115 }
116 116
117 // populate MSP summary information 117 // populate MSP summary information
118 var patchMetadata = this.PopulateSummaryInformation(summaryInfo, symbols, patchIdSymbol, section.Codepage); 118 var patchMetadata = this.PopulateSummaryInformation(summaryInfo, symbols, patchSymbol);
119 119
120 // enumerate transforms 120 // enumerate transforms
121 var productCodes = new SortedSet<string>(); 121 var productCodes = new SortedSet<string>();
@@ -168,7 +168,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
168 mainTransform.Transform.Tables.Remove("Media"); 168 mainTransform.Transform.Tables.Remove("Media");
169 mainTransform.Transform.Tables.Remove("MsiDigitalSignature"); 169 mainTransform.Transform.Tables.Remove("MsiDigitalSignature");
170 170
171 var pairedTransform = this.BuildPairedTransform(summaryInfo, patchMetadata, patchIdSymbol, mainTransform.Transform, mediaSymbol, baselineSymbol, out var productCode); 171 var pairedTransform = this.BuildPairedTransform(summaryInfo, patchMetadata, patchSymbol, mainTransform.Transform, mediaSymbol, baselineSymbol, out var productCode);
172 172
173 productCode = productCode.ToUpperInvariant(); 173 productCode = productCode.ToUpperInvariant();
174 productCodes.Add(productCode); 174 productCodes.Add(productCode);
@@ -211,14 +211,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
211 productCodes = FinalizePatchProductCodes(symbols, productCodes); 211 productCodes = FinalizePatchProductCodes(symbols, productCodes);
212 212
213 // Semicolon delimited list of the product codes that can accept the patch. 213 // Semicolon delimited list of the product codes that can accept the patch.
214 summaryInfo.Add(SummaryInformationType.PatchProductCodes, new SummaryInformationSymbol(patchIdSymbol.SourceLineNumbers) 214 summaryInfo.Add(SummaryInformationType.PatchProductCodes, new SummaryInformationSymbol(patchSymbol.SourceLineNumbers)
215 { 215 {
216 PropertyId = SummaryInformationType.PatchProductCodes, 216 PropertyId = SummaryInformationType.PatchProductCodes,
217 Value = String.Join(";", productCodes) 217 Value = String.Join(";", productCodes)
218 }); 218 });
219 219
220 // Semicolon delimited list of transform substorage names in the order they are applied. 220 // Semicolon delimited list of transform substorage names in the order they are applied.
221 summaryInfo.Add(SummaryInformationType.TransformNames, new SummaryInformationSymbol(patchIdSymbol.SourceLineNumbers) 221 summaryInfo.Add(SummaryInformationType.TransformNames, new SummaryInformationSymbol(patchSymbol.SourceLineNumbers)
222 { 222 {
223 PropertyId = SummaryInformationType.TransformNames, 223 PropertyId = SummaryInformationType.TransformNames,
224 Value = String.Join(";", transformNames) 224 Value = String.Join(";", transformNames)
@@ -262,25 +262,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
262 return result; 262 return result;
263 } 263 }
264 264
265 private Dictionary<string, MsiPatchMetadataSymbol> PopulateSummaryInformation(Dictionary<SummaryInformationType, SummaryInformationSymbol> summaryInfo, List<IntermediateSymbol> symbols, WixPatchIdSymbol patchIdSymbol, int codepage) 265 private Dictionary<string, MsiPatchMetadataSymbol> PopulateSummaryInformation(Dictionary<SummaryInformationType, SummaryInformationSymbol> summaryInfo, List<IntermediateSymbol> symbols, WixPatchSymbol patchSymbol)
266 { 266 {
267 // PID_CODEPAGE 267 // PID_CODEPAGE
268 if (!summaryInfo.ContainsKey(SummaryInformationType.Codepage)) 268 if (!summaryInfo.ContainsKey(SummaryInformationType.Codepage))
269 { 269 {
270 // Set the code page by default to the same code page for the 270 // Set the code page by default to the same code page for the
271 // string pool in the database. 271 // string pool in the database.
272 AddSummaryInformation(SummaryInformationType.Codepage, codepage.ToString(CultureInfo.InvariantCulture), patchIdSymbol.SourceLineNumbers); 272 AddSummaryInformation(SummaryInformationType.Codepage, patchSymbol.Codepage?.ToString(CultureInfo.InvariantCulture) ?? "0", patchSymbol.SourceLineNumbers);
273 } 273 }
274 274
275 // GUID patch code for the patch. 275 // GUID patch code for the patch.
276 AddSummaryInformation(SummaryInformationType.PatchCode, patchIdSymbol.Id.Id, patchIdSymbol.SourceLineNumbers); 276 AddSummaryInformation(SummaryInformationType.PatchCode, patchSymbol.Id.Id, patchSymbol.SourceLineNumbers);
277 277
278 // Indicates the minimum Windows Installer version that is required to install the patch. 278 // Indicates the minimum Windows Installer version that is required to install the patch.
279 AddSummaryInformation(SummaryInformationType.PatchInstallerRequirement, ((int)SummaryInformation.InstallerRequirement.Version31).ToString(CultureInfo.InvariantCulture), patchIdSymbol.SourceLineNumbers); 279 AddSummaryInformation(SummaryInformationType.PatchInstallerRequirement, ((int)SummaryInformation.InstallerRequirement.Version31).ToString(CultureInfo.InvariantCulture), patchSymbol.SourceLineNumbers);
280 280
281 if (!summaryInfo.ContainsKey(SummaryInformationType.Security)) 281 if (!summaryInfo.ContainsKey(SummaryInformationType.Security))
282 { 282 {
283 AddSummaryInformation(SummaryInformationType.Security, "4", patchIdSymbol.SourceLineNumbers); // Read-only enforced; 283 AddSummaryInformation(SummaryInformationType.Security, "4", patchSymbol.SourceLineNumbers); // Read-only enforced;
284 } 284 }
285 285
286 // Use authored comments or default to display name. 286 // Use authored comments or default to display name.
@@ -1090,7 +1090,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1090 /// <summary> 1090 /// <summary>
1091 /// Create the #transform for the given main transform. 1091 /// Create the #transform for the given main transform.
1092 /// </summary> 1092 /// </summary>
1093 private WindowsInstallerData BuildPairedTransform(Dictionary<SummaryInformationType, SummaryInformationSymbol> summaryInfo, Dictionary<string, MsiPatchMetadataSymbol> patchMetadata, WixPatchIdSymbol patchIdSymbol, WindowsInstallerData mainTransform, MediaSymbol mediaSymbol, WixPatchBaselineSymbol baselineSymbol, out string productCode) 1093 private WindowsInstallerData BuildPairedTransform(Dictionary<SummaryInformationType, SummaryInformationSymbol> summaryInfo, Dictionary<string, MsiPatchMetadataSymbol> patchMetadata, WixPatchSymbol patchIdSymbol, WindowsInstallerData mainTransform, MediaSymbol mediaSymbol, WixPatchBaselineSymbol baselineSymbol, out string productCode)
1094 { 1094 {
1095 productCode = null; 1095 productCode = null;
1096 1096
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index fb5d7b83..06b51ba1 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -37,7 +37,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
37 37
38 this.CabbingThreadCount = context.CabbingThreadCount; 38 this.CabbingThreadCount = context.CabbingThreadCount;
39 this.CabCachePath = context.CabCachePath; 39 this.CabCachePath = context.CabCachePath;
40 this.Codepage = context.Codepage;
41 this.DefaultCompressionLevel = context.DefaultCompressionLevel; 40 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
42 this.DelayedFields = context.DelayedFields; 41 this.DelayedFields = context.DelayedFields;
43 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; 42 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
@@ -47,6 +46,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
47 this.OutputPath = context.OutputPath; 46 this.OutputPath = context.OutputPath;
48 this.OutputPdbPath = context.PdbPath; 47 this.OutputPdbPath = context.PdbPath;
49 this.PdbType = context.PdbType; 48 this.PdbType = context.PdbType;
49 this.ResolvedCodepage = context.ResolvedCodepage;
50 this.ResolvedSummaryInformationCodepage = context.ResolvedSummaryInformationCodepage;
51 this.ResolvedLcid = context.ResolvedLcid;
50 this.SuppressLayout = context.SuppressLayout; 52 this.SuppressLayout = context.SuppressLayout;
51 53
52 this.SubStorages = subStorages; 54 this.SubStorages = subStorages;
@@ -67,8 +69,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
67 69
68 private IPathResolver PathResolver { get; } 70 private IPathResolver PathResolver { get; }
69 71
70 private int Codepage { get; }
71
72 private int CabbingThreadCount { get; } 72 private int CabbingThreadCount { get; }
73 73
74 private string CabCachePath { get; } 74 private string CabCachePath { get; }
@@ -95,6 +95,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
95 95
96 private string OutputPdbPath { get; } 96 private string OutputPdbPath { get; }
97 97
98 private int? ResolvedCodepage { get; }
99
100 private int? ResolvedSummaryInformationCodepage { get; }
101
102 private int? ResolvedLcid { get; }
103
98 private bool SuppressAddingValidationRows { get; } 104 private bool SuppressAddingValidationRows { get; }
99 105
100 private bool SuppressLayout { get; } 106 private bool SuppressLayout { get; }
@@ -111,21 +117,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind
111 117
112 public IBindResult Execute() 118 public IBindResult Execute()
113 { 119 {
114 if (!this.Intermediate.HasLevel(Data.IntermediateLevels.Linked) && !this.Intermediate.HasLevel(Data.IntermediateLevels.Resolved)) 120 if (!this.Intermediate.HasLevel(Data.IntermediateLevels.Linked) || !this.Intermediate.HasLevel(Data.IntermediateLevels.Resolved))
115 { 121 {
116 this.Messaging.Write(ErrorMessages.IntermediatesMustBeResolved(this.Intermediate.Id)); 122 this.Messaging.Write(ErrorMessages.IntermediatesMustBeResolved(this.Intermediate.Id));
117 } 123 }
118 124
119 var section = this.Intermediate.Sections.Single(); 125 var section = this.Intermediate.Sections.Single();
120 126
127 var packageSymbol = (section.Type == SectionType.Product) ? this.GetSingleSymbol<WixPackageSymbol>(section) : null;
128 var moduleSymbol = (section.Type == SectionType.Module) ? this.GetSingleSymbol<WixModuleSymbol>(section) : null;
129 var patchSymbol = (section.Type == SectionType.Patch) ? this.GetSingleSymbol<WixPatchSymbol>(section) : null;
130
121 var fileTransfers = new List<IFileTransfer>(); 131 var fileTransfers = new List<IFileTransfer>();
122 var trackedFiles = new List<ITrackedFile>(); 132 var trackedFiles = new List<ITrackedFile>();
123 133
124 var containsMergeModules = false; 134 var containsMergeModules = false;
125 135
126 // If there are any fields to resolve later, create the cache to populate during bind.
127 var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null;
128
129 // Load standard tables, authored custom tables, and extension custom tables. 136 // Load standard tables, authored custom tables, and extension custom tables.
130 TableDefinitionCollection tableDefinitions; 137 TableDefinitionCollection tableDefinitions;
131 { 138 {
@@ -135,7 +142,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
135 tableDefinitions = command.TableDefinitions; 142 tableDefinitions = command.TableDefinitions;
136 } 143 }
137 144
138 // Process the summary information table before the other tables. 145 // Calculate codepage
146 var codepage = this.CalculateCodepage(packageSymbol, moduleSymbol, patchSymbol);
147
148 // Process properties and create the delayed variable cache if needed.
149 Dictionary<string, string> variableCache = null;
150 string productLanguage = null;
151 {
152 var command = new ProcessPropertiesCommand(section, packageSymbol, this.ResolvedLcid ?? 0, this.DelayedFields.Any(), this.WindowsInstallerBackendHelper);
153 command.Execute();
154
155 variableCache = command.DelayedVariablesCache;
156 productLanguage = command.ProductLanguage;
157 }
158
159 // Process the summary information table after properties are processed.
139 bool compressed; 160 bool compressed;
140 bool longNames; 161 bool longNames;
141 int installerVersion; 162 int installerVersion;
@@ -144,7 +165,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
144 { 165 {
145 var branding = this.ServiceProvider.GetService<IWixBranding>(); 166 var branding = this.ServiceProvider.GetService<IWixBranding>();
146 167
147 var command = new BindSummaryInfoCommand(section, this.WindowsInstallerBackendHelper, branding); 168 var command = new BindSummaryInfoCommand(section, this.ResolvedSummaryInformationCodepage, productLanguage, this.WindowsInstallerBackendHelper, branding);
148 command.Execute(); 169 command.Execute();
149 170
150 compressed = command.Compressed; 171 compressed = command.Compressed;
@@ -154,49 +175,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
154 modularizationSuffix = command.ModularizationSuffix; 175 modularizationSuffix = command.ModularizationSuffix;
155 } 176 }
156 177
157 // Add binder variables for all properties.
158 if (SectionType.Product == section.Type || variableCache != null)
159 {
160 foreach (var propertyRow in section.Symbols.OfType<PropertySymbol>())
161 {
162 // Set the ProductCode if it is to be generated.
163 if ("ProductCode".Equals(propertyRow.Id.Id, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
164 {
165 propertyRow.Value = this.WindowsInstallerBackendHelper.CreateGuid();
166
167#if TODO_PATCHING // Is this still necessary?
168
169 // Update the target ProductCode in any instance transforms.
170 foreach (SubStorage subStorage in this.Output.SubStorages)
171 {
172 Output subStorageOutput = subStorage.Data;
173 if (OutputType.Transform != subStorageOutput.Type)
174 {
175 continue;
176 }
177
178 Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
179 foreach (Row row in instanceSummaryInformationTable.Rows)
180 {
181 if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
182 {
183 row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
184 break;
185 }
186 }
187 }
188#endif
189 }
190
191 // Add the property name and value to the variableCache.
192 if (variableCache != null)
193 {
194 var key = String.Concat("property.", propertyRow.Id.Id);
195 variableCache[key] = propertyRow.Value;
196 }
197 }
198 }
199
200 // Sequence all the actions. 178 // Sequence all the actions.
201 { 179 {
202 var command = new SequenceActionsCommand(this.Messaging, section); 180 var command = new SequenceActionsCommand(this.Messaging, section);
@@ -415,7 +393,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
415 // Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better. 393 // Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better.
416 WindowsInstallerData data; 394 WindowsInstallerData data;
417 { 395 {
418 var command = new CreateWindowsInstallerDataFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions, this.WindowsInstallerBackendHelper); 396 var command = new CreateWindowsInstallerDataFromIRCommand(this.Messaging, section, tableDefinitions, codepage, this.BackendExtensions, this.WindowsInstallerBackendHelper);
419 data = command.Execute(); 397 data = command.Execute();
420 } 398 }
421 399
@@ -450,7 +428,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
450 428
451 if (SectionType.Patch == section.Type && this.DeltaBinaryPatch) 429 if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
452 { 430 {
453 var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Symbols.OfType<WixPatchIdSymbol>().FirstOrDefault()); 431 var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Symbols.OfType<WixPatchSymbol>().FirstOrDefault());
454 command.Execute(); 432 command.Execute();
455 } 433 }
456 434
@@ -508,7 +486,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
508 var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); 486 var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final);
509 trackedFiles.Add(trackMsi); 487 trackedFiles.Add(trackMsi);
510 488
511 var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false); 489 var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false);
512 command.Execute(); 490 command.Execute();
513 491
514 trackedFiles.AddRange(command.GeneratedTemporaryFiles); 492 trackedFiles.AddRange(command.GeneratedTemporaryFiles);
@@ -532,7 +510,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
532 if (this.Messaging.EncounteredError) 510 if (this.Messaging.EncounteredError)
533 { 511 {
534 return null; 512 return null;
535 } 513 }
536 514
537 // Validate the output if there are CUBe files and we're not explicitly suppressing validation. 515 // Validate the output if there are CUBe files and we're not explicitly suppressing validation.
538 if (this.CubeFiles != null && !this.SuppressValidation) 516 if (this.CubeFiles != null && !this.SuppressValidation)
@@ -575,6 +553,43 @@ namespace WixToolset.Core.WindowsInstaller.Bind
575 return result; 553 return result;
576 } 554 }
577 555
556 private int CalculateCodepage(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol, WixPatchSymbol patchSymbol)
557 {
558 var codepage = packageSymbol?.Codepage ?? moduleSymbol?.Codepage ?? patchSymbol?.Codepage;
559
560 if (String.IsNullOrEmpty(codepage))
561 {
562 codepage = this.ResolvedCodepage?.ToString() ?? "65001";
563
564 if (packageSymbol != null)
565 {
566 packageSymbol.Codepage = codepage;
567 }
568 else if (moduleSymbol != null)
569 {
570 moduleSymbol.Codepage = codepage;
571 }
572 else if (patchSymbol != null)
573 {
574 patchSymbol.Codepage = codepage;
575 }
576 }
577
578 return this.WindowsInstallerBackendHelper.GetValidCodePage(codepage);
579 }
580
581 private T GetSingleSymbol<T>(IntermediateSection section) where T : IntermediateSymbol
582 {
583 var symbols = section.Symbols.OfType<T>().ToList();
584
585 if (1 != symbols.Count)
586 {
587 throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T)));
588 }
589
590 return symbols[0];
591 }
592
578 private WixOutput CreateWixout(List<ITrackedFile> trackedFiles, Intermediate intermediate, WindowsInstallerData data) 593 private WixOutput CreateWixout(List<ITrackedFile> trackedFiles, Intermediate intermediate, WindowsInstallerData data)
579 { 594 {
580 WixOutput wixout; 595 WixOutput wixout;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
index babe0c1b..41da2a13 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
@@ -14,15 +14,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
14 /// </summary> 14 /// </summary>
15 internal class BindSummaryInfoCommand 15 internal class BindSummaryInfoCommand
16 { 16 {
17 public BindSummaryInfoCommand(IntermediateSection section, IBackendHelper backendHelper, IWixBranding branding) 17 public BindSummaryInfoCommand(IntermediateSection section, int? summaryInformationCodepage, string productLanguage, IBackendHelper backendHelper, IWixBranding branding)
18 { 18 {
19 this.Section = section; 19 this.Section = section;
20 this.SummaryInformationCodepage = summaryInformationCodepage;
21 this.ProductLanguage = productLanguage;
20 this.BackendHelper = backendHelper; 22 this.BackendHelper = backendHelper;
21 this.Branding = branding; 23 this.Branding = branding;
22 } 24 }
23 25
24 private IntermediateSection Section { get; } 26 private IntermediateSection Section { get; }
25 27
28 private int? SummaryInformationCodepage { get; }
29
30 private string ProductLanguage { get; }
31
26 private IBackendHelper BackendHelper { get; } 32 private IBackendHelper BackendHelper { get; }
27 33
28 private IWixBranding Branding { get; } 34 private IWixBranding Branding { get; }
@@ -53,6 +59,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
53 this.InstallerVersion = 0; 59 this.InstallerVersion = 0;
54 this.ModularizationSuffix = null; 60 this.ModularizationSuffix = null;
55 61
62 SummaryInformationSymbol summaryInformationCodepageSymbol = null;
63 SummaryInformationSymbol platformAndLanguageSymbol = null;
56 var foundCreateDateTime = false; 64 var foundCreateDateTime = false;
57 var foundLastSaveDataTime = false; 65 var foundLastSaveDataTime = false;
58 var foundCreatingApplication = false; 66 var foundCreatingApplication = false;
@@ -64,20 +72,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
64 switch (summaryInformationSymbol.PropertyId) 72 switch (summaryInformationSymbol.PropertyId)
65 { 73 {
66 case SummaryInformationType.Codepage: // PID_CODEPAGE 74 case SummaryInformationType.Codepage: // PID_CODEPAGE
67 // make sure the code page is an int and not a web name or null 75 summaryInformationCodepageSymbol = summaryInformationSymbol;
68 var codepage = summaryInformationSymbol.Value;
69
70 if (String.IsNullOrEmpty(codepage))
71 {
72 codepage = "0";
73 }
74 else
75 {
76 summaryInformationSymbol.Value = this.BackendHelper.GetValidCodePage(codepage, false, false, summaryInformationSymbol.SourceLineNumbers).ToString(CultureInfo.InvariantCulture);
77 }
78 break; 76 break;
77
79 case SummaryInformationType.PlatformAndLanguage: 78 case SummaryInformationType.PlatformAndLanguage:
80 this.Platform = GetPlatformFromSummaryInformation(summaryInformationSymbol.Value); 79 platformAndLanguageSymbol = summaryInformationSymbol;
81 break; 80 break;
82 81
83 case SummaryInformationType.PackageCode: // PID_REVNUMBER 82 case SummaryInformationType.PackageCode: // PID_REVNUMBER
@@ -117,7 +116,31 @@ namespace WixToolset.Core.WindowsInstaller.Bind
117 } 116 }
118 } 117 }
119 118
120 // set the revision number (package/patch code) if it should be automatically generated 119 // Ensure the codepage is set properly.
120 if (summaryInformationCodepageSymbol == null)
121 {
122 summaryInformationCodepageSymbol = this.Section.AddSymbol(new SummaryInformationSymbol(null)
123 {
124 PropertyId = SummaryInformationType.Codepage
125 });
126 }
127
128 var codepage = summaryInformationCodepageSymbol.Value;
129
130 if (String.IsNullOrEmpty(codepage))
131 {
132 codepage = this.SummaryInformationCodepage?.ToString(CultureInfo.InvariantCulture) ?? "1252";
133 }
134
135 summaryInformationCodepageSymbol.Value = this.BackendHelper.GetValidCodePage(codepage, onlyAnsi: true).ToString(CultureInfo.InvariantCulture);
136
137 // Ensure the language is set properly and figure out what platform we are targeting.
138 if (platformAndLanguageSymbol != null)
139 {
140 this.Platform = EnsureLanguageAndGetPlatformFromSummaryInformation(platformAndLanguageSymbol, this.ProductLanguage);
141 }
142
143 // Set the revision number (package/patch code) if it should be automatically generated.
121 if (!foundPackageCode) 144 if (!foundPackageCode)
122 { 145 {
123 this.Section.AddSymbol(new SummaryInformationSymbol(null) 146 this.Section.AddSymbol(new SummaryInformationSymbol(null)
@@ -158,11 +181,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
158 } 181 }
159 } 182 }
160 183
161 private static Platform GetPlatformFromSummaryInformation(string value) 184 private static Platform EnsureLanguageAndGetPlatformFromSummaryInformation(SummaryInformationSymbol symbol, string language)
162 { 185 {
186 var value = symbol.Value;
163 var separatorIndex = value.IndexOf(';'); 187 var separatorIndex = value.IndexOf(';');
164 var platformValue = separatorIndex > 0 ? value.Substring(0, separatorIndex) : value; 188 var platformValue = separatorIndex > 0 ? value.Substring(0, separatorIndex) : value;
165 189
190 // If the language was provided and there was language value after the separator
191 // (or the separator was absent) then use the provided language.
192 if (!String.IsNullOrEmpty(language) && (separatorIndex < 0 || separatorIndex + 1 == value.Length))
193 {
194 symbol.Value = platformValue + ';' + language;
195 }
196
166 switch (platformValue) 197 switch (platformValue)
167 { 198 {
168 case "x64": 199 case "x64":
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index 3a9bd545..3379ec5d 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -438,7 +438,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
438 438
439 private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) 439 private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns)
440 { 440 {
441 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true); 441 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, outputPath, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true);
442 command.Execute(); 442 command.Execute();
443 } 443 }
444 } 444 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
index b587e6d9..47d8399f 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
@@ -15,7 +15,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15 /// </summary> 15 /// </summary>
16 internal class CreateDeltaPatchesCommand 16 internal class CreateDeltaPatchesCommand
17 { 17 {
18 public CreateDeltaPatchesCommand(List<IFileFacade> fileFacades, string intermediateFolder, WixPatchIdSymbol wixPatchId) 18 public CreateDeltaPatchesCommand(List<IFileFacade> fileFacades, string intermediateFolder, WixPatchSymbol wixPatchId)
19 { 19 {
20 this.FileFacades = fileFacades; 20 this.FileFacades = fileFacades;
21 this.IntermediateFolder = intermediateFolder; 21 this.IntermediateFolder = intermediateFolder;
@@ -24,7 +24,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
24 24
25 private IEnumerable<IFileFacade> FileFacades { get; } 25 private IEnumerable<IFileFacade> FileFacades { get; }
26 26
27 private WixPatchIdSymbol WixPatchId { get; } 27 private WixPatchSymbol WixPatchId { get; }
28 28
29 private string IntermediateFolder { get; } 29 private string IntermediateFolder { get; }
30 30
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
index f09a2e47..ff03413c 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
@@ -36,23 +36,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
36 public void Execute() 36 public void Execute()
37 { 37 {
38 // write out the table to an IDT file 38 // write out the table to an IDT file
39 Encoding encoding; 39 var encoding = GetCodepageEncoding(this.Codepage);
40
41 // If UTF8 encoding, use the UTF8-specific constructor to avoid writing
42 // the byte order mark at the beginning of the file
43 if (this.Codepage == Encoding.UTF8.CodePage)
44 {
45 encoding = new UTF8Encoding(false, true);
46 }
47 else
48 {
49 if (this.Codepage == 0)
50 {
51 this.Codepage = Encoding.ASCII.CodePage;
52 }
53
54 encoding = Encoding.GetEncoding(this.Codepage, new EncoderExceptionFallback(), new DecoderExceptionFallback());
55 }
56 40
57 this.IdtPath = Path.Combine(this.IntermediateFolder, String.Concat(this.Table.Name, ".idt")); 41 this.IdtPath = Path.Combine(this.IntermediateFolder, String.Concat(this.Table.Name, ".idt"));
58 42
@@ -209,6 +193,30 @@ namespace WixToolset.Core.WindowsInstaller.Bind
209 .Replace('\n', '\x19'); 193 .Replace('\n', '\x19');
210 } 194 }
211 195
196 private static Encoding GetCodepageEncoding(int codepage)
197 {
198 Encoding encoding;
199
200 // If UTF8 encoding, use the UTF8-specific constructor to avoid writing
201 // the byte order mark at the beginning of the file
202 if (codepage == Encoding.UTF8.CodePage)
203 {
204 encoding = new UTF8Encoding(false, true);
205 }
206 else
207 {
208 if (codepage == 0)
209 {
210 codepage = Encoding.ASCII.CodePage;
211 }
212
213 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
214
215 encoding = Encoding.GetEncoding(codepage, new EncoderExceptionFallback(), new DecoderExceptionFallback());
216 }
217
218 return encoding;
219 }
212 220
213 /// <summary> 221 /// <summary>
214 /// Gets the type of the column in IDT format. 222 /// Gets the type of the column in IDT format.
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
index bc95fd4c..1bd00900 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
@@ -18,11 +18,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
18 18
19 internal class CreateWindowsInstallerDataFromIRCommand 19 internal class CreateWindowsInstallerDataFromIRCommand
20 { 20 {
21 public CreateWindowsInstallerDataFromIRCommand(IMessaging messaging, IntermediateSection section, TableDefinitionCollection tableDefinitions, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions, IWindowsInstallerBackendHelper backendHelper) 21 public CreateWindowsInstallerDataFromIRCommand(IMessaging messaging, IntermediateSection section, TableDefinitionCollection tableDefinitions, int codepage, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions, IWindowsInstallerBackendHelper backendHelper)
22 { 22 {
23 this.Messaging = messaging; 23 this.Messaging = messaging;
24 this.Section = section; 24 this.Section = section;
25 this.TableDefinitions = tableDefinitions; 25 this.TableDefinitions = tableDefinitions;
26 this.Codepage = codepage;
26 this.BackendExtensions = backendExtensions; 27 this.BackendExtensions = backendExtensions;
27 this.BackendHelper = backendHelper; 28 this.BackendHelper = backendHelper;
28 this.GeneratedShortNames = new Dictionary<string, List<FileSymbol>>(); 29 this.GeneratedShortNames = new Dictionary<string, List<FileSymbol>>();
@@ -36,6 +37,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
36 37
37 private TableDefinitionCollection TableDefinitions { get; } 38 private TableDefinitionCollection TableDefinitions { get; }
38 39
40 private int Codepage { get; }
41
39 private IntermediateSection Section { get; } 42 private IntermediateSection Section { get; }
40 43
41 private Dictionary<string, List<FileSymbol>> GeneratedShortNames { get; } 44 private Dictionary<string, List<FileSymbol>> GeneratedShortNames { get; }
@@ -46,7 +49,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
46 { 49 {
47 this.Data = new WindowsInstallerData(this.Section.Symbols.First().SourceLineNumbers) 50 this.Data = new WindowsInstallerData(this.Section.Symbols.First().SourceLineNumbers)
48 { 51 {
49 Codepage = this.Section.Codepage, 52 Codepage = this.Codepage,
50 Type = SectionTypeToOutputType(this.Section.Type) 53 Type = SectionTypeToOutputType(this.Section.Type)
51 }; 54 };
52 55
@@ -219,6 +222,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
219 this.AddWixEnsureTableSymbol((WixEnsureTableSymbol)symbol); 222 this.AddWixEnsureTableSymbol((WixEnsureTableSymbol)symbol);
220 break; 223 break;
221 224
225 case SymbolDefinitionType.WixPackage:
226 this.AddWixPackageSymbol((WixPackageSymbol)symbol);
227 break;
228
222 // Symbols used internally and are not added to the output. 229 // Symbols used internally and are not added to the output.
223 case SymbolDefinitionType.WixBuildInfo: 230 case SymbolDefinitionType.WixBuildInfo:
224 case SymbolDefinitionType.WixBindUpdatedFiles: 231 case SymbolDefinitionType.WixBindUpdatedFiles:
@@ -237,7 +244,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
237 case SymbolDefinitionType.WixOrdering: 244 case SymbolDefinitionType.WixOrdering:
238 case SymbolDefinitionType.WixPatchBaseline: 245 case SymbolDefinitionType.WixPatchBaseline:
239 case SymbolDefinitionType.WixPatchFamilyGroup: 246 case SymbolDefinitionType.WixPatchFamilyGroup:
240 case SymbolDefinitionType.WixPatchId: 247 case SymbolDefinitionType.WixPatch:
241 case SymbolDefinitionType.WixPatchRef: 248 case SymbolDefinitionType.WixPatchRef:
242 case SymbolDefinitionType.WixPatchTarget: 249 case SymbolDefinitionType.WixPatchTarget:
243 case SymbolDefinitionType.WixProperty: 250 case SymbolDefinitionType.WixProperty:
@@ -1214,6 +1221,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1214 this.Data.EnsureTable(tableDefinition); 1221 this.Data.EnsureTable(tableDefinition);
1215 } 1222 }
1216 1223
1224 private void AddWixPackageSymbol(WixPackageSymbol symbol)
1225 {
1226 // TODO: Remove the following from the compiler and do it here instead.
1227 //this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "Manufacturer"), manufacturer, false, false, false, true);
1228 //this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductCode"), productCode, false, false, false, true);
1229 //this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductLanguage"), productLanguage, false, false, false, true);
1230 //this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductName"), this.activeName, false, false, false, true);
1231 //this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductVersion"), version, false, false, false, true);
1232 //if (null != upgradeCode)
1233 //{
1234 // this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "UpgradeCode"), upgradeCode, false, false, false, true);
1235 //}
1236
1237 //if (isPerMachine)
1238 //{
1239 // this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ALLUSERS"), "1", false, false, false, false);
1240 //}
1241 }
1242
1217 private bool AddSymbolFromExtension(IntermediateSymbol symbol) 1243 private bool AddSymbolFromExtension(IntermediateSymbol symbol)
1218 { 1244 {
1219 foreach (var extension in this.BackendExtensions) 1245 foreach (var extension in this.BackendExtensions)
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index f125f497..b8cca752 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -18,7 +18,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
18 { 18 {
19 private const string IdtsSubFolder = "_idts"; 19 private const string IdtsSubFolder = "_idts";
20 20
21 public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, int codepage, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory) 21 public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory)
22 { 22 {
23 this.Messaging = messaging; 23 this.Messaging = messaging;
24 this.BackendHelper = backendHelper; 24 this.BackendHelper = backendHelper;
@@ -27,14 +27,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
27 this.OutputPath = outputPath; 27 this.OutputPath = outputPath;
28 this.TableDefinitions = tableDefinitions; 28 this.TableDefinitions = tableDefinitions;
29 this.IntermediateFolder = intermediateFolder; 29 this.IntermediateFolder = intermediateFolder;
30 this.Codepage = codepage;
31 this.KeepAddedColumns = keepAddedColumns; 30 this.KeepAddedColumns = keepAddedColumns;
32 this.SuppressAddingValidationRows = suppressAddingValidationRows; 31 this.SuppressAddingValidationRows = suppressAddingValidationRows;
33 this.UseSubDirectory = useSubdirectory; 32 this.UseSubDirectory = useSubdirectory;
34 } 33 }
35 34
36 private int Codepage { get; }
37
38 private IBackendHelper BackendHelper { get; } 35 private IBackendHelper BackendHelper { get; }
39 36
40 private FileSystemManager FileSystemManager { get; } 37 private FileSystemManager FileSystemManager { get; }
@@ -88,12 +85,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
88 type |= OpenDatabase.OpenPatchFile; 85 type |= OpenDatabase.OpenPatchFile;
89 } 86 }
90 87
91 // Localize the codepage if a value was specified directly.
92 if (-1 != this.Codepage)
93 {
94 this.Data.Codepage = this.Codepage;
95 }
96
97 try 88 try
98 { 89 {
99 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); 90 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
index ef141795..faa03762 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
@@ -9,7 +9,6 @@ namespace WixToolset.Core.WindowsInstaller
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Symbols; 10 using WixToolset.Data.Symbols;
11 using WixToolset.Data.WindowsInstaller; 11 using WixToolset.Data.WindowsInstaller;
12 using WixToolset.Data.WindowsInstaller.Rows;
13 using WixToolset.Extensibility.Services; 12 using WixToolset.Extensibility.Services;
14 13
15 /// <summary> 14 /// <summary>
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs
new file mode 100644
index 00000000..217609be
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs
@@ -0,0 +1,101 @@
1// Copyright (c) .NET Foundation 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.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 using WixToolset.Extensibility.Services;
11
12 internal class ProcessPropertiesCommand
13 {
14 public ProcessPropertiesCommand(IntermediateSection section, WixPackageSymbol packageSymbol, int fallbackLcid, bool populateDelayedVariables, IBackendHelper backendHelper)
15 {
16 this.Section = section;
17 this.PackageSymbol = packageSymbol;
18 this.FallbackLcid = fallbackLcid;
19 this.PopulateDelayedVariables = populateDelayedVariables;
20 this.BackendHelper = backendHelper;
21 }
22
23 private IntermediateSection Section { get; }
24
25 private WixPackageSymbol PackageSymbol { get; }
26
27 private int FallbackLcid { get; }
28
29 private bool PopulateDelayedVariables { get; }
30
31 private IBackendHelper BackendHelper { get; }
32
33 public Dictionary<string, string> DelayedVariablesCache { get; private set; }
34
35 public string ProductLanguage { get; private set; }
36
37 public void Execute()
38 {
39 PropertySymbol languageSymbol = null;
40 var variableCache = this.PopulateDelayedVariables ? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) : null;
41
42 if (SectionType.Product == this.Section.Type || variableCache != null)
43 {
44 foreach (var propertySymbol in this.Section.Symbols.OfType<PropertySymbol>())
45 {
46 // Set the ProductCode if it is to be generated.
47 if ("ProductCode" == propertySymbol.Id.Id && "*".Equals(propertySymbol.Value, StringComparison.Ordinal))
48 {
49 propertySymbol.Value = this.BackendHelper.CreateGuid();
50
51#if TODO_PATCHING // Is this still necessary?
52 // Update the target ProductCode in any instance transforms.
53 foreach (SubStorage subStorage in this.Output.SubStorages)
54 {
55 Output subStorageOutput = subStorage.Data;
56 if (OutputType.Transform != subStorageOutput.Type)
57 {
58 continue;
59 }
60
61 Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
62 foreach (Row row in instanceSummaryInformationTable.Rows)
63 {
64 if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
65 {
66 row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
67 break;
68 }
69 }
70 }
71#endif
72 }
73 else if ("ProductLanguage" == propertySymbol.Id.Id)
74 {
75 languageSymbol = propertySymbol;
76 }
77
78 // Add the property name and value to the variableCache.
79 if (variableCache != null)
80 {
81 variableCache[$"property.{propertySymbol.Id.Id}"] = propertySymbol.Value;
82 }
83 }
84
85 if (this.Section.Type == SectionType.Product && String.IsNullOrEmpty(languageSymbol?.Value))
86 {
87 if (languageSymbol == null)
88 {
89 languageSymbol = this.Section.AddSymbol(new PropertySymbol(this.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, "ProductLanguage")));
90 }
91
92 this.PackageSymbol.Language = this.FallbackLcid.ToString();
93 languageSymbol.Value = this.FallbackLcid.ToString();
94 }
95 }
96
97 this.DelayedVariablesCache = variableCache;
98 this.ProductLanguage = languageSymbol?.Value;
99 }
100 }
101}
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
index 9f649435..f40aed4e 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
@@ -302,7 +302,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
302 302
303 private void GenerateDatabase(WindowsInstallerData output, string databaseFile) 303 private void GenerateDatabase(WindowsInstallerData output, string databaseFile)
304 { 304 {
305 var command = new GenerateDatabaseCommand(this.Messaging, null, null, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false); 305 var command = new GenerateDatabaseCommand(this.Messaging, null, null, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false);
306 command.Execute(); 306 command.Execute();
307 } 307 }
308 } 308 }
diff --git a/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj b/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
index 56262373..b08f337f 100644
--- a/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
+++ b/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
@@ -20,6 +20,7 @@
20 20
21 <ItemGroup> 21 <ItemGroup>
22 <PackageReference Include="System.Reflection.Metadata" Version="1.6.0" /> 22 <PackageReference Include="System.Reflection.Metadata" Version="1.6.0" />
23 <PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" />
23 </ItemGroup> 24 </ItemGroup>
24 25
25 <ItemGroup> 26 <ItemGroup>
diff --git a/src/WixToolset.Core/BindContext.cs b/src/WixToolset.Core/BindContext.cs
index 6b0a09a2..09454824 100644
--- a/src/WixToolset.Core/BindContext.cs
+++ b/src/WixToolset.Core/BindContext.cs
@@ -26,8 +26,6 @@ namespace WixToolset.Core
26 26
27 public string CabCachePath { get; set; } 27 public string CabCachePath { get; set; }
28 28
29 public int Codepage { get; set; }
30
31 public CompressionLevel? DefaultCompressionLevel { get; set; } 29 public CompressionLevel? DefaultCompressionLevel { get; set; }
32 30
33 public IEnumerable<IDelayedField> DelayedFields { get; set; } 31 public IEnumerable<IDelayedField> DelayedFields { get; set; }
@@ -50,6 +48,12 @@ namespace WixToolset.Core
50 48
51 public string PdbPath { get; set; } 49 public string PdbPath { get; set; }
52 50
51 public int? ResolvedCodepage { get; set; }
52
53 public int? ResolvedSummaryInformationCodepage { get; set; }
54
55 public int? ResolvedLcid { get; set; }
56
53 public IEnumerable<string> SuppressIces { get; set; } 57 public IEnumerable<string> SuppressIces { get; set; }
54 58
55 public bool SuppressValidation { get; set; } 59 public bool SuppressValidation { get; set; }
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs
index 9efef830..59aa2f1f 100644
--- a/src/WixToolset.Core/CommandLine/BuildCommand.cs
+++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs
@@ -338,7 +338,9 @@ namespace WixToolset.Core.CommandLine
338 var context = this.ServiceProvider.GetService<IBindContext>(); 338 var context = this.ServiceProvider.GetService<IBindContext>();
339 //context.CabbingThreadCount = this.CabbingThreadCount; 339 //context.CabbingThreadCount = this.CabbingThreadCount;
340 context.CabCachePath = cabCachePath; 340 context.CabCachePath = cabCachePath;
341 context.Codepage = resolveResult.Codepage; 341 context.ResolvedCodepage = resolveResult.Codepage;
342 context.ResolvedSummaryInformationCodepage = resolveResult.SummaryInformationCodepage;
343 context.ResolvedLcid = resolveResult.PackageLcid;
342 context.DefaultCompressionLevel = this.DefaultCompressionLevel; 344 context.DefaultCompressionLevel = this.DefaultCompressionLevel;
343 context.DelayedFields = resolveResult.DelayedFields; 345 context.DelayedFields = resolveResult.DelayedFields;
344 context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles; 346 context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs
index c2783481..926a46c5 100644
--- a/src/WixToolset.Core/Compiler.cs
+++ b/src/WixToolset.Core/Compiler.cs
@@ -384,7 +384,7 @@ namespace WixToolset.Core
384 { 384 {
385 var id = String.Concat(this.Core.ActiveSection.Id, ".", propertyId.Id); 385 var id = String.Concat(this.Core.ActiveSection.Id, ".", propertyId.Id);
386 386
387 section = this.Core.CreateSection(id, SectionType.Fragment, this.Core.ActiveSection.Codepage, this.Context.CompilationId); 387 section = this.Core.CreateSection(id, SectionType.Fragment, this.Context.CompilationId);
388 388
389 // Reference the property in the active section. 389 // Reference the property in the active section.
390 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, propertyId.Id); 390 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, propertyId.Id);
@@ -6159,7 +6159,7 @@ namespace WixToolset.Core
6159 6159
6160 // NOTE: Id is not required for Fragments, this is a departure from the normal run of the mill processing. 6160 // NOTE: Id is not required for Fragments, this is a departure from the normal run of the mill processing.
6161 6161
6162 this.Core.CreateActiveSection(id?.Id, SectionType.Fragment, 0, this.Context.CompilationId); 6162 this.Core.CreateActiveSection(id?.Id, SectionType.Fragment, this.Context.CompilationId);
6163 6163
6164 var featureDisplay = 0; 6164 var featureDisplay = 0;
6165 foreach (var child in node.Elements()) 6165 foreach (var child in node.Elements())
diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs
index 0bc63d79..df532d74 100644
--- a/src/WixToolset.Core/CompilerCore.cs
+++ b/src/WixToolset.Core/CompilerCore.cs
@@ -680,7 +680,7 @@ namespace WixToolset.Core
680 680
681 Debug.Assert(minimum > CompilerConstants.IntegerNotSet && minimum > CompilerConstants.IllegalInteger, "The legal values for this attribute collide with at least one sentinel used during parsing."); 681 Debug.Assert(minimum > CompilerConstants.IntegerNotSet && minimum > CompilerConstants.IllegalInteger, "The legal values for this attribute collide with at least one sentinel used during parsing.");
682 682
683 string value = this.GetAttributeValue(sourceLineNumbers, attribute); 683 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
684 684
685 if (0 < value.Length) 685 if (0 < value.Length)
686 { 686 {
@@ -692,7 +692,7 @@ namespace WixToolset.Core
692 { 692 {
693 try 693 try
694 { 694 {
695 int integer = Convert.ToInt32(value, CultureInfo.InvariantCulture.NumberFormat); 695 var integer = Convert.ToInt32(value, CultureInfo.InvariantCulture.NumberFormat);
696 696
697 if (CompilerConstants.IntegerNotSet == integer || CompilerConstants.IllegalInteger == integer) 697 if (CompilerConstants.IntegerNotSet == integer || CompilerConstants.IllegalInteger == integer)
698 { 698 {
@@ -1036,12 +1036,11 @@ namespace WixToolset.Core
1036 /// </summary> 1036 /// </summary>
1037 /// <param name="id">Unique identifier for the section.</param> 1037 /// <param name="id">Unique identifier for the section.</param>
1038 /// <param name="type">Type of section to create.</param> 1038 /// <param name="type">Type of section to create.</param>
1039 /// <param name="codepage">Codepage for the resulting database for this ection.</param> 1039 /// <param name="compilationId">Unique identifier for the compilation.</param>
1040 /// <param name="compilationId"></param>
1041 /// <returns>New section.</returns> 1040 /// <returns>New section.</returns>
1042 internal IntermediateSection CreateActiveSection(string id, SectionType type, int codepage, string compilationId) 1041 internal IntermediateSection CreateActiveSection(string id, SectionType type, string compilationId)
1043 { 1042 {
1044 this.ActiveSection = this.CreateSection(id, type, codepage, compilationId); 1043 this.ActiveSection = this.CreateSection(id, type, compilationId);
1045 1044
1046 this.activeSectionCachedInlinedDirectoryIds = new Dictionary<string, string>(); 1045 this.activeSectionCachedInlinedDirectoryIds = new Dictionary<string, string>();
1047 this.activeSectionSimpleReferences = new HashSet<string>(); 1046 this.activeSectionSimpleReferences = new HashSet<string>();
@@ -1054,12 +1053,11 @@ namespace WixToolset.Core
1054 /// </summary> 1053 /// </summary>
1055 /// <param name="id">Unique identifier for the section.</param> 1054 /// <param name="id">Unique identifier for the section.</param>
1056 /// <param name="type">Type of section to create.</param> 1055 /// <param name="type">Type of section to create.</param>
1057 /// <param name="codepage">Codepage for the resulting database for this ection.</param> 1056 /// <param name="compilationId">Unique identifier for the compilation.</param>
1058 /// <param name="compilationId"></param>
1059 /// <returns>New section.</returns> 1057 /// <returns>New section.</returns>
1060 internal IntermediateSection CreateSection(string id, SectionType type, int codepage, string compilationId) 1058 internal IntermediateSection CreateSection(string id, SectionType type, string compilationId)
1061 { 1059 {
1062 var section = new IntermediateSection(id, type, codepage, compilationId); 1060 var section = new IntermediateSection(id, type, compilationId);
1063 1061
1064 this.intermediate.Sections.Add(section); 1062 this.intermediate.Sections.Add(section);
1065 1063
diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs
index 7e07532e..779ad376 100644
--- a/src/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/WixToolset.Core/Compiler_Bundle.cs
@@ -259,7 +259,7 @@ namespace WixToolset.Core
259 } 259 }
260 260
261 this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name; 261 this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name;
262 this.Core.CreateActiveSection(this.activeName, SectionType.Bundle, 0, this.Context.CompilationId); 262 this.Core.CreateActiveSection(this.activeName, SectionType.Bundle, this.Context.CompilationId);
263 263
264 // Now that the active section is initialized, process only extension attributes and the special ProviderKey attribute. 264 // Now that the active section is initialized, process only extension attributes and the special ProviderKey attribute.
265 foreach (var attrib in node.Attributes()) 265 foreach (var attrib in node.Attributes())
diff --git a/src/WixToolset.Core/Compiler_Module.cs b/src/WixToolset.Core/Compiler_Module.cs
index 597bc25c..59fe9164 100644
--- a/src/WixToolset.Core/Compiler_Module.cs
+++ b/src/WixToolset.Core/Compiler_Module.cs
@@ -104,7 +104,7 @@ namespace WixToolset.Core
104 try 104 try
105 { 105 {
106 this.compilingModule = true; // notice that we are actually building a Merge Module here 106 this.compilingModule = true; // notice that we are actually building a Merge Module here
107 this.Core.CreateActiveSection(this.activeName, SectionType.Module, codepage, this.Context.CompilationId); 107 this.Core.CreateActiveSection(this.activeName, SectionType.Module, this.Context.CompilationId);
108 108
109 foreach (var child in node.Elements()) 109 foreach (var child in node.Elements())
110 { 110 {
@@ -232,15 +232,6 @@ namespace WixToolset.Core
232 232
233 if (!this.Core.EncounteredError) 233 if (!this.Core.EncounteredError)
234 { 234 {
235 if (!setCodepage)
236 {
237 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
238 {
239 PropertyId = SummaryInformationType.Codepage,
240 Value = "1252"
241 });
242 }
243
244 if (!setPackageName) 235 if (!setPackageName)
245 { 236 {
246 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) 237 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
@@ -259,21 +250,20 @@ namespace WixToolset.Core
259 }); 250 });
260 } 251 }
261 252
262 var symbol = this.Core.AddSymbol(new ModuleSignatureSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, this.activeName, this.activeLanguage)) 253 var symbol = this.Core.AddSymbol(new WixModuleSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, this.activeName, this.activeLanguage))
263 { 254 {
264 ModuleID = this.activeName, 255 ModuleId = this.activeName,
256 Language = this.activeLanguage,
265 Version = version 257 Version = version
266 }); 258 });
267 259
268 symbol.Set((int)ModuleSignatureSymbolFields.Language, this.activeLanguage);
269
270 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) 260 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
271 { 261 {
272 PropertyId = SummaryInformationType.PackageCode, 262 PropertyId = SummaryInformationType.PackageCode,
273 Value = moduleId 263 Value = moduleId
274 }); 264 });
275 265
276 this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform); 266 this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform, this.activeLanguage);
277 } 267 }
278 } 268 }
279 finally 269 finally
diff --git a/src/WixToolset.Core/Compiler_Package.cs b/src/WixToolset.Core/Compiler_Package.cs
index fed08001..afe02f08 100644
--- a/src/WixToolset.Core/Compiler_Package.cs
+++ b/src/WixToolset.Core/Compiler_Package.cs
@@ -5,7 +5,6 @@ namespace WixToolset.Core
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.Diagnostics.CodeAnalysis;
9 using System.Globalization; 8 using System.Globalization;
10 using System.IO; 9 using System.IO;
11 using System.Xml.Linq; 10 using System.Xml.Linq;
@@ -28,10 +27,10 @@ namespace WixToolset.Core
28 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 27 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
29 var compressed = YesNoDefaultType.Default; 28 var compressed = YesNoDefaultType.Default;
30 var sourceBits = 0; 29 var sourceBits = 0;
31 var codepage = 65001; 30 string codepage = null;
32 var productCode = "*"; 31 var productCode = "*";
32 string productLanguage = null;
33 var isPerMachine = true; 33 var isPerMachine = true;
34 string installScope = null;
35 string upgradeCode = null; 34 string upgradeCode = null;
36 string manufacturer = null; 35 string manufacturer = null;
37 string version = null; 36 string version = null;
@@ -53,7 +52,7 @@ namespace WixToolset.Core
53 switch (attrib.Name.LocalName) 52 switch (attrib.Name.LocalName)
54 { 53 {
55 case "Codepage": 54 case "Codepage":
56 codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib); 55 codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib);
57 break; 56 break;
58 case "Compressed": 57 case "Compressed":
59 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); 58 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
@@ -62,7 +61,7 @@ namespace WixToolset.Core
62 msiVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); 61 msiVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
63 break; 62 break;
64 case "Language": 63 case "Language":
65 this.activeLanguage = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); 64 productLanguage = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
66 break; 65 break;
67 case "Manufacturer": 66 case "Manufacturer":
68 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters); 67 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters);
@@ -82,7 +81,7 @@ namespace WixToolset.Core
82 productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true); 81 productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
83 break; 82 break;
84 case "Scope": 83 case "Scope":
85 installScope = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 84 var installScope = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
86 switch (installScope) 85 switch (installScope)
87 { 86 {
88 case "perMachine": 87 case "perMachine":
@@ -129,11 +128,6 @@ namespace WixToolset.Core
129 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); 128 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
130 } 129 }
131 130
132 if (null == this.activeLanguage)
133 {
134 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
135 }
136
137 if (null == manufacturer) 131 if (null == manufacturer)
138 { 132 {
139 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer")); 133 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
@@ -171,11 +165,11 @@ namespace WixToolset.Core
171 try 165 try
172 { 166 {
173 this.compilingProduct = true; 167 this.compilingProduct = true;
174 this.Core.CreateActiveSection(productCode, SectionType.Product, codepage, this.Context.CompilationId); 168 this.Core.CreateActiveSection(productCode, SectionType.Product, this.Context.CompilationId);
175 169
176 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "Manufacturer"), manufacturer, false, false, false, true); 170 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "Manufacturer"), manufacturer, false, false, false, true);
177 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductCode"), productCode, false, false, false, true); 171 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductCode"), productCode, false, false, false, true);
178 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductLanguage"), this.activeLanguage, false, false, false, true); 172 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductLanguage"), productLanguage, false, false, false, true);
179 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductName"), this.activeName, false, false, false, true); 173 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductName"), this.activeName, false, false, false, true);
180 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductVersion"), version, false, false, false, true); 174 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductVersion"), version, false, false, false, true);
181 if (null != upgradeCode) 175 if (null != upgradeCode)
@@ -188,7 +182,7 @@ namespace WixToolset.Core
188 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ALLUSERS"), "1", false, false, false, false); 182 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ALLUSERS"), "1", false, false, false, false);
189 } 183 }
190 184
191 this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform); 185 this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform, productLanguage);
192 186
193 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) 187 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
194 { 188 {
@@ -198,7 +192,7 @@ namespace WixToolset.Core
198 192
199 var contextValues = new Dictionary<string, string> 193 var contextValues = new Dictionary<string, string>
200 { 194 {
201 ["ProductLanguage"] = this.activeLanguage, 195 ["ProductLanguage"] = productLanguage,
202 ["ProductVersion"] = version, 196 ["ProductVersion"] = version,
203 ["UpgradeCode"] = upgradeCode 197 ["UpgradeCode"] = upgradeCode
204 }; 198 };
@@ -360,14 +354,17 @@ namespace WixToolset.Core
360 354
361 if (!this.Core.EncounteredError) 355 if (!this.Core.EncounteredError)
362 { 356 {
363 if (!isCodepageSet) 357 this.Core.AddSymbol(new WixPackageSymbol(sourceLineNumbers)
364 { 358 {
365 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) 359 PackageId = productCode,
366 { 360 UpgradeCode = upgradeCode,
367 PropertyId = SummaryInformationType.Codepage, 361 Name = this.activeName,
368 Value = "1252" 362 Language = productLanguage,
369 }); 363 Version = version,
370 } 364 Manufacturer = manufacturer,
365 Attributes = isPerMachine ? WixPackageAttributes.PerMachine : WixPackageAttributes.None,
366 Codepage = codepage,
367 });
371 368
372 if (!isPackageNameSet) 369 if (!isPackageNameSet)
373 { 370 {
@@ -435,7 +432,7 @@ namespace WixToolset.Core
435 } 432 }
436 } 433 }
437 434
438 private void ValidateAndAddCommonSummaryInformationSymbols(SourceLineNumber sourceLineNumbers, int msiVersion, string platform) 435 private void ValidateAndAddCommonSummaryInformationSymbols(SourceLineNumber sourceLineNumbers, int msiVersion, string platform, string language)
439 { 436 {
440 if (String.Equals(platform, "X64", StringComparison.OrdinalIgnoreCase) && 200 > msiVersion) 437 if (String.Equals(platform, "X64", StringComparison.OrdinalIgnoreCase) && 200 > msiVersion)
441 { 438 {
@@ -464,7 +461,7 @@ namespace WixToolset.Core
464 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) 461 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
465 { 462 {
466 PropertyId = SummaryInformationType.PlatformAndLanguage, 463 PropertyId = SummaryInformationType.PlatformAndLanguage,
467 Value = String.Format(CultureInfo.InvariantCulture, "{0};{1}", platform, this.activeLanguage) 464 Value = $"{platform};{language}"
468 }); 465 });
469 466
470 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) 467 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
@@ -478,7 +475,6 @@ namespace WixToolset.Core
478 PropertyId = SummaryInformationType.Security, 475 PropertyId = SummaryInformationType.Security,
479 Value = "2" 476 Value = "2"
480 }); 477 });
481
482 } 478 }
483 479
484 /// <summary> 480 /// <summary>
diff --git a/src/WixToolset.Core/Compiler_Patch.cs b/src/WixToolset.Core/Compiler_Patch.cs
index a2cadd67..c9cae183 100644
--- a/src/WixToolset.Core/Compiler_Patch.cs
+++ b/src/WixToolset.Core/Compiler_Patch.cs
@@ -24,7 +24,7 @@ namespace WixToolset.Core
24 { 24 {
25 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 25 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
26 string patchId = null; 26 string patchId = null;
27 var codepage = 0; 27 string codepage = null;
28 ////bool versionMismatches = false; 28 ////bool versionMismatches = false;
29 ////bool productMismatches = false; 29 ////bool productMismatches = false;
30 var allowRemoval = false; 30 var allowRemoval = false;
@@ -53,7 +53,7 @@ namespace WixToolset.Core
53 patchId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true); 53 patchId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
54 break; 54 break;
55 case "Codepage": 55 case "Codepage":
56 codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib); 56 codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib);
57 break; 57 break;
58 case "AllowMajorVersionMismatches": 58 case "AllowMajorVersionMismatches":
59 ////versionMismatches = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); 59 ////versionMismatches = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
@@ -149,7 +149,7 @@ namespace WixToolset.Core
149 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer")); 149 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
150 } 150 }
151 151
152 this.Core.CreateActiveSection(this.activeName, SectionType.Patch, codepage, this.Context.CompilationId); 152 this.Core.CreateActiveSection(this.activeName, SectionType.Patch, this.Context.CompilationId);
153 153
154 foreach (var child in node.Elements()) 154 foreach (var child in node.Elements())
155 { 155 {
@@ -197,8 +197,9 @@ namespace WixToolset.Core
197 197
198 if (!this.Core.EncounteredError) 198 if (!this.Core.EncounteredError)
199 { 199 {
200 this.Core.AddSymbol(new WixPatchIdSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, patchId)) 200 this.Core.AddSymbol(new WixPatchSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, patchId))
201 { 201 {
202 Codepage = codepage,
202 ClientPatchId = clientPatchId, 203 ClientPatchId = clientPatchId,
203 OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles, 204 OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles,
204 ApiPatchingSymbolFlags = apiPatchingSymbolFlags, 205 ApiPatchingSymbolFlags = apiPatchingSymbolFlags,
diff --git a/src/WixToolset.Core/Compiler_PatchCreation.cs b/src/WixToolset.Core/Compiler_PatchCreation.cs
index 7675a5c0..81ae4121 100644
--- a/src/WixToolset.Core/Compiler_PatchCreation.cs
+++ b/src/WixToolset.Core/Compiler_PatchCreation.cs
@@ -82,7 +82,7 @@ namespace WixToolset.Core
82 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); 82 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
83 } 83 }
84 84
85 this.Core.CreateActiveSection(this.activeName, SectionType.PatchCreation, codepage, this.Context.CompilationId); 85 this.Core.CreateActiveSection(this.activeName, SectionType.PatchCreation, this.Context.CompilationId);
86 86
87 foreach (var child in node.Elements()) 87 foreach (var child in node.Elements())
88 { 88 {
diff --git a/src/WixToolset.Core/IResolver.cs b/src/WixToolset.Core/IResolver.cs
index 3004ad2c..db25edbe 100644
--- a/src/WixToolset.Core/IResolver.cs
+++ b/src/WixToolset.Core/IResolver.cs
@@ -4,9 +4,16 @@ namespace WixToolset.Core
4{ 4{
5 using WixToolset.Extensibility.Data; 5 using WixToolset.Extensibility.Data;
6 6
7#pragma warning disable 1591 // TODO: add documentation 7 /// <summary>
8 /// Resolves localization and bind variables.
9 /// </summary>
8 public interface IResolver 10 public interface IResolver
9 { 11 {
12 /// <summary>
13 /// Resolve localization and bind variables.
14 /// </summary>
15 /// <param name="context">Resolve context.</param>
16 /// <returns>Resolve result.</returns>
10 IResolveResult Resolve(IResolveContext context); 17 IResolveResult Resolve(IResolveContext context);
11 } 18 }
12} 19}
diff --git a/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs b/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs
index ffa66210..d5c69838 100644
--- a/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs
+++ b/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs
@@ -1,4 +1,4 @@
1// Copyright (c) .NET 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.Core.Link 3namespace WixToolset.Core.Link
4{ 4{
@@ -65,7 +65,7 @@ namespace WixToolset.Core.Link
65 } 65 }
66 } 66 }
67 67
68 return new Localization(existingLocalization.Codepage, existingLocalization.Culture, variables, controls); 68 return new Localization(existingLocalization.Codepage ?? localization.Codepage, existingLocalization.SummaryInformationCodepage ?? localization.SummaryInformationCodepage, existingLocalization.Culture, variables, controls);
69 } 69 }
70 } 70 }
71} 71}
diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs
index 320f7d1f..2c8508a8 100644
--- a/src/WixToolset.Core/Linker.cs
+++ b/src/WixToolset.Core/Linker.cs
@@ -180,7 +180,7 @@ namespace WixToolset.Core
180 180
181 // Create a new section to hold the linked content. Start with the entry section's 181 // Create a new section to hold the linked content. Start with the entry section's
182 // metadata. 182 // metadata.
183 var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type, find.EntrySection.Codepage); 183 var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type);
184 184
185 var sectionCount = 0; 185 var sectionCount = 0;
186 186
diff --git a/src/WixToolset.Core/LocalizationParser.cs b/src/WixToolset.Core/LocalizationParser.cs
index dd5144ca..d6113fc6 100644
--- a/src/WixToolset.Core/LocalizationParser.cs
+++ b/src/WixToolset.Core/LocalizationParser.cs
@@ -86,7 +86,8 @@ namespace WixToolset.Core
86 private static Localization ParseWixLocalizationElement(IMessaging messaging, XElement node) 86 private static Localization ParseWixLocalizationElement(IMessaging messaging, XElement node)
87 { 87 {
88 var sourceLineNumbers = SourceLineNumber.CreateFromXObject(node); 88 var sourceLineNumbers = SourceLineNumber.CreateFromXObject(node);
89 var codepage = -1; 89 int? codepage = null;
90 int? summaryInformationCodepage = null;
90 string culture = null; 91 string culture = null;
91 92
92 foreach (var attrib in node.Attributes()) 93 foreach (var attrib in node.Attributes())
@@ -98,6 +99,9 @@ namespace WixToolset.Core
98 case "Codepage": 99 case "Codepage":
99 codepage = Common.GetValidCodePage(attrib.Value, true, false, sourceLineNumbers); 100 codepage = Common.GetValidCodePage(attrib.Value, true, false, sourceLineNumbers);
100 break; 101 break;
102 case "SummaryInformationCodepage":
103 summaryInformationCodepage = Common.GetValidCodePage(attrib.Value, true, false, sourceLineNumbers);
104 break;
101 case "Culture": 105 case "Culture":
102 culture = attrib.Value; 106 culture = attrib.Value;
103 break; 107 break;
@@ -143,7 +147,7 @@ namespace WixToolset.Core
143 } 147 }
144 } 148 }
145 149
146 return messaging.EncounteredError ? null : new Localization(codepage, culture, variables, localizedControls); 150 return messaging.EncounteredError ? null : new Localization(codepage, summaryInformationCodepage, culture, variables, localizedControls);
147 } 151 }
148 152
149 /// <summary> 153 /// <summary>
@@ -254,24 +258,12 @@ namespace WixToolset.Core
254 break; 258 break;
255 case "RightToLeft": 259 case "RightToLeft":
256 rightToLeft = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib); 260 rightToLeft = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib);
257 //if (YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib))
258 //{
259 // attribs |= MsiInterop.MsidbControlAttributesRTLRO;
260 //}
261 break; 261 break;
262 case "RightAligned": 262 case "RightAligned":
263 rightAligned = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib); 263 rightAligned = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib);
264 //if (YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib))
265 //{
266 // attribs |= MsiInterop.MsidbControlAttributesRightAligned;
267 //}
268 break; 264 break;
269 case "LeftScroll": 265 case "LeftScroll":
270 leftScroll = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib); 266 leftScroll = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib);
271 //if (YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib))
272 //{
273 // attribs |= MsiInterop.MsidbControlAttributesLeftScroll;
274 //}
275 break; 267 break;
276 default: 268 default:
277 Common.UnexpectedAttribute(messaging, sourceLineNumbers, attrib); 269 Common.UnexpectedAttribute(messaging, sourceLineNumbers, attrib);
diff --git a/src/WixToolset.Core/ResolveResult.cs b/src/WixToolset.Core/ResolveResult.cs
index 6b6bc7c4..38b432b0 100644
--- a/src/WixToolset.Core/ResolveResult.cs
+++ b/src/WixToolset.Core/ResolveResult.cs
@@ -8,7 +8,11 @@ namespace WixToolset.Core
8 8
9 internal class ResolveResult : IResolveResult 9 internal class ResolveResult : IResolveResult
10 { 10 {
11 public int Codepage { get; set; } 11 public int? Codepage { get; set; }
12
13 public int? SummaryInformationCodepage { get; set; }
14
15 public int? PackageLcid { get; set; }
12 16
13 public IEnumerable<IDelayedField> DelayedFields { get; set; } 17 public IEnumerable<IDelayedField> DelayedFields { get; set; }
14 18
diff --git a/src/WixToolset.Core/Resolver.cs b/src/WixToolset.Core/Resolver.cs
index 92c2a9c9..f4cb2fd6 100644
--- a/src/WixToolset.Core/Resolver.cs
+++ b/src/WixToolset.Core/Resolver.cs
@@ -4,6 +4,7 @@ namespace WixToolset.Core
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.Linq; 8 using System.Linq;
8 using WixToolset.Core.Bind; 9 using WixToolset.Core.Bind;
9 using WixToolset.Data; 10 using WixToolset.Data;
@@ -22,26 +23,12 @@ namespace WixToolset.Core
22 this.ServiceProvider = serviceProvider; 23 this.ServiceProvider = serviceProvider;
23 24
24 this.Messaging = serviceProvider.GetService<IMessaging>(); 25 this.Messaging = serviceProvider.GetService<IMessaging>();
25
26 this.VariableResolver = serviceProvider.GetService<IVariableResolver>();
27 } 26 }
28 27
29 private IServiceProvider ServiceProvider { get; } 28 private IServiceProvider ServiceProvider { get; }
30 29
31 private IMessaging Messaging { get; } 30 private IMessaging Messaging { get; }
32 31
33 private IVariableResolver VariableResolver { get; set; }
34
35 public IEnumerable<IBindPath> BindPaths { get; set; }
36
37 public string IntermediateFolder { get; set; }
38
39 public Intermediate IntermediateRepresentation { get; set; }
40
41 public IEnumerable<Localization> Localizations { get; set; }
42
43 public IEnumerable<string> FilterCultures { get; set; }
44
45 public IResolveResult Resolve(IResolveContext context) 32 public IResolveResult Resolve(IResolveContext context)
46 { 33 {
47 foreach (var extension in context.Extensions) 34 foreach (var extension in context.Extensions)
@@ -52,11 +39,26 @@ namespace WixToolset.Core
52 ResolveResult resolveResult = null; 39 ResolveResult resolveResult = null;
53 try 40 try
54 { 41 {
55 var codepage = this.PopulateVariableResolver(context); 42 var filteredLocalizations = FilterLocalizations(context);
43
44 var variableResolver = this.CreateVariableResolver(context, filteredLocalizations);
45
46 this.LocalizeUI(variableResolver, context.IntermediateRepresentation);
47
48 resolveResult = this.DoResolve(context, variableResolver);
49
50 var primaryLocalization = filteredLocalizations.FirstOrDefault();
51
52 if (primaryLocalization != null)
53 {
54 this.TryGetCultureInfo(primaryLocalization.Culture, out var cultureInfo);
56 55
57 this.LocalizeUI(context); 56 resolveResult.Codepage = primaryLocalization.Codepage ?? cultureInfo?.TextInfo.ANSICodePage;
58 57
59 resolveResult = this.DoResolve(context, codepage); 58 resolveResult.SummaryInformationCodepage = primaryLocalization.SummaryInformationCodepage ?? primaryLocalization.Codepage ?? cultureInfo?.TextInfo.ANSICodePage;
59
60 resolveResult.PackageLcid = cultureInfo?.LCID;
61 }
60 } 62 }
61 finally 63 finally
62 { 64 {
@@ -69,7 +71,7 @@ namespace WixToolset.Core
69 return resolveResult; 71 return resolveResult;
70 } 72 }
71 73
72 private ResolveResult DoResolve(IResolveContext context, int? codepage) 74 private ResolveResult DoResolve(IResolveContext context, IVariableResolver variableResolver)
73 { 75 {
74 var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch); 76 var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
75 77
@@ -80,7 +82,7 @@ namespace WixToolset.Core
80 var command = new ResolveFieldsCommand(); 82 var command = new ResolveFieldsCommand();
81 command.Messaging = this.Messaging; 83 command.Messaging = this.Messaging;
82 command.BuildingPatch = buildingPatch; 84 command.BuildingPatch = buildingPatch;
83 command.VariableResolver = this.VariableResolver; 85 command.VariableResolver = variableResolver;
84 command.BindPaths = context.BindPaths; 86 command.BindPaths = context.BindPaths;
85 command.Extensions = context.Extensions; 87 command.Extensions = context.Extensions;
86 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; 88 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
@@ -118,7 +120,6 @@ namespace WixToolset.Core
118 120
119 return new ResolveResult 121 return new ResolveResult
120 { 122 {
121 Codepage = codepage.HasValue ? codepage.Value : -1,
122 ExpectedEmbeddedFiles = expectedEmbeddedFiles, 123 ExpectedEmbeddedFiles = expectedEmbeddedFiles,
123 DelayedFields = delayedFields, 124 DelayedFields = delayedFields,
124 IntermediateRepresentation = context.IntermediateRepresentation 125 IntermediateRepresentation = context.IntermediateRepresentation
@@ -128,13 +129,13 @@ namespace WixToolset.Core
128 /// <summary> 129 /// <summary>
129 /// Localize dialogs and controls. 130 /// Localize dialogs and controls.
130 /// </summary> 131 /// </summary>
131 private void LocalizeUI(IResolveContext context) 132 private void LocalizeUI(IVariableResolver variableResolver, Intermediate intermediate)
132 { 133 {
133 foreach (var section in context.IntermediateRepresentation.Sections) 134 foreach (var section in intermediate.Sections)
134 { 135 {
135 foreach (var symbol in section.Symbols.OfType<DialogSymbol>()) 136 foreach (var symbol in section.Symbols.OfType<DialogSymbol>())
136 { 137 {
137 if (this.VariableResolver.TryGetLocalizedControl(symbol.Id.Id, null, out var localizedControl)) 138 if (variableResolver.TryGetLocalizedControl(symbol.Id.Id, null, out var localizedControl))
138 { 139 {
139 if (CompilerConstants.IntegerNotSet != localizedControl.X) 140 if (CompilerConstants.IntegerNotSet != localizedControl.X)
140 { 141 {
@@ -169,7 +170,7 @@ namespace WixToolset.Core
169 170
170 foreach (var symbol in section.Symbols.OfType<ControlSymbol>()) 171 foreach (var symbol in section.Symbols.OfType<ControlSymbol>())
171 { 172 {
172 if (this.VariableResolver.TryGetLocalizedControl(symbol.DialogRef, symbol.Control, out var localizedControl)) 173 if (variableResolver.TryGetLocalizedControl(symbol.DialogRef, symbol.Control, out var localizedControl))
173 { 174 {
174 if (CompilerConstants.IntegerNotSet != localizedControl.X) 175 if (CompilerConstants.IntegerNotSet != localizedControl.X)
175 { 176 {
@@ -204,24 +205,42 @@ namespace WixToolset.Core
204 } 205 }
205 } 206 }
206 207
207 private int? PopulateVariableResolver(IResolveContext context) 208 private IVariableResolver CreateVariableResolver(IResolveContext context, IEnumerable<Localization> filteredLocalizations)
208 { 209 {
209 var localizations = FilterLocalizations(context); 210 var variableResolver = this.ServiceProvider.GetService<IVariableResolver>();
210 var codepage = localizations.FirstOrDefault()?.Codepage;
211 211
212 foreach (var localization in localizations) 212 foreach (var localization in filteredLocalizations)
213 { 213 {
214 this.VariableResolver.AddLocalization(localization); 214 variableResolver.AddLocalization(localization);
215 } 215 }
216 216
217 // Gather all the wix variables. 217 // Gather all the wix variables.
218 var wixVariableSymbols = context.IntermediateRepresentation.Sections.SelectMany(s => s.Symbols).OfType<WixVariableSymbol>(); 218 var wixVariableSymbols = context.IntermediateRepresentation.Sections.SelectMany(s => s.Symbols).OfType<WixVariableSymbol>();
219 foreach (var symbol in wixVariableSymbols) 219 foreach (var symbol in wixVariableSymbols)
220 { 220 {
221 this.VariableResolver.AddVariable(symbol.SourceLineNumbers, symbol.Id.Id, symbol.Value, symbol.Overridable); 221 variableResolver.AddVariable(symbol.SourceLineNumbers, symbol.Id.Id, symbol.Value, symbol.Overridable);
222 }
223
224 return variableResolver;
225 }
226
227 private bool TryGetCultureInfo(string culture, out CultureInfo cultureInfo)
228 {
229 cultureInfo = null;
230
231 if (!String.IsNullOrEmpty(culture))
232 {
233 try
234 {
235 cultureInfo = new CultureInfo(culture, useUserOverride: false);
236 }
237 catch
238 {
239 this.Messaging.Write("");
240 }
222 } 241 }
223 242
224 return codepage; 243 return cultureInfo != null;
225 } 244 }
226 245
227 private static IEnumerable<Localization> FilterLocalizations(IResolveContext context) 246 private static IEnumerable<Localization> FilterLocalizations(IResolveContext context)
diff --git a/src/test/WixToolsetTest.CoreIntegration/LanguageFixture.cs b/src/test/WixToolsetTest.CoreIntegration/LanguageFixture.cs
new file mode 100644
index 00000000..319b0788
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/LanguageFixture.cs
@@ -0,0 +1,155 @@
1// Copyright (c) .NET Foundation 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 WixToolsetTest.CoreIntegration
4{
5 using System.IO;
6 using System.Linq;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11 using Xunit;
12
13 public class LanguageFixture
14 {
15 [Fact]
16 public void CanBuildWithDefaultProductLanguage()
17 {
18 var folder = TestData.Get(@"TestData", "Language");
19
20 using (var fs = new DisposableFileSystem())
21 {
22 var baseFolder = fs.GetFolder();
23
24 var result = WixRunner.Execute(new[]
25 {
26 "build",
27 Path.Combine(folder, "Package.wxs"),
28 "-loc", Path.Combine(folder, "Package.wxl"),
29 "-bindpath", Path.Combine(folder, "data"),
30 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
31 "-o", Path.Combine(baseFolder, @"bin\test.msi")
32 });
33
34 result.AssertSuccess();
35
36 var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
37 var section = intermediate.Sections.Single();
38
39 var propertySymbol = section.Symbols.OfType<PropertySymbol>().Single(p => p.Id.Id == "ProductLanguage");
40 Assert.Equal("0", propertySymbol.Value);
41
42 var summaryPlatform = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.PlatformAndLanguage);
43 Assert.Equal("Intel;0", summaryPlatform.Value);
44
45 var summaryCodepage = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.Codepage);
46 Assert.Equal("1252", summaryCodepage.Value);
47 }
48 }
49
50 [Fact]
51 public void CanBuildEnuWxl()
52 {
53 var folder = TestData.Get(@"TestData", "Language");
54
55 using (var fs = new DisposableFileSystem())
56 {
57 var baseFolder = fs.GetFolder();
58
59 var result = WixRunner.Execute(new[]
60 {
61 "build",
62 Path.Combine(folder, "Package.wxs"),
63 "-loc", Path.Combine(folder, "Package.en-us.wxl"),
64 "-bindpath", Path.Combine(folder, "data"),
65 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
66 "-o", Path.Combine(baseFolder, @"bin\test.msi")
67 });
68
69 result.AssertSuccess();
70
71 var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
72 var section = intermediate.Sections.Single();
73
74 var propertySymbol = section.Symbols.OfType<PropertySymbol>().Single(p => p.Id.Id == "ProductLanguage");
75 Assert.Equal("1033", propertySymbol.Value);
76
77 var summaryPlatform = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.PlatformAndLanguage);
78 Assert.Equal("Intel;1033", summaryPlatform.Value);
79
80 var summaryCodepage = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.Codepage);
81 Assert.Equal("1252", summaryCodepage.Value);
82 }
83 }
84
85 [Fact]
86 public void CanBuildJpnWxl()
87 {
88 var folder = TestData.Get(@"TestData", "Language");
89
90 using (var fs = new DisposableFileSystem())
91 {
92 var baseFolder = fs.GetFolder();
93
94 var result = WixRunner.Execute(new[]
95 {
96 "build",
97 Path.Combine(folder, "Package.wxs"),
98 "-loc", Path.Combine(folder, "Package.ja-jp.wxl"),
99 "-bindpath", Path.Combine(folder, "data"),
100 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
101 "-o", Path.Combine(baseFolder, @"bin\test.msi")
102 });
103
104 result.AssertSuccess();
105
106 var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
107 var section = intermediate.Sections.Single();
108
109 var propertySymbol = section.Symbols.OfType<PropertySymbol>().Single(p => p.Id.Id == "ProductLanguage");
110 Assert.Equal("1041", propertySymbol.Value);
111
112 var summaryPlatform = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.PlatformAndLanguage);
113 Assert.Equal("Intel;1041", summaryPlatform.Value);
114
115 var summaryCodepage = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.Codepage);
116 Assert.Equal("932", summaryCodepage.Value);
117 }
118 }
119
120 [Fact]
121 public void CanBuildJpnWxlWithEnuSummaryInfo()
122 {
123 var folder = TestData.Get(@"TestData", "Language");
124
125 using (var fs = new DisposableFileSystem())
126 {
127 var baseFolder = fs.GetFolder();
128
129 var result = WixRunner.Execute(new[]
130 {
131 "build",
132 Path.Combine(folder, "Package.wxs"),
133 "-loc", Path.Combine(folder, "PackageWithEnSummaryInfo.ja-jp.wxl"),
134 "-bindpath", Path.Combine(folder, "data"),
135 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
136 "-o", Path.Combine(baseFolder, @"bin\test.msi")
137 });
138
139 result.AssertSuccess();
140
141 var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"bin\test.wixpdb"));
142 var section = intermediate.Sections.Single();
143
144 var propertySymbol = section.Symbols.OfType<PropertySymbol>().Single(p => p.Id.Id == "ProductLanguage");
145 Assert.Equal("1041", propertySymbol.Value);
146
147 var summaryPlatform = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.PlatformAndLanguage);
148 Assert.Equal("Intel;1041", summaryPlatform.Value);
149
150 var summaryCodepage = section.Symbols.OfType<SummaryInformationSymbol>().Single(s => s.PropertyId == SummaryInformationType.Codepage);
151 Assert.Equal("1252", summaryCodepage.Value);
152 }
153 }
154 }
155}
diff --git a/src/test/WixToolsetTest.CoreIntegration/LinkerFixture.cs b/src/test/WixToolsetTest.CoreIntegration/LinkerFixture.cs
index 41c1d773..f73a57d0 100644
--- a/src/test/WixToolsetTest.CoreIntegration/LinkerFixture.cs
+++ b/src/test/WixToolsetTest.CoreIntegration/LinkerFixture.cs
@@ -19,8 +19,8 @@ namespace WixToolsetTest.CoreIntegration
19 [Fact] 19 [Fact]
20 public void MustCompileBeforeLinking() 20 public void MustCompileBeforeLinking()
21 { 21 {
22 var intermediate1 = new Intermediate("TestIntermediate1", new[] { new IntermediateSection("test1", SectionType.Product, 65001) }, null); 22 var intermediate1 = new Intermediate("TestIntermediate1", new[] { new IntermediateSection("test1", SectionType.Product) }, null);
23 var intermediate2 = new Intermediate("TestIntermediate2", new[] { new IntermediateSection("test2", SectionType.Fragment, 65001) }, null); 23 var intermediate2 = new Intermediate("TestIntermediate2", new[] { new IntermediateSection("test2", SectionType.Fragment) }, null);
24 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider(); 24 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider();
25 25
26 var listener = new TestMessageListener(); 26 var listener = new TestMessageListener();
diff --git a/src/test/WixToolsetTest.CoreIntegration/ParseFixture.cs b/src/test/WixToolsetTest.CoreIntegration/ParseFixture.cs
index 2d98a66c..cdba85de 100644
--- a/src/test/WixToolsetTest.CoreIntegration/ParseFixture.cs
+++ b/src/test/WixToolsetTest.CoreIntegration/ParseFixture.cs
@@ -16,7 +16,7 @@ namespace WixToolsetTest.CoreIntegration
16 public void GeneratesCorrectCustomActionIdentifiers() 16 public void GeneratesCorrectCustomActionIdentifiers()
17 { 17 {
18 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider(); 18 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider();
19 var section = new IntermediateSection("section", SectionType.Fragment, 0); 19 var section = new IntermediateSection("section", SectionType.Fragment);
20 var parseHelper = serviceProvider.GetService<IParseHelper>(); 20 var parseHelper = serviceProvider.GetService<IParseHelper>();
21 21
22 parseHelper.CreateCustomActionReference(null, section, "CustomAction32", Platform.X86, CustomActionPlatforms.X86); 22 parseHelper.CreateCustomActionReference(null, section, "CustomAction32", Platform.X86, CustomActionPlatforms.X86);
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl
new file mode 100644
index 00000000..f7453566
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.en-us.wxl
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="utf-8"?>
2<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
3
4 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
5 <String Id="FeatureTitle">MsiPackage</String>
6
7</WixLocalization>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl
new file mode 100644
index 00000000..ef287da7
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.ja-jp.wxl
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="utf-8"?>
2<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP">
3
4 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
5 <String Id="FeatureTitle">MsiPackage</String>
6
7</WixLocalization>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxl
new file mode 100644
index 00000000..10ebf2c5
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxl
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="utf-8"?>
2<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl">
3
4 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
5 <String Id="FeatureTitle">MsiPackage</String>
6
7</WixLocalization>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxs
new file mode 100644
index 00000000..2bbc14f9
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/Package.wxs
@@ -0,0 +1,16 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="~DefaultLanguagePackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3
4 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
5
6 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
7 <Component Directory="INSTALLFOLDER">
8 <File Source="test.txt" />
9 </Component>
10 </Feature>
11 </Package>
12
13 <Fragment>
14 <Directory Id="INSTALLFOLDER" Name="ProgramFilesFolder:\MsiPackage" />
15 </Fragment>
16</Wix>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/Language/PackageWithEnSummaryInfo.ja-jp.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/PackageWithEnSummaryInfo.ja-jp.wxl
new file mode 100644
index 00000000..596ee077
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/PackageWithEnSummaryInfo.ja-jp.wxl
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="utf-8"?>
2<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" SummaryInformationCodepage="1252" Culture="ja-JP">
3
4 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
5 <String Id="FeatureTitle">MsiPackage</String>
6
7</WixLocalization>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/Language/data/test.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/data/test.txt
new file mode 100644
index 00000000..cd0db0e1
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/Language/data/test.txt
@@ -0,0 +1 @@
This is test.txt. \ No newline at end of file
diff --git a/src/test/WixToolsetTest.CoreIntegration/VariableResolverFixture.cs b/src/test/WixToolsetTest.CoreIntegration/VariableResolverFixture.cs
index 28c68e99..15e5d334 100644
--- a/src/test/WixToolsetTest.CoreIntegration/VariableResolverFixture.cs
+++ b/src/test/WixToolsetTest.CoreIntegration/VariableResolverFixture.cs
@@ -25,7 +25,7 @@ namespace WixToolsetTest.CoreIntegration
25 { "ProductNameEditionVersion", new BindVariable() { Id = "ProductNameEditionVersion", Value = "!(loc.ProductNameEdition) v1.2.3" } }, 25 { "ProductNameEditionVersion", new BindVariable() { Id = "ProductNameEditionVersion", Value = "!(loc.ProductNameEdition) v1.2.3" } },
26 }; 26 };
27 27
28 var localization = new Localization(0, "x-none", variables, new Dictionary<string,LocalizedControl>()); 28 var localization = new Localization(0, null, "x-none", variables, new Dictionary<string,LocalizedControl>());
29 29
30 variableResolver.AddLocalization(localization); 30 variableResolver.AddLocalization(localization);
31 31