diff options
author | Rob Mensching <rob@firegiant.com> | 2018-12-26 12:40:58 -0800 |
---|---|---|
committer | Rob Mensching <rob@robmensching.com> | 2018-12-26 14:03:10 -0800 |
commit | 232176210981a8e21793f6096dd651eba29caf8e (patch) | |
tree | 5880b1b9c968f494fbab3f78c07cba95ed309c40 | |
parent | 758bcf4bb6d61dd9d6471d9f9629d4f7b18d43cb (diff) | |
download | wix-232176210981a8e21793f6096dd651eba29caf8e.tar.gz wix-232176210981a8e21793f6096dd651eba29caf8e.tar.bz2 wix-232176210981a8e21793f6096dd651eba29caf8e.zip |
Populate MsiAssemblyName table
Fixes wixtoolset/issues#5865
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 | |||
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 | } | ||
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 | |||
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 | } | ||
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 | |||
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 | } | ||
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"> |