2 #region Modification Notes
5 // Modified by Jose Luis Barreda G. (joseluiseco 'at' hotmail 'dot' com)
6 // Changed name 'AssemblyCache' for 'GacHelper'
7 // Added 'FindAssembly' method and removed anything not needed by this method.
8 // Based on code from http://www.codeproject.com/csharp/GacApi.asp
9 // Used to simulate 'Assembly.LoadWithPartialName' (obsolete since .NET 2.0 Beta)
13 #region Original notes
14 // Source: Microsoft KB Article KB317540
18 The native code application programming interfaces (APIs) that allow you to interact with the Global Assembly Cache (GAC) are not documented
19 in the .NET Framework Software Development Kit (SDK) documentation.
22 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,
23 development, or design-time operations. Only administrative tools and setup programs must use these APIs. If you use the GAC, this directly
24 exposes your application to assembly binding fragility or may cause your application to work improperly on future versions of the .NET
27 The GAC stores assemblies that are shared across all applications on a computer. The actual storage location and structure of the GAC is
28 not documented and is subject to change in future versions of the .NET Framework and the Microsoft Windows operating system.
30 The only supported method to access assemblies in the GAC is through the APIs that are documented in this article.
32 Most applications do not have to use these APIs because the assembly binding is performed automatically by the common language runtime.
33 Only custom setup programs or management tools must use these APIs. Microsoft Windows Installer has native support for installing assemblies
36 For more information about assemblies and the GAC, see the .NET Framework SDK.
38 Use the GAC API in the following scenarios:
39 When you install nativeName assembly to the GAC.
40 When you remove nativeName assembly from the GAC.
41 When you export nativeName assembly from the GAC.
42 When you enumerate assemblies that are available in the GAC.
43 NOTE: CoInitialize(Ex) must be called before you use any of the functions and interfaces that are described in this specification.
47 #region Using directives
50 using System
.Collections
.Generic
;
52 using System
.Reflection
;
53 using System
.Runtime
.InteropServices
;
54 using System
.Globalization
;
58 namespace AspectSharp
.Lang
61 /// Provides a method to find nativeName assembly in the GAC by it's simple name.
63 internal static class GacHelper
71 /// <param name="assemblyName"></param>
72 /// <returns></returns>
73 public static Assembly
FindAssembly(string assemblyName
)
75 IAssemblyEnum assemblyEnum
= GacHelper
.CreateGACEnum();
76 IAssemblyName nativeName
;
78 List
<AssemblyName
> matches
= new List
<AssemblyName
>();
80 while(GacHelper
.GetNextAssembly(assemblyEnum
, out nativeName
) == 0)
82 string nameString
= GacHelper
.GetName(nativeName
);
84 if(StringComparer
.InvariantCultureIgnoreCase
.Compare(nameString
, assemblyName
) == 0)
86 AssemblyName name
= GacHelper
.GetAssemblyName(nativeName
);
91 AssemblyName last
= null;
93 for(int i
= 0; i
< matches
.Count
; i
++)
95 AssemblyName current
= matches
[i
];
99 if(last
.Version
.Major
<= current
.Version
.Major
)
101 if(last
.Version
.Major
< current
.Version
.Major
)
107 if(last
.Version
.Minor
< current
.Version
.Minor
)
122 return Assembly
.Load(last
);
132 #region GetAssemblyName
134 private static AssemblyName
GetAssemblyName(IAssemblyName nameRef
)
136 AssemblyName name
= new AssemblyName();
137 name
.Name
= GacHelper
.GetName(nameRef
);
138 name
.Version
= GacHelper
.GetVersion(nameRef
);
139 name
.CultureInfo
= GacHelper
.GetCulture(nameRef
);
140 name
.SetPublicKeyToken(GacHelper
.GetPublicKeyToken(nameRef
));
149 private static string GetName(IAssemblyName name
)
151 uint bufferSize
= 255;
152 StringBuilder buffer
= new StringBuilder((int)bufferSize
);
153 name
.GetName(ref bufferSize
, buffer
);
155 return buffer
.ToString();
162 private static Version
GetVersion(IAssemblyName name
)
166 name
.GetVersion(out majorOut
, out minorOut
);
168 int major
= (int)majorOut
>> 16;
169 int minor
= (int)majorOut
& 0xFFFF;
170 int build
= (int)minorOut
>> 16;
171 int revision
= (int)minorOut
& 0xFFFF;
190 revision
= revision
* -1;
193 return new Version(major
, minor
, build
, revision
);
200 private static CultureInfo
GetCulture(IAssemblyName name
)
202 uint bufferSize
= 255;
203 IntPtr buffer
= Marshal
.AllocHGlobal((int)bufferSize
);
204 name
.GetProperty(ASM_NAME
.ASM_NAME_CULTURE
, buffer
, ref bufferSize
);
205 string result
= Marshal
.PtrToStringAuto(buffer
);
206 Marshal
.FreeHGlobal(buffer
);
208 return new CultureInfo(result
);
213 #region GetPublicKeyToken
215 private static byte[] GetPublicKeyToken(IAssemblyName name
)
217 byte[] result
= new byte[8];
219 IntPtr buffer
= Marshal
.AllocHGlobal((int)bufferSize
);
220 name
.GetProperty(ASM_NAME
.ASM_NAME_PUBLIC_KEY_TOKEN
, buffer
, ref bufferSize
);
221 for(int i
= 0; i
< 8; i
++)
222 result
[i
] = Marshal
.ReadByte(buffer
, i
);
223 Marshal
.FreeHGlobal(buffer
);
229 #region CreateGACEnum
231 private static IAssemblyEnum
CreateGACEnum()
235 GacHelper
.CreateAssemblyEnum(out ae
, (IntPtr
)0, null, ASM_CACHE_FLAGS
.ASM_CACHE_GAC
, (IntPtr
)0);
242 #region GetNextAssembly
245 /// Get the next assembly name in the current enumerator or fail
247 /// <param name="enumerator"></param>
248 /// <param name="name"></param>
249 /// <returns>0 if the enumeration is not at its end</returns>
250 private static int GetNextAssembly(IAssemblyEnum enumerator
, out IAssemblyName name
)
252 return enumerator
.GetNextAssembly((IntPtr
)0, out name
, 0);
257 #region External methods
260 /// To obtain nativeName instance of the CreateAssemblyEnum API, call the CreateAssemblyNameObject API.
262 /// <param name="pEnum">Pointer to a memory location that contains the IAssemblyEnum pointer.</param>
263 /// <param name="pUnkReserved">Must be null.</param>
264 /// <param name="pName">An assembly name that is used to filter the enumeration. Can be null to enumerate all assemblies in the GAC.</param>
265 /// <param name="dwFlags">Exactly one bit from the ASM_CACHE_FLAGS enumeration.</param>
266 /// <param name="pvReserved">Must be NULL.</param>
267 [DllImport("fusion.dll", SetLastError
= true, PreserveSig
= false)]
268 private static extern void CreateAssemblyEnum(
269 out IAssemblyEnum pEnum
,
272 ASM_CACHE_FLAGS dwFlags
,
281 #region ASM_DISPLAY_FLAGS
284 /// <see cref="IAssemblyName.GetDisplayName"/>
287 internal enum ASM_DISPLAY_FLAGS
291 PUBLIC_KEY_TOKEN
= 0x4,
294 PROCESSORARCHITECTURE
= 0x20,
300 #region ASM_CMP_FLAGS
303 internal enum ASM_CMP_FLAGS
309 REVISION_NUMBER
= 0x10,
310 PUBLIC_KEY_TOKEN
= 0x20,
313 ALL
= NAME
| MAJOR_VERSION
| MINOR_VERSION
|
314 REVISION_NUMBER
| BUILD_NUMBER
|
315 PUBLIC_KEY_TOKEN
| CULTURE
| CUSTOM
,
324 /// The ASM_NAME enumeration property ID describes the valid names of the name-value pairs in nativeName assembly name.
325 /// See the .NET Framework SDK for a description of these properties.
327 internal enum ASM_NAME
329 ASM_NAME_PUBLIC_KEY
= 0,
330 ASM_NAME_PUBLIC_KEY_TOKEN
,
333 ASM_NAME_MAJOR_VERSION
,
334 ASM_NAME_MINOR_VERSION
,
335 ASM_NAME_BUILD_NUMBER
,
336 ASM_NAME_REVISION_NUMBER
,
338 ASM_NAME_PROCESSOR_ID_ARRAY
,
339 ASM_NAME_OSINFO_ARRAY
,
342 ASM_NAME_CODEBASE_URL
,
343 ASM_NAME_CODEBASE_LASTMOD
,
344 ASM_NAME_NULL_PUBLIC_KEY
,
345 ASM_NAME_NULL_PUBLIC_KEY_TOKEN
,
347 ASM_NAME_NULL_CUSTOM
,
354 #region ASM_CACHE_FLAGS
357 /// The ASM_CACHE_FLAGS enumeration contains the following values:
358 /// ASM_CACHE_ZAP - Enumerates the cache of precompiled assemblies by using Ngen.exe.
359 /// ASM_CACHE_GAC - Enumerates the GAC.
360 /// ASM_CACHE_DOWNLOAD - Enumerates the assemblies that have been downloaded on-demand or that have been shadow-copied.
363 internal enum ASM_CACHE_FLAGS
367 ASM_CACHE_DOWNLOAD
= 0x4
374 #region IAssemblyName interface
377 /// The IAssemblyName interface represents nativeName assembly name. An assembly name includes a predetermined set of name-value pairs.
378 /// The assembly name is described in detail in the .NET Framework SDK.
380 [ComImport
, Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E"),
381 InterfaceType(ComInterfaceType
.InterfaceIsIUnknown
)]
382 internal interface IAssemblyName
385 /// The IAssemblyName::SetProperty method adds a name-value pair to the assembly name, or, if a name-value pair
386 /// with the same name already exists, modifies or deletes the value of a name-value pair.
388 /// <param name="PropertyId">The ID that represents the name part of the name-value pair that is to be
389 /// added or to be modified. Valid property IDs are defined in the ASM_NAME enumeration.</param>
390 /// <param name="pvProperty">A pointer to a buffer that contains the value of the property.</param>
391 /// <param name="cbProperty">The length of the pvProperty buffer in bytes. If cbProperty is zero, the name-value pair
392 /// is removed from the assembly name.</param>
393 /// <returns></returns>
401 /// The IAssemblyName::GetProperty method retrieves the value of a name-value pair in the assembly name that specifies the name.
403 /// <param name="PropertyId">The ID that represents the name of the name-value pair whose value is to be retrieved.
404 /// Specified property IDs are defined in the ASM_NAME enumeration.</param>
405 /// <param name="pvProperty">A pointer to a buffer that is to contain the value of the property.</param>
406 /// <param name="pcbProperty">The length of the pvProperty buffer, in bytes.</param>
407 /// <returns></returns>
412 ref uint pcbProperty
);
415 /// The IAssemblyName::Finalize method freezes nativeName assembly name. Additional calls to IAssemblyName::SetProperty are
416 /// unsuccessful after this method has been called.
418 /// <returns></returns>
423 /// The IAssemblyName::GetDisplayName method returns a string representation of the assembly name.
425 /// <param name="szDisplayName">A pointer to a buffer that is to contain the display name. The display name is returned in Unicode.</param>
426 /// <param name="pccDisplayName">The size of the buffer in characters (on input). The length of the returned display name (on return).</param>
427 /// <param name="dwDisplayFlags">One or more of the bits defined in the ASM_DISPLAY_FLAGS enumeration:
428 /// *_VERSION - Includes the version number as part of the display name.
429 /// *_CULTURE - Includes the culture.
430 /// *_PUBLIC_KEY_TOKEN - Includes the public key token.
431 /// *_PUBLIC_KEY - Includes the public key.
432 /// *_CUSTOM - Includes the custom part of the assembly name.
433 /// *_PROCESSORARCHITECTURE - Includes the processor architecture.
434 /// *_LANGUAGEID - Includes the language ID.</param>
435 /// <returns></returns>
436 /// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondefaultmarshalingforstrings.asp</remarks>
439 [Out
, MarshalAs(UnmanagedType
.LPWStr
)] StringBuilder szDisplayName
,
440 ref uint pccDisplayName
,
441 ASM_DISPLAY_FLAGS dwDisplayFlags
);
446 /// <param name="refIID"></param>
447 /// <param name="pUnkSink"></param>
448 /// <param name="pUnkContext"></param>
449 /// <param name="szCodeBase"></param>
450 /// <param name="llFlags"></param>
451 /// <param name="pvReserved"></param>
452 /// <param name="cbReserved"></param>
453 /// <param name="ppv"></param>
454 /// <returns></returns>
458 [MarshalAs(UnmanagedType
.IUnknown
)] object pUnkSink
,
459 [MarshalAs(UnmanagedType
.IUnknown
)] object pUnkContext
,
460 [MarshalAs(UnmanagedType
.LPWStr
)] string szCodeBase
,
467 /// The IAssemblyName::GetName method returns the name part of the assembly name.
469 /// <param name="lpcwBuffer">Size of the pwszName buffer (on input). Length of the name (on return).</param>
470 /// <param name="pwzName">Pointer to the buffer that is to contain the name part of the assembly name.</param>
471 /// <returns></returns>
472 /// <remarks>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondefaultmarshalingforstrings.asp</remarks>
476 [Out
, MarshalAs(UnmanagedType
.LPWStr
)] StringBuilder pwzName
);
479 /// The IAssemblyName::GetVersion method returns the version part of the assembly name.
481 /// <param name="pdwVersionHi">Pointer to a DWORD that contains the upper 32 bits of the version number.</param>
482 /// <param name="pdwVersionLow">Pointer to a DWORD that contain the lower 32 bits of the version number.</param>
483 /// <returns></returns>
486 out uint pdwVersionHi
,
487 out uint pdwVersionLow
);
490 /// The IAssemblyName::IsEqual method compares the assembly name to another assembly names.
492 /// <param name="pName">The assembly name to compare to.</param>
493 /// <param name="dwCmpFlags">Indicates which part of the assembly name to use in the comparison.
494 /// Values are one or more of the bits defined in the ASM_CMP_FLAGS enumeration.</param>
495 /// <returns></returns>
499 ASM_CMP_FLAGS dwCmpFlags
);
502 /// The IAssemblyName::Clone method creates a copy of nativeName assembly name.
504 /// <param name="pName"></param>
505 /// <returns></returns>
508 out IAssemblyName pName
);
513 #region IAssemblyEnum interface
516 /// The IAssemblyEnum interface enumerates the assemblies in the GAC.
518 [ComImport
, Guid("21b8916c-f28e-11d2-a473-00c04f8ef448"),
519 InterfaceType(ComInterfaceType
.InterfaceIsIUnknown
)]
520 internal interface IAssemblyEnum
523 /// The IAssemblyEnum::GetNextAssembly method enumerates the assemblies in the GAC.
525 /// <param name="pvReserved">Must be null.</param>
526 /// <param name="ppName">Pointer to a memory location that is to receive the interface pointer to the assembly
527 /// name of the next assembly that is enumerated.</param>
528 /// <param name="dwFlags">Must be zero.</param>
529 /// <returns></returns>
533 out IAssemblyName ppName
,
537 /// Undocumented. Best guess: reset the enumeration to the first assembly.
539 /// <returns></returns>
544 /// Undocumented. Create a copy of the assembly enum that is independently enumerable.
546 /// <param name="ppEnum"></param>
547 /// <returns></returns>
550 out IAssemblyEnum ppEnum
);