@joebigelow / wix / commits / 3dc92067

Remove dependency on Dtf.WindowsInstaller from Core

Rob Mensching committed Mar 25, 2021 at 10:00 UTC 3dc92067f57e3803d4e1bac6d055e2a23f797414
5 files changed +257 -358
src/WixToolset.Core.Burn/Bind/ProcessBundleSoftwareTagsCommand.cs
+6 -15
@@ -8,9 +8,9 @@ namespace WixToolset.Core.Burn.Bind
8 using System.Linq;
9 using System.Text;
10 using System.Xml;
11 + using WixToolset.Core.Native.Msi;
12 using WixToolset.Data;
13 using WixToolset.Data.Symbols;
13 - using WixToolset.Dtf.WindowsInstaller;
14
15 internal class ProcessBundleSoftwareTagsCommand
16 {
@@ -69,22 +69,13 @@ namespace WixToolset.Core.Burn.Bind
69 {
70 var payload = payloadSymbolsById[msiPackage.PayloadRef];
71
72 - using (var db = new Database(payload.SourceFile.Path))
72 + using (var db = new Database(payload.SourceFile.Path, OpenDatabase.ReadOnly))
73 {
74 - using (var view = db.OpenView("SELECT `Regid`, `TagId` FROM `SoftwareIdentificationTag`"))
74 + using (var view = db.OpenExecuteView("SELECT `Regid`, `TagId` FROM `SoftwareIdentificationTag`"))
75 {
76 - view.Execute();
77 - while (true)
76 + foreach (var record in view.Records)
77 {
79 - using (var record = view.Fetch())
80 - {
81 - if (null == record)
82 - {
83 - break;
84 - }
85 -
86 - tags.Add(new SoftwareTag { Regid = record.GetString(1), Id = record.GetString(2) });
87 - }
78 + tags.Add(new SoftwareTag { Regid = record.GetString(1), Id = record.GetString(2) });
79 }
80 }
81 }
@@ -98,7 +89,7 @@ namespace WixToolset.Core.Burn.Bind
89 {
90 var versionScheme = Version.TryParse(version, out _) ? "multipartnumeric" : "alphanumeric";
91
101 - using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true}))
92 + using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true }))
93 {
94 writer.WriteStartDocument();
95 writer.WriteStartElement("SoftwareIdentity", "http://standards.iso.org/iso/19770/-2/2015/schema.xsd");
src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+213 -290
@@ -4,24 +4,23 @@ namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 using System.Collections.Generic;
7 - using System.Diagnostics;
7 using System.Globalization;
8 using System.IO;
9 using System.Linq;
10 using WixToolset.Data;
11 using WixToolset.Extensibility;
13 - using Dtf = WixToolset.Dtf.WindowsInstaller;
12 using WixToolset.Extensibility.Services;
13 using WixToolset.Data.Symbols;
14 using WixToolset.Data.WindowsInstaller;
15 using WixToolset.Extensibility.Data;
16 + using WixToolset.Core.Native.Msi;
17
18 /// <summary>
19 /// Initializes package state from the MSI contents.
20 /// </summary>
21 internal class ProcessMsiPackageCommand
22 {
24 - private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'";
23 + private const string PropertySqlQuery = "SELECT `Value` FROM `Property` WHERE `Property` = ?";
24
25 public ProcessMsiPackageCommand(IServiceProvider serviceProvider, IEnumerable<IBurnBackendBinderExtension> backendExtensions, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads)
26 {
@@ -64,41 +63,60 @@ namespace WixToolset.Core.Burn.Bundles
63 var compressed = false;
64 try
65 {
67 - // Read data out of the msi database...
68 - using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false))
66 + using (var db = new Database(sourcePath, OpenDatabase.ReadOnly))
67 {
70 - // 1 is the Word Count summary information stream bit that means
71 - // the MSI uses short file names when set. We care about long file
72 - // names so check when the bit is not set.
73 - longNamesInImage = 0 == (sumInfo.WordCount & 1);
74 -
75 - // 2 is the Word Count summary information stream bit that means
76 - // files are compressed in the MSI by default when the bit is set.
77 - compressed = 2 == (sumInfo.WordCount & 2);
78 -
79 - // 8 is the Word Count summary information stream bit that means
80 - // "Elevated privileges are not required to install this package."
81 - // in MSI 4.5 and below, if this bit is 0, elevation is required.
82 - var perMachine = (0 == (sumInfo.WordCount & 8));
83 - var x64 = sumInfo.Template.Contains("x64");
84 -
85 - this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
86 - this.Facade.PackageSymbol.Win64 = x64;
87 - }
68 + // Read data out of the msi database...
69 + using (var sumInfo = new SummaryInformation(db))
70 + {
71 + var fileAndElevateFlags = sumInfo.GetNumericProperty(SummaryInformation.Package.FileAndElevatedFlags);
72 + var platformsAndLanguages = sumInfo.GetProperty(SummaryInformation.Package.PlatformsAndLanguages);
73
89 - using (var db = new Dtf.Database(sourcePath))
90 - {
91 - msiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode");
92 - msiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode");
93 - msiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer");
94 - msiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture);
95 - msiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion");
74 + // 1 is the Word Count summary information stream bit that means
75 + // the MSI uses short file names when set. We care about long file
76 + // names so check when the bit is not set.
77 +
78 + longNamesInImage = 0 == (fileAndElevateFlags & 1);
79 +
80 + // 2 is the Word Count summary information stream bit that means
81 + // files are compressed in the MSI by default when the bit is set.
82 + compressed = 2 == (fileAndElevateFlags & 2);
83 +
84 + // 8 is the Word Count summary information stream bit that means
85 + // "Elevated privileges are not required to install this package."
86 + // in MSI 4.5 and below, if this bit is 0, elevation is required.
87 + var perMachine = (0 == (fileAndElevateFlags & 8));
88 + var x64 = platformsAndLanguages.Contains("x64");
89 +
90 + this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
91 + this.Facade.PackageSymbol.Win64 = x64;
92 + }
93 +
94 + string packageName = null;
95 + string packageDescription = null;
96 + string allusers = null;
97 + string fastInstall = null;
98 + string systemComponent = null;
99 +
100 + using (var view = db.OpenView(PropertySqlQuery))
101 + {
102 + packageName = ProcessMsiPackageCommand.GetProperty(view, "ProductName");
103 + packageDescription = ProcessMsiPackageCommand.GetProperty(view, "ARPCOMMENTS");
104 + allusers = ProcessMsiPackageCommand.GetProperty(view, "ALLUSERS");
105 + fastInstall = ProcessMsiPackageCommand.GetProperty(view, "MSIFASTINSTALL");
106 + systemComponent = ProcessMsiPackageCommand.GetProperty(view, "ARPSYSTEMCOMPONENT");
107 +
108 + msiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(view, "ProductCode");
109 + msiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(view, "UpgradeCode");
110 + msiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(view, "Manufacturer");
111 + msiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(view, "ProductLanguage"), CultureInfo.InvariantCulture);
112 + msiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(view, "ProductVersion");
113 + }
114
115 if (!this.BackendHelper.IsValidFourPartVersion(msiPackage.ProductVersion))
116 {
117 // not a proper .NET version (e.g., five fields); can we get a valid four-part version number?
118 string version = null;
101 - string[] versionParts = msiPackage.ProductVersion.Split('.');
119 + var versionParts = msiPackage.ProductVersion.Split('.');
120 var count = versionParts.Length;
121 if (0 < count)
122 {
@@ -127,12 +145,12 @@ namespace WixToolset.Core.Burn.Bundles
145
146 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName))
147 {
130 - this.Facade.PackageSymbol.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName");
148 + this.Facade.PackageSymbol.DisplayName = packageName;
149 }
150
151 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Description))
152 {
135 - this.Facade.PackageSymbol.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS");
153 + this.Facade.PackageSymbol.Description = packageDescription;
154 }
155
156 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Version))
@@ -144,13 +162,13 @@ namespace WixToolset.Core.Burn.Bundles
162
163 var msiPropertyNames = this.GetMsiPropertyNames(packagePayload.Id.Id);
164
147 - this.SetPerMachineAppropriately(db, msiPackage, sourcePath);
165 + this.SetPerMachineAppropriately(allusers, msiPackage, sourcePath);
166
167 // Ensure the MSI package is appropriately marked visible or not.
150 - this.SetPackageVisibility(db, msiPackage, msiPropertyNames);
168 + this.SetPackageVisibility(systemComponent, msiPackage, msiPropertyNames);
169
170 // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance.
153 - if (!msiPropertyNames.Contains("MSIFASTINSTALL") && !ProcessMsiPackageCommand.HasProperty(db, "MSIFASTINSTALL"))
171 + if (!String.IsNullOrEmpty(fastInstall))
172 {
173 this.AddMsiProperty(msiPackage, "MSIFASTINSTALL", "7");
174 }
@@ -171,10 +189,10 @@ namespace WixToolset.Core.Burn.Bundles
189 this.Facade.PackageSymbol.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames);
190
191 // Add all dependency providers from the MSI.
174 - this.ImportDependencyProviders(msiPackage, db);
192 + this.ImportDependencyProviders(db, msiPackage);
193 }
194 }
177 - catch (Dtf.InstallerException e)
195 + catch (MsiException e)
196 {
197 this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(this.Facade.PackageSymbol.SourceLineNumbers, sourcePath, e.Message));
198 }
@@ -196,7 +214,7 @@ namespace WixToolset.Core.Burn.Bundles
214 return new HashSet<string>(properties, StringComparer.Ordinal);
215 }
216
199 - private void SetPerMachineAppropriately(Dtf.Database db, WixBundleMsiPackageSymbol msiPackage, string sourcePath)
217 + private void SetPerMachineAppropriately(string allusers, WixBundleMsiPackageSymbol msiPackage, string sourcePath)
218 {
219 if (msiPackage.ForcePerMachine)
220 {
@@ -211,8 +229,6 @@ namespace WixToolset.Core.Burn.Bundles
229 }
230 else
231 {
214 - var allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS");
215 -
232 if (String.IsNullOrEmpty(allusers))
233 {
234 // Not forced per-machine and no ALLUSERS property, flip back to per-user.
@@ -240,269 +256,219 @@ namespace WixToolset.Core.Burn.Bundles
256 }
257 }
258
243 - private void SetPackageVisibility(Dtf.Database db, WixBundleMsiPackageSymbol msiPackage, ISet<string> msiPropertyNames)
259 + private void SetPackageVisibility(string systemComponent, WixBundleMsiPackageSymbol msiPackage, ISet<string> msiPropertyNames)
260 {
245 - var alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT");
246 - var visible = (this.Facade.PackageSymbol.Attributes & WixBundlePackageAttributes.Visible) == WixBundlePackageAttributes.Visible;
247 -
248 - // If not already set to the correct visibility.
249 - if (alreadyVisible != visible)
261 + // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again.
262 + if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT"))
263 {
251 - // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again.
252 - if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT"))
264 + var alreadyVisible = String.IsNullOrEmpty(systemComponent);
265 + var visible = (this.Facade.PackageSymbol.Attributes & WixBundlePackageAttributes.Visible) == WixBundlePackageAttributes.Visible;
266 +
267 + // If not already set to the correct visibility.
268 + if (alreadyVisible != visible)
269 {
270 this.AddMsiProperty(msiPackage, "ARPSYSTEMCOMPONENT", visible ? String.Empty : "1");
271 }
272 }
273 }
274
259 - private void CreateRelatedPackages(Dtf.Database db)
275 + private void CreateRelatedPackages(Database db)
276 {
277 // Represent the Upgrade table as related packages.
262 - if (db.Tables.Contains("Upgrade"))
278 + if (db.TableExists("Upgrade"))
279 {
264 - using (var view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`"))
280 + using (var view = db.OpenExecuteView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`"))
281 {
266 - view.Execute();
267 - while (true)
282 + foreach (var record in view.Records)
283 {
269 - using (var record = view.Fetch())
270 - {
271 - if (null == record)
272 - {
273 - break;
274 - }
284 + var recordAttributes = record.GetInteger(5);
285
276 - var recordAttributes = record.GetInteger(5);
286 + var attributes = WixBundleRelatedPackageAttributes.None;
287 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect ? WixBundleRelatedPackageAttributes.OnlyDetect : 0;
288 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive ? WixBundleRelatedPackageAttributes.MinInclusive : 0;
289 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive ? WixBundleRelatedPackageAttributes.MaxInclusive : 0;
290 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive ? WixBundleRelatedPackageAttributes.LangInclusive : 0;
291
278 - var attributes = WixBundleRelatedPackageAttributes.None;
279 - attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect ? WixBundleRelatedPackageAttributes.OnlyDetect : 0;
280 - attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive ? WixBundleRelatedPackageAttributes.MinInclusive : 0;
281 - attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive ? WixBundleRelatedPackageAttributes.MaxInclusive : 0;
282 - attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive ? WixBundleRelatedPackageAttributes.LangInclusive : 0;
283 -
284 - this.Section.AddSymbol(new WixBundleRelatedPackageSymbol(this.Facade.PackageSymbol.SourceLineNumbers)
285 - {
286 - PackageRef = this.Facade.PackageId,
287 - RelatedId = record.GetString(1),
288 - MinVersion = record.GetString(2),
289 - MaxVersion = record.GetString(3),
290 - Languages = record.GetString(4),
291 - Attributes = attributes,
292 - });
293 - }
292 + this.Section.AddSymbol(new WixBundleRelatedPackageSymbol(this.Facade.PackageSymbol.SourceLineNumbers)
293 + {
294 + PackageRef = this.Facade.PackageId,
295 + RelatedId = record.GetString(1),
296 + MinVersion = record.GetString(2),
297 + MaxVersion = record.GetString(3),
298 + Languages = record.GetString(4),
299 + Attributes = attributes,
300 + });
301 }
302 }
303 }
304 }
305
299 - private void CreateMsiFeatures(Dtf.Database db)
306 + private void CreateMsiFeatures(Database db)
307 {
301 - if (db.Tables.Contains("Feature"))
308 + if (db.TableExists("Feature"))
309 {
310 + using (var allFeaturesView = db.OpenExecuteView("SELECT * FROM `Feature`"))
311 using (var featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?"))
312 using (var componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?"))
313 {
306 - using (var featureRecord = new Dtf.Record(1))
307 - using (var componentRecord = new Dtf.Record(1))
314 + using (var featureRecord = new Record(1))
315 + using (var componentRecord = new Record(1))
316 {
309 - using (var allFeaturesView = db.OpenView("SELECT * FROM `Feature`"))
317 + foreach (var allFeaturesResultRecord in allFeaturesView.Records)
318 {
311 - allFeaturesView.Execute();
319 + var featureName = allFeaturesResultRecord.GetString(1);
320 +
321 + // Calculate the Feature size.
322 + featureRecord.SetString(1, featureName);
323 + featureView.Execute(featureRecord);
324
313 - while (true)
325 + // Loop over all the components for the feature to calculate the size of the feature.
326 + long size = 0;
327 + foreach (var componentResultRecord in featureView.Records)
328 {
315 - using (var allFeaturesResultRecord = allFeaturesView.Fetch())
329 + var component = componentResultRecord.GetString(1);
330 + componentRecord.SetString(1, component);
331 + componentView.Execute(componentRecord);
332 +
333 + foreach (var fileResultRecord in componentView.Records)
334 {
317 - if (null == allFeaturesResultRecord)
318 - {
319 - break;
320 - }
321 -
322 - var featureName = allFeaturesResultRecord.GetString(1);
323 -
324 - // Calculate the Feature size.
325 - featureRecord.SetString(1, featureName);
326 - featureView.Execute(featureRecord);
327 -
328 - // Loop over all the components for the feature to calculate the size of the feature.
329 - long size = 0;
330 - while (true)
331 - {
332 - using (var componentResultRecord = featureView.Fetch())
333 - {
334 - if (null == componentResultRecord)
335 - {
336 - break;
337 - }
338 -
339 - var component = componentResultRecord.GetString(1);
340 - componentRecord.SetString(1, component);
341 - componentView.Execute(componentRecord);
342 -
343 - while (true)
344 - {
345 - using (var fileResultRecord = componentView.Fetch())
346 - {
347 - if (null == fileResultRecord)
348 - {
349 - break;
350 - }
351 -
352 - var fileSize = fileResultRecord.GetString(1);
353 - size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat);
354 - }
355 - }
356 - }
357 - }
358 -
359 - this.Section.AddSymbol(new WixBundleMsiFeatureSymbol(this.Facade.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, this.Facade.PackageId, featureName))
360 - {
361 - PackageRef = this.Facade.PackageId,
362 - Name = featureName,
363 - Parent = allFeaturesResultRecord.GetString(2),
364 - Title = allFeaturesResultRecord.GetString(3),
365 - Description = allFeaturesResultRecord.GetString(4),
366 - Display = allFeaturesResultRecord.GetInteger(5),
367 - Level = allFeaturesResultRecord.GetInteger(6),
368 - Directory = allFeaturesResultRecord.GetString(7),
369 - Attributes = allFeaturesResultRecord.GetInteger(8),
370 - Size = size
371 - });
335 + var fileSize = fileResultRecord.GetString(1);
336 + size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat);
337 }
338 }
339 +
340 + this.Section.AddSymbol(new WixBundleMsiFeatureSymbol(this.Facade.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, this.Facade.PackageId, featureName))
341 + {
342 + PackageRef = this.Facade.PackageId,
343 + Name = featureName,
344 + Parent = allFeaturesResultRecord.GetString(2),
345 + Title = allFeaturesResultRecord.GetString(3),
346 + Description = allFeaturesResultRecord.GetString(4),
347 + Display = allFeaturesResultRecord.GetInteger(5),
348 + Level = allFeaturesResultRecord.GetInteger(6),
349 + Directory = allFeaturesResultRecord.GetString(7),
350 + Attributes = allFeaturesResultRecord.GetInteger(8),
351 + Size = size
352 + });
353 }
354 }
355 }
356 }
357 }
358
380 - private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadSymbol packagePayload, ISet<string> payloadNames)
359 + private void ImportExternalCabinetAsPayloads(Database db, WixBundlePayloadSymbol packagePayload, ISet<string> payloadNames)
360 {
382 - if (db.Tables.Contains("Media"))
361 + if (db.TableExists("Media"))
362 {
384 - foreach (var cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`"))
363 + using (var view = db.OpenExecuteView("SELECT `Cabinet` FROM `Media`"))
364 {
386 - if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal))
365 + foreach (var cabinetRecord in view.Records)
366 {
388 - // If we didn't find the Payload as an existing child of the package, we need to
389 - // add it. We expect the file to exist on-disk in the same relative location as
390 - // the MSI expects to find it...
391 - var cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet);
367 + var cabinet = cabinetRecord.GetString(1);
368
393 - if (!payloadNames.Contains(cabinetName))
369 + if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal))
370 {
395 - var generatedId = this.BackendHelper.GenerateIdentifier("cab", packagePayload.Id.Id, cabinet);
396 - var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.PackageSymbol.SourceLineNumbers);
371 + // If we didn't find the Payload as an existing child of the package, we need to
372 + // add it. We expect the file to exist on-disk in the same relative location as
373 + // the MSI expects to find it...
374 + var cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet);
375
398 - this.Section.AddSymbol(new WixGroupSymbol(this.Facade.PackageSymbol.SourceLineNumbers)
376 + if (!payloadNames.Contains(cabinetName))
377 {
400 - ParentType = ComplexReferenceParentType.Package,
401 - ParentId = this.Facade.PackageId,
402 - ChildType = ComplexReferenceChildType.Payload,
403 - ChildId = generatedId
404 - });
378 + var generatedId = this.BackendHelper.GenerateIdentifier("cab", packagePayload.Id.Id, cabinet);
379 + var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.PackageSymbol.SourceLineNumbers);
380
406 - this.Section.AddSymbol(new WixBundlePayloadSymbol(this.Facade.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
407 - {
408 - Name = cabinetName,
409 - SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
410 - Compressed = packagePayload.Compressed,
411 - UnresolvedSourceFile = cabinetName,
412 - ContainerRef = packagePayload.ContainerRef,
413 - ContentFile = true,
414 - Packaging = packagePayload.Packaging,
415 - ParentPackagePayloadRef = packagePayload.Id.Id,
416 - });
381 + this.Section.AddSymbol(new WixGroupSymbol(this.Facade.PackageSymbol.SourceLineNumbers)
382 + {
383 + ParentType = ComplexReferenceParentType.Package,
384 + ParentId = this.Facade.PackageId,
385 + ChildType = ComplexReferenceChildType.Payload,
386 + ChildId = generatedId
387 + });
388 +
389 + this.Section.AddSymbol(new WixBundlePayloadSymbol(this.Facade.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
390 + {
391 + Name = cabinetName,
392 + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
393 + Compressed = packagePayload.Compressed,
394 + UnresolvedSourceFile = cabinetName,
395 + ContainerRef = packagePayload.ContainerRef,
396 + ContentFile = true,
397 + Packaging = packagePayload.Packaging,
398 + ParentPackagePayloadRef = packagePayload.Id.Id,
399 + });
400 + }
401 }
402 }
403 }
404 }
405 }
406
423 - private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadSymbol packagePayload, bool longNamesInImage, bool compressed, ISet<string> payloadNames)
407 + private long ImportExternalFileAsPayloadsAndReturnInstallSize(Database db, WixBundlePayloadSymbol packagePayload, bool longNamesInImage, bool compressed, ISet<string> payloadNames)
408 {
409 long size = 0;
410
427 - if (db.Tables.Contains("Component") && db.Tables.Contains("Directory") && db.Tables.Contains("File"))
411 + if (db.TableExists("Component") && db.TableExists("Directory") && db.TableExists("File"))
412 {
413 var directories = new Dictionary<string, IResolvedDirectory>();
414
415 // Load up the directory hash table so we will be able to resolve source paths
416 // for files in the MSI database.
433 - using (var view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
417 + using (var view = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
418 {
435 - view.Execute();
436 - while (true)
419 + foreach (var record in view.Records)
420 {
438 - using (var record = view.Fetch())
439 - {
440 - if (null == record)
441 - {
442 - break;
443 - }
421 + var sourceName = this.BackendHelper.GetMsiFileName(record.GetString(3), true, longNamesInImage);
422
445 - var sourceName = this.BackendHelper.GetMsiFileName(record.GetString(3), true, longNamesInImage);
423 + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(record.GetString(2), sourceName);
424
447 - var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(record.GetString(2), sourceName);
448 -
449 - directories.Add(record.GetString(1), resolvedDirectory);
450 - }
425 + directories.Add(record.GetString(1), resolvedDirectory);
426 }
427 }
428
429 // Resolve the source paths to external files and add each file size to the total
430 // install size of the package.
456 - using (var view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`"))
431 + using (var view = db.OpenExecuteView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`"))
432 {
458 - view.Execute();
459 - while (true)
433 + foreach (var record in view.Records)
434 {
461 - using (var record = view.Fetch())
435 + // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not
436 + // explicitly marked compressed then this is an external file.
437 + var compressionBit = record.GetInteger(4);
438 + if (WindowsInstallerConstants.MsidbFileAttributesNoncompressed == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) ||
439 + (!compressed && 0 == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesCompressed)))
440 {
463 - if (null == record)
464 - {
465 - break;
466 - }
441 + var fileSourcePath = this.PathResolver.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage);
442 + var name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath);
443
468 - // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not
469 - // explicitly marked compressed then this is an external file.
470 - var compressionBit = record.GetInteger(4);
471 - if (WindowsInstallerConstants.MsidbFileAttributesNoncompressed == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) ||
472 - (!compressed && 0 == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesCompressed)))
444 + if (!payloadNames.Contains(name))
445 {
474 - var fileSourcePath = this.PathResolver.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage);
475 - var name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath);
446 + var generatedId = this.BackendHelper.GenerateIdentifier("f", packagePayload.Id.Id, record.GetString(2));
447 + var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.PackageSymbol.SourceLineNumbers);
448
477 - if (!payloadNames.Contains(name))
449 + this.Section.AddSymbol(new WixGroupSymbol(this.Facade.PackageSymbol.SourceLineNumbers)
450 {
479 - var generatedId = this.BackendHelper.GenerateIdentifier("f", packagePayload.Id.Id, record.GetString(2));
480 - var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.PackageSymbol.SourceLineNumbers);
481 -
482 - this.Section.AddSymbol(new WixGroupSymbol(this.Facade.PackageSymbol.SourceLineNumbers)
483 - {
484 - ParentType = ComplexReferenceParentType.Package,
485 - ParentId = this.Facade.PackageId,
486 - ChildType = ComplexReferenceChildType.Payload,
487 - ChildId = generatedId
488 - });
489 -
490 - this.Section.AddSymbol(new WixBundlePayloadSymbol(this.Facade.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
491 - {
492 - Name = name,
493 - SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
494 - Compressed = packagePayload.Compressed,
495 - UnresolvedSourceFile = name,
496 - ContainerRef = packagePayload.ContainerRef,
497 - ContentFile = true,
498 - Packaging = packagePayload.Packaging,
499 - ParentPackagePayloadRef = packagePayload.Id.Id,
500 - });
501 - }
502 - }
451 + ParentType = ComplexReferenceParentType.Package,
452 + ParentId = this.Facade.PackageId,
453 + ChildType = ComplexReferenceChildType.Payload,
454 + ChildId = generatedId
455 + });
456
504 - size += record.GetInteger(5);
457 + this.Section.AddSymbol(new WixBundlePayloadSymbol(this.Facade.PackageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
458 + {
459 + Name = name,
460 + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
461 + Compressed = packagePayload.Compressed,
462 + UnresolvedSourceFile = name,
463 + ContainerRef = packagePayload.ContainerRef,
464 + ContentFile = true,
465 + Packaging = packagePayload.Packaging,
466 + ParentPackagePayloadRef = packagePayload.Id.Id,
467 + });
468 + }
469 }
470 +
471 + size += record.GetInteger(5);
472 }
473 }
474 }
@@ -520,36 +486,25 @@ namespace WixToolset.Core.Burn.Bundles
486 });
487 }
488
523 - private void ImportDependencyProviders(WixBundleMsiPackageSymbol msiPackage, Dtf.Database db)
489 + private void ImportDependencyProviders(Database db, WixBundleMsiPackageSymbol msiPackage)
490 {
525 - if (db.Tables.Contains("WixDependencyProvider"))
491 + if (db.TableExists("WixDependencyProvider"))
492 {
527 - var query = "SELECT `WixDependencyProvider`, `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`";
528 -
529 - using (var view = db.OpenView(query))
493 + using (var view = db.OpenExecuteView("SELECT `WixDependencyProvider`, `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`"))
494 {
531 - view.Execute();
532 - while (true)
495 + foreach (var record in view.Records)
496 {
534 - using (var record = view.Fetch())
535 - {
536 - if (null == record)
537 - {
538 - break;
539 - }
497 + var id = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", msiPackage.Id.Id, record.GetString(1)));
498
541 - var id = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", msiPackage.Id.Id, record.GetString(1)));
542 -
543 - // Import the provider key and attributes.
544 - this.Section.AddSymbol(new WixDependencyProviderSymbol(msiPackage.SourceLineNumbers, id)
545 - {
546 - ParentRef = msiPackage.Id.Id,
547 - ProviderKey = record.GetString(2),
548 - Version = record.GetString(3) ?? msiPackage.ProductVersion,
549 - DisplayName = record.GetString(4) ?? this.Facade.PackageSymbol.DisplayName,
550 - Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported | (WixDependencyProviderAttributes)record.GetInteger(5),
551 - });
552 - }
499 + // Import the provider key and attributes.
500 + this.Section.AddSymbol(new WixDependencyProviderSymbol(msiPackage.SourceLineNumbers, id)
501 + {
502 + ParentRef = msiPackage.Id.Id,
503 + ProviderKey = record.GetString(2),
504 + Version = record.GetString(3) ?? msiPackage.ProductVersion,
505 + DisplayName = record.GetString(4) ?? this.Facade.PackageSymbol.DisplayName,
506 + Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported | (WixDependencyProviderAttributes)record.GetInteger(5),
507 + });
508 }
509 }
510 }
@@ -585,51 +540,19 @@ namespace WixToolset.Core.Burn.Bundles
540 return resolvedPath;
541 }
542
588 - /// <summary>
589 - /// Queries a Windows Installer database for a Property value.
590 - /// </summary>
591 - /// <param name="db">Database to query.</param>
592 - /// <param name="property">Property to examine.</param>
593 - /// <returns>String value for result or null if query doesn't match a single result.</returns>
594 - private static string GetProperty(Dtf.Database db, string property)
543 + private static string GetProperty(View view, string property)
544 {
596 - try
597 - {
598 - return db.ExecuteScalar(PropertyQuery(property)).ToString();
599 - }
600 - catch (Dtf.InstallerException)
545 + using (var queryRecord = new Record(1))
546 {
602 - }
547 + queryRecord[1] = property;
548
604 - return null;
605 - }
549 + view.Execute(queryRecord);
550
607 - /// <summary>
608 - /// Queries a Windows Installer database to determine if one or more rows exist in the Property table.
609 - /// </summary>
610 - /// <param name="db">Database to query.</param>
611 - /// <param name="property">Property to examine.</param>
612 - /// <returns>True if query matches at least one result.</returns>
613 - private static bool HasProperty(Dtf.Database db, string property)
614 - {
615 - try
616 - {
617 - return 0 < db.ExecuteQuery(PropertyQuery(property)).Count;
618 - }
619 - catch (Dtf.InstallerException)
620 - {
551 + using (var record = view.Fetch())
552 + {
553 + return record?.GetString(1);
554 + }
555 }
622 -
623 - return false;
624 - }
625 -
626 - private static string PropertyQuery(string property)
627 - {
628 - // quick sanity check that we'll be creating a valid query...
629 - // TODO: Are there any other special characters we should be looking for?
630 - Debug.Assert(!property.Contains("'"));
631 -
632 - return String.Format(CultureInfo.InvariantCulture, ProcessMsiPackageCommand.PropertySqlFormat, property);
556 }
557 }
558 }
src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
+38 -51
@@ -4,23 +4,27 @@ namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 using System.Collections.Generic;
7 - using System.Diagnostics;
8 - using System.Globalization;
7 using System.IO;
8 using System.Text;
9 using System.Xml;
10 + using WixToolset.Core.Native.Msi;
11 using WixToolset.Data;
12 using WixToolset.Data.Symbols;
13 using WixToolset.Extensibility.Services;
15 - using Dtf = WixToolset.Dtf.WindowsInstaller;
14
15 /// <summary>
16 /// Initializes package state from the Msp contents.
17 /// </summary>
18 internal class ProcessMspPackageCommand
19 {
22 - private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'";
23 - private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false);
20 + private const string PatchMetadataQuery = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = ?";
21 + private static readonly XmlWriterSettings XmlSettings = new XmlWriterSettings()
22 + {
23 + Encoding = new UTF8Encoding(false),
24 + Indent = false,
25 + NewLineChars = String.Empty,
26 + NewLineHandling = NewLineHandling.Replace,
27 + };
28
29 public ProcessMspPackageCommand(IMessaging messaging, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> payloadSymbols)
30 {
@@ -52,30 +56,34 @@ namespace WixToolset.Core.Burn.Bundles
56
57 try
58 {
55 - // Read data out of the msp database...
56 - using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false))
57 - {
58 - mspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38);
59 - }
60 -
61 - using (var db = new Dtf.Database(sourcePath))
59 + using (var db = new Database(sourcePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile))
60 {
63 - if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName))
61 + // Read data out of the msp database...
62 + using (var sumInfo = new SummaryInformation(db))
63 {
65 - this.Facade.PackageSymbol.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName");
64 + var patchCode = sumInfo.GetProperty(SummaryInformation.Patch.PatchCode);
65 + mspPackage.PatchCode = patchCode.Substring(0, 38);
66 }
67
68 - if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Description))
68 + using (var view = db.OpenView(PatchMetadataQuery))
69 {
70 - this.Facade.PackageSymbol.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description");
71 - }
70 + if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName))
71 + {
72 + this.Facade.PackageSymbol.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(view, "DisplayName");
73 + }
74 +
75 + if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Description))
76 + {
77 + this.Facade.PackageSymbol.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(view, "Description");
78 + }
79
73 - mspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName");
80 + mspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(view, "ManufacturerName");
81 + }
82 }
83
84 this.ProcessPatchXml(packagePayload, mspPackage, sourcePath);
85 }
78 - catch (Dtf.InstallerException e)
86 + catch (MsiException e)
87 {
88 this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message));
89 return;
@@ -91,7 +99,7 @@ namespace WixToolset.Core.Burn.Bundles
99 {
100 var uniqueTargetCodes = new HashSet<string>();
101
94 - var patchXml = Dtf.Installer.ExtractPatchXmlData(sourcePath);
102 + var patchXml = Installer.ExtractPatchXml(sourcePath);
103
104 var doc = new XmlDocument();
105 doc.LoadXml(patchXml);
@@ -146,15 +154,7 @@ namespace WixToolset.Core.Burn.Bundles
154 // Save the XML as compact as possible.
155 using (var writer = new StringWriter())
156 {
149 - var settings = new XmlWriterSettings()
150 - {
151 - Encoding = ProcessMspPackageCommand.XmlOutputEncoding,
152 - Indent = false,
153 - NewLineChars = String.Empty,
154 - NewLineHandling = NewLineHandling.Replace,
155 - };
156 -
157 - using (var xmlWriter = XmlWriter.Create(writer, settings))
157 + using (var xmlWriter = XmlWriter.Create(writer, XmlSettings))
158 {
159 doc.WriteTo(xmlWriter);
160 }
@@ -163,32 +163,19 @@ namespace WixToolset.Core.Burn.Bundles
163 }
164 }
165
166 - /// <summary>
167 - /// Queries a Windows Installer patch database for a Property value from the MsiPatchMetadata table.
168 - /// </summary>
169 - /// <param name="db">Database to query.</param>
170 - /// <param name="property">Property to examine.</param>
171 - /// <returns>String value for result or null if query doesn't match a single result.</returns>
172 - private static string GetPatchMetadataProperty(Dtf.Database db, string property)
166 + private static string GetPatchMetadataProperty(View view, string property)
167 {
174 - try
175 - {
176 - return db.ExecuteScalar(PatchMetadataPropertyQuery(property)).ToString();
177 - }
178 - catch (Dtf.InstallerException)
168 + using (var queryRecord = new Record(1))
169 {
180 - }
181 -
182 - return null;
183 - }
170 + queryRecord[1] = property;
171
185 - private static string PatchMetadataPropertyQuery(string property)
186 - {
187 - // quick sanity check that we'll be creating a valid query...
188 - // TODO: Are there any other special characters we should be looking for?
189 - Debug.Assert(!property.Contains("'"));
172 + view.Execute(queryRecord);
173
191 - return String.Format(CultureInfo.InvariantCulture, ProcessMspPackageCommand.PatchMetadataFormat, property);
174 + using (var record = view.Fetch())
175 + {
176 + return record?.GetString(1);
177 + }
178 + }
179 }
180
181 private static bool TargetsCode(XmlNode node) => "true" == node?.Attributes["Validate"]?.Value;
src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
-1
@@ -27,7 +27,6 @@
27 <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" />
28 <PackageReference Include="WixToolset.Data" Version="4.0.*" />
29 <PackageReference Include="WixToolset.Dtf.Resources" Version="4.0.*" />
30 - <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" />
30 <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" />
31 </ItemGroup>
32
src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
-1
@@ -15,7 +15,6 @@
15 <ItemGroup>
16 <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" />
17 <PackageReference Include="WixToolset.Data" Version="4.0.*" />
18 - <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" />
18 <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" />
19 </ItemGroup>
20