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">