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