diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs new file mode 100644 index 00000000..cd9444ee --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | |||
@@ -0,0 +1,533 @@ | |||
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.WindowsInstaller.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Collections.Specialized; | ||
8 | using System.ComponentModel; | ||
9 | using System.Globalization; | ||
10 | using System.IO; | ||
11 | using System.Linq; | ||
12 | using System.Xml; | ||
13 | using System.Xml.XPath; | ||
14 | using WixToolset.Clr.Interop; | ||
15 | using WixToolset.Core.Bind; | ||
16 | using WixToolset.Data; | ||
17 | using WixToolset.Data.Rows; | ||
18 | using WixToolset.Msi; | ||
19 | |||
20 | /// <summary> | ||
21 | /// Update file information. | ||
22 | /// </summary> | ||
23 | internal class UpdateFileFacadesCommand | ||
24 | { | ||
25 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
26 | |||
27 | public IEnumerable<FileFacade> UpdateFileFacades { private get; set; } | ||
28 | |||
29 | public string ModularizationGuid { private get; set; } | ||
30 | |||
31 | public Output Output { private get; set; } | ||
32 | |||
33 | public bool OverwriteHash { private get; set; } | ||
34 | |||
35 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
36 | |||
37 | public IDictionary<string, string> VariableCache { private get; set; } | ||
38 | |||
39 | public void Execute() | ||
40 | { | ||
41 | foreach (FileFacade file in this.UpdateFileFacades) | ||
42 | { | ||
43 | this.UpdateFileFacade(file); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | private void UpdateFileFacade(FileFacade file) | ||
48 | { | ||
49 | FileInfo fileInfo = null; | ||
50 | try | ||
51 | { | ||
52 | fileInfo = new FileInfo(file.WixFile.Source); | ||
53 | } | ||
54 | catch (ArgumentException) | ||
55 | { | ||
56 | Messaging.Instance.OnMessage(WixDataErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
57 | return; | ||
58 | } | ||
59 | catch (PathTooLongException) | ||
60 | { | ||
61 | Messaging.Instance.OnMessage(WixDataErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
62 | return; | ||
63 | } | ||
64 | catch (NotSupportedException) | ||
65 | { | ||
66 | Messaging.Instance.OnMessage(WixDataErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
67 | return; | ||
68 | } | ||
69 | |||
70 | if (!fileInfo.Exists) | ||
71 | { | ||
72 | Messaging.Instance.OnMessage(WixErrors.CannotFindFile(file.File.SourceLineNumbers, file.File.File, file.File.FileName, file.WixFile.Source)); | ||
73 | return; | ||
74 | } | ||
75 | |||
76 | using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||
77 | { | ||
78 | if (Int32.MaxValue < fileStream.Length) | ||
79 | { | ||
80 | throw new WixException(WixErrors.FileTooLarge(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
81 | } | ||
82 | |||
83 | file.File.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); | ||
84 | } | ||
85 | |||
86 | string version = null; | ||
87 | string language = null; | ||
88 | try | ||
89 | { | ||
90 | Installer.GetFileVersion(fileInfo.FullName, out version, out language); | ||
91 | } | ||
92 | catch (Win32Exception e) | ||
93 | { | ||
94 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | ||
95 | { | ||
96 | throw new WixException(WixErrors.FileNotFound(file.File.SourceLineNumbers, fileInfo.FullName)); | ||
97 | } | ||
98 | else | ||
99 | { | ||
100 | throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, e.Message)); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | // If there is no version, it is assumed there is no language because it won't matter in the versioning of the install. | ||
105 | if (String.IsNullOrEmpty(version)) // unversioned files have their hashes added to the MsiFileHash table | ||
106 | { | ||
107 | if (!this.OverwriteHash) | ||
108 | { | ||
109 | // not overwriting hash, so don't do the rest of these options. | ||
110 | } | ||
111 | else if (null != file.File.Version) | ||
112 | { | ||
113 | // Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks | ||
114 | // very expensive and you're probably thinking it would be better to create an index of some sort to do an O(1) look up. | ||
115 | // That's a reasonable thought but companion file usage is usually pretty rare so we'd be doing something expensive (indexing | ||
116 | // all the file rows) for a relatively uncommon situation. Let's not do that. | ||
117 | // | ||
118 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version | ||
119 | // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. | ||
120 | if (!this.FileFacades.Any(r => file.File.Version.Equals(r.File.File, StringComparison.Ordinal))) | ||
121 | { | ||
122 | Messaging.Instance.OnMessage(WixWarnings.DefaultVersionUsedForUnversionedFile(file.File.SourceLineNumbers, file.File.Version, file.File.File)); | ||
123 | } | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | if (null != file.File.Language) | ||
128 | { | ||
129 | Messaging.Instance.OnMessage(WixWarnings.DefaultLanguageUsedForUnversionedFile(file.File.SourceLineNumbers, file.File.Language, file.File.File)); | ||
130 | } | ||
131 | |||
132 | int[] hash; | ||
133 | try | ||
134 | { | ||
135 | Installer.GetFileHash(fileInfo.FullName, 0, out hash); | ||
136 | } | ||
137 | catch (Win32Exception e) | ||
138 | { | ||
139 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | ||
140 | { | ||
141 | throw new WixException(WixErrors.FileNotFound(file.File.SourceLineNumbers, fileInfo.FullName)); | ||
142 | } | ||
143 | else | ||
144 | { | ||
145 | throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, fileInfo.FullName, e.Message)); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | if (null == file.Hash) | ||
150 | { | ||
151 | Table msiFileHashTable = this.Output.EnsureTable(this.TableDefinitions["MsiFileHash"]); | ||
152 | file.Hash = msiFileHashTable.CreateRow(file.File.SourceLineNumbers); | ||
153 | } | ||
154 | |||
155 | file.Hash[0] = file.File.File; | ||
156 | file.Hash[1] = 0; | ||
157 | file.Hash[2] = hash[0]; | ||
158 | file.Hash[3] = hash[1]; | ||
159 | file.Hash[4] = hash[2]; | ||
160 | file.Hash[5] = hash[3]; | ||
161 | } | ||
162 | } | ||
163 | else // update the file row with the version and language information. | ||
164 | { | ||
165 | // If no version was provided by the user, use the version from the file itself. | ||
166 | // This is the most common case. | ||
167 | if (String.IsNullOrEmpty(file.File.Version)) | ||
168 | { | ||
169 | file.File.Version = version; | ||
170 | } | ||
171 | else if (!this.FileFacades.Any(r => file.File.Version.Equals(r.File.File, StringComparison.Ordinal))) // this looks expensive, but see explanation below. | ||
172 | { | ||
173 | // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching | ||
174 | // the version value). We didn't find it so, we will override the default version they provided with the actual | ||
175 | // version from the file itself. Now, I know it looks expensive to search through all the file rows trying to match | ||
176 | // on the Id. However, the alternative is to build a big index of all file rows to do look ups. Since this case | ||
177 | // where the file version is already present is rare (companion files are pretty uncommon), we'll do the more | ||
178 | // CPU intensive search to save on the memory intensive index that wouldn't be used much. | ||
179 | // | ||
180 | // Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism. | ||
181 | // That's typically even more rare than companion files so again, no index, just search. | ||
182 | file.File.Version = version; | ||
183 | } | ||
184 | |||
185 | if (!String.IsNullOrEmpty(file.File.Language) && String.IsNullOrEmpty(language)) | ||
186 | { | ||
187 | Messaging.Instance.OnMessage(WixWarnings.DefaultLanguageUsedForVersionedFile(file.File.SourceLineNumbers, file.File.Language, file.File.File)); | ||
188 | } | ||
189 | else // override the default provided by the user (usually nothing) with the actual language from the file itself. | ||
190 | { | ||
191 | file.File.Language = language; | ||
192 | } | ||
193 | |||
194 | // Populate the binder variables for this file information if requested. | ||
195 | if (null != this.VariableCache) | ||
196 | { | ||
197 | if (!String.IsNullOrEmpty(file.File.Version)) | ||
198 | { | ||
199 | string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)); | ||
200 | this.VariableCache[key] = file.File.Version; | ||
201 | } | ||
202 | |||
203 | if (!String.IsNullOrEmpty(file.File.Language)) | ||
204 | { | ||
205 | string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", Common.Demodularize(this.Output.Type, ModularizationGuid, file.File.File)); | ||
206 | this.VariableCache[key] = file.File.Language; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | // If this is a CLR assembly, load the assembly and get the assembly name information | ||
212 | if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType) | ||
213 | { | ||
214 | bool targetNetfx1 = false; | ||
215 | StringDictionary assemblyNameValues = new StringDictionary(); | ||
216 | |||
217 | ClrInterop.IReferenceIdentity referenceIdentity = null; | ||
218 | Guid referenceIdentityGuid = ClrInterop.ReferenceIdentityGuid; | ||
219 | uint result = ClrInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out referenceIdentity); | ||
220 | if (0 == result && null != referenceIdentity) | ||
221 | { | ||
222 | string imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion"); | ||
223 | if (null != imageRuntimeVersion) | ||
224 | { | ||
225 | targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase); | ||
226 | } | ||
227 | |||
228 | string culture = referenceIdentity.GetAttribute(null, "Culture") ?? "neutral"; | ||
229 | assemblyNameValues.Add("Culture", culture); | ||
230 | |||
231 | string name = referenceIdentity.GetAttribute(null, "Name"); | ||
232 | if (null != name) | ||
233 | { | ||
234 | assemblyNameValues.Add("Name", name); | ||
235 | } | ||
236 | |||
237 | string processorArchitecture = referenceIdentity.GetAttribute(null, "ProcessorArchitecture"); | ||
238 | if (null != processorArchitecture) | ||
239 | { | ||
240 | assemblyNameValues.Add("ProcessorArchitecture", processorArchitecture); | ||
241 | } | ||
242 | |||
243 | string publicKeyToken = referenceIdentity.GetAttribute(null, "PublicKeyToken"); | ||
244 | if (null != publicKeyToken) | ||
245 | { | ||
246 | bool publicKeyIsNeutral = (String.Equals(publicKeyToken, "neutral", StringComparison.OrdinalIgnoreCase)); | ||
247 | |||
248 | // Managed code expects "null" instead of "neutral", and | ||
249 | // this won't be installed to the GAC since it's not signed anyway. | ||
250 | assemblyNameValues.Add("publicKeyToken", publicKeyIsNeutral ? "null" : publicKeyToken.ToUpperInvariant()); | ||
251 | assemblyNameValues.Add("publicKeyTokenPreservedCase", publicKeyIsNeutral ? "null" : publicKeyToken); | ||
252 | } | ||
253 | else if (file.WixFile.AssemblyApplication == null) | ||
254 | { | ||
255 | throw new WixException(WixErrors.GacAssemblyNoStrongName(file.File.SourceLineNumbers, fileInfo.FullName, file.File.Component)); | ||
256 | } | ||
257 | |||
258 | string assemblyVersion = referenceIdentity.GetAttribute(null, "Version"); | ||
259 | if (null != version) | ||
260 | { | ||
261 | assemblyNameValues.Add("Version", assemblyVersion); | ||
262 | } | ||
263 | } | ||
264 | else | ||
265 | { | ||
266 | Messaging.Instance.OnMessage(WixErrors.InvalidAssemblyFile(file.File.SourceLineNumbers, fileInfo.FullName, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", result))); | ||
267 | return; | ||
268 | } | ||
269 | |||
270 | Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
271 | if (assemblyNameValues.ContainsKey("name")) | ||
272 | { | ||
273 | this.SetMsiAssemblyName(assemblyNameTable, file, "name", assemblyNameValues["name"]); | ||
274 | } | ||
275 | |||
276 | if (!String.IsNullOrEmpty(version)) | ||
277 | { | ||
278 | this.SetMsiAssemblyName(assemblyNameTable, file, "fileVersion", version); | ||
279 | } | ||
280 | |||
281 | if (assemblyNameValues.ContainsKey("version")) | ||
282 | { | ||
283 | string assemblyVersion = assemblyNameValues["version"]; | ||
284 | |||
285 | if (!targetNetfx1) | ||
286 | { | ||
287 | // There is a bug in v1 fusion that requires the assembly's "version" attribute | ||
288 | // to be equal to or longer than the "fileVersion" in length when its present; | ||
289 | // the workaround is to prepend zeroes to the last version number in the assembly | ||
290 | // version. | ||
291 | if (null != version && version.Length > assemblyVersion.Length) | ||
292 | { | ||
293 | string padding = new string('0', version.Length - assemblyVersion.Length); | ||
294 | string[] assemblyVersionNumbers = assemblyVersion.Split('.'); | ||
295 | |||
296 | if (assemblyVersionNumbers.Length > 0) | ||
297 | { | ||
298 | assemblyVersionNumbers[assemblyVersionNumbers.Length - 1] = String.Concat(padding, assemblyVersionNumbers[assemblyVersionNumbers.Length - 1]); | ||
299 | assemblyVersion = String.Join(".", assemblyVersionNumbers); | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | |||
304 | this.SetMsiAssemblyName(assemblyNameTable, file, "version", assemblyVersion); | ||
305 | } | ||
306 | |||
307 | if (assemblyNameValues.ContainsKey("culture")) | ||
308 | { | ||
309 | this.SetMsiAssemblyName(assemblyNameTable, file, "culture", assemblyNameValues["culture"]); | ||
310 | } | ||
311 | |||
312 | if (assemblyNameValues.ContainsKey("publicKeyToken")) | ||
313 | { | ||
314 | this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", assemblyNameValues["publicKeyToken"]); | ||
315 | } | ||
316 | |||
317 | if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture)) | ||
318 | { | ||
319 | this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", file.WixFile.ProcessorArchitecture); | ||
320 | } | ||
321 | |||
322 | if (assemblyNameValues.ContainsKey("processorArchitecture")) | ||
323 | { | ||
324 | this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", assemblyNameValues["processorArchitecture"]); | ||
325 | } | ||
326 | |||
327 | // add the assembly name to the information cache | ||
328 | if (null != this.VariableCache) | ||
329 | { | ||
330 | string fileId = Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File); | ||
331 | string key = String.Concat("assemblyfullname.", fileId); | ||
332 | string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]); | ||
333 | if (assemblyNameValues.ContainsKey("processorArchitecture")) | ||
334 | { | ||
335 | assemblyName = String.Concat(assemblyName, ", processorArchitecture=", assemblyNameValues["processorArchitecture"]); | ||
336 | } | ||
337 | |||
338 | this.VariableCache[key] = assemblyName; | ||
339 | |||
340 | // Add entries with the preserved case publicKeyToken | ||
341 | string pcAssemblyNameKey = String.Concat("assemblyfullnamepreservedcase.", fileId); | ||
342 | this.VariableCache[pcAssemblyNameKey] = (assemblyNameValues["publicKeyToken"] == assemblyNameValues["publicKeyTokenPreservedCase"]) ? assemblyName : assemblyName.Replace(assemblyNameValues["publicKeyToken"], assemblyNameValues["publicKeyTokenPreservedCase"]); | ||
343 | |||
344 | string pcPublicKeyTokenKey = String.Concat("assemblypublickeytokenpreservedcase.", fileId); | ||
345 | this.VariableCache[pcPublicKeyTokenKey] = assemblyNameValues["publicKeyTokenPreservedCase"]; | ||
346 | } | ||
347 | } | ||
348 | else if (FileAssemblyType.Win32Assembly == file.WixFile.AssemblyType) | ||
349 | { | ||
350 | // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through | ||
351 | // all files like this. Even though this is a rare case it looks like we might be able to index the | ||
352 | // file earlier. | ||
353 | FileFacade fileManifest = this.FileFacades.SingleOrDefault(r => r.File.File.Equals(file.WixFile.AssemblyManifest, StringComparison.Ordinal)); | ||
354 | if (null == fileManifest) | ||
355 | { | ||
356 | Messaging.Instance.OnMessage(WixErrors.MissingManifestForWin32Assembly(file.File.SourceLineNumbers, file.File.File, file.WixFile.AssemblyManifest)); | ||
357 | } | ||
358 | |||
359 | string win32Type = null; | ||
360 | string win32Name = null; | ||
361 | string win32Version = null; | ||
362 | string win32ProcessorArchitecture = null; | ||
363 | string win32PublicKeyToken = null; | ||
364 | |||
365 | // loading the dom is expensive we want more performant APIs than the DOM | ||
366 | // Navigator is cheaper than dom. Perhaps there is a cheaper API still. | ||
367 | try | ||
368 | { | ||
369 | XPathDocument doc = new XPathDocument(fileManifest.WixFile.Source); | ||
370 | XPathNavigator nav = doc.CreateNavigator(); | ||
371 | nav.MoveToRoot(); | ||
372 | |||
373 | // this assumes a particular schema for a win32 manifest and does not | ||
374 | // provide error checking if the file does not conform to schema. | ||
375 | // The fallback case here is that nothing is added to the MsiAssemblyName | ||
376 | // table for an out of tolerance Win32 manifest. Perhaps warnings needed. | ||
377 | if (nav.MoveToFirstChild()) | ||
378 | { | ||
379 | while (nav.NodeType != XPathNodeType.Element || nav.Name != "assembly") | ||
380 | { | ||
381 | nav.MoveToNext(); | ||
382 | } | ||
383 | |||
384 | if (nav.MoveToFirstChild()) | ||
385 | { | ||
386 | bool hasNextSibling = true; | ||
387 | while (nav.NodeType != XPathNodeType.Element || nav.Name != "assemblyIdentity" && hasNextSibling) | ||
388 | { | ||
389 | hasNextSibling = nav.MoveToNext(); | ||
390 | } | ||
391 | if (!hasNextSibling) | ||
392 | { | ||
393 | Messaging.Instance.OnMessage(WixErrors.InvalidManifestContent(file.File.SourceLineNumbers, fileManifest.WixFile.Source)); | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | if (nav.MoveToAttribute("type", String.Empty)) | ||
398 | { | ||
399 | win32Type = nav.Value; | ||
400 | nav.MoveToParent(); | ||
401 | } | ||
402 | |||
403 | if (nav.MoveToAttribute("name", String.Empty)) | ||
404 | { | ||
405 | win32Name = nav.Value; | ||
406 | nav.MoveToParent(); | ||
407 | } | ||
408 | |||
409 | if (nav.MoveToAttribute("version", String.Empty)) | ||
410 | { | ||
411 | win32Version = nav.Value; | ||
412 | nav.MoveToParent(); | ||
413 | } | ||
414 | |||
415 | if (nav.MoveToAttribute("processorArchitecture", String.Empty)) | ||
416 | { | ||
417 | win32ProcessorArchitecture = nav.Value; | ||
418 | nav.MoveToParent(); | ||
419 | } | ||
420 | |||
421 | if (nav.MoveToAttribute("publicKeyToken", String.Empty)) | ||
422 | { | ||
423 | win32PublicKeyToken = nav.Value; | ||
424 | nav.MoveToParent(); | ||
425 | } | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | catch (FileNotFoundException fe) | ||
430 | { | ||
431 | Messaging.Instance.OnMessage(WixErrors.FileNotFound(new SourceLineNumber(fileManifest.WixFile.Source), fe.FileName, "AssemblyManifest")); | ||
432 | } | ||
433 | catch (XmlException xe) | ||
434 | { | ||
435 | Messaging.Instance.OnMessage(WixErrors.InvalidXml(new SourceLineNumber(fileManifest.WixFile.Source), "manifest", xe.Message)); | ||
436 | } | ||
437 | |||
438 | Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
439 | if (!String.IsNullOrEmpty(win32Name)) | ||
440 | { | ||
441 | this.SetMsiAssemblyName(assemblyNameTable, file, "name", win32Name); | ||
442 | } | ||
443 | |||
444 | if (!String.IsNullOrEmpty(win32Version)) | ||
445 | { | ||
446 | this.SetMsiAssemblyName(assemblyNameTable, file, "version", win32Version); | ||
447 | } | ||
448 | |||
449 | if (!String.IsNullOrEmpty(win32Type)) | ||
450 | { | ||
451 | this.SetMsiAssemblyName(assemblyNameTable, file, "type", win32Type); | ||
452 | } | ||
453 | |||
454 | if (!String.IsNullOrEmpty(win32ProcessorArchitecture)) | ||
455 | { | ||
456 | this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", win32ProcessorArchitecture); | ||
457 | } | ||
458 | |||
459 | if (!String.IsNullOrEmpty(win32PublicKeyToken)) | ||
460 | { | ||
461 | this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", win32PublicKeyToken); | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | |||
466 | /// <summary> | ||
467 | /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise | ||
468 | /// create a new row. | ||
469 | /// </summary> | ||
470 | /// <param name="assemblyNameTable">MsiAssemblyName table.</param> | ||
471 | /// <param name="file">FileFacade containing the assembly read for the MsiAssemblyName row.</param> | ||
472 | /// <param name="name">MsiAssemblyName name.</param> | ||
473 | /// <param name="value">MsiAssemblyName value.</param> | ||
474 | private void SetMsiAssemblyName(Table assemblyNameTable, FileFacade file, string name, string value) | ||
475 | { | ||
476 | // check for null value (this can occur when grabbing the file version from an assembly without one) | ||
477 | if (String.IsNullOrEmpty(value)) | ||
478 | { | ||
479 | Messaging.Instance.OnMessage(WixWarnings.NullMsiAssemblyNameValue(file.File.SourceLineNumbers, file.File.Component, name)); | ||
480 | } | ||
481 | else | ||
482 | { | ||
483 | Row assemblyNameRow = null; | ||
484 | |||
485 | // override directly authored value | ||
486 | foreach (Row row in assemblyNameTable.Rows) | ||
487 | { | ||
488 | if ((string)row[0] == file.File.Component && (string)row[1] == name) | ||
489 | { | ||
490 | assemblyNameRow = row; | ||
491 | break; | ||
492 | } | ||
493 | } | ||
494 | |||
495 | // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. | ||
496 | if ("name" == name && FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType && | ||
497 | String.IsNullOrEmpty(file.WixFile.AssemblyApplication) && | ||
498 | !String.Equals(Path.GetFileNameWithoutExtension(file.File.LongFileName), value, StringComparison.OrdinalIgnoreCase)) | ||
499 | { | ||
500 | Messaging.Instance.OnMessage(WixErrors.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.LongFileName), value)); | ||
501 | } | ||
502 | |||
503 | if (null == assemblyNameRow) | ||
504 | { | ||
505 | assemblyNameRow = assemblyNameTable.CreateRow(file.File.SourceLineNumbers); | ||
506 | assemblyNameRow[0] = file.File.Component; | ||
507 | assemblyNameRow[1] = name; | ||
508 | assemblyNameRow[2] = value; | ||
509 | |||
510 | // put the MsiAssemblyName row in the same section as the related File row | ||
511 | assemblyNameRow.SectionId = file.File.SectionId; | ||
512 | |||
513 | if (null == file.AssemblyNames) | ||
514 | { | ||
515 | file.AssemblyNames = new List<Row>(); | ||
516 | } | ||
517 | |||
518 | file.AssemblyNames.Add(assemblyNameRow); | ||
519 | } | ||
520 | else | ||
521 | { | ||
522 | assemblyNameRow[2] = value; | ||
523 | } | ||
524 | |||
525 | if (this.VariableCache != null) | ||
526 | { | ||
527 | string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)).ToLowerInvariant(); | ||
528 | this.VariableCache[key] = (string)assemblyNameRow[2]; | ||
529 | } | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | } | ||