main
cs 644 lines 30.7 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.IO;
10 using System.Linq;
11 using WixToolset.Data;
12 using WixToolset.Extensibility;
13 using WixToolset.Extensibility.Services;
14 using WixToolset.Data.Symbols;
15 using WixToolset.Data.WindowsInstaller;
16 using WixToolset.Extensibility.Data;
17 using WixToolset.Core.Native.Msi;
18
19 /// <summary>
20 /// Initializes package state from the MSI contents.
21 /// </summary>
22 internal class ProcessMsiPackageCommand
23 {
24 private const string PropertySqlQuery = "SELECT `Value` FROM `Property` WHERE `Property` = ?";
25
26 public ProcessMsiPackageCommand(IServiceProvider serviceProvider, IEnumerable<IBurnBackendBinderExtension> backendExtensions, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads)
27 {
28 this.Messaging = serviceProvider.GetService<IMessaging>();
29 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
30 this.PathResolver = serviceProvider.GetService<IPathResolver>();
31
32 this.BackendExtensions = backendExtensions;
33
34 this.PackagePayloads = packagePayloads;
35 this.Section = section;
36
37 this.ChainPackage = facade.PackageSymbol;
38 this.MsiPackage = (WixBundleMsiPackageSymbol)facade.SpecificPackageSymbol;
39 this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef];
40 }
41
42 private IMessaging Messaging { get; }
43
44 private IBackendHelper BackendHelper { get; }
45
46 private IPathResolver PathResolver { get; }
47
48 private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
49
50 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; }
51
52 private WixBundlePackageSymbol ChainPackage { get; }
53
54 private WixBundleMsiPackageSymbol MsiPackage { get; }
55
56 private string PackageId => this.ChainPackage.Id.Id;
57
58 private WixBundlePayloadSymbol PackagePayload { get; }
59
60 private IntermediateSection Section { get; }
61
62 /// <summary>
63 /// Processes the MSI packages to add properties and payloads from the MSI packages.
64 /// </summary>
65 public void Execute()
66 {
67 var harvestedMsiPackage = this.Section.Symbols.OfType<WixBundleHarvestedMsiPackageSymbol>()
68 .Where(h => h.Id.Id == this.PackagePayload.Id.Id)
69 .SingleOrDefault();
70
71 if (harvestedMsiPackage == null)
72 {
73 harvestedMsiPackage = this.HarvestPackage();
74
75 if (harvestedMsiPackage == null)
76 {
77 return;
78 }
79 }
80
81 foreach (var childPayload in this.Section.Symbols.OfType<WixBundlePayloadSymbol>().Where(p => p.ParentPackagePayloadRef == this.PackagePayload.Id.Id).ToList())
82 {
83 this.Section.AddSymbol(new WixGroupSymbol(childPayload.SourceLineNumbers)
84 {
85 ParentType = ComplexReferenceParentType.Package,
86 ParentId = this.PackageId,
87 ChildType = ComplexReferenceChildType.Payload,
88 ChildId = childPayload.Id.Id,
89 });
90 }
91
92 this.ChainPackage.PerMachine = harvestedMsiPackage.PerMachine;
93 this.ChainPackage.Win64 = harvestedMsiPackage.Win64;
94
95 this.MsiPackage.ProductCode = harvestedMsiPackage.ProductCode;
96 this.MsiPackage.UpgradeCode = harvestedMsiPackage.UpgradeCode;
97 this.MsiPackage.Manufacturer = harvestedMsiPackage.Manufacturer;
98 this.MsiPackage.ProductLanguage = Convert.ToInt32(harvestedMsiPackage.ProductLanguage, CultureInfo.InvariantCulture);
99 this.MsiPackage.ProductVersion = harvestedMsiPackage.ProductVersion;
100
101 if (String.IsNullOrEmpty(this.ChainPackage.CacheId))
102 {
103 this.ChainPackage.CacheId = CacheIdGenerator.GenerateLocalCacheId(this.Messaging, harvestedMsiPackage, this.PackagePayload, this.MsiPackage.SourceLineNumbers, "MsiPackage");
104 }
105
106 if (String.IsNullOrEmpty(this.ChainPackage.DisplayName))
107 {
108 this.ChainPackage.DisplayName = harvestedMsiPackage.ProductName;
109 }
110
111 if (String.IsNullOrEmpty(this.ChainPackage.Description))
112 {
113 this.ChainPackage.Description = harvestedMsiPackage.ArpComments;
114 }
115
116 if (String.IsNullOrEmpty(this.ChainPackage.Version))
117 {
118 this.ChainPackage.Version = this.MsiPackage.ProductVersion;
119 }
120
121 if (!this.BackendHelper.IsValidMsiProductVersion(this.MsiPackage.ProductVersion))
122 {
123 this.Messaging.Write(WarningMessages.InvalidMsiProductVersion(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId));
124 }
125
126 this.SetPerMachineAppropriately(harvestedMsiPackage.AllUsers);
127
128 var msiPropertyNames = this.GetMsiPropertyNames();
129
130 // Ensure the MSI package is appropriately marked visible or not.
131 this.SetPackageVisibility(harvestedMsiPackage.ArpSystemComponent, msiPropertyNames);
132
133 // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance.
134 if (String.IsNullOrEmpty(harvestedMsiPackage.MsiFastInstall) && !msiPropertyNames.Contains("MSIFASTINSTALL"))
135 {
136 this.AddMsiProperty("MSIFASTINSTALL", "7");
137 }
138
139 this.ChainPackage.InstallSize = harvestedMsiPackage.InstallSize;
140 }
141
142 public WixBundleHarvestedMsiPackageSymbol HarvestPackage()
143 {
144 bool perMachine;
145 bool win64;
146 string productName;
147 string arpComments;
148 string allUsers;
149 string msiFastInstall;
150 string arpSystemComponent;
151 string productCode;
152 string upgradeCode;
153 string manufacturer;
154 string productLanguage;
155 string productVersion;
156 long installSize;
157
158 var sourcePath = this.PackagePayload.SourceFile.Path;
159
160 try
161 {
162 var longNamesInImage = false;
163 var compressed = false;
164
165 this.CheckIfWindowsInstallerFileTooLarge(this.PackagePayload.SourceLineNumbers, sourcePath, "MSI");
166
167 using (var db = new Database(sourcePath, OpenDatabase.ReadOnly))
168 {
169 // Read data out of the msi database...
170 using (var sumInfo = new SummaryInformation(db))
171 {
172 var fileAndElevateFlags = sumInfo.GetNumericProperty(SummaryInformation.Package.FileAndElevatedFlags);
173 var platformsAndLanguages = sumInfo.GetProperty(SummaryInformation.Package.PlatformsAndLanguages);
174
175 // 1 is the Word Count summary information stream bit that means
176 // the MSI uses short file names when set. We care about long file
177 // names so check when the bit is not set.
178
179 longNamesInImage = 0 == (fileAndElevateFlags & 1);
180
181 // 2 is the Word Count summary information stream bit that means
182 // files are compressed in the MSI by default when the bit is set.
183 compressed = 2 == (fileAndElevateFlags & 2);
184
185 // 8 is the Word Count summary information stream bit that means
186 // "Elevated privileges are not required to install this package."
187 // in MSI 4.5 and below, if this bit is 0, elevation is required.
188 perMachine = (0 == (fileAndElevateFlags & 8));
189 win64 = this.IsWin64(sourcePath, platformsAndLanguages);
190 }
191
192 using (var view = db.OpenView(PropertySqlQuery))
193 {
194 productName = ProcessMsiPackageCommand.GetProperty(view, "ProductName");
195 arpComments = ProcessMsiPackageCommand.GetProperty(view, "ARPCOMMENTS");
196 allUsers = ProcessMsiPackageCommand.GetProperty(view, "ALLUSERS");
197 msiFastInstall = ProcessMsiPackageCommand.GetProperty(view, "MSIFASTINSTALL");
198 arpSystemComponent = ProcessMsiPackageCommand.GetProperty(view, "ARPSYSTEMCOMPONENT");
199
200 productCode = ProcessMsiPackageCommand.GetProperty(view, "ProductCode");
201 upgradeCode = ProcessMsiPackageCommand.GetProperty(view, "UpgradeCode");
202 manufacturer = ProcessMsiPackageCommand.GetProperty(view, "Manufacturer");
203 productLanguage = ProcessMsiPackageCommand.GetProperty(view, "ProductLanguage");
204 productVersion = ProcessMsiPackageCommand.GetProperty(view, "ProductVersion");
205 }
206
207 var payloadNames = this.GetPayloadTargetNames();
208
209 this.CreateRelatedPackages(db);
210
211 this.CreateMsiFeatures(db);
212
213 // Add all external cabinets as package payloads.
214 this.ImportExternalCabinetAsPayloads(db, payloadNames);
215
216 // Add all external files as package payloads and calculate the total install size as the rollup of
217 // File table's sizes.
218 installSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, longNamesInImage, compressed, payloadNames);
219
220 // Add all dependency providers from the MSI.
221 this.ImportDependencyProviders(db);
222 }
223 }
224 catch (MsiException e)
225 {
226 this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(this.PackagePayload.SourceLineNumbers, sourcePath, e.Message));
227 return null;
228 }
229
230 return this.Section.AddSymbol(new WixBundleHarvestedMsiPackageSymbol(this.PackagePayload.SourceLineNumbers, this.PackagePayload.Id)
231 {
232 PerMachine = perMachine,
233 Win64 = win64,
234 ProductName = productName,
235 ArpComments = arpComments,
236 AllUsers = allUsers,
237 MsiFastInstall = msiFastInstall,
238 ArpSystemComponent = arpSystemComponent,
239 ProductCode = productCode,
240 UpgradeCode = upgradeCode,
241 Manufacturer = manufacturer,
242 ProductLanguage = productLanguage,
243 ProductVersion = productVersion,
244 InstallSize = installSize,
245 });
246 }
247
248 private ISet<string> GetPayloadTargetNames()
249 {
250 var payloadNames = this.PackagePayloads.Values.Select(p => p.Name);
251
252 return new HashSet<string>(payloadNames, StringComparer.OrdinalIgnoreCase);
253 }
254
255 private ISet<string> GetMsiPropertyNames()
256 {
257 var properties = this.Section.Symbols.OfType<WixBundleMsiPropertySymbol>()
258 .Where(p => p.PackageRef == this.PackageId)
259 .Select(p => p.Name);
260
261 return new HashSet<string>(properties, StringComparer.Ordinal);
262 }
263
264 // https://docs.microsoft.com/en-us/windows/win32/msi/template-summary
265 private bool IsWin64(string sourcePath, string platformsAndLanguages)
266 {
267 var separatorIndex = platformsAndLanguages.IndexOf(';');
268 var platformValue = separatorIndex > 0 ? platformsAndLanguages.Substring(0, separatorIndex) : platformsAndLanguages;
269
270 switch (platformValue)
271 {
272 case "Arm64":
273 case "Intel64":
274 case "x64":
275 return true;
276
277 case "Arm":
278 case "Intel":
279 return false;
280
281 default:
282 this.Messaging.Write(BurnBackendWarnings.UnknownMsiPackagePlatform(this.PackagePayload.SourceLineNumbers, sourcePath, platformValue));
283 return true;
284 }
285 }
286
287 private void SetPerMachineAppropriately(string allusers)
288 {
289 Debug.Assert(this.ChainPackage.PerMachine.HasValue);
290 var perMachine = this.ChainPackage.PerMachine.Value;
291
292 // Can ignore ALLUSERS from MsiProperties because it is not allowed there.
293 if (this.MsiPackage.ForcePerMachine)
294 {
295 if (!perMachine)
296 {
297 this.Messaging.Write(WarningMessages.PerUserButForcingPerMachine(this.PackagePayload.SourceLineNumbers, this.PackageId));
298 this.ChainPackage.PerMachine = true; // ensure that we think the package is per-machine.
299 }
300
301 // Force ALLUSERS=1 via the MSI command-line.
302 this.AddMsiProperty("ALLUSERS", "1");
303 }
304 else
305 {
306 if (String.IsNullOrEmpty(allusers))
307 {
308 // Not forced per-machine and no ALLUSERS property, flip back to per-user.
309 if (perMachine)
310 {
311 this.Messaging.Write(WarningMessages.ImplicitlyPerUser(this.ChainPackage.SourceLineNumbers, this.PackageId));
312 this.ChainPackage.PerMachine = false;
313 }
314 }
315 else if (allusers.Equals("1", StringComparison.Ordinal))
316 {
317 if (!perMachine)
318 {
319 this.Messaging.Write(ErrorMessages.PerUserButAllUsersEquals1(this.ChainPackage.SourceLineNumbers, this.PackageId));
320 }
321 }
322 else if (allusers.Equals("2", StringComparison.Ordinal))
323 {
324 this.Messaging.Write(WarningMessages.DiscouragedAllUsersValue(this.ChainPackage.SourceLineNumbers, this.PackageId, perMachine ? "machine" : "user"));
325 }
326 else
327 {
328 this.Messaging.Write(ErrorMessages.UnsupportedAllUsersValue(this.ChainPackage.SourceLineNumbers, this.PackageId, allusers));
329 }
330 }
331 }
332
333 private void SetPackageVisibility(string systemComponent, ISet<string> msiPropertyNames)
334 {
335 // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again.
336 if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT"))
337 {
338 var alreadyVisible = String.IsNullOrEmpty(systemComponent);
339 var visible = this.ChainPackage.Visible;
340
341 // If not already set to the correct visibility.
342 if (alreadyVisible != visible)
343 {
344 this.AddMsiProperty("ARPSYSTEMCOMPONENT", visible ? String.Empty : "1");
345 }
346 }
347 }
348
349 private void CreateRelatedPackages(Database db)
350 {
351 // Represent the Upgrade table as related packages.
352 if (db.TableExists("Upgrade"))
353 {
354 using (var view = db.OpenExecuteView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`"))
355 {
356 foreach (var record in view.Records)
357 {
358 var recordAttributes = record.GetInteger(5);
359
360 var attributes = WixBundleRelatedPackageAttributes.None;
361 attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect ? WixBundleRelatedPackageAttributes.OnlyDetect : 0;
362 attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive ? WixBundleRelatedPackageAttributes.MinInclusive : 0;
363 attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive ? WixBundleRelatedPackageAttributes.MaxInclusive : 0;
364 attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive ? 0 : WixBundleRelatedPackageAttributes.LangInclusive;
365
366 this.Section.AddSymbol(new WixBundleRelatedPackageSymbol(this.PackagePayload.SourceLineNumbers)
367 {
368 PackagePayloadRef = this.PackagePayload.Id.Id,
369 RelatedId = record.GetString(1),
370 MinVersion = record.GetString(2),
371 MaxVersion = record.GetString(3),
372 Languages = record.GetString(4),
373 Attributes = attributes,
374 });
375 }
376 }
377 }
378 }
379
380 private void CreateMsiFeatures(Database db)
381 {
382 if (db.TableExists("Feature") && db.TableExists("FeatureComponents"))
383 {
384 using (var allFeaturesView = db.OpenExecuteView("SELECT * FROM `Feature`"))
385 using (var featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?"))
386 using (var componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?"))
387 {
388 using (var featureRecord = new Record(1))
389 using (var componentRecord = new Record(1))
390 {
391 foreach (var allFeaturesResultRecord in allFeaturesView.Records)
392 {
393 var featureName = allFeaturesResultRecord.GetString(1);
394
395 // Calculate the Feature size.
396 featureRecord.SetString(1, featureName);
397 featureView.Execute(featureRecord);
398
399 // Loop over all the components for the feature to calculate the size of the feature.
400 long size = 0;
401 foreach (var componentResultRecord in featureView.Records)
402 {
403 var component = componentResultRecord.GetString(1);
404 componentRecord.SetString(1, component);
405 componentView.Execute(componentRecord);
406
407 foreach (var fileResultRecord in componentView.Records)
408 {
409 var fileSize = fileResultRecord.GetString(1);
410 size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat);
411 }
412 }
413
414 this.Section.AddSymbol(new WixBundleMsiFeatureSymbol(this.PackagePayload.SourceLineNumbers, new Identifier(AccessModifier.Section, this.PackagePayload.Id.Id, featureName))
415 {
416 PackagePayloadRef = this.PackagePayload.Id.Id,
417 Name = featureName,
418 Parent = allFeaturesResultRecord.GetString(2),
419 Title = allFeaturesResultRecord.GetString(3),
420 Description = allFeaturesResultRecord.GetString(4),
421 Display = allFeaturesResultRecord.GetInteger(5),
422 Level = allFeaturesResultRecord.GetInteger(6),
423 Directory = allFeaturesResultRecord.GetString(7),
424 Attributes = allFeaturesResultRecord.GetInteger(8),
425 Size = size
426 });
427 }
428 }
429 }
430 }
431 }
432
433 private void ImportExternalCabinetAsPayloads(Database db, ISet<string> payloadNames)
434 {
435 if (db.TableExists("Media"))
436 {
437 using (var view = db.OpenExecuteView("SELECT `Cabinet` FROM `Media`"))
438 {
439 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
440
441 foreach (var cabinetRecord in view.Records)
442 {
443 var cabinet = cabinetRecord.GetString(1);
444
445 if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal))
446 {
447 // If we didn't find the Payload as an existing child of the package, we need to
448 // add it. We expect the file to exist on-disk in the same relative location as
449 // the MSI expects to find it...
450 var cabinetName = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), cabinet);
451
452 if (!payloadNames.Contains(cabinetName))
453 {
454 var generatedId = this.BackendHelper.GenerateIdentifier("cab", this.PackagePayload.Id.Id, cabinet);
455 var payloadSourceFile = this.ResolveRelatedFile(this.PackagePayload.SourceFile.Path, this.PackagePayload.UnresolvedSourceFile, cabinet, "cabinet", sourceLineNumbers);
456
457 this.Section.AddSymbol(new WixBundlePayloadSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
458 {
459 Name = cabinetName,
460 SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
461 Compressed = this.PackagePayload.Compressed,
462 UnresolvedSourceFile = cabinetName,
463 ContainerRef = this.PackagePayload.ContainerRef,
464 DownloadUrl = this.PackagePayload.DownloadUrl,
465 Packaging = this.PackagePayload.Packaging,
466 ParentPackagePayloadRef = this.PackagePayload.Id.Id,
467 });
468
469 this.CheckIfWindowsInstallerFileTooLarge(sourceLineNumbers, payloadSourceFile, "cabinet");
470 }
471 }
472 }
473 }
474 }
475 }
476
477 private long ImportExternalFileAsPayloadsAndReturnInstallSize(Database db, bool longNamesInImage, bool compressed, ISet<string> payloadNames)
478 {
479 long size = 0;
480
481 if (db.TableExists("Component") && db.TableExists("Directory") && db.TableExists("File"))
482 {
483 var directories = new Dictionary<string, IResolvedDirectory>();
484
485 // Load up the directory hash table so we will be able to resolve source paths
486 // for files in the MSI database.
487 using (var view = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
488 {
489 foreach (var record in view.Records)
490 {
491 var sourceName = this.BackendHelper.GetMsiFileName(record.GetString(3), true, longNamesInImage);
492
493 var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(record.GetString(2), sourceName);
494
495 directories.Add(record.GetString(1), resolvedDirectory);
496 }
497 }
498
499 // Resolve the source paths to external files and add each file size to the total
500 // install size of the package.
501 using (var view = db.OpenExecuteView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`"))
502 {
503 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
504
505 foreach (var record in view.Records)
506 {
507 // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not
508 // explicitly marked compressed then this is an external file.
509 var compressionBit = record.GetInteger(4);
510 if (WindowsInstallerConstants.MsidbFileAttributesNoncompressed == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) ||
511 (!compressed && 0 == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesCompressed)))
512 {
513 var fileSourcePath = this.PathResolver.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage);
514 var name = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), fileSourcePath);
515
516 if (!payloadNames.Contains(name))
517 {
518 var generatedId = this.BackendHelper.GenerateIdentifier("f", this.PackagePayload.Id.Id, record.GetString(2));
519 var payloadSourceFile = this.ResolveRelatedFile(this.PackagePayload.SourceFile.Path, this.PackagePayload.UnresolvedSourceFile, fileSourcePath, "payload", sourceLineNumbers);
520
521 this.Section.AddSymbol(new WixBundlePayloadSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
522 {
523 Name = name,
524 SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
525 Compressed = this.PackagePayload.Compressed,
526 UnresolvedSourceFile = name,
527 ContainerRef = this.PackagePayload.ContainerRef,
528 DownloadUrl = this.PackagePayload.DownloadUrl,
529 Packaging = this.PackagePayload.Packaging,
530 ParentPackagePayloadRef = this.PackagePayload.Id.Id,
531 });
532 }
533 }
534
535 size += record.GetInteger(5);
536 }
537 }
538 }
539
540 return size;
541 }
542
543 private void AddMsiProperty(string name, string value)
544 {
545 this.Section.AddSymbol(new WixBundleMsiPropertySymbol(this.PackagePayload.SourceLineNumbers, new Identifier(AccessModifier.Section, this.PackageId, name))
546 {
547 PackageRef = this.PackageId,
548 Name = name,
549 Value = value,
550 });
551 }
552
553 private void ImportDependencyProviders(Database db)
554 {
555 this.ImportDependencyProvidersFromTable(db, "WixDependencyProvider");
556 this.ImportDependencyProvidersFromTable(db, "Wix4DependencyProvider");
557 }
558
559 private void ImportDependencyProvidersFromTable(Database db, string tableName)
560 {
561 if (db.TableExists(tableName))
562 {
563 using (var view = db.OpenExecuteView($"SELECT `WixDependencyProvider`, `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `{tableName}`"))
564 {
565 foreach (var record in view.Records)
566 {
567 var id = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", this.PackagePayload.Id.Id, record.GetString(1)));
568
569 // Import the provider key and attributes.
570 this.Section.AddSymbol(new WixBundleHarvestedDependencyProviderSymbol(this.PackagePayload.SourceLineNumbers, id)
571 {
572 PackagePayloadRef = this.PackagePayload.Id.Id,
573 ProviderKey = record.GetString(2),
574 Version = record.GetString(3) ?? this.MsiPackage.ProductVersion,
575 DisplayName = record.GetString(4) ?? this.ChainPackage.DisplayName,
576 ProviderAttributes = record.GetInteger(5),
577 });
578 }
579 }
580 }
581 }
582
583 private string ResolveRelatedFile(string resolvedSource, string unresolvedSource, string relatedSource, string type, SourceLineNumber sourceLineNumbers)
584 {
585 var checkedPaths = new List<string>();
586
587 foreach (var extension in this.BackendExtensions)
588 {
589 var resolved = extension.ResolveRelatedFile(unresolvedSource, relatedSource, type, sourceLineNumbers);
590
591 if (resolved?.CheckedPaths != null)
592 {
593 checkedPaths.AddRange(resolved.CheckedPaths);
594 }
595
596 if (!String.IsNullOrEmpty(resolved?.Path))
597 {
598 return resolved?.Path;
599 }
600 }
601
602 var resolvedPath = Path.Combine(Path.GetDirectoryName(resolvedSource), relatedSource);
603
604 if (!File.Exists(resolvedPath))
605 {
606 checkedPaths.Add(resolvedPath);
607 this.Messaging.Write(ErrorMessages.FileNotFound(sourceLineNumbers, resolvedPath, type, checkedPaths));
608 }
609
610 return resolvedPath;
611 }
612
613 private void CheckIfWindowsInstallerFileTooLarge(SourceLineNumber sourceLineNumber, string path, string description)
614 {
615 // Best effort check to see if the file is too large for the Windows Installer.
616 try
617 {
618 var fi = new FileInfo(path);
619 if (fi.Length > Int32.MaxValue)
620 {
621 this.Messaging.Write(WarningMessages.WindowsInstallerFileTooLarge(sourceLineNumber, path, description));
622 }
623 }
624 catch
625 {
626 }
627 }
628
629 private static string GetProperty(View view, string property)
630 {
631 using (var queryRecord = new Record(1))
632 {
633 queryRecord[1] = property;
634
635 view.Execute(queryRecord);
636
637 using (var record = view.Fetch())
638 {
639 return record?.GetString(1);
640 }
641 }
642 }
643 }
644 }