aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-02 14:41:49 -0700
committerRob Mensching <rob@firegiant.com>2021-04-02 14:58:00 -0700
commit4449fcc5b8d104817c67135229682c66c3d892ca (patch)
tree327f617de2e296ddb4e62c50bf07ec8b5dcf0a3e /src/WixToolset.Core.WindowsInstaller
parent9cca339473d77c7036035f949239f5231c325968 (diff)
downloadwix-4449fcc5b8d104817c67135229682c66c3d892ca.tar.gz
wix-4449fcc5b8d104817c67135229682c66c3d892ca.tar.bz2
wix-4449fcc5b8d104817c67135229682c66c3d892ca.zip
Enable codepages and languages to be set via .wxl files
Fixes wixtoolset/issues#5801
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller')
-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
12 files changed, 291 insertions, 119 deletions
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>