36 static readonly Version FrameworkVersion =
new Version(4, 0, 0, 0);
37 static readonly Version FrameworkVersionNext =
new Version(5, 0, 0, 0);
38 static readonly Version SilverlightVersion =
new Version(2, 0, 5, 0);
39 static readonly Version SilverlightVersionMinimum =
new Version(2, 0, 0, 0);
40 static readonly Version SilverlightVersionMaximum =
new Version(5, 9, 0, 0);
42 const string PublicKeyTokenEcma =
"b77a5c561934e089";
43 const string PublicKeyTokenMicrosoft =
"b03f5f7f11d50a3a";
44 const string PublicKeyTokenSilverlight =
"7cec85d7bea7798e";
45 const string PublicKeyTokenWinFX =
"31bf3856ad364e35";
57 internal static bool CompareAssemblyIdentityNative(
string coreLibName,
string assemblyIdentity1,
bool unified1,
string assemblyIdentity2,
bool unified2, out AssemblyComparisonResult result)
59 Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out var equivalent, out result));
63 [DllImport(
"fusion", CharSet = CharSet.Unicode)]
64 static extern int CompareAssemblyIdentity(
string pwzAssemblyIdentity1,
bool fUnified1,
string pwzAssemblyIdentity2,
bool fUnified2, out
bool pfEquivalent, out AssemblyComparisonResult pResult);
78 internal static bool CompareAssemblyIdentityPure(
string coreLibName,
string assemblyIdentity1,
bool unified1,
string assemblyIdentity2,
bool unified2, out AssemblyComparisonResult result)
80 var r1 = ParseAssemblyName(assemblyIdentity1, out var name1);
81 var r2 = ParseAssemblyName(assemblyIdentity2, out var name2);
86 if (name1.Name ==
null || !ParseVersion(name1.Version, out version1) || version1 ==
null || version1.Revision == -1 || name1.Culture ==
null || name1.PublicKeyToken ==
null || name1.PublicKeyToken.Length < 2)
88 result = AssemblyComparisonResult.NonEquivalent;
89 throw new ArgumentException();
93 if (!ParseVersion(name2.Version, out var version2) || version2 ==
null || version2.Revision == -1 || name2.Culture ==
null || name2.PublicKeyToken ==
null || name2.PublicKeyToken.Length < 2)
95 result = AssemblyComparisonResult.NonEquivalent;
96 throw new ArgumentException();
99 if (name2.Name !=
null && name2.Name.Equals(coreLibName, StringComparison.OrdinalIgnoreCase))
101 if (name1.Name !=
null && name1.Name.Equals(name2.Name, StringComparison.OrdinalIgnoreCase))
103 result = AssemblyComparisonResult.EquivalentFullMatch;
108 result = AssemblyComparisonResult.NonEquivalent;
115 result = AssemblyComparisonResult.NonEquivalent;
118 ParseAssemblyResult.DuplicateKey =>
new System.IO.FileLoadException(),
119 _ =>
new ArgumentException(),
125 result = AssemblyComparisonResult.NonEquivalent;
128 ParseAssemblyResult.DuplicateKey =>
new System.IO.FileLoadException(),
129 _ =>
new ArgumentException(),
133 if (!ParseVersion(name1.Version, out version1))
135 result = AssemblyComparisonResult.NonEquivalent;
136 throw new ArgumentException();
139 bool partial = IsPartial(name1, version1);
141 if (partial && name1.Retargetable.HasValue)
143 result = AssemblyComparisonResult.NonEquivalent;
144 throw new System.IO.FileLoadException();
146 if ((partial && unified1) || IsPartial(name2, version2))
148 result = AssemblyComparisonResult.NonEquivalent;
149 throw new ArgumentException();
151 if (!name1.Name.Equals(name2.Name, StringComparison.OrdinalIgnoreCase))
153 result = AssemblyComparisonResult.NonEquivalent;
156 if (partial && name1.Culture ==
null)
159 else if (!name1.Culture.Equals(name2.Culture, StringComparison.OrdinalIgnoreCase))
161 result = AssemblyComparisonResult.NonEquivalent;
165 if (!name1.Retargetable.GetValueOrDefault() && name2.Retargetable.GetValueOrDefault())
167 result = AssemblyComparisonResult.NonEquivalent;
173 if (name1.PublicKeyToken == name2.PublicKeyToken
175 && name1.Retargetable.GetValueOrDefault()
176 && !name2.Retargetable.GetValueOrDefault()
177 && GetRemappedPublicKeyToken(
ref name1, version1) !=
null)
179 name1.Retargetable =
false;
182 string remappedPublicKeyToken1 =
null;
183 string remappedPublicKeyToken2 =
null;
185 if (version1 !=
null && (remappedPublicKeyToken1 = GetRemappedPublicKeyToken(
ref name1, version1)) !=
null)
187 name1.PublicKeyToken = remappedPublicKeyToken1;
188 version1 = FrameworkVersion;
191 if ((remappedPublicKeyToken2 = GetRemappedPublicKeyToken(
ref name2, version2)) !=
null)
193 name2.PublicKeyToken = remappedPublicKeyToken2;
194 version2 = FrameworkVersion;
197 if (name1.Retargetable.GetValueOrDefault())
199 if (name2.Retargetable.GetValueOrDefault())
201 if (remappedPublicKeyToken1 !=
null ^ remappedPublicKeyToken2 !=
null)
203 result = AssemblyComparisonResult.NonEquivalent;
207 else if (remappedPublicKeyToken1 ==
null || remappedPublicKeyToken2 !=
null)
209 result = AssemblyComparisonResult.Unknown;
214 var fxUnified =
false;
216 if (version1 ==
null)
218 result = AssemblyComparisonResult.Unknown;
222 if (version2 ==
null)
224 result = AssemblyComparisonResult.Unknown;
229 var fxVersionMatch = version1.Major == version2.Major && version1.Minor == version2.Minor;
230 if (IsFrameworkAssembly(name1))
232 fxUnified |= !fxVersionMatch;
233 version1 = FrameworkVersion;
235 if (IsFrameworkAssembly(name2) && version2 < FrameworkVersionNext)
237 fxUnified |= !fxVersionMatch;
238 version2 = FrameworkVersion;
241 if (IsStrongNamed(name2))
243 if (name1.PublicKeyToken !=
null && name1.PublicKeyToken != name2.PublicKeyToken)
245 result = AssemblyComparisonResult.NonEquivalent;
248 else if (version1 ==
null)
250 result = AssemblyComparisonResult.EquivalentPartialMatch;
253 else if (version1.Revision == -1 || version2.Revision == -1)
255 result = AssemblyComparisonResult.NonEquivalent;
256 throw new ArgumentException();
258 else if (version1 < version2)
262 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
267 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
271 else if (version1 > version2)
275 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
280 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
284 else if (fxUnified || version1 != version2)
286 result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
291 result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
295 else if (IsStrongNamed(name1))
297 result = AssemblyComparisonResult.NonEquivalent;
302 result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
316 case "System.Data.DataSetExtensions":
317 case "System.Data.Linq":
318 case "System.Data.OracleClient":
319 case "System.Data.Services":
320 case "System.Data.Services.Client":
321 case "System.IdentityModel":
322 case "System.IdentityModel.Selectors":
323 case "System.IO.Compression":
324 case "System.Numerics":
325 case "System.Reflection.Context":
326 case "System.Runtime.Remoting":
327 case "System.Runtime.Serialization":
328 case "System.Runtime.WindowsRuntime":
329 case "System.Runtime.WindowsRuntime.UI.Xaml":
330 case "System.ServiceModel":
331 case "System.Transactions":
332 case "System.Windows.Forms":
334 case "System.Xml.Linq":
335 case "System.Xml.Serialization":
336 return name.PublicKeyToken == PublicKeyTokenEcma;
337 case "Microsoft.CSharp":
338 case "Microsoft.VisualBasic":
339 case "System.Collections":
340 case "System.Collections.Concurrent":
341 case "System.ComponentModel":
342 case "System.ComponentModel.Annotations":
343 case "System.ComponentModel.EventBasedAsync":
344 case "System.Configuration":
345 case "System.Configuration.Install":
346 case "System.Design":
347 case "System.Diagnostics.Contracts":
348 case "System.Diagnostics.Debug":
349 case "System.Diagnostics.Tools":
350 case "System.Diagnostics.Tracing":
351 case "System.DirectoryServices":
352 case "System.Drawing":
353 case "System.Drawing.Design":
354 case "System.Dynamic.Runtime":
355 case "System.EnterpriseServices":
356 case "System.Globalization":
359 case "System.Linq.Expressions":
360 case "System.Linq.Parallel":
361 case "System.Linq.Queryable":
362 case "System.Management":
363 case "System.Messaging":
365 case "System.Net.Http":
366 case "System.Net.Http.Rtc":
367 case "System.Net.NetworkInformation":
368 case "System.Net.Primitives":
369 case "System.Net.Requests":
370 case "System.ObjectModel":
371 case "System.Reflection":
372 case "System.Reflection.Extensions":
373 case "System.Reflection.Primitives":
374 case "System.Resources.ResourceManager":
375 case "System.Runtime":
376 case "System.Runtime.Extensions":
377 case "System.Runtime.InteropServices":
378 case "System.Runtime.InteropServices.WindowsRuntime":
379 case "System.Runtime.Numerics":
380 case "System.Runtime.Serialization.Formatters.Soap":
381 case "System.Runtime.Serialization.Json":
382 case "System.Runtime.Serialization.Primitives":
383 case "System.Runtime.Serialization.Xml":
384 case "System.Security":
385 case "System.Security.Principal":
386 case "System.ServiceModel.Duplex":
387 case "System.ServiceModel.Http":
388 case "System.ServiceModel.NetTcp":
389 case "System.ServiceModel.Primitives":
390 case "System.ServiceModel.Security":
391 case "System.ServiceProcess":
392 case "System.Text.Encoding":
393 case "System.Text.Encoding.Extensions":
394 case "System.Text.RegularExpressions":
395 case "System.Threading":
396 case "System.Threading.Tasks":
397 case "System.Threading.Tasks.Parallel":
399 case "System.Web.Mobile":
400 case "System.Web.Services":
401 case "System.Windows":
402 case "System.Xml.ReaderWriter":
403 case "System.Xml.XDocument":
404 case "System.Xml.XmlSerializer":
405 return name.PublicKeyToken == PublicKeyTokenMicrosoft;
406 case "System.ComponentModel.DataAnnotations":
407 case "System.ServiceModel.Web":
408 case "System.Web.Abstractions":
409 case "System.Web.Extensions":
410 case "System.Web.Extensions.Design":
411 case "System.Web.DynamicData":
412 case "System.Web.Routing":
413 return name.PublicKeyToken == PublicKeyTokenWinFX;
421 if (name.Retargetable.GetValueOrDefault() && version < SilverlightVersion)
424 if (name.PublicKeyToken ==
"ddd0da4d3e678217" && name.Name ==
"System.ComponentModel.DataAnnotations" && name.Retargetable.GetValueOrDefault())
425 return PublicKeyTokenWinFX;
427 if (SilverlightVersionMinimum <= version && version <= SilverlightVersionMaximum)
429 switch (name.PublicKeyToken)
431 case PublicKeyTokenSilverlight:
436 return PublicKeyTokenEcma;
438 if (name.Retargetable.GetValueOrDefault())
442 case "System.Runtime.Serialization":
444 return PublicKeyTokenEcma;
446 case "System.Windows":
447 return PublicKeyTokenMicrosoft;
448 case "System.ServiceModel.Web":
449 return PublicKeyTokenWinFX;
453 case PublicKeyTokenWinFX:
456 case "System.ComponentModel.Composition":
457 return PublicKeyTokenEcma;
459 if (name.Retargetable.GetValueOrDefault())
463 case "Microsoft.CSharp":
464 return PublicKeyTokenMicrosoft;
465 case "System.Numerics":
466 case "System.ServiceModel":
467 case "System.Xml.Serialization":
468 case "System.Xml.Linq":
469 return PublicKeyTokenEcma;
479 internal static ParseAssemblyResult ParseAssemblySimpleName(
string fullName, out
int pos, out
string simpleName)
482 if (!TryParse(fullName,
ref pos, out simpleName) || simpleName.Length == 0)
485 if (pos == fullName.Length && fullName[fullName.Length - 1] ==
',')
491 static bool TryParse(
string fullName,
ref int pos, out
string value)
495 var sb =
new StringBuilder();
496 while (pos < fullName.Length &&
char.IsWhiteSpace(fullName[pos]))
500 if (pos < fullName.Length && (fullName[pos] ==
'"' || fullName[pos] ==
'\''))
501 quote = fullName[pos++];
503 for (; pos < fullName.Length; pos++)
505 var ch = fullName[pos];
508 if (++pos == fullName.Length)
515 else if (ch == quote)
517 for (pos++; pos != fullName.Length; pos++)
520 if (ch ==
',' || ch ==
'=')
522 if (!
char.IsWhiteSpace(ch))
527 else if (quote == -1 && (ch ==
'"' || ch ==
'\''))
531 else if (quote == -1 && (ch ==
',' || ch ==
'='))
539 value = sb.ToString().Trim();
540 return value.Length != 0 || quote != -1;
543 static bool TryConsume(
string fullName,
char ch,
ref int pos)
545 if (pos < fullName.Length && fullName[pos] == ch)
554 static bool TryParseAssemblyAttribute(
string fullName,
ref int pos,
ref string key,
ref string value)
556 return TryConsume(fullName,
',',
ref pos)
557 && TryParse(fullName,
ref pos, out key)
558 && TryConsume(fullName,
'=',
ref pos)
559 && TryParse(fullName,
ref pos, out value);
564 const int ERROR_SXS_IDENTITIES_DIFFERENT = unchecked((
int)0x80073716);
567 var res = ParseAssemblySimpleName(fullName, out var pos, out parsedName.Name);
571 System.Collections.Generic.Dictionary<string,
string> unknownAttributes =
null;
572 bool hasProcessorArchitecture =
false;
573 bool hasContentType =
false;
574 bool hasPublicKeyToken =
false;
575 string publicKeyToken;
577 while (pos != fullName.Length)
581 if (!TryParseAssemblyAttribute(fullName,
ref pos,
ref key,
ref value))
584 key = key.ToLowerInvariant();
588 if (parsedName.Version !=
null)
591 parsedName.Version = value;
594 if (parsedName.Culture !=
null)
596 if (!ParseCulture(value, out parsedName.Culture))
600 case "publickeytoken":
601 if (hasPublicKeyToken)
603 if (!ParsePublicKeyToken(value, out publicKeyToken))
605 if (parsedName.HasPublicKey && parsedName.PublicKeyToken != publicKeyToken)
606 Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT);
608 parsedName.PublicKeyToken = publicKeyToken;
609 hasPublicKeyToken =
true;
612 if (parsedName.HasPublicKey)
614 if (!ParsePublicKey(value, out publicKeyToken))
616 if (hasPublicKeyToken && parsedName.PublicKeyToken != publicKeyToken)
617 Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT);
619 parsedName.PublicKeyToken = publicKeyToken;
620 parsedName.HasPublicKey =
true;
623 if (parsedName.Retargetable.HasValue)
626 switch (value.ToLowerInvariant())
629 parsedName.Retargetable =
true;
632 parsedName.Retargetable =
false;
639 case "processorarchitecture":
640 if (hasProcessorArchitecture)
643 hasProcessorArchitecture =
true;
644 switch (value.ToLowerInvariant())
647 parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
650 parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
653 parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
656 parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
659 parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
662 parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
673 hasContentType =
true;
674 if (!value.Equals(
"windowsruntime", StringComparison.OrdinalIgnoreCase))
677 parsedName.WindowsRuntime =
true;
683 unknownAttributes ??=
new System.Collections.Generic.Dictionary<string,
string>();
684 if (unknownAttributes.ContainsKey(key))
687 unknownAttributes.Add(key,
null);
695 static bool ParseVersion(
string str, out Version version)
703 var parts = str.Split(
'.');
704 if (parts.Length < 2 || parts.Length > 4)
709 return parts.Length == 1 && ushort.TryParse(parts[0],
System.Globalization.NumberStyles.Integer,
null, out _);
712 if (parts[0] ==
"" || parts[1] ==
"")
719 ushort major, minor, build = 65535, revision = 65535;
720 if (ushort.TryParse(parts[0],
System.Globalization.NumberStyles.Integer,
null, out major)
721 && ushort.TryParse(parts[1],
System.Globalization.NumberStyles.Integer,
null, out minor)
722 && (parts.Length <= 2 || parts[2] ==
"" || ushort.TryParse(parts[2],
System.Globalization.NumberStyles.Integer,
null, out build))
723 && (parts.Length <= 3 || parts[3] ==
"" || (parts[2] !=
"" && ushort.TryParse(parts[3],
System.Globalization.NumberStyles.Integer,
null, out revision))))
725 if (parts.Length == 4 && parts[3] !=
"" && parts[2] !=
"")
726 version =
new Version(major, minor, build, revision);
727 else if (parts.Length == 3 && parts[2] !=
"")
728 version =
new Version(major, minor, build);
730 version =
new Version(major, minor);
739 static bool ParseCulture(
string str, out
string culture)
751 static bool ParsePublicKeyToken(
string str, out
string publicKeyToken)
755 publicKeyToken =
null;
759 publicKeyToken = str.ToLowerInvariant();
763 static bool ParsePublicKey(
string str, out
string publicKeyToken)
767 publicKeyToken =
null;
771 publicKeyToken = AssemblyName.ComputePublicKeyToken(str);
777 return version ==
null || name.Culture ==
null || name.PublicKeyToken ==
null;
782 return name.PublicKeyToken !=
null && name.PublicKeyToken !=
"null";