如何通过编程确定.NET程序集是否已安装在全局程序集缓存中?

16

如何以编程方式最简单地检查注册在本地机器的GAC(全局程序集缓存)中的程序集?是否有一种简单易用的.NET API,我可以给它一个程序集DLL的位置或一个Assembly对象本身来检查它是否存在于本地机器的GAC中?在我的情况下,我要检查的程序集将已经加载到正在检查的程序的当前AppDomain中,因此我不确定调用Assembly.ReflectionOnlyLoad并捕获异常是否像我在其他帖子中看到的那样起作用,而且这似乎有点笨拙。

理想情况下,我希望避免调用像gacutil.exe这样的外部可执行文件来进行检查。


可能是重复的问题:检查程序集的GAC - JMK
使用Fusion API,IAssemblyCache.QueryAssemblyInfo - Hans Passant
@Hans 我可能应该明确指出我想要一个完全托管的解决方案,但你是正确的,Fusion API也可以满足我的需求。请查看我的回答,了解我最终选择的完全托管解决方案。 - Scott Lerch
唉。这是C#代码。一点也没有不安全的关键字,每个C#程序都使用Fusion。你发的是On Error Resume Next代码,那是Visual Basic代码。 - Hans Passant
@Hans 确实,这不是不安全的代码,但如果可能的话,我也喜欢避免使用COM,因为它似乎更简单,而且代码量更少。如果我要在一个紧密循环中多次调用它,我可能会选择使用Fusion API来避免可怕的try/catch控制逻辑,但即使是互操作性也有性能损失,尽管我猜测这比从异常捕获中进行堆栈展开的代价要小得多。 - Scott Lerch
@HansPassant 我更新了我的答案,我认为你是对的,使用融合 API 是正确的方法。 - Scott Lerch
2个回答

24

这个问题与以下问题非常相似,但我的问题更加精确。而且,这些问题都没有被接受的答案,提供的答案也不完整或最佳:

最初,我认为以下方法是最好的选择,但它只有在指定程序集的完整名称时才起作用,而且由于try/catch的缘故,有点hacky。但它很简单,并且适用于许多情况:

public static class GacUtil
{
    public static bool IsAssemblyInGAC(string assemblyFullName)
    {
        try
        {
            return Assembly.ReflectionOnlyLoad(assemblyFullName)
                           .GlobalAssemblyCache;
        }
        catch
        {
            return false;
        }
    }

    public static bool IsAssemblyInGAC(Assembly assembly)
    {
        return assembly.GlobalAssemblyCache;
    }
}

通过使用Fusion API,这是一个更好的方法,可以在不使用try/catch的情况下工作。 这需要更多的代码,但它可以处理部分程序集名称:

public static class GacUtil
{
    [DllImport("fusion.dll")]
    private static extern IntPtr CreateAssemblyCache(
        out IAssemblyCache ppAsmCache, 
        int reserved);

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")]
    private interface IAssemblyCache
    {
        int Dummy1();

        [PreserveSig()]
        IntPtr QueryAssemblyInfo(
            int flags, 
            [MarshalAs(UnmanagedType.LPWStr)] string assemblyName, 
            ref AssemblyInfo assemblyInfo);

        int Dummy2();
        int Dummy3();
        int Dummy4();
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct AssemblyInfo
    {
        public int cbAssemblyInfo;
        public int assemblyFlags;
        public long assemblySizeInKB;

        [MarshalAs(UnmanagedType.LPWStr)]
        public string currentAssemblyPath;

        public int cchBuf;
    }

    public static bool IsAssemblyInGAC(string assemblyName)
    {
        var assembyInfo = new AssemblyInfo { cchBuf = 512 };
        assembyInfo.currentAssemblyPath = new string('\0', assembyInfo.cchBuf);

        IAssemblyCache assemblyCache;

        var hr = CreateAssemblyCache(out assemblyCache, 0);

        if (hr == IntPtr.Zero)
        {
            hr = assemblyCache.QueryAssemblyInfo(
                1, 
                assemblyName, 
                ref assembyInfo);

            if (hr != IntPtr.Zero)
            {
                return false;
            }

            return true;
        }

        Marshal.ThrowExceptionForHR(hr.ToInt32());
        return false;
    }
}

4
根据我的经验,assemblyFullName必须是完整的。这包括processorArchitecture=MSIL或类似的内容。因此,在我的测试中,我在此测试中使用AssemblyName.FullName+", processorArchitecture=MSIL"。在我说“不”之前,我也会尝试另外两种替代方法(“AMD64”和“x86”)。没有使用processorArchitecture=MSIL的唯一匹配项是位于“c:\Windows\assembly\GAC”中的程序集,这是非常少见的。 - Wolf5
不仅如此,今天我正在研究一种情况,即添加ProcessorArchitecture=MSIL对某些人失败,而对其他人成功,而从完整名称中删除ProcessorArchitecture则会导致哪些失败和哪些成功的情况发生变化。 - jnm2
使用 Assembly.ReflectionOnlyLoad() 方法时,我会收到 HRESULT: 0x80131047 的异常,但是将其替换为 Assembly.LoadFile() 后,它就可以完美运行。 - ilCosmico

-1

检查 CodeBase 是否为空

if (asm.CodeBase == null) {
     // IN GAC
}

不起作用。即使类型在当前执行的程序集中,它也是空的。 - toothie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接