Added container accessor to Castle.Core
[castle.git] / AspectSharp / AspectSharp.Lang / GacHelper.cs
bloba4fb632db2c597632178ba1598a788341c94478e
2 #region Modification Notes
4 // August 12 2005
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)
11 #endregion
13 #region Original notes
14 // Source: Microsoft KB Article KB317540
17 SUMMARY
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.
21 MORE INFORMATION
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
25 Framework.
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
34 to the GAC.
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.
45 #endregion
47 #region Using directives
49 using System;
50 using System.Collections.Generic;
51 using System.Text;
52 using System.Reflection;
53 using System.Runtime.InteropServices;
54 using System.Globalization;
56 #endregion
58 namespace AspectSharp.Lang
60 /// <summary>
61 /// Provides a method to find nativeName assembly in the GAC by it's simple name.
62 /// </summary>
63 internal static class GacHelper
66 #region FindAssembly
68 /// <summary>
69 ///
70 /// </summary>
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);
87 matches.Add(name);
91 AssemblyName last = null;
93 for(int i = 0; i < matches.Count; i++)
95 AssemblyName current = matches[i];
97 if(last != null)
99 if(last.Version.Major <= current.Version.Major)
101 if(last.Version.Major < current.Version.Major)
103 last = current;
105 else
107 if(last.Version.Minor < current.Version.Minor)
109 last = current;
114 else
116 last = current;
120 if(last != null)
122 return Assembly.Load(last);
124 else
126 return null;
130 #endregion
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));
142 return name;
145 #endregion
147 #region GetName
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();
158 #endregion
160 #region GetVersion
162 private static Version GetVersion(IAssemblyName name)
164 uint majorOut;
165 uint minorOut;
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;
173 if(major < 0)
175 major = major * -1;
178 if(minor < 0)
180 minor = minor * -1;
183 if(build < 0)
185 build = build * -1;
188 if(revision < 0)
190 revision = revision * -1;
193 return new Version(major, minor, build, revision);
196 #endregion
198 #region GetCulture
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);
211 #endregion
213 #region GetPublicKeyToken
215 private static byte[] GetPublicKeyToken(IAssemblyName name)
217 byte[] result = new byte[8];
218 uint bufferSize = 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);
224 return result;
227 #endregion
229 #region CreateGACEnum
231 private static IAssemblyEnum CreateGACEnum()
233 IAssemblyEnum ae;
235 GacHelper.CreateAssemblyEnum(out ae, (IntPtr)0, null, ASM_CACHE_FLAGS.ASM_CACHE_GAC, (IntPtr)0);
237 return ae;
240 #endregion
242 #region GetNextAssembly
244 /// <summary>
245 /// Get the next assembly name in the current enumerator or fail
246 /// </summary>
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);
255 #endregion
257 #region External methods
259 /// <summary>
260 /// To obtain nativeName instance of the CreateAssemblyEnum API, call the CreateAssemblyNameObject API.
261 /// </summary>
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,
270 IntPtr pUnkReserved,
271 IAssemblyName pName,
272 ASM_CACHE_FLAGS dwFlags,
273 IntPtr pvReserved);
275 #endregion
279 #region Flags
281 #region ASM_DISPLAY_FLAGS
283 /// <summary>
284 /// <see cref="IAssemblyName.GetDisplayName"/>
285 /// </summary>
286 [Flags]
287 internal enum ASM_DISPLAY_FLAGS
289 VERSION = 0x1,
290 CULTURE = 0x2,
291 PUBLIC_KEY_TOKEN = 0x4,
292 PUBLIC_KEY = 0x8,
293 CUSTOM = 0x10,
294 PROCESSORARCHITECTURE = 0x20,
295 LANGUAGEID = 0x40
298 #endregion
300 #region ASM_CMP_FLAGS
302 [Flags]
303 internal enum ASM_CMP_FLAGS
305 NAME = 0x1,
306 MAJOR_VERSION = 0x2,
307 MINOR_VERSION = 0x4,
308 BUILD_NUMBER = 0x8,
309 REVISION_NUMBER = 0x10,
310 PUBLIC_KEY_TOKEN = 0x20,
311 CULTURE = 0x40,
312 CUSTOM = 0x80,
313 ALL = NAME | MAJOR_VERSION | MINOR_VERSION |
314 REVISION_NUMBER | BUILD_NUMBER |
315 PUBLIC_KEY_TOKEN | CULTURE | CUSTOM,
316 DEFAULT = 0x100
319 #endregion
321 #region ASM_NAME
323 /// <summary>
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.
326 /// </summary>
327 internal enum ASM_NAME
329 ASM_NAME_PUBLIC_KEY = 0,
330 ASM_NAME_PUBLIC_KEY_TOKEN,
331 ASM_NAME_HASH_VALUE,
332 ASM_NAME_NAME,
333 ASM_NAME_MAJOR_VERSION,
334 ASM_NAME_MINOR_VERSION,
335 ASM_NAME_BUILD_NUMBER,
336 ASM_NAME_REVISION_NUMBER,
337 ASM_NAME_CULTURE,
338 ASM_NAME_PROCESSOR_ID_ARRAY,
339 ASM_NAME_OSINFO_ARRAY,
340 ASM_NAME_HASH_ALGID,
341 ASM_NAME_ALIAS,
342 ASM_NAME_CODEBASE_URL,
343 ASM_NAME_CODEBASE_LASTMOD,
344 ASM_NAME_NULL_PUBLIC_KEY,
345 ASM_NAME_NULL_PUBLIC_KEY_TOKEN,
346 ASM_NAME_CUSTOM,
347 ASM_NAME_NULL_CUSTOM,
348 ASM_NAME_MVID,
349 ASM_NAME_MAX_PARAMS
352 #endregion
354 #region ASM_CACHE_FLAGS
356 /// <summary>
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.
361 /// </summary>
362 [Flags]
363 internal enum ASM_CACHE_FLAGS
365 ASM_CACHE_ZAP = 0x1,
366 ASM_CACHE_GAC = 0x2,
367 ASM_CACHE_DOWNLOAD = 0x4
370 #endregion
372 #endregion
374 #region IAssemblyName interface
376 /// <summary>
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.
379 /// </summary>
380 [ComImport, Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E"),
381 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
382 internal interface IAssemblyName
384 /// <summary>
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.
387 /// </summary>
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>
394 [PreserveSig]
395 int SetProperty(
396 ASM_NAME PropertyId,
397 IntPtr pvProperty,
398 uint cbProperty);
400 /// <summary>
401 /// The IAssemblyName::GetProperty method retrieves the value of a name-value pair in the assembly name that specifies the name.
402 /// </summary>
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>
408 [PreserveSig]
409 int GetProperty(
410 ASM_NAME PropertyId,
411 IntPtr pvProperty,
412 ref uint pcbProperty);
414 /// <summary>
415 /// The IAssemblyName::Finalize method freezes nativeName assembly name. Additional calls to IAssemblyName::SetProperty are
416 /// unsuccessful after this method has been called.
417 /// </summary>
418 /// <returns></returns>
419 [PreserveSig]
420 int Finalize();
422 /// <summary>
423 /// The IAssemblyName::GetDisplayName method returns a string representation of the assembly name.
424 /// </summary>
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>
437 [PreserveSig]
438 int GetDisplayName(
439 [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName,
440 ref uint pccDisplayName,
441 ASM_DISPLAY_FLAGS dwDisplayFlags);
443 /// <summary>
444 /// Undocumented
445 /// </summary>
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>
455 [PreserveSig]
456 int BindToObject(
457 ref Guid refIID,
458 [MarshalAs(UnmanagedType.IUnknown)] object pUnkSink,
459 [MarshalAs(UnmanagedType.IUnknown)] object pUnkContext,
460 [MarshalAs(UnmanagedType.LPWStr)] string szCodeBase,
461 long llFlags,
462 IntPtr pvReserved,
463 uint cbReserved,
464 out IntPtr ppv);
466 /// <summary>
467 /// The IAssemblyName::GetName method returns the name part of the assembly name.
468 /// </summary>
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>
473 [PreserveSig]
474 int GetName(
475 ref uint lpcwBuffer,
476 [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName);
478 /// <summary>
479 /// The IAssemblyName::GetVersion method returns the version part of the assembly name.
480 /// </summary>
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>
484 [PreserveSig]
485 int GetVersion(
486 out uint pdwVersionHi,
487 out uint pdwVersionLow);
489 /// <summary>
490 /// The IAssemblyName::IsEqual method compares the assembly name to another assembly names.
491 /// </summary>
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>
496 [PreserveSig]
497 int IsEqual(
498 IAssemblyName pName,
499 ASM_CMP_FLAGS dwCmpFlags);
501 /// <summary>
502 /// The IAssemblyName::Clone method creates a copy of nativeName assembly name.
503 /// </summary>
504 /// <param name="pName"></param>
505 /// <returns></returns>
506 [PreserveSig]
507 int Clone(
508 out IAssemblyName pName);
511 #endregion
513 #region IAssemblyEnum interface
515 /// <summary>
516 /// The IAssemblyEnum interface enumerates the assemblies in the GAC.
517 /// </summary>
518 [ComImport, Guid("21b8916c-f28e-11d2-a473-00c04f8ef448"),
519 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
520 internal interface IAssemblyEnum
522 /// <summary>
523 /// The IAssemblyEnum::GetNextAssembly method enumerates the assemblies in the GAC.
524 /// </summary>
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>
530 [PreserveSig()]
531 int GetNextAssembly(
532 IntPtr pvReserved,
533 out IAssemblyName ppName,
534 uint dwFlags);
536 /// <summary>
537 /// Undocumented. Best guess: reset the enumeration to the first assembly.
538 /// </summary>
539 /// <returns></returns>
540 [PreserveSig()]
541 int Reset();
543 /// <summary>
544 /// Undocumented. Create a copy of the assembly enum that is independently enumerable.
545 /// </summary>
546 /// <param name="ppEnum"></param>
547 /// <returns></returns>
548 [PreserveSig()]
549 int Clone(
550 out IAssemblyEnum ppEnum);
553 #endregion