3 #region Modification Notes
6 // Modified by Jose Luis Barreda G. (joseluiseco 'at' hotmail 'dot' com)
7 // Changed name 'AssemblyCache' for 'GacHelper'
8 // Added 'FindAssembly' method and removed anything not needed by this method.
9 // Based on code from http://www.codeproject.com/csharp/GacApi.asp
10 // Used to simulate 'Assembly.LoadWithPartialName' (obsolete since .NET 2.0 Beta)
14 #region Original notes
15 // Source: Microsoft KB Article KB317540
19 The native code application programming interfaces (APIs) that allow you to interact with the Global Assembly Cache (GAC) are not documented
20 in the .NET Framework Software Development Kit (SDK) documentation.
23 CAUTION: Do not use these APIs in your application to perform assembly binds or to test for the presence of assemblies or other run time,
24 development, or design-time operations. Only administrative tools and setup programs must use these APIs. If you use the GAC, this directly
25 exposes your application to assembly binding fragility or may cause your application to work improperly on future versions of the .NET
28 The GAC stores assemblies that are shared across all applications on a computer. The actual storage location and structure of the GAC is
29 not documented and is subject to change in future versions of the .NET Framework and the Microsoft Windows operating system.
31 The only supported method to access assemblies in the GAC is through the APIs that are documented in this article.
33 Most applications do not have to use these APIs because the assembly binding is performed automatically by the common language runtime.
34 Only custom setup programs or management tools must use these APIs. Microsoft Windows Installer has native support for installing assemblies
37 For more information about assemblies and the GAC, see the .NET Framework SDK.
39 Use the GAC API in the following scenarios:
40 When you install nativeName assembly to the GAC.
41 When you remove nativeName assembly from the GAC.
42 When you export nativeName assembly from the GAC.
43 When you enumerate assemblies that are available in the GAC.
44 NOTE: CoInitialize(Ex) must be called before you use any of the functions and interfaces that are described in this specification.
48 #region Using directives
51 using System
.Collections
.Generic
;
53 using System
.Reflection
;
54 using System
.Runtime
.InteropServices
;
55 using System
.Globalization
;
59 namespace AspectSharp
.Lang
62 /// Provides a method to find nativeName assembly in the GAC by it's simple name.
64 internal static class GacHelper
72 /// <param name="assemblyName"></param>
73 /// <returns></returns>
74 public static Assembly
FindAssembly(string assemblyName
)
76 IAssemblyEnum assemblyEnum
= GacHelper
.CreateGACEnum();
77 IAssemblyName nativeName
;
79 List
<AssemblyName
> matches
= new List
<AssemblyName
>();
81 while(GacHelper
.GetNextAssembly(assemblyEnum
, out nativeName
) == 0)
83 string nameString
= GacHelper
.GetName(nativeName
);
85 if(StringComparer
.InvariantCultureIgnoreCase
.Compare(nameString
, assemblyName
) == 0)
87 AssemblyName name
= GacHelper
.GetAssemblyName(nativeName
);
92 AssemblyName last
= null;
94 for(int i
= 0; i
< matches
.Count
; i
++)
96 AssemblyName current
= matches
[i
];
100 if(last
.Version
.Major
<= current
.Version
.Major
)
102 if(last
.Version
.Major
< current
.Version
.Major
)
108 if(last
.Version
.Minor
< current
.Version
.Minor
)
123 return Assembly
.Load(last
);
133 #region GetAssemblyName
135 private static AssemblyName
GetAssemblyName(IAssemblyName nameRef
)
137 AssemblyName name
= new AssemblyName();
138 name
.Name
= GacHelper
.GetName(nameRef
);
139 name
.Version
= GacHelper
.GetVersion(nameRef
);
140 name
.CultureInfo
= GacHelper
.GetCulture(nameRef
);
141 name
.SetPublicKeyToken(GacHelper
.GetPublicKeyToken(nameRef
));
150 private static string GetName(IAssemblyName name
)
152 uint bufferSize
= 255;
153 StringBuilder buffer
= new StringBuilder((int)bufferSize
);
154 name
.GetName(ref bufferSize
, buffer
);
156 return buffer
.ToString();
163 private static Version
GetVersion(IAssemblyName name
)
167 name
.GetVersion(out majorOut
, out minorOut
);
169 int major
= (int)majorOut
>> 16;
170 int minor
= (int)majorOut
& 0xFFFF;
171 int build
= (int)minorOut
>> 16;
172 int revision
= (int)minorOut
& 0xFFFF;
191 revision
= revision
* -1;
194 return new Version(major
, minor
, build
, revision
);
201 private static CultureInfo
GetCulture(IAssemblyName name
)
203 uint bufferSize
= 255;
204 IntPtr buffer
= Marshal
.AllocHGlobal((int)bufferSize
);
205 name
.GetProperty(ASM_NAME
.ASM_NAME_CULTURE
, buffer
, ref bufferSize
);
206 string result
= Marshal
.PtrToStringAuto(buffer
);
207 Marshal
.FreeHGlobal(buffer
);
209 return new CultureInfo(result
);
214 #region GetPublicKeyToken
216 private static byte[] GetPublicKeyToken(IAssemblyName name
)
218 byte[] result
= new byte[8];
220 IntPtr buffer
= Marshal
.AllocHGlobal((int)bufferSize
);
221 name
.GetProperty(ASM_NAME
.ASM_NAME_PUBLIC_KEY_TOKEN
, buffer
, ref bufferSize
);
222 for(int i
= 0; i
< 8; i
++)
223 result
[i
] = Marshal
.ReadByte(buffer
, i
);
224 Marshal
.FreeHGlobal(buffer
);
230 #region CreateGACEnum
232 private static IAssemblyEnum
CreateGACEnum()
236 GacHelper
.CreateAssemblyEnum(out ae
, (IntPtr
)0, null, ASM_CACHE_FLAGS
.ASM_CACHE_GAC
, (IntPtr
)0);
243 #region GetNextAssembly
246 /// Get the next assembly name in the current enumerator or fail
248 /// <param name="enumerator"></param>
249 /// <param name="name"></param>
250 /// <returns>0 if the enumeration is not at its end</returns>
251 private static int GetNextAssembly(IAssemblyEnum enumerator
, out IAssemblyName name
)
253 return enumerator
.GetNextAssembly((IntPtr
)0, out name
, 0);
258 #region External methods
261 /// To obtain nativeName instance of the CreateAssemblyEnum API, call the CreateAssemblyNameObject API.
263 /// <param name="pEnum">Pointer to a memory location that contains the IAssemblyEnum pointer.</param>
264 /// <param name="pUnkReserved">Must be null.</param>
265 /// <param name="pName">An assembly name that is used to filter the enumeration. Can be null to enumerate all assemblies in the GAC.</param>
266 /// <param name="dwFlags">Exactly one bit from the ASM_CACHE_FLAGS enumeration.</param>
267 /// <param name="pvReserved">Must be NULL.</param>
268 [DllImport("fusion.dll", SetLastError
= true, PreserveSig
= false)]
269 private static extern void CreateAssemblyEnum(
270 out IAssemblyEnum pEnum
,
273 ASM_CACHE_FLAGS dwFlags
,
282 #region ASM_DISPLAY_FLAGS
285 /// <see cref="IAssemblyName.GetDisplayName"/>
288 internal enum ASM_DISPLAY_FLAGS
292 PUBLIC_KEY_TOKEN
= 0x4,
295 PROCESSORARCHITECTURE
= 0x20,
301 #region ASM_CMP_FLAGS
304 internal enum ASM_CMP_FLAGS
310 REVISION_NUMBER
= 0x10,
311 PUBLIC_KEY_TOKEN
= 0x20,
314 ALL
= NAME
| MAJOR_VERSION
| MINOR_VERSION
|
315 REVISION_NUMBER
| BUILD_NUMBER
|
316 PUBLIC_KEY_TOKEN
| CULTURE
| CUSTOM
,
325 /// The ASM_NAME enumeration property ID describes the valid names of the name-value pairs in nativeName assembly name.
326 /// See the .NET Framework SDK for a description of these properties.
328 internal enum ASM_NAME
330 ASM_NAME_PUBLIC_KEY
= 0,
331 ASM_NAME_PUBLIC_KEY_TOKEN
,
334 ASM_NAME_MAJOR_VERSION
,
335 ASM_NAME_MINOR_VERSION
,
336 ASM_NAME_BUILD_NUMBER
,
337 ASM_NAME_REVISION_NUMBER
,
339 ASM_NAME_PROCESSOR_ID_ARRAY
,
340 ASM_NAME_OSINFO_ARRAY
,
343 ASM_NAME_CODEBASE_URL
,
344 ASM_NAME_CODEBASE_LASTMOD
,
345 ASM_NAME_NULL_PUBLIC_KEY
,
346 ASM_NAME_NULL_PUBLIC_KEY_TOKEN
,
348 ASM_NAME_NULL_CUSTOM
,
355 #region ASM_CACHE_FLAGS
358 /// The ASM_CACHE_FLAGS enumeration contains the following values:
359 /// ASM_CACHE_ZAP - Enumerates the cache of precompiled assemblies by using Ngen.exe.
360 /// ASM_CACHE_GAC - Enumerates the GAC.
361 /// ASM_CACHE_DOWNLOAD - Enumerates the assemblies that have been downloaded on-demand or that have been shadow-copied.
364 internal enum ASM_CACHE_FLAGS
368 ASM_CACHE_DOWNLOAD
= 0x4
375 #region IAssemblyName interface
378 /// The IAssemblyName interface represents nativeName assembly name. An assembly name includes a predetermined set of name-value pairs.
379 /// The assembly name is described in detail in the .NET Framework SDK.
381 [ComImport
, Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E"),
382 InterfaceType(ComInterfaceType
.InterfaceIsIUnknown
)]
383 internal interface IAssemblyName
386 /// The IAssemblyName::SetProperty method adds a name-value pair to the assembly name, or, if a name-value pair
387 /// with the same name already exists, modifies or deletes the value of a name-value pair.
389 /// <param name="PropertyId">The ID that represents the name part of the name-value pair that is to be
390 /// added or to be modified. Valid property IDs are defined in the ASM_NAME enumeration.</param>
391 /// <param name="pvProperty">A pointer to a buffer that contains the value of the property.</param>
392 /// <param name="cbProperty">The length of the pvProperty buffer in bytes. If cbProperty is zero, the name-value pair
393 /// is removed from the assembly name.</param>
394 /// <returns></returns>
402 /// The IAssemblyName::GetProperty method retrieves the value of a name-value pair in the assembly name that specifies the name.
404 /// <param name="PropertyId">The ID that represents the name of the name-value pair whose value is to be retrieved.
405 /// Specified property IDs are defined in the ASM_NAME enumeration.</param>
406 /// <param name="pvProperty">A pointer to a buffer that is to contain the value of the property.</param>
407 /// <param name="pcbProperty">The length of the pvProperty buffer, in bytes.</param>
408 /// <returns></returns>
413 ref uint pcbProperty
);
416 /// The IAssemblyName::Finalize method freezes nativeName assembly name. Additional calls to IAssemblyName::SetProperty are
417 /// unsuccessful after this method has been called.
419 /// <returns></returns>
424 /// The IAssemblyName::GetDisplayName method returns a string representation of the assembly name.
426 /// <param name="szDisplayName">A pointer to a buffer that is to contain the display name. The display name is returned in Unicode.</param>
427 /// <param name="pccDisplayName">The size of the buffer in characters (on input). The length of the returned display name (on return).</param>
428 /// <param name="dwDisplayFlags">One or more of the bits defined in the ASM_DISPLAY_FLAGS enumeration:
429 /// *_VERSION - Includes the version number as part of the display name.
430 /// *_CULTURE - Includes the culture.
431 /// *_PUBLIC_KEY_TOKEN - Includes the public key token.
432 /// *_PUBLIC_KEY - Includes the public key.
433 /// *_CUSTOM - Includes the custom part of the assembly name.
434 /// *_PROCESSORARCHITECTURE - Includes the processor architecture.
435 /// *_LANGUAGEID - Includes the language ID.</param>
436 /// <returns></returns>
437 /// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondefaultmarshalingforstrings.asp</remarks>
440 [Out
, MarshalAs(UnmanagedType
.LPWStr
)] StringBuilder szDisplayName
,
441 ref uint pccDisplayName
,
442 ASM_DISPLAY_FLAGS dwDisplayFlags
);
447 /// <param name="refIID"></param>
448 /// <param name="pUnkSink"></param>
449 /// <param name="pUnkContext"></param>
450 /// <param name="szCodeBase"></param>
451 /// <param name="llFlags"></param>
452 /// <param name="pvReserved"></param>
453 /// <param name="cbReserved"></param>
454 /// <param name="ppv"></param>
455 /// <returns></returns>
459 [MarshalAs(UnmanagedType
.IUnknown
)] object pUnkSink
,
460 [MarshalAs(UnmanagedType
.IUnknown
)] object pUnkContext
,
461 [MarshalAs(UnmanagedType
.LPWStr
)] string szCodeBase
,
468 /// The IAssemblyName::GetName method returns the name part of the assembly name.
470 /// <param name="lpcwBuffer">Size of the pwszName buffer (on input). Length of the name (on return).</param>
471 /// <param name="pwzName">Pointer to the buffer that is to contain the name part of the assembly name.</param>
472 /// <returns></returns>
473 /// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondefaultmarshalingforstrings.asp</remarks>
477 [Out
, MarshalAs(UnmanagedType
.LPWStr
)] StringBuilder pwzName
);
480 /// The IAssemblyName::GetVersion method returns the version part of the assembly name.
482 /// <param name="pdwVersionHi">Pointer to a DWORD that contains the upper 32 bits of the version number.</param>
483 /// <param name="pdwVersionLow">Pointer to a DWORD that contain the lower 32 bits of the version number.</param>
484 /// <returns></returns>
487 out uint pdwVersionHi
,
488 out uint pdwVersionLow
);
491 /// The IAssemblyName::IsEqual method compares the assembly name to another assembly names.
493 /// <param name="pName">The assembly name to compare to.</param>
494 /// <param name="dwCmpFlags">Indicates which part of the assembly name to use in the comparison.
495 /// Values are one or more of the bits defined in the ASM_CMP_FLAGS enumeration.</param>
496 /// <returns></returns>
500 ASM_CMP_FLAGS dwCmpFlags
);
503 /// The IAssemblyName::Clone method creates a copy of nativeName assembly name.
505 /// <param name="pName"></param>
506 /// <returns></returns>
509 out IAssemblyName pName
);
514 #region IAssemblyEnum interface
517 /// The IAssemblyEnum interface enumerates the assemblies in the GAC.
519 [ComImport
, Guid("21b8916c-f28e-11d2-a473-00c04f8ef448"),
520 InterfaceType(ComInterfaceType
.InterfaceIsIUnknown
)]
521 internal interface IAssemblyEnum
524 /// The IAssemblyEnum::GetNextAssembly method enumerates the assemblies in the GAC.
526 /// <param name="pvReserved">Must be null.</param>
527 /// <param name="ppName">Pointer to a memory location that is to receive the interface pointer to the assembly
528 /// name of the next assembly that is enumerated.</param>
529 /// <param name="dwFlags">Must be zero.</param>
530 /// <returns></returns>
534 out IAssemblyName ppName
,
538 /// Undocumented. Best guess: reset the enumeration to the first assembly.
540 /// <returns></returns>
545 /// Undocumented. Create a copy of the assembly enum that is independently enumerable.
547 /// <param name="ppEnum"></param>
548 /// <returns></returns>
551 out IAssemblyEnum ppEnum
);