【问题标题】:How to programmatically determine if .NET assembly is installed in GAC?如何以编程方式确定 GAC 中是否安装了 .NET 程序集?
【发布时间】:2013-10-18 18:23:36
【问题描述】:

以编程方式检查程序集是否已在本地计算机的 GAC(全局程序集缓存)中注册的最简单方法是什么?是否有一些易于使用的 .NET API,我可以在其中为程序集 DLL 或程序集对象本身提供一个位置,以检查它是否存在于本地计算机上的 GAC 中?在我的情况下,我正在检查的程序集将已经加载到程序检查的当前 AppDomain 中,所以我不确定调用 Assembly.ReflectionOnlyLoad 并捕获异常会像我在其他帖子中看到的那样工作,而且这似乎有点老套。

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

【问题讨论】:

  • Check GAC for an assembly 的可能重复项
  • 使用融合 api,IAssemblyCache.QueryAssemblyInfo
  • @Hans 我可能应该指定我想要一个完全托管的解决方案,但你是对的,Fusion API 也可以满足我的需求。请参阅我的答案,了解我最终为完全托管的解决方案做了什么。
  • 叹息。它是 C# 代码。看不到 unsafe 关键字,每个 C# 程序都使用 Fusion。您发布的是 On Error Resume Next 代码,即 Visual Basic 代码。
  • @Hans 是的,这不是不安全的代码,但如果可以的话,我也喜欢避免使用 COM,它看起来更简单,代码也少了很多。如果我称这是一个紧密的循环,我可能会使用 Fusion API 以避免糟糕的 try/catch 控制逻辑,但即使互操作也会有性能损失,尽管我猜测比堆栈展开要少得多来自异常捕获。

标签: c# .net .net-assembly global-assembly-cache


【解决方案1】:

这个问题与以下问题非常相似,但我的问题更精确一些,而且两者都没有公认的答案,而且提供的答案似乎都不完整或最佳:

最初我认为以下是最好的方法,但除非您指定程序集的全名,否则它不起作用,并且由于 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 无需尝试/捕获即可工作。这是一堆更多的代码,但它适用于部分程序集名称:

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;
    }
}

【讨论】:

  • 注。根据我的经验,assemblyFullName 必须是 FULL。这包括 processorArchitecture=MSIL 等。所以在我的测试中,我在这个测试中使用了 AssemblyName.FullName +", processorArchitecture=MSIL"。在我说“不”之前,还要尝试其他两种选择。 (“AMD64”和“x86”)没有它我得到的唯一匹配是“c:\ Windows \ assembly \ GAC”中的程序集,这是一组非常少的。
  • 不仅如此,今天我正在研究这样一种情况:添加 ProcessorArchitecture=MSIL 对某些人来说是失败的,而对另一些人来说是成功的,从全名中删除 ProcessorArchitecture 会切换哪些失败哪些成功。
  • 使用 Assembly.ReflectionOnlyLoad() 方法我从 HRESULT: 0x80131047 得到异常,但是用 Assembly.LoadFile() 替换它,它就像一个魅力。
【解决方案2】:

检查 CodeBase 是否为空

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

【讨论】:

  • 不起作用。即使类型在当前执行的程序集中,这也是 null。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-14
相关资源
最近更新 更多