IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
Fusion.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2010-2013 Jeroen Frijters
3 Copyright (C) 2011 Marek Safar
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20
21 Jeroen Frijters
22 jeroen@frijters.net
23
24*/
25
26using System;
27using System.Runtime.InteropServices;
28using System.Text;
29
30namespace IKVM.Reflection
31{
32
33 static class Fusion
34 {
35
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);
41
42 const string PublicKeyTokenEcma = "b77a5c561934e089";
43 const string PublicKeyTokenMicrosoft = "b03f5f7f11d50a3a";
44 const string PublicKeyTokenSilverlight = "7cec85d7bea7798e";
45 const string PublicKeyTokenWinFX = "31bf3856ad364e35";
46
57 internal static bool CompareAssemblyIdentityNative(string coreLibName, string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
58 {
59 Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out var equivalent, out result));
60 return equivalent;
61 }
62
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);
65
78 internal static bool CompareAssemblyIdentityPure(string coreLibName, string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
79 {
80 var r1 = ParseAssemblyName(assemblyIdentity1, out var name1);
81 var r2 = ParseAssemblyName(assemblyIdentity2, out var name2);
82
83 Version version1;
84 if (unified1)
85 {
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)
87 {
88 result = AssemblyComparisonResult.NonEquivalent;
89 throw new ArgumentException();
90 }
91 }
92
93 if (!ParseVersion(name2.Version, out var version2) || version2 == null || version2.Revision == -1 || name2.Culture == null || name2.PublicKeyToken == null || name2.PublicKeyToken.Length < 2)
94 {
95 result = AssemblyComparisonResult.NonEquivalent;
96 throw new ArgumentException();
97 }
98
99 if (name2.Name != null && name2.Name.Equals(coreLibName, StringComparison.OrdinalIgnoreCase))
100 {
101 if (name1.Name != null && name1.Name.Equals(name2.Name, StringComparison.OrdinalIgnoreCase))
102 {
103 result = AssemblyComparisonResult.EquivalentFullMatch;
104 return true;
105 }
106 else
107 {
108 result = AssemblyComparisonResult.NonEquivalent;
109 return false;
110 }
111 }
112
113 if (r1 != ParseAssemblyResult.OK)
114 {
115 result = AssemblyComparisonResult.NonEquivalent;
116 throw r1 switch
117 {
118 ParseAssemblyResult.DuplicateKey => new System.IO.FileLoadException(),
119 _ => new ArgumentException(),
120 };
121 }
122
123 if (r2 != ParseAssemblyResult.OK)
124 {
125 result = AssemblyComparisonResult.NonEquivalent;
126 throw r2 switch
127 {
128 ParseAssemblyResult.DuplicateKey => new System.IO.FileLoadException(),
129 _ => new ArgumentException(),
130 };
131 }
132
133 if (!ParseVersion(name1.Version, out version1))
134 {
135 result = AssemblyComparisonResult.NonEquivalent;
136 throw new ArgumentException();
137 }
138
139 bool partial = IsPartial(name1, version1);
140
141 if (partial && name1.Retargetable.HasValue)
142 {
143 result = AssemblyComparisonResult.NonEquivalent;
144 throw new System.IO.FileLoadException();
145 }
146 if ((partial && unified1) || IsPartial(name2, version2))
147 {
148 result = AssemblyComparisonResult.NonEquivalent;
149 throw new ArgumentException();
150 }
151 if (!name1.Name.Equals(name2.Name, StringComparison.OrdinalIgnoreCase))
152 {
153 result = AssemblyComparisonResult.NonEquivalent;
154 return false;
155 }
156 if (partial && name1.Culture == null)
157 {
158 }
159 else if (!name1.Culture.Equals(name2.Culture, StringComparison.OrdinalIgnoreCase))
160 {
161 result = AssemblyComparisonResult.NonEquivalent;
162 return false;
163 }
164
165 if (!name1.Retargetable.GetValueOrDefault() && name2.Retargetable.GetValueOrDefault())
166 {
167 result = AssemblyComparisonResult.NonEquivalent;
168 return false;
169 }
170
171 // HACK handle the case "System.Net, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes"
172 // compared with "System.Net, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=No"
173 if (name1.PublicKeyToken == name2.PublicKeyToken
174 && version1 != null
175 && name1.Retargetable.GetValueOrDefault()
176 && !name2.Retargetable.GetValueOrDefault()
177 && GetRemappedPublicKeyToken(ref name1, version1) != null)
178 {
179 name1.Retargetable = false;
180 }
181
182 string remappedPublicKeyToken1 = null;
183 string remappedPublicKeyToken2 = null;
184
185 if (version1 != null && (remappedPublicKeyToken1 = GetRemappedPublicKeyToken(ref name1, version1)) != null)
186 {
187 name1.PublicKeyToken = remappedPublicKeyToken1;
188 version1 = FrameworkVersion;
189 }
190
191 if ((remappedPublicKeyToken2 = GetRemappedPublicKeyToken(ref name2, version2)) != null)
192 {
193 name2.PublicKeyToken = remappedPublicKeyToken2;
194 version2 = FrameworkVersion;
195 }
196
197 if (name1.Retargetable.GetValueOrDefault())
198 {
199 if (name2.Retargetable.GetValueOrDefault())
200 {
201 if (remappedPublicKeyToken1 != null ^ remappedPublicKeyToken2 != null)
202 {
203 result = AssemblyComparisonResult.NonEquivalent;
204 return false;
205 }
206 }
207 else if (remappedPublicKeyToken1 == null || remappedPublicKeyToken2 != null)
208 {
209 result = AssemblyComparisonResult.Unknown;
210 return false;
211 }
212 }
213
214 var fxUnified = false;
215
216 if (version1 == null)
217 {
218 result = AssemblyComparisonResult.Unknown;
219 return false;
220 }
221
222 if (version2 == null)
223 {
224 result = AssemblyComparisonResult.Unknown;
225 return false;
226 }
227
228 // build and revision numbers are ignored
229 var fxVersionMatch = version1.Major == version2.Major && version1.Minor == version2.Minor;
230 if (IsFrameworkAssembly(name1))
231 {
232 fxUnified |= !fxVersionMatch;
233 version1 = FrameworkVersion;
234 }
235 if (IsFrameworkAssembly(name2) && version2 < FrameworkVersionNext)
236 {
237 fxUnified |= !fxVersionMatch;
238 version2 = FrameworkVersion;
239 }
240
241 if (IsStrongNamed(name2))
242 {
243 if (name1.PublicKeyToken != null && name1.PublicKeyToken != name2.PublicKeyToken)
244 {
245 result = AssemblyComparisonResult.NonEquivalent;
246 return false;
247 }
248 else if (version1 == null)
249 {
250 result = AssemblyComparisonResult.EquivalentPartialMatch;
251 return true;
252 }
253 else if (version1.Revision == -1 || version2.Revision == -1)
254 {
255 result = AssemblyComparisonResult.NonEquivalent;
256 throw new ArgumentException();
257 }
258 else if (version1 < version2)
259 {
260 if (unified2)
261 {
262 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
263 return true;
264 }
265 else
266 {
267 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
268 return false;
269 }
270 }
271 else if (version1 > version2)
272 {
273 if (unified1)
274 {
275 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
276 return true;
277 }
278 else
279 {
280 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
281 return false;
282 }
283 }
284 else if (fxUnified || version1 != version2)
285 {
286 result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
287 return true;
288 }
289 else
290 {
291 result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
292 return true;
293 }
294 }
295 else if (IsStrongNamed(name1))
296 {
297 result = AssemblyComparisonResult.NonEquivalent;
298 return false;
299 }
300 else
301 {
302 result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
303 return true;
304 }
305 }
306
307 static bool IsFrameworkAssembly(ParsedAssemblyName name)
308 {
309 // Framework assemblies use different unification rules, so when
310 // a new framework is released the new assemblies need to be added.
311 switch (name.Name)
312 {
313 case "System":
314 case "System.Core":
315 case "System.Data":
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":
333 case "System.Xml":
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":
357 case "System.IO":
358 case "System.Linq":
359 case "System.Linq.Expressions":
360 case "System.Linq.Parallel":
361 case "System.Linq.Queryable":
362 case "System.Management":
363 case "System.Messaging":
364 case "System.Net":
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":
398 case "System.Web":
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;
414 }
415
416 return false;
417 }
418
419 static string GetRemappedPublicKeyToken(ref ParsedAssemblyName name, Version version)
420 {
421 if (name.Retargetable.GetValueOrDefault() && version < SilverlightVersion)
422 return null;
423
424 if (name.PublicKeyToken == "ddd0da4d3e678217" && name.Name == "System.ComponentModel.DataAnnotations" && name.Retargetable.GetValueOrDefault())
425 return PublicKeyTokenWinFX;
426
427 if (SilverlightVersionMinimum <= version && version <= SilverlightVersionMaximum)
428 {
429 switch (name.PublicKeyToken)
430 {
431 case PublicKeyTokenSilverlight:
432 switch (name.Name)
433 {
434 case "System":
435 case "System.Core":
436 return PublicKeyTokenEcma;
437 }
438 if (name.Retargetable.GetValueOrDefault())
439 {
440 switch (name.Name)
441 {
442 case "System.Runtime.Serialization":
443 case "System.Xml":
444 return PublicKeyTokenEcma;
445 case "System.Net":
446 case "System.Windows":
447 return PublicKeyTokenMicrosoft;
448 case "System.ServiceModel.Web":
449 return PublicKeyTokenWinFX;
450 }
451 }
452 break;
453 case PublicKeyTokenWinFX:
454 switch (name.Name)
455 {
456 case "System.ComponentModel.Composition":
457 return PublicKeyTokenEcma;
458 }
459 if (name.Retargetable.GetValueOrDefault())
460 {
461 switch (name.Name)
462 {
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;
470 }
471 }
472 break;
473 }
474 }
475
476 return null;
477 }
478
479 internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out int pos, out string simpleName)
480 {
481 pos = 0;
482 if (!TryParse(fullName, ref pos, out simpleName) || simpleName.Length == 0)
483 return ParseAssemblyResult.GenericError;
484
485 if (pos == fullName.Length && fullName[fullName.Length - 1] == ',')
486 return ParseAssemblyResult.GenericError;
487
488 return ParseAssemblyResult.OK;
489 }
490
491 static bool TryParse(string fullName, ref int pos, out string value)
492 {
493 value = null;
494
495 var sb = new StringBuilder();
496 while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos]))
497 pos++;
498
499 int quote = -1;
500 if (pos < fullName.Length && (fullName[pos] == '"' || fullName[pos] == '\''))
501 quote = fullName[pos++];
502
503 for (; pos < fullName.Length; pos++)
504 {
505 var ch = fullName[pos];
506 if (ch == '\\')
507 {
508 if (++pos == fullName.Length)
509 return false;
510
511 ch = fullName[pos];
512 if (ch == '\\')
513 return false;
514 }
515 else if (ch == quote)
516 {
517 for (pos++; pos != fullName.Length; pos++)
518 {
519 ch = fullName[pos];
520 if (ch == ',' || ch == '=')
521 break;
522 if (!char.IsWhiteSpace(ch))
523 return false;
524 }
525 break;
526 }
527 else if (quote == -1 && (ch == '"' || ch == '\''))
528 {
529 return false;
530 }
531 else if (quote == -1 && (ch == ',' || ch == '='))
532 {
533 break;
534 }
535
536 sb.Append(ch);
537 }
538
539 value = sb.ToString().Trim();
540 return value.Length != 0 || quote != -1;
541 }
542
543 static bool TryConsume(string fullName, char ch, ref int pos)
544 {
545 if (pos < fullName.Length && fullName[pos] == ch)
546 {
547 pos++;
548 return true;
549 }
550
551 return false;
552 }
553
554 static bool TryParseAssemblyAttribute(string fullName, ref int pos, ref string key, ref string value)
555 {
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);
560 }
561
562 internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
563 {
564 const int ERROR_SXS_IDENTITIES_DIFFERENT = unchecked((int)0x80073716);
565
566 parsedName = new ParsedAssemblyName();
567 var res = ParseAssemblySimpleName(fullName, out var pos, out parsedName.Name);
568 if (res != ParseAssemblyResult.OK)
569 return res;
570
571 System.Collections.Generic.Dictionary<string, string> unknownAttributes = null;
572 bool hasProcessorArchitecture = false;
573 bool hasContentType = false;
574 bool hasPublicKeyToken = false;
575 string publicKeyToken;
576
577 while (pos != fullName.Length)
578 {
579 string key = null;
580 string value = null;
581 if (!TryParseAssemblyAttribute(fullName, ref pos, ref key, ref value))
582 return ParseAssemblyResult.GenericError;
583
584 key = key.ToLowerInvariant();
585 switch (key)
586 {
587 case "version":
588 if (parsedName.Version != null)
589 return ParseAssemblyResult.DuplicateKey;
590
591 parsedName.Version = value;
592 break;
593 case "culture":
594 if (parsedName.Culture != null)
595 return ParseAssemblyResult.DuplicateKey;
596 if (!ParseCulture(value, out parsedName.Culture))
597 return ParseAssemblyResult.GenericError;
598
599 break;
600 case "publickeytoken":
601 if (hasPublicKeyToken)
602 return ParseAssemblyResult.DuplicateKey;
603 if (!ParsePublicKeyToken(value, out publicKeyToken))
604 return ParseAssemblyResult.GenericError;
605 if (parsedName.HasPublicKey && parsedName.PublicKeyToken != publicKeyToken)
606 Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT);
607
608 parsedName.PublicKeyToken = publicKeyToken;
609 hasPublicKeyToken = true;
610 break;
611 case "publickey":
612 if (parsedName.HasPublicKey)
613 return ParseAssemblyResult.DuplicateKey;
614 if (!ParsePublicKey(value, out publicKeyToken))
615 return ParseAssemblyResult.GenericError;
616 if (hasPublicKeyToken && parsedName.PublicKeyToken != publicKeyToken)
617 Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT);
618
619 parsedName.PublicKeyToken = publicKeyToken;
620 parsedName.HasPublicKey = true;
621 break;
622 case "retargetable":
623 if (parsedName.Retargetable.HasValue)
624 return ParseAssemblyResult.DuplicateKey;
625
626 switch (value.ToLowerInvariant())
627 {
628 case "yes":
629 parsedName.Retargetable = true;
630 break;
631 case "no":
632 parsedName.Retargetable = false;
633 break;
634 default:
635 return ParseAssemblyResult.GenericError;
636 }
637
638 break;
639 case "processorarchitecture":
640 if (hasProcessorArchitecture)
641 return ParseAssemblyResult.DuplicateKey;
642
643 hasProcessorArchitecture = true;
644 switch (value.ToLowerInvariant())
645 {
646 case "none":
647 parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
648 break;
649 case "msil":
650 parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
651 break;
652 case "x86":
653 parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
654 break;
655 case "ia64":
656 parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
657 break;
658 case "amd64":
659 parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
660 break;
661 case "arm":
662 parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
663 break;
664 default:
665 return ParseAssemblyResult.GenericError;
666 }
667
668 break;
669 case "contenttype":
670 if (hasContentType)
671 return ParseAssemblyResult.DuplicateKey;
672
673 hasContentType = true;
674 if (!value.Equals("windowsruntime", StringComparison.OrdinalIgnoreCase))
675 return ParseAssemblyResult.GenericError;
676
677 parsedName.WindowsRuntime = true;
678 break;
679 default:
680 if (key.Length == 0)
681 return ParseAssemblyResult.GenericError;
682
683 unknownAttributes ??= new System.Collections.Generic.Dictionary<string, string>();
684 if (unknownAttributes.ContainsKey(key))
685 return ParseAssemblyResult.DuplicateKey;
686
687 unknownAttributes.Add(key, null);
688 break;
689 }
690 }
691
692 return ParseAssemblyResult.OK;
693 }
694
695 static bool ParseVersion(string str, out Version version)
696 {
697 if (str == null)
698 {
699 version = null;
700 return true;
701 }
702
703 var parts = str.Split('.');
704 if (parts.Length < 2 || parts.Length > 4)
705 {
706 version = null;
707
708 // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name
709 return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out _);
710 }
711
712 if (parts[0] == "" || parts[1] == "")
713 {
714 // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name
715 version = null;
716 return true;
717 }
718
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))))
724 {
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);
729 else
730 version = new Version(major, minor);
731
732 return true;
733 }
734
735 version = null;
736 return false;
737 }
738
739 static bool ParseCulture(string str, out string culture)
740 {
741 if (str == null)
742 {
743 culture = null;
744 return false;
745 }
746
747 culture = str;
748 return true;
749 }
750
751 static bool ParsePublicKeyToken(string str, out string publicKeyToken)
752 {
753 if (str == null)
754 {
755 publicKeyToken = null;
756 return false;
757 }
758
759 publicKeyToken = str.ToLowerInvariant();
760 return true;
761 }
762
763 static bool ParsePublicKey(string str, out string publicKeyToken)
764 {
765 if (str == null)
766 {
767 publicKeyToken = null;
768 return false;
769 }
770
771 publicKeyToken = AssemblyName.ComputePublicKeyToken(str);
772 return true;
773 }
774
775 static bool IsPartial(ParsedAssemblyName name, Version version)
776 {
777 return version == null || name.Culture == null || name.PublicKeyToken == null;
778 }
779
780 static bool IsStrongNamed(ParsedAssemblyName name)
781 {
782 return name.PublicKeyToken != null && name.PublicKeyToken != "null";
783 }
784
785 }
786
787}