I have a list of assembly fully qualified names, and I check if some assembly
is in the GAC
where my app is running.
For example, I'm looking to do something like:
var result = AssemblyIsInGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
I've tried using the fusion
API (CreateAssemblyCache
and QueryAssemblyInfo
), as it is described in this blog post, but unfortunately it only works with partial names of assemblies
If I call QueryAssemblyInfo("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
, it gives me a FileNotFound
exception if I call it QueryAssemblyInfo("mscorlib")
it returns the latest version of mscorlib
, which is not always the version I'm looking for.
Any ideas on how I can do this?
UPDATE: I cannot load the assembly into my app so cannot use Assembly.Load
, Assembly.ReflectionOnlyLoad
, etc.
You can use CreateAssemblyNameObject and CreateAssemblyEnum
For example =>
IAssemblyEnum pAssemblyEnum = null;
IAssemblyName pAssemblyName = null;
HRESULT hr = HRESULT.E_FAIL;
string sAssemblyName = "mscorlib";
hr = CreateAssemblyNameObject(out pAssemblyName, sAssemblyName, 0, IntPtr.Zero);
if (hr == HRESULT.S_OK)
{
hr = CreateAssemblyEnum(out pAssemblyEnum, IntPtr.Zero, pAssemblyName, ASM_CACHE_FLAGS.ASM_CACHE_GAC, IntPtr.Zero);
if (hr == HRESULT.S_OK)
{
while (pAssemblyEnum.GetNextAssembly(IntPtr.Zero, out pAssemblyName, 0) == 0 && pAssemblyName != null)
{
int nSize = 260;
StringBuilder sbDisplayName = new StringBuilder(nSize);
hr = pAssemblyName.GetDisplayName(sbDisplayName, ref nSize, ASM_DISPLAY_FLAGS.ASM_DISPLAYF_FULL);
Console.WriteLine("Display Name: {0}", sbDisplayName.ToString());
}
Marshal.ReleaseComObject(pAssemblyEnum);
}
}
I get (I have put the full name, you can reduce it to filter with a string as a function parameter) :
Display Name: mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=AMD64
Display Name: mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86
Display Name: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=AMD64
Display Name: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86
with declarations =>
public enum HRESULT : int
{
S_OK = 0,
S_FALSE = 1,
E_NOINTERFACE = unchecked((int)0x80004002),
E_NOTIMPL = unchecked((int)0x80004001),
E_FAIL = unchecked((int)0x80004005),
}
[Guid("21b8916c-f28e-11d2-a473-00c04f8ef448")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAssemblyEnum
{
HRESULT GetNextAssembly(IntPtr pvReserved, out IAssemblyName ppName, int dwFlags);
HRESULT Reset();
HRESULT Clone(out IAssemblyEnum ppEnum);
}
public enum ASM_CACHE_FLAGS
{
ASM_CACHE_ZAP = 0x01,
ASM_CACHE_GAC = 0x02,
ASM_CACHE_DOWNLOAD = 0x04,
ASM_CACHE_ROOT = 0x08,
ASM_CACHE_ROOT_EX = 0x80
}
[DllImport("Fusion.dll", SetLastError = true)]
public static extern HRESULT CreateAssemblyEnum(out IAssemblyEnum pEnum, IntPtr pUnkReserved, IAssemblyName pName, ASM_CACHE_FLAGS dwFlags, IntPtr pvReserved);
[Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAssemblyName
{
HRESULT SetProperty(int PropertyId, IntPtr pvProperty, int cbProperty);
HRESULT GetProperty(int PropertyId, out IntPtr pvProperty, ref int pcbProperty);
HRESULT Finalize();
HRESULT GetDisplayName(StringBuilder szDisplayName, ref int pccDisplayName, ASM_DISPLAY_FLAGS dwDisplayFlags);
HRESULT Reserved([In, MarshalAs(UnmanagedType.LPStruct)] Guid refIID, IntPtr pUnkReserved1, IntPtr pUnkReserved2, string szReserved, UInt64 llReserved, IntPtr pvReserved, int cbReserved, out IntPtr ppReserved);
HRESULT GetName(ref int lpcwBuffer, StringBuilder pwzName);
HRESULT GetVersion(out int pdwVersionHi, out int pdwVersionLow);
HRESULT IsEqual(IAssemblyName pName, int dwCmpFlags);
HRESULT Clone(out IAssemblyName pName);
}
public enum ASM_DISPLAY_FLAGS
{
ASM_DISPLAYF_VERSION = 0x1,
ASM_DISPLAYF_CULTURE = 0x2,
ASM_DISPLAYF_PUBLIC_KEY_TOKEN = 0x4,
ASM_DISPLAYF_PUBLIC_KEY = 0x8,
ASM_DISPLAYF_CUSTOM = 0x10,
ASM_DISPLAYF_PROCESSORARCHITECTURE = 0x20,
ASM_DISPLAYF_LANGUAGEID = 0x40,
ASM_DISPLAYF_RETARGET = 0x80,
ASM_DISPLAYF_CONFIG_MASK = 0x100,
ASM_DISPLAYF_MVID = 0x200,
ASM_DISPLAYF_CONTENT_TYPE = 0x400,
ASM_DISPLAYF_FULL = (((((ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE) | ASM_DISPLAYF_PUBLIC_KEY_TOKEN) | ASM_DISPLAYF_RETARGET) | ASM_DISPLAYF_PROCESSORARCHITECTURE) | ASM_DISPLAYF_CONTENT_TYPE)
}
[DllImport("Fusion.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern HRESULT CreateAssemblyNameObject(out IAssemblyName ppAssemblyNameObj, [MarshalAs(UnmanagedType.LPWStr)] string szAssemblyName, CREATE_ASM_NAME_OBJ_FLAGS flags, IntPtr pvReserved);
public enum CREATE_ASM_NAME_OBJ_FLAGS
{
CANOF_PARSE_DISPLAY_NAME = 0x1,
CANOF_SET_DEFAULT_VALUES = 0x2,
CANOF_VERIFY_FRIEND_ASSEMBLYNAME = 0x4,
CANOF_PARSE_FRIEND_DISPLAY_NAME = (CANOF_PARSE_DISPLAY_NAME | CANOF_VERIFY_FRIEND_ASSEMBLYNAME)
}
static class AssemblyHelper
{
public static bool AssemblyIsInGAC(string assemblyFullName)
{
try
{
var assembly = Assembly.ReflectionOnlyLoad(assemblyFullName);
return assembly.FullName == assemblyFullName && // makes "mscorlib, Version=wrong version ..." work properly
assembly.GlobalAssemblyCache;
}
catch(FileNotFoundException)
{
return false;
}
}
}
Usage:
static void Main()
{
Demo("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
Demo("mscorlib, Version=wrong version, Culture=neutral, PublicKeyToken=b77a5c561934e089");
Demo("msshrtmi, Version=2.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
Demo("msshrtmi, Version=2.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
Demo("msshrtmi, Version=2.7.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
Demo("msshrtmi, Version=2.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
}
static void Demo(string assemblyFullName) =>
Console.WriteLine($"{assemblyFullName} is in GAC: {AssemblyHelper.AssemblyIsInGAC(assemblyFullName)}");
Output:
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is in GAC: True
mscorlib, Version=wrong version, Culture=neutral, PublicKeyToken=b77a5c561934e089 is in GAC: False
msshrtmi, Version=2.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 is in GAC: True
msshrtmi, Version=2.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 is in GAC: True
msshrtmi, Version=2.7.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 is in GAC: True
msshrtmi, Version=2.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 is in GAC: False
I use this utility, as you see it's modified for mscorlib or special assemblies:
Console.WriteLine(GetAssemblyPath("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"));
// dumps C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
...
public static string GetAssemblyPath(string name, bool matchClrVersion = true, bool throwOnError = false)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
string finalName = name;
var aInfo = new ASSEMBLY_INFO();
aInfo.cchBuf = 1024;
aInfo.pszCurrentAssemblyPathBuf = new string('\0', aInfo.cchBuf);
var hr = CreateAssemblyCache(out IAssemblyCache ac, 0);
if (hr >= 0)
{
hr = ac.QueryAssemblyInfo(0, finalName, ref aInfo);
if (hr < 0 && matchClrVersion)
{
var asmName = new AssemblyName(name);
finalName = asmName.Name + ", Version=" + Environment.Version.Major + "." + Environment.Version.Minor;
aInfo.pszCurrentAssemblyPathBuf = new string('\0', aInfo.cchBuf);
hr = ac.QueryAssemblyInfo(0, finalName, ref aInfo);
}
}
if (hr < 0)
{
if (throwOnError)
Marshal.ThrowExceptionForHR(hr);
return null;
}
return aInfo.pszCurrentAssemblyPathBuf;
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")]
private interface IAssemblyCache
{
void UninstallAssembly(); // not fully defined
[PreserveSig]
int QueryAssemblyInfo(int flags, [MarshalAs(UnmanagedType.LPWStr)] string assemblyName, ref ASSEMBLY_INFO assemblyInfo);
}
[StructLayout(LayoutKind.Sequential)]
private struct ASSEMBLY_INFO
{
public int cbAssemblyInfo;
public int dwAssemblyFlags;
public long uliAssemblySizeInKB;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszCurrentAssemblyPathBuf;
public int cchBuf;
}
[DllImport("fusion")]
private static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, int reserved);