diff options
Diffstat (limited to 'src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs')
-rw-r--r-- | src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs new file mode 100644 index 00000000..322187f9 --- /dev/null +++ b/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs | |||
@@ -0,0 +1,576 @@ | |||
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; | ||
7 | using System.Collections.Generic; | ||
8 | using System.Diagnostics; | ||
9 | using System.Globalization; | ||
10 | using System.IO; | ||
11 | using System.Linq; | ||
12 | using WixToolset.Data; | ||
13 | using WixToolset.Data.Rows; | ||
14 | using WixToolset.Extensibility; | ||
15 | using WixToolset.Core.Native; | ||
16 | using Dtf = WixToolset.Dtf.WindowsInstaller; | ||
17 | using WixToolset.Bind; | ||
18 | using WixToolset.Data.Bind; | ||
19 | |||
20 | /// <summary> | ||
21 | /// Initializes package state from the MSI contents. | ||
22 | /// </summary> | ||
23 | internal class ProcessMsiPackageCommand | ||
24 | { | ||
25 | private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'"; | ||
26 | |||
27 | public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; } | ||
28 | |||
29 | public PackageFacade Facade { private get; set; } | ||
30 | |||
31 | public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; } | ||
32 | |||
33 | public Table MsiFeatureTable { private get; set; } | ||
34 | |||
35 | public Table MsiPropertyTable { private get; set; } | ||
36 | |||
37 | public Table PayloadTable { private get; set; } | ||
38 | |||
39 | public Table RelatedPackageTable { private get; set; } | ||
40 | |||
41 | /// <summary> | ||
42 | /// Processes the MSI packages to add properties and payloads from the MSI packages. | ||
43 | /// </summary> | ||
44 | public void Execute() | ||
45 | { | ||
46 | WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); | ||
47 | |||
48 | string sourcePath = packagePayload.FullFileName; | ||
49 | bool longNamesInImage = false; | ||
50 | bool compressed = false; | ||
51 | bool x64 = false; | ||
52 | try | ||
53 | { | ||
54 | // Read data out of the msi database... | ||
55 | using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false)) | ||
56 | { | ||
57 | // 1 is the Word Count summary information stream bit that means | ||
58 | // the MSI uses short file names when set. We care about long file | ||
59 | // names so check when the bit is not set. | ||
60 | longNamesInImage = 0 == (sumInfo.WordCount & 1); | ||
61 | |||
62 | // 2 is the Word Count summary information stream bit that means | ||
63 | // files are compressed in the MSI by default when the bit is set. | ||
64 | compressed = 2 == (sumInfo.WordCount & 2); | ||
65 | |||
66 | x64 = (sumInfo.Template.Contains("x64") || sumInfo.Template.Contains("Intel64")); | ||
67 | |||
68 | // 8 is the Word Count summary information stream bit that means | ||
69 | // "Elevated privileges are not required to install this package." | ||
70 | // in MSI 4.5 and below, if this bit is 0, elevation is required. | ||
71 | this.Facade.Package.PerMachine = (0 == (sumInfo.WordCount & 8)) ? YesNoDefaultType.Yes : YesNoDefaultType.No; | ||
72 | this.Facade.Package.x64 = x64 ? YesNoType.Yes : YesNoType.No; | ||
73 | } | ||
74 | |||
75 | using (Dtf.Database db = new Dtf.Database(sourcePath)) | ||
76 | { | ||
77 | this.Facade.MsiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode"); | ||
78 | this.Facade.MsiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode"); | ||
79 | this.Facade.MsiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer"); | ||
80 | this.Facade.MsiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture); | ||
81 | this.Facade.MsiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion"); | ||
82 | |||
83 | if (!Common.IsValidModuleOrBundleVersion(this.Facade.MsiPackage.ProductVersion)) | ||
84 | { | ||
85 | // not a proper .NET version (e.g., five fields); can we get a valid four-part version number? | ||
86 | string version = null; | ||
87 | string[] versionParts = this.Facade.MsiPackage.ProductVersion.Split('.'); | ||
88 | int count = versionParts.Length; | ||
89 | if (0 < count) | ||
90 | { | ||
91 | version = versionParts[0]; | ||
92 | for (int i = 1; i < 4 && i < count; ++i) | ||
93 | { | ||
94 | version = String.Concat(version, ".", versionParts[i]); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (!String.IsNullOrEmpty(version) && Common.IsValidModuleOrBundleVersion(version)) | ||
99 | { | ||
100 | Messaging.Instance.OnMessage(WixWarnings.VersionTruncated(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath, version)); | ||
101 | this.Facade.MsiPackage.ProductVersion = version; | ||
102 | } | ||
103 | else | ||
104 | { | ||
105 | Messaging.Instance.OnMessage(WixErrors.InvalidProductVersion(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath)); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) | ||
110 | { | ||
111 | this.Facade.Package.CacheId = String.Format("{0}v{1}", this.Facade.MsiPackage.ProductCode, this.Facade.MsiPackage.ProductVersion); | ||
112 | } | ||
113 | |||
114 | if (String.IsNullOrEmpty(this.Facade.Package.DisplayName)) | ||
115 | { | ||
116 | this.Facade.Package.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName"); | ||
117 | } | ||
118 | |||
119 | if (String.IsNullOrEmpty(this.Facade.Package.Description)) | ||
120 | { | ||
121 | this.Facade.Package.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS"); | ||
122 | } | ||
123 | |||
124 | ISet<string> payloadNames = this.GetPayloadTargetNames(); | ||
125 | |||
126 | ISet<string> msiPropertyNames = this.GetMsiPropertyNames(); | ||
127 | |||
128 | this.SetPerMachineAppropriately(db, sourcePath); | ||
129 | |||
130 | // Ensure the MSI package is appropriately marked visible or not. | ||
131 | this.SetPackageVisibility(db, msiPropertyNames); | ||
132 | |||
133 | // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance. | ||
134 | if (!msiPropertyNames.Contains("MSIFASTINSTALL") && !ProcessMsiPackageCommand.HasProperty(db, "MSIFASTINSTALL")) | ||
135 | { | ||
136 | this.AddMsiProperty("MSIFASTINSTALL", "7"); | ||
137 | } | ||
138 | |||
139 | this.CreateRelatedPackages(db); | ||
140 | |||
141 | // If feature selection is enabled, represent the Feature table in the manifest. | ||
142 | if (this.Facade.MsiPackage.EnableFeatureSelection) | ||
143 | { | ||
144 | this.CreateMsiFeatures(db); | ||
145 | } | ||
146 | |||
147 | // Add all external cabinets as package payloads. | ||
148 | this.ImportExternalCabinetAsPayloads(db, packagePayload, payloadNames); | ||
149 | |||
150 | // Add all external files as package payloads and calculate the total install size as the rollup of | ||
151 | // File table's sizes. | ||
152 | this.Facade.Package.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames); | ||
153 | |||
154 | // Add all dependency providers from the MSI. | ||
155 | this.ImportDependencyProviders(db); | ||
156 | } | ||
157 | } | ||
158 | catch (Dtf.InstallerException e) | ||
159 | { | ||
160 | Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(this.Facade.Package.SourceLineNumbers, sourcePath, e.Message)); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | private ISet<string> GetPayloadTargetNames() | ||
165 | { | ||
166 | IEnumerable<string> payloadNames = this.PayloadTable.RowsAs<WixBundlePayloadRow>() | ||
167 | .Where(r => r.Package == this.Facade.Package.WixChainItemId) | ||
168 | .Select(r => r.Name); | ||
169 | |||
170 | return new HashSet<string>(payloadNames, StringComparer.OrdinalIgnoreCase); | ||
171 | } | ||
172 | |||
173 | private ISet<string> GetMsiPropertyNames() | ||
174 | { | ||
175 | IEnumerable<string> properties = this.MsiPropertyTable.RowsAs<WixBundleMsiPropertyRow>() | ||
176 | .Where(r => r.ChainPackageId == this.Facade.Package.WixChainItemId) | ||
177 | .Select(r => r.Name); | ||
178 | |||
179 | return new HashSet<string>(properties, StringComparer.Ordinal); | ||
180 | } | ||
181 | |||
182 | private void SetPerMachineAppropriately(Dtf.Database db, string sourcePath) | ||
183 | { | ||
184 | if (this.Facade.MsiPackage.ForcePerMachine) | ||
185 | { | ||
186 | if (YesNoDefaultType.No == this.Facade.Package.PerMachine) | ||
187 | { | ||
188 | Messaging.Instance.OnMessage(WixWarnings.PerUserButForcingPerMachine(this.Facade.Package.SourceLineNumbers, sourcePath)); | ||
189 | this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // ensure that we think the package is per-machine. | ||
190 | } | ||
191 | |||
192 | // Force ALLUSERS=1 via the MSI command-line. | ||
193 | this.AddMsiProperty("ALLUSERS", "1"); | ||
194 | } | ||
195 | else | ||
196 | { | ||
197 | string allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS"); | ||
198 | |||
199 | if (String.IsNullOrEmpty(allusers)) | ||
200 | { | ||
201 | // Not forced per-machine and no ALLUSERS property, flip back to per-user. | ||
202 | if (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) | ||
203 | { | ||
204 | Messaging.Instance.OnMessage(WixWarnings.ImplicitlyPerUser(this.Facade.Package.SourceLineNumbers, sourcePath)); | ||
205 | this.Facade.Package.PerMachine = YesNoDefaultType.No; | ||
206 | } | ||
207 | } | ||
208 | else if (allusers.Equals("1", StringComparison.Ordinal)) | ||
209 | { | ||
210 | if (YesNoDefaultType.No == this.Facade.Package.PerMachine) | ||
211 | { | ||
212 | Messaging.Instance.OnMessage(WixErrors.PerUserButAllUsersEquals1(this.Facade.Package.SourceLineNumbers, sourcePath)); | ||
213 | } | ||
214 | } | ||
215 | else if (allusers.Equals("2", StringComparison.Ordinal)) | ||
216 | { | ||
217 | Messaging.Instance.OnMessage(WixWarnings.DiscouragedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) ? "machine" : "user")); | ||
218 | } | ||
219 | else | ||
220 | { | ||
221 | Messaging.Instance.OnMessage(WixErrors.UnsupportedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, allusers)); | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | |||
226 | private void SetPackageVisibility(Dtf.Database db, ISet<string> msiPropertyNames) | ||
227 | { | ||
228 | bool alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT"); | ||
229 | |||
230 | if (alreadyVisible != this.Facade.Package.Visible) // if not already set to the correct visibility. | ||
231 | { | ||
232 | // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again. | ||
233 | if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT")) | ||
234 | { | ||
235 | this.AddMsiProperty("ARPSYSTEMCOMPONENT", this.Facade.Package.Visible ? String.Empty : "1"); | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
240 | private void CreateRelatedPackages(Dtf.Database db) | ||
241 | { | ||
242 | // Represent the Upgrade table as related packages. | ||
243 | if (db.Tables.Contains("Upgrade")) | ||
244 | { | ||
245 | using (Dtf.View view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`")) | ||
246 | { | ||
247 | view.Execute(); | ||
248 | while (true) | ||
249 | { | ||
250 | using (Dtf.Record record = view.Fetch()) | ||
251 | { | ||
252 | if (null == record) | ||
253 | { | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | WixBundleRelatedPackageRow related = (WixBundleRelatedPackageRow)this.RelatedPackageTable.CreateRow(this.Facade.Package.SourceLineNumbers); | ||
258 | related.ChainPackageId = this.Facade.Package.WixChainItemId; | ||
259 | related.Id = record.GetString(1); | ||
260 | related.MinVersion = record.GetString(2); | ||
261 | related.MaxVersion = record.GetString(3); | ||
262 | related.Languages = record.GetString(4); | ||
263 | |||
264 | int attributes = record.GetInteger(5); | ||
265 | related.OnlyDetect = (attributes & MsiInterop.MsidbUpgradeAttributesOnlyDetect) == MsiInterop.MsidbUpgradeAttributesOnlyDetect; | ||
266 | related.MinInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMinInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMinInclusive; | ||
267 | related.MaxInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive; | ||
268 | related.LangInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesLanguagesExclusive) == 0; | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | |||
275 | private void CreateMsiFeatures(Dtf.Database db) | ||
276 | { | ||
277 | if (db.Tables.Contains("Feature")) | ||
278 | { | ||
279 | using (Dtf.View featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?")) | ||
280 | using (Dtf.View componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?")) | ||
281 | { | ||
282 | using (Dtf.Record featureRecord = new Dtf.Record(1)) | ||
283 | using (Dtf.Record componentRecord = new Dtf.Record(1)) | ||
284 | { | ||
285 | using (Dtf.View allFeaturesView = db.OpenView("SELECT * FROM `Feature`")) | ||
286 | { | ||
287 | allFeaturesView.Execute(); | ||
288 | |||
289 | while (true) | ||
290 | { | ||
291 | using (Dtf.Record allFeaturesResultRecord = allFeaturesView.Fetch()) | ||
292 | { | ||
293 | if (null == allFeaturesResultRecord) | ||
294 | { | ||
295 | break; | ||
296 | } | ||
297 | |||
298 | string featureName = allFeaturesResultRecord.GetString(1); | ||
299 | |||
300 | // Calculate the Feature size. | ||
301 | featureRecord.SetString(1, featureName); | ||
302 | featureView.Execute(featureRecord); | ||
303 | |||
304 | // Loop over all the components for the feature to calculate the size of the feature. | ||
305 | long size = 0; | ||
306 | while (true) | ||
307 | { | ||
308 | using (Dtf.Record componentResultRecord = featureView.Fetch()) | ||
309 | { | ||
310 | if (null == componentResultRecord) | ||
311 | { | ||
312 | break; | ||
313 | } | ||
314 | string component = componentResultRecord.GetString(1); | ||
315 | componentRecord.SetString(1, component); | ||
316 | componentView.Execute(componentRecord); | ||
317 | |||
318 | while (true) | ||
319 | { | ||
320 | using (Dtf.Record fileResultRecord = componentView.Fetch()) | ||
321 | { | ||
322 | if (null == fileResultRecord) | ||
323 | { | ||
324 | break; | ||
325 | } | ||
326 | |||
327 | string fileSize = fileResultRecord.GetString(1); | ||
328 | size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat); | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | WixBundleMsiFeatureRow feature = (WixBundleMsiFeatureRow)this.MsiFeatureTable.CreateRow(this.Facade.Package.SourceLineNumbers); | ||
335 | feature.ChainPackageId = this.Facade.Package.WixChainItemId; | ||
336 | feature.Name = featureName; | ||
337 | feature.Parent = allFeaturesResultRecord.GetString(2); | ||
338 | feature.Title = allFeaturesResultRecord.GetString(3); | ||
339 | feature.Description = allFeaturesResultRecord.GetString(4); | ||
340 | feature.Display = allFeaturesResultRecord.GetInteger(5); | ||
341 | feature.Level = allFeaturesResultRecord.GetInteger(6); | ||
342 | feature.Directory = allFeaturesResultRecord.GetString(7); | ||
343 | feature.Attributes = allFeaturesResultRecord.GetInteger(8); | ||
344 | feature.Size = size; | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | |||
353 | private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadRow packagePayload, ISet<string> payloadNames) | ||
354 | { | ||
355 | if (db.Tables.Contains("Media")) | ||
356 | { | ||
357 | foreach (string cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`")) | ||
358 | { | ||
359 | if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal)) | ||
360 | { | ||
361 | // If we didn't find the Payload as an existing child of the package, we need to | ||
362 | // add it. We expect the file to exist on-disk in the same relative location as | ||
363 | // the MSI expects to find it... | ||
364 | string cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet); | ||
365 | |||
366 | if (!payloadNames.Contains(cabinetName)) | ||
367 | { | ||
368 | string generatedId = Common.GenerateIdentifier("cab", packagePayload.Id, cabinet); | ||
369 | string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.Package.SourceLineNumbers, BindStage.Normal); | ||
370 | |||
371 | WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); | ||
372 | payload.Id = generatedId; | ||
373 | payload.Name = cabinetName; | ||
374 | payload.SourceFile = payloadSourceFile; | ||
375 | payload.Compressed = packagePayload.Compressed; | ||
376 | payload.UnresolvedSourceFile = cabinetName; | ||
377 | payload.Package = packagePayload.Package; | ||
378 | payload.Container = packagePayload.Container; | ||
379 | payload.ContentFile = true; | ||
380 | payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation; | ||
381 | payload.Packaging = packagePayload.Packaging; | ||
382 | payload.ParentPackagePayload = packagePayload.Id; | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | |||
389 | private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadRow packagePayload, bool longNamesInImage, bool compressed, ISet<string> payloadNames) | ||
390 | { | ||
391 | long size = 0; | ||
392 | |||
393 | if (db.Tables.Contains("Component") && db.Tables.Contains("Directory") && db.Tables.Contains("File")) | ||
394 | { | ||
395 | Hashtable directories = new Hashtable(); | ||
396 | |||
397 | // Load up the directory hash table so we will be able to resolve source paths | ||
398 | // for files in the MSI database. | ||
399 | using (Dtf.View view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) | ||
400 | { | ||
401 | view.Execute(); | ||
402 | while (true) | ||
403 | { | ||
404 | using (Dtf.Record record = view.Fetch()) | ||
405 | { | ||
406 | if (null == record) | ||
407 | { | ||
408 | break; | ||
409 | } | ||
410 | |||
411 | string sourceName = Common.GetName(record.GetString(3), true, longNamesInImage); | ||
412 | directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName)); | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | |||
417 | // Resolve the source paths to external files and add each file size to the total | ||
418 | // install size of the package. | ||
419 | using (Dtf.View view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`")) | ||
420 | { | ||
421 | view.Execute(); | ||
422 | while (true) | ||
423 | { | ||
424 | using (Dtf.Record record = view.Fetch()) | ||
425 | { | ||
426 | if (null == record) | ||
427 | { | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | // Skip adding the loose files as payloads if it was suppressed. | ||
432 | if (!this.Facade.MsiPackage.SuppressLooseFilePayloadGeneration) | ||
433 | { | ||
434 | // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not | ||
435 | // explicitly marked compressed then this is an external file. | ||
436 | if (MsiInterop.MsidbFileAttributesNoncompressed == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesNoncompressed) || | ||
437 | (!compressed && 0 == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesCompressed))) | ||
438 | { | ||
439 | string fileSourcePath = Binder.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage); | ||
440 | string name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath); | ||
441 | |||
442 | if (!payloadNames.Contains(name)) | ||
443 | { | ||
444 | string generatedId = Common.GenerateIdentifier("f", packagePayload.Id, record.GetString(2)); | ||
445 | string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.Package.SourceLineNumbers, BindStage.Normal); | ||
446 | |||
447 | WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); | ||
448 | payload.Id = generatedId; | ||
449 | payload.Name = name; | ||
450 | payload.SourceFile = payloadSourceFile; | ||
451 | payload.Compressed = packagePayload.Compressed; | ||
452 | payload.UnresolvedSourceFile = name; | ||
453 | payload.Package = packagePayload.Package; | ||
454 | payload.Container = packagePayload.Container; | ||
455 | payload.ContentFile = true; | ||
456 | payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation; | ||
457 | payload.Packaging = packagePayload.Packaging; | ||
458 | payload.ParentPackagePayload = packagePayload.Id; | ||
459 | } | ||
460 | } | ||
461 | } | ||
462 | |||
463 | size += record.GetInteger(5); | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | |||
469 | return size; | ||
470 | } | ||
471 | |||
472 | private void AddMsiProperty(string name, string value) | ||
473 | { | ||
474 | WixBundleMsiPropertyRow row = (WixBundleMsiPropertyRow)this.MsiPropertyTable.CreateRow(this.Facade.MsiPackage.SourceLineNumbers); | ||
475 | row.ChainPackageId = this.Facade.Package.WixChainItemId; | ||
476 | row.Name = name; | ||
477 | row.Value = value; | ||
478 | } | ||
479 | |||
480 | private void ImportDependencyProviders(Dtf.Database db) | ||
481 | { | ||
482 | if (db.Tables.Contains("WixDependencyProvider")) | ||
483 | { | ||
484 | string query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`"; | ||
485 | |||
486 | using (Dtf.View view = db.OpenView(query)) | ||
487 | { | ||
488 | view.Execute(); | ||
489 | while (true) | ||
490 | { | ||
491 | using (Dtf.Record record = view.Fetch()) | ||
492 | { | ||
493 | if (null == record) | ||
494 | { | ||
495 | break; | ||
496 | } | ||
497 | |||
498 | // Import the provider key and attributes. | ||
499 | string providerKey = record.GetString(1); | ||
500 | string version = record.GetString(2) ?? this.Facade.MsiPackage.ProductVersion; | ||
501 | string displayName = record.GetString(3) ?? this.Facade.Package.DisplayName; | ||
502 | int attributes = record.GetInteger(4); | ||
503 | |||
504 | ProvidesDependency dependency = new ProvidesDependency(providerKey, version, displayName, attributes); | ||
505 | dependency.Imported = true; | ||
506 | |||
507 | this.Facade.Provides.Add(dependency); | ||
508 | } | ||
509 | } | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | |||
514 | private string ResolveRelatedFile(string sourceFile, string relatedSource, string type, SourceLineNumber sourceLineNumbers, BindStage stage) | ||
515 | { | ||
516 | foreach (var extension in this.BackendExtensions) | ||
517 | { | ||
518 | var relatedFile = extension.ResolveRelatedFile(sourceFile, relatedSource, type, sourceLineNumbers, stage); | ||
519 | |||
520 | if (!String.IsNullOrEmpty(relatedFile)) | ||
521 | { | ||
522 | return relatedFile; | ||
523 | } | ||
524 | } | ||
525 | |||
526 | return null; | ||
527 | } | ||
528 | |||
529 | /// <summary> | ||
530 | /// Queries a Windows Installer database for a Property value. | ||
531 | /// </summary> | ||
532 | /// <param name="db">Database to query.</param> | ||
533 | /// <param name="property">Property to examine.</param> | ||
534 | /// <returns>String value for result or null if query doesn't match a single result.</returns> | ||
535 | private static string GetProperty(Dtf.Database db, string property) | ||
536 | { | ||
537 | try | ||
538 | { | ||
539 | return db.ExecuteScalar(PropertyQuery(property)).ToString(); | ||
540 | } | ||
541 | catch (Dtf.InstallerException) | ||
542 | { | ||
543 | } | ||
544 | |||
545 | return null; | ||
546 | } | ||
547 | |||
548 | /// <summary> | ||
549 | /// Queries a Windows Installer database to determine if one or more rows exist in the Property table. | ||
550 | /// </summary> | ||
551 | /// <param name="db">Database to query.</param> | ||
552 | /// <param name="property">Property to examine.</param> | ||
553 | /// <returns>True if query matches at least one result.</returns> | ||
554 | private static bool HasProperty(Dtf.Database db, string property) | ||
555 | { | ||
556 | try | ||
557 | { | ||
558 | return 0 < db.ExecuteQuery(PropertyQuery(property)).Count; | ||
559 | } | ||
560 | catch (Dtf.InstallerException) | ||
561 | { | ||
562 | } | ||
563 | |||
564 | return false; | ||
565 | } | ||
566 | |||
567 | private static string PropertyQuery(string property) | ||
568 | { | ||
569 | // quick sanity check that we'll be creating a valid query... | ||
570 | // TODO: Are there any other special characters we should be looking for? | ||
571 | Debug.Assert(!property.Contains("'")); | ||
572 | |||
573 | return String.Format(CultureInfo.InvariantCulture, ProcessMsiPackageCommand.PropertySqlFormat, property); | ||
574 | } | ||
575 | } | ||
576 | } | ||