检测特定版本的程序集是否在 GAC 中
Detect if a specific version of an assembly is in the GAC
我有一个 程序集完全限定名称列表,我检查是否有一些 assembly
在 GAC
我的应用 [=43] =].
例如,我想做类似的事情:
var result = AssemblyIsInGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
我已经尝试使用 fusion
API(CreateAssemblyCache
和 QueryAssemblyInfo
),如 this blog post 中所述,但不幸的是它只使用 程序集的部分名称
如果我调用 QueryAssemblyInfo("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
,它会给我一个 FileNotFound
异常,如果我调用它 QueryAssemblyInfo("mscorlib")
它 returns mscorlib
的最新版本,它并不总是我要找的版本。
关于如何执行此操作的任何想法?
更新:我无法将程序集加载到我的应用程序中 因此无法使用 Assembly.Load
、Assembly.ReflectionOnlyLoad
等
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;
}
}
}
用法:
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)}");
输出:
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
我使用这个实用程序,如您所见,它针对 mscorlib 或特殊程序集进行了修改:
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('[=10=]', 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('[=10=]', 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);
您可以使用CreateAssemblyNameObject and CreateAssemblyEnum
例如=>
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);
}
}
我明白了(我把全名写进去了,你可以把它缩减为用一个字符串作为函数参数来过滤):
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
有声明=>
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)
}
我有一个 程序集完全限定名称列表,我检查是否有一些 assembly
在 GAC
我的应用 [=43] =].
例如,我想做类似的事情:
var result = AssemblyIsInGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
我已经尝试使用 fusion
API(CreateAssemblyCache
和 QueryAssemblyInfo
),如 this blog post 中所述,但不幸的是它只使用 程序集的部分名称
如果我调用 QueryAssemblyInfo("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
,它会给我一个 FileNotFound
异常,如果我调用它 QueryAssemblyInfo("mscorlib")
它 returns mscorlib
的最新版本,它并不总是我要找的版本。
关于如何执行此操作的任何想法?
更新:我无法将程序集加载到我的应用程序中 因此无法使用 Assembly.Load
、Assembly.ReflectionOnlyLoad
等
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;
}
}
}
用法:
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)}");
输出:
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
我使用这个实用程序,如您所见,它针对 mscorlib 或特殊程序集进行了修改:
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('[=10=]', 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('[=10=]', 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);
您可以使用CreateAssemblyNameObject and CreateAssemblyEnum
例如=>
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);
}
}
我明白了(我把全名写进去了,你可以把它缩减为用一个字符串作为函数参数来过滤):
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
有声明=>
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)
}