@joebigelow / wix-1 / commits / 23217621

Populate MsiAssemblyName table

Fixes wixtoolset/issues#5865

Rob Mensching committed Dec 26, 2018 at 12:40 UTC 232176210981a8e21793f6096dd651eba29caf8e
7 files changed +325 -360
src/WixToolset.Core.WindowsInstaller/Bind/AssemblyName.cs new
+60
@@ -0,0 +1,60 @@
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.Bind
4 +{
5 + using System;
6 + using System.Text;
7 +
8 + internal class AssemblyName
9 + {
10 + public AssemblyName(string name, string culture, string version, string fileVersion, string architecture, string publicKeyToken, string type)
11 + {
12 + this.Name = name;
13 + this.Culture = culture ?? "neutral";
14 + this.Version = version;
15 + this.FileVersion = fileVersion;
16 + this.Architecture = architecture;
17 +
18 + this.StrongNamedSigned = !String.IsNullOrEmpty(publicKeyToken);
19 + this.PublicKeyToken = publicKeyToken ?? "null";
20 + this.Type = type;
21 + }
22 +
23 + public string Name { get; }
24 +
25 + public string Culture { get; }
26 +
27 + public string Version { get; }
28 +
29 + public string FileVersion { get; }
30 +
31 + public string Architecture { get; }
32 +
33 + public string PublicKeyToken { get; }
34 +
35 + public bool StrongNamedSigned { get; }
36 +
37 + public string Type { get; }
38 +
39 + public string GetFullName()
40 + {
41 + var assemblyName = new StringBuilder();
42 +
43 + assemblyName.Append(this.Name);
44 + assemblyName.Append(", Version=");
45 + assemblyName.Append(this.Version);
46 + assemblyName.Append(", Culture=");
47 + assemblyName.Append(this.Culture);
48 + assemblyName.Append(", PublicKeyToken=");
49 + assemblyName.Append(this.PublicKeyToken);
50 +
51 + if (!String.IsNullOrEmpty(this.Architecture))
52 + {
53 + assemblyName.Append(", ProcessorArchitecture=");
54 + assemblyName.Append(this.Architecture);
55 + }
56 +
57 + return assemblyName.ToString();
58 + }
59 + }
60 +}
src/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs new
+192
@@ -0,0 +1,192 @@
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.Bind
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Reflection.Metadata;
8 + using System.Reflection.PortableExecutable;
9 + using System.Security.Cryptography;
10 + using System.Text;
11 + using System.Xml;
12 + using System.Xml.XPath;
13 + using WixToolset.Data;
14 +
15 + internal static class AssemblyNameReader
16 + {
17 + public static AssemblyName ReadAssembly(SourceLineNumber sourceLineNumbers, string assemblyPath, string fileVersion)
18 + {
19 + try
20 + {
21 + using (var stream = File.OpenRead(assemblyPath))
22 + using (var peReader = new PEReader(stream))
23 + {
24 + var reader = peReader.GetMetadataReader();
25 + var headers = peReader.PEHeaders;
26 +
27 + var assembly = reader.GetAssemblyDefinition();
28 + var attributes = assembly.GetCustomAttributes();
29 +
30 + var name = ReadString(reader, assembly.Name);
31 + var culture = ReadString(reader, assembly.Culture);
32 + var architecture = headers.PEHeader.Magic == PEMagic.PE32Plus ? "x64" : (headers.CorHeader.Flags & CorFlags.Requires32Bit) == CorFlags.Requires32Bit ? "x86" : null;
33 + var version = assembly.Version.ToString();
34 + var publicKeyToken = ReadPublicKeyToken(reader, assembly.PublicKey);
35 +
36 + // There is a bug in v1 fusion that requires the assembly's "version" attribute
37 + // to be equal to or longer than the "fileVersion" in length when its present;
38 + // the workaround is to prepend zeroes to the last version number in the assembly
39 + // version.
40 + var targetNetfx1 = (headers.CorHeader.MajorRuntimeVersion == 2) && (headers.CorHeader.MinorRuntimeVersion == 0);
41 + if (targetNetfx1 && !String.IsNullOrEmpty(fileVersion) && fileVersion.Length > version.Length)
42 + {
43 + var versionParts = version.Split('.');
44 +
45 + if (versionParts.Length > 0)
46 + {
47 + var padding = new string('0', fileVersion.Length - version.Length);
48 +
49 + versionParts[versionParts.Length - 1] = String.Concat(padding, versionParts[versionParts.Length - 1]);
50 + version = String.Join(".", versionParts);
51 + }
52 + }
53 +
54 + return new AssemblyName(name, culture, version, fileVersion, architecture, publicKeyToken, null);
55 + }
56 + }
57 + catch (Exception e) when (e is FileNotFoundException || e is BadImageFormatException || e is InvalidOperationException)
58 + {
59 + throw new WixException(ErrorMessages.InvalidAssemblyFile(sourceLineNumbers, assemblyPath, $"{e.GetType().Name}: {e.Message}"));
60 + }
61 + }
62 +
63 + public static AssemblyName ReadAssemblyManifest(SourceLineNumber sourceLineNumbers, string manifestPath)
64 + {
65 + string win32Type = null;
66 + string win32Name = null;
67 + string win32Version = null;
68 + string win32ProcessorArchitecture = null;
69 + string win32PublicKeyToken = null;
70 +
71 + // Loading the dom is expensive we want more performant APIs than the DOM
72 + // Navigator is cheaper than dom. Perhaps there is a cheaper API still.
73 + try
74 + {
75 + var doc = new XPathDocument(manifestPath);
76 + var nav = doc.CreateNavigator();
77 + nav.MoveToRoot();
78 +
79 + // This assumes a particular schema for a win32 manifest and does not
80 + // provide error checking if the file does not conform to schema.
81 + // The fallback case here is that nothing is added to the MsiAssemblyName
82 + // table for an out of tolerance Win32 manifest. Perhaps warnings needed.
83 + if (nav.MoveToFirstChild())
84 + {
85 + while (nav.NodeType != XPathNodeType.Element || nav.Name != "assembly")
86 + {
87 + nav.MoveToNext();
88 + }
89 +
90 + if (nav.MoveToFirstChild())
91 + {
92 + var hasNextSibling = true;
93 + while (nav.NodeType != XPathNodeType.Element || nav.Name != "assemblyIdentity" && hasNextSibling)
94 + {
95 + hasNextSibling = nav.MoveToNext();
96 + }
97 +
98 + if (!hasNextSibling)
99 + {
100 + throw new WixException(ErrorMessages.InvalidManifestContent(sourceLineNumbers, manifestPath));
101 + }
102 +
103 + if (nav.MoveToAttribute("type", String.Empty))
104 + {
105 + win32Type = nav.Value;
106 + nav.MoveToParent();
107 + }
108 +
109 + if (nav.MoveToAttribute("name", String.Empty))
110 + {
111 + win32Name = nav.Value;
112 + nav.MoveToParent();
113 + }
114 +
115 + if (nav.MoveToAttribute("version", String.Empty))
116 + {
117 + win32Version = nav.Value;
118 + nav.MoveToParent();
119 + }
120 +
121 + if (nav.MoveToAttribute("processorArchitecture", String.Empty))
122 + {
123 + win32ProcessorArchitecture = nav.Value;
124 + nav.MoveToParent();
125 + }
126 +
127 + if (nav.MoveToAttribute("publicKeyToken", String.Empty))
128 + {
129 + win32PublicKeyToken = nav.Value;
130 + nav.MoveToParent();
131 + }
132 + }
133 + }
134 + }
135 + catch (FileNotFoundException fe)
136 + {
137 + throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, fe.FileName, "AssemblyManifest"));
138 + }
139 + catch (XmlException xe)
140 + {
141 + throw new WixException(ErrorMessages.InvalidXml(sourceLineNumbers, "manifest", xe.Message));
142 + }
143 +
144 + return new AssemblyName(win32Name, null, win32Version, null, win32ProcessorArchitecture, win32PublicKeyToken, win32Type);
145 + }
146 +
147 + private static string ReadString(MetadataReader reader, StringHandle handle)
148 + {
149 + return handle.IsNil ? null : reader.GetString(handle);
150 + }
151 +
152 + private static string ReadPublicKeyToken(MetadataReader reader, BlobHandle handle)
153 + {
154 + if (handle.IsNil)
155 + {
156 + return null;
157 + }
158 +
159 + var bytes = reader.GetBlobBytes(handle);
160 + if (bytes.Length == 0)
161 + {
162 + return null;
163 + }
164 +
165 + var result = new StringBuilder();
166 +
167 + // If we have the full public key, calculate the public key token from the
168 + // last 8 bytes (in reverse order) of the public key's SHA1 hash.
169 + if (bytes.Length > 8)
170 + {
171 + using (var sha1 = SHA1.Create())
172 + {
173 + var hash = sha1.ComputeHash(bytes);
174 +
175 + for (var i = 1; i <= 8; ++i)
176 + {
177 + result.Append(hash[hash.Length - i].ToString("X2"));
178 + }
179 + }
180 + }
181 + else
182 + {
183 + foreach (var b in bytes)
184 + {
185 + result.Append(b.ToString("X2"));
186 + }
187 + }
188 +
189 + return result.ToString();
190 + }
191 + }
192 +}
src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+47 -209
@@ -8,9 +8,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
8 using System.Globalization;
9 using System.IO;
10 using System.Linq;
11 - using System.Xml;
12 - using System.Xml.XPath;
13 - using WixToolset.Clr.Interop;
11 using WixToolset.Core.Bind;
12 using WixToolset.Data;
13 using WixToolset.Data.Tuples;
@@ -45,7 +42,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
42
43 public void Execute()
44 {
48 - foreach (FileFacade file in this.UpdateFileFacades)
45 + foreach (var file in this.UpdateFileFacades)
46 {
47 this.UpdateFileFacade(file);
48 }
@@ -86,7 +83,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
83 return;
84 }
85
89 - using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
86 + using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
87 {
88 if (Int32.MaxValue < fileStream.Length)
89 {
@@ -224,136 +221,47 @@ namespace WixToolset.Core.WindowsInstaller.Bind
221 // If this is a CLR assembly, load the assembly and get the assembly name information
222 if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType)
223 {
227 - bool targetNetfx1 = false;
228 - var assemblyNameValues = new Dictionary<string, string>();
229 -
230 - Guid referenceIdentityGuid = ClrInterop.ReferenceIdentityGuid;
231 - var result = ClrInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out var referenceIdentity);
232 - if (0 == result && null != referenceIdentity)
224 + try
225 {
234 - var imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion");
235 - if (null != imageRuntimeVersion)
236 - {
237 - targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase);
238 - }
226 + var assemblyName = AssemblyNameReader.ReadAssembly(file.File.SourceLineNumbers, fileInfo.FullName, version);
227
240 - string culture = referenceIdentity.GetAttribute(null, "Culture") ?? "neutral";
241 - assemblyNameValues.Add("Culture", culture);
228 + this.SetMsiAssemblyName(assemblyNameTuples, file, "name", assemblyName.Name);
229 + this.SetMsiAssemblyName(assemblyNameTuples, file, "culture", assemblyName.Culture);
230 + this.SetMsiAssemblyName(assemblyNameTuples, file, "version", assemblyName.Version);
231
243 - string name = referenceIdentity.GetAttribute(null, "Name");
244 - if (null != name)
232 + if (!String.IsNullOrEmpty(assemblyName.Architecture))
233 {
246 - assemblyNameValues.Add("Name", name);
234 + this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", assemblyName.Architecture);
235 }
236 + // TODO: WiX v3 seemed to do this but not clear it should actually be done.
237 + //else if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture))
238 + //{
239 + // this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", file.WixFile.ProcessorArchitecture);
240 + //}
241
249 - string processorArchitecture = referenceIdentity.GetAttribute(null, "ProcessorArchitecture");
250 - if (null != processorArchitecture)
242 + if (assemblyName.StrongNamedSigned)
243 {
252 - assemblyNameValues.Add("ProcessorArchitecture", processorArchitecture);
253 - }
254 -
255 - string publicKeyToken = referenceIdentity.GetAttribute(null, "PublicKeyToken");
256 - if (null != publicKeyToken)
257 - {
258 - bool publicKeyIsNeutral = (String.Equals(publicKeyToken, "neutral", StringComparison.OrdinalIgnoreCase));
259 -
260 - // Managed code expects "null" instead of "neutral", and
261 - // this won't be installed to the GAC since it's not signed anyway.
262 - assemblyNameValues.Add("publicKeyToken", publicKeyIsNeutral ? "null" : publicKeyToken.ToUpperInvariant());
263 - assemblyNameValues.Add("publicKeyTokenPreservedCase", publicKeyIsNeutral ? "null" : publicKeyToken);
244 + this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", assemblyName.PublicKeyToken);
245 }
246 else if (file.WixFile.File_AssemblyApplication == null)
247 {
248 throw new WixException(ErrorMessages.GacAssemblyNoStrongName(file.File.SourceLineNumbers, fileInfo.FullName, file.File.Component_));
249 }
250
270 - string assemblyVersion = referenceIdentity.GetAttribute(null, "Version");
271 - if (null != version)
251 + if (!String.IsNullOrEmpty(assemblyName.FileVersion))
252 {
273 - assemblyNameValues.Add("Version", assemblyVersion);
253 + this.SetMsiAssemblyName(assemblyNameTuples, file, "fileVersion", assemblyName.FileVersion);
254 }
275 - }
276 - else
277 - {
278 - this.Messaging.Write(ErrorMessages.InvalidAssemblyFile(file.File.SourceLineNumbers, fileInfo.FullName, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", result)));
279 - return;
280 - }
281 -
282 - if (assemblyNameValues.TryGetValue("name", out var value))
283 - {
284 - this.SetMsiAssemblyName(assemblyNameTuples, file, "name", value);
285 - }
286 -
287 - if (!String.IsNullOrEmpty(version))
288 - {
289 - this.SetMsiAssemblyName(assemblyNameTuples, file, "fileVersion", version);
290 - }
255
292 - if (assemblyNameValues.ContainsKey("version"))
293 - {
294 - string assemblyVersion = assemblyNameValues["version"];
295 -
296 - if (!targetNetfx1)
256 + // add the assembly name to the information cache
257 + if (null != this.VariableCache)
258 {
298 - // There is a bug in v1 fusion that requires the assembly's "version" attribute
299 - // to be equal to or longer than the "fileVersion" in length when its present;
300 - // the workaround is to prepend zeroes to the last version number in the assembly
301 - // version.
302 - if (null != version && version.Length > assemblyVersion.Length)
303 - {
304 - string padding = new string('0', version.Length - assemblyVersion.Length);
305 - string[] assemblyVersionNumbers = assemblyVersion.Split('.');
306 -
307 - if (assemblyVersionNumbers.Length > 0)
308 - {
309 - assemblyVersionNumbers[assemblyVersionNumbers.Length - 1] = String.Concat(padding, assemblyVersionNumbers[assemblyVersionNumbers.Length - 1]);
310 - assemblyVersion = String.Join(".", assemblyVersionNumbers);
311 - }
312 - }
259 + this.VariableCache[$"assemblyfullname.{file.File.File}"] = assemblyName.GetFullName();
260 }
314 -
315 - this.SetMsiAssemblyName(assemblyNameTuples, file, "version", assemblyVersion);
316 - }
317 -
318 - if (assemblyNameValues.ContainsKey("culture"))
319 - {
320 - this.SetMsiAssemblyName(assemblyNameTuples, file, "culture", assemblyNameValues["culture"]);
321 - }
322 -
323 - if (assemblyNameValues.ContainsKey("publicKeyToken"))
324 - {
325 - this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", assemblyNameValues["publicKeyToken"]);
326 - }
327 -
328 - if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture))
329 - {
330 - this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", file.WixFile.ProcessorArchitecture);
331 - }
332 -
333 - if (assemblyNameValues.ContainsKey("processorArchitecture"))
334 - {
335 - this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", assemblyNameValues["processorArchitecture"]);
261 }
337 -
338 - // add the assembly name to the information cache
339 - if (null != this.VariableCache)
262 + catch (WixException e)
263 {
341 - string fileId = file.File.File;
342 - string key = String.Concat("assemblyfullname.", fileId);
343 - string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]);
344 - if (assemblyNameValues.ContainsKey("processorArchitecture"))
345 - {
346 - assemblyName = String.Concat(assemblyName, ", processorArchitecture=", assemblyNameValues["processorArchitecture"]);
347 - }
348 -
349 - this.VariableCache[key] = assemblyName;
350 -
351 - // Add entries with the preserved case publicKeyToken
352 - string pcAssemblyNameKey = String.Concat("assemblyfullnamepreservedcase.", fileId);
353 - this.VariableCache[pcAssemblyNameKey] = (assemblyNameValues["publicKeyToken"] == assemblyNameValues["publicKeyTokenPreservedCase"]) ? assemblyName : assemblyName.Replace(assemblyNameValues["publicKeyToken"], assemblyNameValues["publicKeyTokenPreservedCase"]);
354 -
355 - string pcPublicKeyTokenKey = String.Concat("assemblypublickeytokenpreservedcase.", fileId);
356 - this.VariableCache[pcPublicKeyTokenKey] = assemblyNameValues["publicKeyTokenPreservedCase"];
264 + this.Messaging.Write(e.Error);
265 }
266 }
267 else if (FileAssemblyType.Win32Assembly == file.WixFile.AssemblyType)
@@ -361,114 +269,44 @@ namespace WixToolset.Core.WindowsInstaller.Bind
269 // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through
270 // all files like this. Even though this is a rare case it looks like we might be able to index the
271 // file earlier.
364 - FileFacade fileManifest = this.FileFacades.SingleOrDefault(r => r.File.File.Equals(file.WixFile.File_AssemblyManifest, StringComparison.Ordinal));
272 + var fileManifest = this.FileFacades.SingleOrDefault(r => r.File.File.Equals(file.WixFile.File_AssemblyManifest, StringComparison.Ordinal));
273 if (null == fileManifest)
274 {
275 this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(file.File.SourceLineNumbers, file.File.File, file.WixFile.File_AssemblyManifest));
276 }
277
370 - string win32Type = null;
371 - string win32Name = null;
372 - string win32Version = null;
373 - string win32ProcessorArchitecture = null;
374 - string win32PublicKeyToken = null;
375 -
376 - // loading the dom is expensive we want more performant APIs than the DOM
377 - // Navigator is cheaper than dom. Perhaps there is a cheaper API still.
278 try
279 {
380 - XPathDocument doc = new XPathDocument(fileManifest.WixFile.Source.Path);
381 - XPathNavigator nav = doc.CreateNavigator();
382 - nav.MoveToRoot();
383 -
384 - // this assumes a particular schema for a win32 manifest and does not
385 - // provide error checking if the file does not conform to schema.
386 - // The fallback case here is that nothing is added to the MsiAssemblyName
387 - // table for an out of tolerance Win32 manifest. Perhaps warnings needed.
388 - if (nav.MoveToFirstChild())
389 - {
390 - while (nav.NodeType != XPathNodeType.Element || nav.Name != "assembly")
391 - {
392 - nav.MoveToNext();
393 - }
280 + var assemblyName = AssemblyNameReader.ReadAssemblyManifest(file.File.SourceLineNumbers, fileManifest.WixFile.Source.Path);
281
395 - if (nav.MoveToFirstChild())
396 - {
397 - bool hasNextSibling = true;
398 - while (nav.NodeType != XPathNodeType.Element || nav.Name != "assemblyIdentity" && hasNextSibling)
399 - {
400 - hasNextSibling = nav.MoveToNext();
401 - }
402 - if (!hasNextSibling)
403 - {
404 - this.Messaging.Write(ErrorMessages.InvalidManifestContent(file.File.SourceLineNumbers, fileManifest.WixFile.Source.Path));
405 - return;
406 - }
407 -
408 - if (nav.MoveToAttribute("type", String.Empty))
409 - {
410 - win32Type = nav.Value;
411 - nav.MoveToParent();
412 - }
413 -
414 - if (nav.MoveToAttribute("name", String.Empty))
415 - {
416 - win32Name = nav.Value;
417 - nav.MoveToParent();
418 - }
419 -
420 - if (nav.MoveToAttribute("version", String.Empty))
421 - {
422 - win32Version = nav.Value;
423 - nav.MoveToParent();
424 - }
425 -
426 - if (nav.MoveToAttribute("processorArchitecture", String.Empty))
427 - {
428 - win32ProcessorArchitecture = nav.Value;
429 - nav.MoveToParent();
430 - }
431 -
432 - if (nav.MoveToAttribute("publicKeyToken", String.Empty))
433 - {
434 - win32PublicKeyToken = nav.Value;
435 - nav.MoveToParent();
436 - }
437 - }
282 + if (!String.IsNullOrEmpty(assemblyName.Name))
283 + {
284 + this.SetMsiAssemblyName(assemblyNameTuples, file, "name", assemblyName.Name);
285 }
439 - }
440 - catch (FileNotFoundException fe)
441 - {
442 - this.Messaging.Write(ErrorMessages.FileNotFound(new SourceLineNumber(fileManifest.WixFile.Source.Path), fe.FileName, "AssemblyManifest"));
443 - }
444 - catch (XmlException xe)
445 - {
446 - this.Messaging.Write(ErrorMessages.InvalidXml(new SourceLineNumber(fileManifest.WixFile.Source.Path), "manifest", xe.Message));
447 - }
286
449 - if (!String.IsNullOrEmpty(win32Name))
450 - {
451 - this.SetMsiAssemblyName(assemblyNameTuples, file, "name", win32Name);
452 - }
287 + if (!String.IsNullOrEmpty(assemblyName.Version))
288 + {
289 + this.SetMsiAssemblyName(assemblyNameTuples, file, "version", assemblyName.Version);
290 + }
291
454 - if (!String.IsNullOrEmpty(win32Version))
455 - {
456 - this.SetMsiAssemblyName(assemblyNameTuples, file, "version", win32Version);
457 - }
292 + if (!String.IsNullOrEmpty(assemblyName.Type))
293 + {
294 + this.SetMsiAssemblyName(assemblyNameTuples, file, "type", assemblyName.Type);
295 + }
296
459 - if (!String.IsNullOrEmpty(win32Type))
460 - {
461 - this.SetMsiAssemblyName(assemblyNameTuples, file, "type", win32Type);
462 - }
297 + if (!String.IsNullOrEmpty(assemblyName.Architecture))
298 + {
299 + this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", assemblyName.Architecture);
300 + }
301
464 - if (!String.IsNullOrEmpty(win32ProcessorArchitecture))
465 - {
466 - this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", win32ProcessorArchitecture);
302 + if (!String.IsNullOrEmpty(assemblyName.PublicKeyToken))
303 + {
304 + this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", assemblyName.PublicKeyToken);
305 + }
306 }
468 -
469 - if (!String.IsNullOrEmpty(win32PublicKeyToken))
307 + catch (WixException e)
308 {
471 - this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", win32PublicKeyToken);
309 + this.Messaging.Write(e.Error);
310 }
311 }
312 }
@@ -500,7 +338,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
338
339 // override directly authored value
340 var lookup = String.Concat(file.File.Component_, "/", name);
503 - if (assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow))
341 + if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow))
342 {
343 assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers);
344 assemblyNameRow.Component_ = file.File.Component_;
src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs deleted
-147
@@ -1,147 +0,0 @@
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.Clr.Interop
4 -{
5 - using System;
6 - using System.Runtime.InteropServices;
7 -
8 - /// <summary>
9 - /// Interop class for mscorwks.dll assembly name APIs.
10 - /// </summary>
11 - internal sealed class ClrInterop
12 - {
13 - private static readonly Guid referenceIdentityGuid = new Guid("6eaf5ace-7917-4f3c-b129-e046a9704766");
14 -
15 - /// <summary>
16 - /// Protect the constructor.
17 - /// </summary>
18 - private ClrInterop()
19 - {
20 - }
21 -
22 - /// <summary>
23 - /// Represents a reference to the unique signature of a code object.
24 - /// </summary>
25 - [ComImport]
26 - [Guid("6eaf5ace-7917-4f3c-b129-e046a9704766")]
27 - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
28 - internal interface IReferenceIdentity
29 - {
30 - /// <summary>
31 - /// Get an assembly attribute.
32 - /// </summary>
33 - /// <param name="attributeNamespace">Attribute namespace.</param>
34 - /// <param name="attributeName">Attribute name.</param>
35 - /// <returns>The assembly attribute.</returns>
36 - [return: MarshalAs(UnmanagedType.LPWStr)]
37 - string GetAttribute(
38 - [In, MarshalAs(UnmanagedType.LPWStr)] string attributeNamespace,
39 - [In, MarshalAs(UnmanagedType.LPWStr)] string attributeName);
40 -
41 - /// <summary>
42 - /// Set an assembly attribute.
43 - /// </summary>
44 - /// <param name="attributeNamespace">Attribute namespace.</param>
45 - /// <param name="attributeName">Attribute name.</param>
46 - /// <param name="attributeValue">Attribute value.</param>
47 - void SetAttribute(
48 - [In, MarshalAs(UnmanagedType.LPWStr)] string attributeNamespace,
49 - [In, MarshalAs(UnmanagedType.LPWStr)] string attributeName,
50 - [In, MarshalAs(UnmanagedType.LPWStr)] string attributeValue);
51 -
52 - /// <summary>
53 - /// Get an iterator for the assembly's attributes.
54 - /// </summary>
55 - /// <returns>Assembly attribute enumerator.</returns>
56 - IEnumIDENTITY_ATTRIBUTE EnumAttributes();
57 -
58 - /// <summary>
59 - /// Clone an IReferenceIdentity.
60 - /// </summary>
61 - /// <param name="countOfDeltas">Count of deltas.</param>
62 - /// <param name="deltas">The deltas.</param>
63 - /// <returns>Cloned IReferenceIdentity.</returns>
64 - IReferenceIdentity Clone(
65 - [In] IntPtr /*SIZE_T*/ countOfDeltas,
66 - [In, MarshalAs(UnmanagedType.LPArray)] IDENTITY_ATTRIBUTE[] deltas);
67 - }
68 -
69 - /// <summary>
70 - /// IEnumIDENTITY_ATTRIBUTE interface.
71 - /// </summary>
72 - [ComImport]
73 - [Guid("9cdaae75-246e-4b00-a26d-b9aec137a3eb")]
74 - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
75 - internal interface IEnumIDENTITY_ATTRIBUTE
76 - {
77 - /// <summary>
78 - /// Gets the next attributes.
79 - /// </summary>
80 - /// <param name="celt">Count of elements.</param>
81 - /// <param name="attributes">Array of attributes being returned.</param>
82 - /// <returns>The next attribute.</returns>
83 - uint Next(
84 - [In] uint celt,
85 - [Out, MarshalAs(UnmanagedType.LPArray)] IDENTITY_ATTRIBUTE[] attributes);
86 -
87 - /// <summary>
88 - /// Copy the current attribute into a buffer.
89 - /// </summary>
90 - /// <param name="available">Number of available bytes.</param>
91 - /// <param name="data">Buffer into which attribute should be written.</param>
92 - /// <returns>Pointer to buffer containing the attribute.</returns>
93 - IntPtr CurrentIntoBuffer(
94 - [In] IntPtr /*SIZE_T*/ available,
95 - [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data);
96 -
97 - /// <summary>
98 - /// Skip past a number of elements.
99 - /// </summary>
100 - /// <param name="celt">Count of elements to skip.</param>
101 - void Skip([In] uint celt);
102 -
103 - /// <summary>
104 - /// Reset the enumeration to the beginning.
105 - /// </summary>
106 - void Reset();
107 -
108 - /// <summary>
109 - /// Clone this attribute enumeration.
110 - /// </summary>
111 - /// <returns>Clone of a IEnumIDENTITY_ATTRIBUTE.</returns>
112 - IEnumIDENTITY_ATTRIBUTE Clone();
113 - }
114 -
115 - /// <summary>
116 - /// Gets the guid.
117 - /// </summary>
118 - public static Guid ReferenceIdentityGuid
119 - {
120 - get { return referenceIdentityGuid; }
121 - }
122 -
123 - /// <summary>
124 - /// Gets an interface pointer to an object with the specified IID, in the assembly at the specified file path.
125 - /// </summary>
126 - /// <param name="wszAssemblyPath">A valid path to the requested assembly.</param>
127 - /// <param name="riid">The IID of the interface to return.</param>
128 - /// <param name="i">The returned interface pointer.</param>
129 - /// <returns>The error code.</returns>
130 - [DllImport("mscorwks.dll", CharSet = CharSet.Unicode, EntryPoint = "GetAssemblyIdentityFromFile")]
131 - internal static extern uint GetAssemblyIdentityFromFile(System.String wszAssemblyPath, ref Guid riid, out IReferenceIdentity i);
132 -
133 - /// <summary>
134 - /// Assembly attributes. Contains data about an IReferenceIdentity.
135 - /// </summary>
136 - [StructLayout(LayoutKind.Sequential)]
137 - internal struct IDENTITY_ATTRIBUTE
138 - {
139 - [MarshalAs(UnmanagedType.LPWStr)]
140 - public string AttributeNamespace;
141 - [MarshalAs(UnmanagedType.LPWStr)]
142 - public string AttributeName;
143 - [MarshalAs(UnmanagedType.LPWStr)]
144 - public string AttributeValue;
145 - }
146 - }
147 -}
src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
+4
@@ -23,6 +23,10 @@
23 <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" NoWarn="NU1701" />
24 </ItemGroup>
25
26 + <ItemGroup>
27 + <PackageReference Include="System.Reflection.Metadata" Version="1.6.0" />
28 + </ItemGroup>
29 +
30 <ItemGroup>
31 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63102-01" PrivateAssets="All" />
32 <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="all" />
src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
+21 -3
@@ -409,7 +409,7 @@ namespace WixToolsetTest.CoreIntegration
409 }
410 }
411
412 - [Fact(Skip = "Assembly information not getting gathered yet.")]
412 + [Fact]
413 public void CanBuildWithAssembly()
414 {
415 var folder = TestData.Get(@"TestData\Assembly");
@@ -436,7 +436,7 @@ namespace WixToolsetTest.CoreIntegration
436 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
437 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\AssemblyMsiPackage\candle.exe")));
438
439 - var intermediate = Intermediate.Load(Path.Combine(baseFolder, @"test.wir"));
439 + var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"test.wir"));
440 var section = intermediate.Sections.Single();
441
442 var wixFile = section.Tuples.OfType<WixFileTuple>().Single();
@@ -444,7 +444,25 @@ namespace WixToolsetTest.CoreIntegration
444 Assert.Equal(@"candle.exe", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path);
445
446 var msiAssemblyNameTuples = section.Tuples.OfType<MsiAssemblyNameTuple>();
447 - Assert.NotEmpty(msiAssemblyNameTuples);
447 + Assert.Equal(new[]
448 + {
449 + "culture",
450 + "fileVersion",
451 + "name",
452 + "processorArchitecture",
453 + "publicKeyToken",
454 + "version"
455 + }, msiAssemblyNameTuples.OrderBy(a => a.Name).Select(a => a.Name).ToArray());
456 +
457 + Assert.Equal(new[]
458 + {
459 + "neutral",
460 + "3.11.11810.0",
461 + "candle",
462 + "x86",
463 + "256B3414DFA97718",
464 + "3.0.0.0"
465 + }, msiAssemblyNameTuples.OrderBy(a => a.Name).Select(a => a.Value).ToArray());
466 }
467 }
468
src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+1 -1
@@ -1,4 +1,4 @@
1 -<?xml version="1.0" encoding="utf-8"?>
1 +<?xml version="1.0" encoding="utf-8"?>
2 <!-- 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. -->
3
4 <Project Sdk="Microsoft.NET.Sdk">