aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2018-12-26 12:40:58 -0800
committerRob Mensching <rob@robmensching.com>2018-12-26 14:03:10 -0800
commit232176210981a8e21793f6096dd651eba29caf8e (patch)
tree5880b1b9c968f494fbab3f78c07cba95ed309c40
parent758bcf4bb6d61dd9d6471d9f9629d4f7b18d43cb (diff)
downloadwix-232176210981a8e21793f6096dd651eba29caf8e.tar.gz
wix-232176210981a8e21793f6096dd651eba29caf8e.tar.bz2
wix-232176210981a8e21793f6096dd651eba29caf8e.zip
Populate MsiAssemblyName table
Fixes wixtoolset/issues#5865
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/AssemblyName.cs60
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs192
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs256
-rw-r--r--src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs147
-rw-r--r--src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj4
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs24
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj2
7 files changed, 325 insertions, 360 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssemblyName.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssemblyName.cs
new file mode 100644
index 00000000..0df1a7e9
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssemblyName.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
new file mode 100644
index 00000000..4815cb35
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
index 10eae8f8..81300322 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
@@ -8,9 +8,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
8 using System.Globalization; 8 using System.Globalization;
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using System.Xml;
12 using System.Xml.XPath;
13 using WixToolset.Clr.Interop;
14 using WixToolset.Core.Bind; 11 using WixToolset.Core.Bind;
15 using WixToolset.Data; 12 using WixToolset.Data;
16 using WixToolset.Data.Tuples; 13 using WixToolset.Data.Tuples;
@@ -45,7 +42,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
45 42
46 public void Execute() 43 public void Execute()
47 { 44 {
48 foreach (FileFacade file in this.UpdateFileFacades) 45 foreach (var file in this.UpdateFileFacades)
49 { 46 {
50 this.UpdateFileFacade(file); 47 this.UpdateFileFacade(file);
51 } 48 }
@@ -86,7 +83,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
86 return; 83 return;
87 } 84 }
88 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))
90 { 87 {
91 if (Int32.MaxValue < fileStream.Length) 88 if (Int32.MaxValue < fileStream.Length)
92 { 89 {
@@ -224,136 +221,47 @@ namespace WixToolset.Core.WindowsInstaller.Bind
224 // If this is a CLR assembly, load the assembly and get the assembly name information 221 // If this is a CLR assembly, load the assembly and get the assembly name information
225 if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType) 222 if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType)
226 { 223 {
227 bool targetNetfx1 = false; 224 try
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)
233 { 225 {
234 var imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion"); 226 var assemblyName = AssemblyNameReader.ReadAssembly(file.File.SourceLineNumbers, fileInfo.FullName, version);
235 if (null != imageRuntimeVersion)
236 {
237 targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase);
238 }
239 227
240 string culture = referenceIdentity.GetAttribute(null, "Culture") ?? "neutral"; 228 this.SetMsiAssemblyName(assemblyNameTuples, file, "name", assemblyName.Name);
241 assemblyNameValues.Add("Culture", culture); 229 this.SetMsiAssemblyName(assemblyNameTuples, file, "culture", assemblyName.Culture);
230 this.SetMsiAssemblyName(assemblyNameTuples, file, "version", assemblyName.Version);
242 231
243 string name = referenceIdentity.GetAttribute(null, "Name"); 232 if (!String.IsNullOrEmpty(assemblyName.Architecture))
244 if (null != name)
245 { 233 {
246 assemblyNameValues.Add("Name", name); 234 this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", assemblyName.Architecture);
247 } 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 //}
248 241
249 string processorArchitecture = referenceIdentity.GetAttribute(null, "ProcessorArchitecture"); 242 if (assemblyName.StrongNamedSigned)
250 if (null != processorArchitecture)
251 { 243 {
252 assemblyNameValues.Add("ProcessorArchitecture", processorArchitecture); 244 this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", assemblyName.PublicKeyToken);
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);
264 } 245 }
265 else if (file.WixFile.File_AssemblyApplication == null) 246 else if (file.WixFile.File_AssemblyApplication == null)
266 { 247 {
267 throw new WixException(ErrorMessages.GacAssemblyNoStrongName(file.File.SourceLineNumbers, fileInfo.FullName, file.File.Component_)); 248 throw new WixException(ErrorMessages.GacAssemblyNoStrongName(file.File.SourceLineNumbers, fileInfo.FullName, file.File.Component_));
268 } 249 }
269 250
270 string assemblyVersion = referenceIdentity.GetAttribute(null, "Version"); 251 if (!String.IsNullOrEmpty(assemblyName.FileVersion))
271 if (null != version)
272 { 252 {
273 assemblyNameValues.Add("Version", assemblyVersion); 253 this.SetMsiAssemblyName(assemblyNameTuples, file, "fileVersion", assemblyName.FileVersion);
274 } 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 }
291 255
292 if (assemblyNameValues.ContainsKey("version")) 256 // add the assembly name to the information cache
293 { 257 if (null != this.VariableCache)
294 string assemblyVersion = assemblyNameValues["version"];
295
296 if (!targetNetfx1)
297 { 258 {
298 // There is a bug in v1 fusion that requires the assembly's "version" attribute 259 this.VariableCache[$"assemblyfullname.{file.File.File}"] = assemblyName.GetFullName();
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 }
313 } 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"]);
336 } 261 }
337 262 catch (WixException e)
338 // add the assembly name to the information cache
339 if (null != this.VariableCache)
340 { 263 {
341 string fileId = file.File.File; 264 this.Messaging.Write(e.Error);
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"];
357 } 265 }
358 } 266 }
359 else if (FileAssemblyType.Win32Assembly == file.WixFile.AssemblyType) 267 else if (FileAssemblyType.Win32Assembly == file.WixFile.AssemblyType)
@@ -361,114 +269,44 @@ namespace WixToolset.Core.WindowsInstaller.Bind
361 // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through 269 // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through
362 // all files like this. Even though this is a rare case it looks like we might be able to index the 270 // all files like this. Even though this is a rare case it looks like we might be able to index the
363 // file earlier. 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));
365 if (null == fileManifest) 273 if (null == fileManifest)
366 { 274 {
367 this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(file.File.SourceLineNumbers, file.File.File, file.WixFile.File_AssemblyManifest)); 275 this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(file.File.SourceLineNumbers, file.File.File, file.WixFile.File_AssemblyManifest));
368 } 276 }
369 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.
378 try 278 try
379 { 279 {
380 XPathDocument doc = new XPathDocument(fileManifest.WixFile.Source.Path); 280 var assemblyName = AssemblyNameReader.ReadAssemblyManifest(file.File.SourceLineNumbers, 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 }
394 281
395 if (nav.MoveToFirstChild()) 282 if (!String.IsNullOrEmpty(assemblyName.Name))
396 { 283 {
397 bool hasNextSibling = true; 284 this.SetMsiAssemblyName(assemblyNameTuples, file, "name", assemblyName.Name);
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 }
438 } 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 }
448 286
449 if (!String.IsNullOrEmpty(win32Name)) 287 if (!String.IsNullOrEmpty(assemblyName.Version))
450 { 288 {
451 this.SetMsiAssemblyName(assemblyNameTuples, file, "name", win32Name); 289 this.SetMsiAssemblyName(assemblyNameTuples, file, "version", assemblyName.Version);
452 } 290 }
453 291
454 if (!String.IsNullOrEmpty(win32Version)) 292 if (!String.IsNullOrEmpty(assemblyName.Type))
455 { 293 {
456 this.SetMsiAssemblyName(assemblyNameTuples, file, "version", win32Version); 294 this.SetMsiAssemblyName(assemblyNameTuples, file, "type", assemblyName.Type);
457 } 295 }
458 296
459 if (!String.IsNullOrEmpty(win32Type)) 297 if (!String.IsNullOrEmpty(assemblyName.Architecture))
460 { 298 {
461 this.SetMsiAssemblyName(assemblyNameTuples, file, "type", win32Type); 299 this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", assemblyName.Architecture);
462 } 300 }
463 301
464 if (!String.IsNullOrEmpty(win32ProcessorArchitecture)) 302 if (!String.IsNullOrEmpty(assemblyName.PublicKeyToken))
465 { 303 {
466 this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", win32ProcessorArchitecture); 304 this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", assemblyName.PublicKeyToken);
305 }
467 } 306 }
468 307 catch (WixException e)
469 if (!String.IsNullOrEmpty(win32PublicKeyToken))
470 { 308 {
471 this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", win32PublicKeyToken); 309 this.Messaging.Write(e.Error);
472 } 310 }
473 } 311 }
474 } 312 }
@@ -500,7 +338,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
500 338
501 // override directly authored value 339 // override directly authored value
502 var lookup = String.Concat(file.File.Component_, "/", name); 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))
504 { 342 {
505 assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers); 343 assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers);
506 assemblyNameRow.Component_ = file.File.Component_; 344 assemblyNameRow.Component_ = file.File.Component_;
diff --git a/src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs b/src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs
deleted file mode 100644
index 4157f23a..00000000
--- a/src/WixToolset.Core.WindowsInstaller/CLR/Interop/CLRInterop.cs
+++ /dev/null
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj b/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
index a08fde61..c251b8dd 100644
--- a/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
+++ b/src/WixToolset.Core.WindowsInstaller/WixToolset.Core.WindowsInstaller.csproj
@@ -24,6 +24,10 @@
24 </ItemGroup> 24 </ItemGroup>
25 25
26 <ItemGroup> 26 <ItemGroup>
27 <PackageReference Include="System.Reflection.Metadata" Version="1.6.0" />
28 </ItemGroup>
29
30 <ItemGroup>
27 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63102-01" PrivateAssets="All" /> 31 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63102-01" PrivateAssets="All" />
28 <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="all" /> 32 <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="all" />
29 </ItemGroup> 33 </ItemGroup>
diff --git a/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs b/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
index 0efe5635..76f57676 100644
--- a/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
+++ b/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
@@ -409,7 +409,7 @@ namespace WixToolsetTest.CoreIntegration
409 } 409 }
410 } 410 }
411 411
412 [Fact(Skip = "Assembly information not getting gathered yet.")] 412 [Fact]
413 public void CanBuildWithAssembly() 413 public void CanBuildWithAssembly()
414 { 414 {
415 var folder = TestData.Get(@"TestData\Assembly"); 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"))); 436 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
437 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\AssemblyMsiPackage\candle.exe"))); 437 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\AssemblyMsiPackage\candle.exe")));
438 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(); 440 var section = intermediate.Sections.Single();
441 441
442 var wixFile = section.Tuples.OfType<WixFileTuple>().Single(); 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); 444 Assert.Equal(@"candle.exe", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path);
445 445
446 var msiAssemblyNameTuples = section.Tuples.OfType<MsiAssemblyNameTuple>(); 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());
448 } 466 }
449 } 467 }
450 468
diff --git a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
index 9cea0f4c..2a20fcff 100644
--- a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+++ b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
@@ -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. --> 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 3
4<Project Sdk="Microsoft.NET.Sdk"> 4<Project Sdk="Microsoft.NET.Sdk">