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