简体   繁体   English

枚举GAC中的程序集

[英]enumerating assemblies in GAC

How can I enumerate all available assemblies in GAC in C#? 如何在C#中枚举GAC中的所有可用程序集?

Actually I am facing an issue with a stupid code - the assembly called Telerik.Web.UI.dll is referred and used in project - this particular DLL is not present in BIN folder. 实际上我遇到了一个愚蠢的代码问题 - 在项目中引用并使用名为Telerik.Web.UI.dll的程序集 - 这个特定的DLL不存在于BIN文件夹中。 And application is working fine on the server - but on my machine, obviously compiler is giving error. 应用程序在服务器上工作正常 - 但在我的机器上,显然编译器会出错。 Ex-Developer insists that it is not in GAC on the server and 'it is what it is'. Ex-Developer坚持认为它不在服务器上的GAC中,而是“它就是这样”。 Now I need to check whether this DLL is registered in GAC or not. 现在我需要检查这个DLL是否在GAC中注册。

Help will be appreciated. 帮助将不胜感激。

Good Day; 美好的一天;

If you have limited access to the server then this might work: 如果您对服务器的访问权限有限,那么这可能有效:

// List of all the different types of GAC folders for both 32bit and 64bit
// environments.
List<string> gacFolders = new List<string>() { 
    "GAC", "GAC_32", "GAC_64", "GAC_MSIL", 
    "NativeImages_v2.0.50727_32", 
    "NativeImages_v2.0.50727_64",
    "NativeImages_v4.0.30319_32",
    "NativeImages_v4.0.30319_64"
};

foreach (string folder in gacFolders)
{
    string path = Path.Combine(
       Environment.ExpandEnvironmentVariables(@"%systemroot%\assembly"), 
       folder);

    if(Directory.Exists(path))
    {
        Response.Write("<hr/>" + folder + "<hr/>");

        string[] assemblyFolders = Directory.GetDirectories(path);
        foreach (string assemblyFolder in assemblyFolders)
        {
            Response.Write(assemblyFolder + "<br/>");
        }
    }
}

It basically enumerates the raw GAC folders. 它基本上枚举了原始GAC文件夹。 It works on DiscountASP shared hosting so might work for your hosting environment. 它适用于DiscountASP共享主机,因此可能适用于您的托管环境。

It could be embellished by enumerating deeper into each assembly's folder to yank out the version number and public key token. 它可以通过枚举更深入到每个程序集的文件夹来修饰版本号和公钥令牌。

Check this codeproject GAC API Interface article. 查看此代码项目GAC API接口文章。 This uses undocumented fusion.dll to enumurate the GAC. 这使用未记录的fusion.dll来累积GAC。 But author claims that 但作者声称

This code is known to work with .NET 1.1 and 2.0. 已知此代码适用于.NET 1.1和2.0。 Please note that the DLL fusion.dll is different in 1.1 and 2.0. 请注意,DLL fusion.dll在1.1和2.0中有所不同。 So if you have both frameworks installed, make sure to point the code to the right DLL. 因此,如果您安装了两个框架,请确保将代码指向正确的DLL。 Otherwise most API-calls will fail. 否则大多数API调用都将失败。 The default implementation uses the one found in the WINDOWS directory. 默认实现使用WINDOWS目录中的实现。

You don't really need to write something in C# to find out if this specific dll is in the gac. 你真的不需要在C#中写一些东西来找出这个特定的dll是否在gac中。 You can use gacutil.exe with the /l option from the command line to list the contents of the GAC. 您可以在命令行中使用带有/ l选项的gacutil.exe来列出GAC的内容。

a simple method to enumerate dll files in a directory 枚举目录中dll文件的简单方法

public static string[] GetGlobalAssemblyCacheFiles(string path)
{
    List<string> files = new List<string>();

    DirectoryInfo di = new DirectoryInfo(path);

    foreach (FileInfo fi in di.GetFiles("*.dll"))
    {
        files.Add(fi.FullName);
    }

    foreach (DirectoryInfo diChild in di.GetDirectories())
    {
        var files2 = GetGlobalAssemblyCacheFiles(diChild.FullName);
        files.AddRange(files2);
    }

    return files.ToArray();
}

and you can get all files 你可以得到所有文件

string gacPath = Environment.GetFolderPath(System.Environment.SpecialFolder.Windows) + "\\assembly";
var files = GetGlobalAssemblyCacheFiles(gacPath);

If you need to load each assembly, You get a exception for different run-time version. 如果需要加载每个程序集,则会获得不同运行时版本的异常。 For this reason you can load assembly with Assembly.ReflectionOnlyLoadFrom to load Assembly in reflection only. 因此,您可以使用Assembly.ReflectionOnlyLoadFrom加载程序集,仅在反射中加载程序集。 Then, Assembly don't load in your AppDomain and don't throw exception. 然后,程序集不会加载到您的AppDomain中,也不会抛出异常。

If it's plain-jane ASP.NET site, and the assembly is not in the site's bin folder or in the server's GAC (and there is nothing fishy in the web.config), perhaps the site is a sub-site of some sort and one of the site's higher up contains the reference in the bin folder (or something fishy in its web.config, since sub-sites/folders inherit the web.configs of their parents)? 如果它是普通的ASP.NET站点,并且程序集不在站点的bin文件夹中或服务器的GAC中(并且web.config中没有任何可疑内容),那么该站点可能是某种类型的子站点,其中一个网站的更高版本包含bin文件夹中的引用(或其web.config中的一些内容,因为子站点/文件夹继承了其父项的web.configs)?

Now, if you have an environment where the file is being loaded correctly (and it sounds like you do), you can just ask .NET where the .dll is coming from and display that instead, for example: 现在,如果你有一个正确加载文件的环境(听起来像你这样做),你可以问.NET .dll来自哪里,然后显示它,例如:

Assembly a = Assembly.Load("Microsoft.VisualStudio.Shell, Version=2.0.0.0, "
           + "PublicKeyToken=b03f5f7f11d50a3a, Culture=Neutral");

Console.WriteLine(a.Location);

Will load an assembly and display its on-disk location, my output is: C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.VisualStudio.Shell\\2.0.0.0__b03f5f7f11d50a3a\\Microsoft.VisualStudio.Shell.dll 将加载程序集并显示其磁盘位置,我的输出是: C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.VisualStudio.Shell\\2.0.0.0__b03f5f7f11d50a3a\\Microsoft.VisualStudio.Shell.dll

If it's under "C:\\Windows\\assembly\\" you know it's in the GAC. 如果它在“C:\\ Windows \\ assembly \\”下,你知道它在GAC中。

You can also do this without an Assembly.Load call if you already referencing the assembly you can enumerate the assemblies loaded into the current app.domain and just print out (or render to a literal control, or Response.Write, etc...) what their .Location properties are. 如果你已经引用了程序集,你可以枚举加载到当前app.domain中的程)他们的.Location属性是什么。

Edit: The code for that looks something like this: 编辑:代码看起来像这样:

foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
    Console.WriteLine(a.GetName().FullName);
    Console.WriteLine(a.Location);
    Console.WriteLine();
}

Edit: On a full trust environment, the following code (console app) will enumerate the GAC and write out each assembly, it should be easy to modify it into an ASP.NET app, but I'm not sure if it will work in an environment that is less than full trust (just guessing here, you might get lucky): 编辑:在完全信任环境中,以下代码(控制台应用程序)将枚举GAC并写出每个程序集,应该很容易将其修改为ASP.NET应用程序,但我不确定它是否可以在一个不完全信任的环境(只是在这里猜测,你可能会很幸运):

static void ProcessFile(string file)
{
    try
    {
        Assembly a = Assembly.LoadFile(file);

        Console.WriteLine(a.GetName().FullName);
    }
    catch { /* do nothing */ }
}

static void ProcessFolder(string folder)
{
    foreach (string file in Directory.GetFiles(folder))
    {
        ProcessFile(file);
    }

    foreach (string subFolder in Directory.GetDirectories(folder))
    {
        ProcessFolder(subFolder);
    }
}

static void Main(string[] args)
{
    ProcessFolder(@"C:\Windows\Assembly");
}

Figure i might as well throw in my linq based solution (handy if your just looking for a single file and don't want to enumerate through every file) 图我可以投入我的基于linq的解决方案(如果您只是寻找单个文件并且不想枚举每个文件,则很方便)

function to find the files: 找到文件的功能:

    public static IEnumerable<string> FindFiles (string path, string filter = "", int depth = int.MaxValue) {
        DirectoryInfo di = new DirectoryInfo(path);

        IEnumerable<string> results = (! string.IsNullOrEmpty(filter) ? di.GetFiles(filter) : di.GetFiles()).Select(f => f.FullName);
        if (depth > 0) {
            results = results.Concat(di.GetDirectories().SelectMany(d => FindFiles(d.FullName, filter, depth - 1)));
        }

        return results;
    }

And to call (i didn't have Environment.SpecialFolder.Windows so used Environment.SpecialFolder.System instead) 并调用(我没有Environment.SpecialFolder.Windows所以改为使用Environment.SpecialFolder.System)

string path = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\..\\assembly";
foreach (string s in FindFiles(path, "*.dll")) {
     Console.WriteLine(s);
}

Further, as a FYI this method enumerates the entire GAC and you may find duplicate DLLs in tmp/ and temp/, Tmp is used for installation, and Temp is used for uninstallation. 此外,作为FYI,此方法枚举整个GAC,您可能会在tmp /和temp /中找到重复的DLL,Tmp用于安装,Temp用于卸载。

Check the way how fusion.dll is used in ILSpy to enumerate all the GAC assemblies. 检查在ILSpy中如何使用fusion.dll枚举所有GAC程序集的方式。

Fusion COM Wrappers Fusion COM Wrappers

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace GacWithFusion
{
    // .NET Fusion COM interfaces
    [ComImport, Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAssemblyName
    {
        [PreserveSig]
        int SetProperty(uint PropertyId, IntPtr pvProperty, uint cbProperty);

        [PreserveSig]
        int GetProperty(uint PropertyId, IntPtr pvProperty, ref uint pcbProperty);

        [PreserveSig]
        int Finalize();

        [PreserveSig]
        int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName,
                           ref uint pccDisplayName,
                           uint dwDisplayFlags);

        [PreserveSig]
        int BindToObject(object refIID,
                         object pAsmBindSink,
                         IApplicationContext pApplicationContext,
                         [MarshalAs(UnmanagedType.LPWStr)] string szCodeBase,
                         long llFlags,
                         int pvReserved,
                         uint cbReserved,
                         out int ppv);

        [PreserveSig]
        int GetName(ref uint lpcwBuffer,
                    [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName);

        [PreserveSig]
        int GetVersion(out uint pdwVersionHi, out uint pdwVersionLow);

        [PreserveSig]
        int IsEqual(IAssemblyName pName,
                    uint dwCmpFlags);

        [PreserveSig]
        int Clone(out IAssemblyName pName);
    }

    [ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IApplicationContext
    {
        void SetContextNameObject(IAssemblyName pName);

        void GetContextNameObject(out IAssemblyName ppName);

        void Set([MarshalAs(UnmanagedType.LPWStr)] string szName,
                 int pvValue,
                 uint cbValue,
                 uint dwFlags);

        void Get([MarshalAs(UnmanagedType.LPWStr)] string szName,
                 out int pvValue,
                 ref uint pcbValue,
                 uint dwFlags);

        void GetDynamicDirectory(out int wzDynamicDir,
                                 ref uint pdwSize);
    }

    [ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAssemblyEnum
    {
        [PreserveSig]
        int GetNextAssembly(out IApplicationContext ppAppCtx,
                            out IAssemblyName ppName,
                            uint dwFlags);

        [PreserveSig]
        int Reset();

        [PreserveSig]
        int Clone(out IAssemblyEnum ppEnum);
    }

    internal static class Fusion
    {
        // dwFlags: 1 = Enumerate native image (NGEN) assemblies
        //          2 = Enumerate GAC assemblies
        //          4 = Enumerate Downloaded assemblies
        //
        [DllImport("fusion.dll", CharSet = CharSet.Auto)]
        internal static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum,
                                                      IApplicationContext pAppCtx,
                                                      IAssemblyName pName,
                                                      uint dwFlags,
                                                      int pvReserved);
    }
}

Main 主要

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace GacWithFusion
{
    public class Program
    {
        static void Main(string[] args)
        {
            foreach (var assemblyName in GetGacAssemblyFullNames())
            {
                Console.WriteLine(assemblyName.FullName);
            }
        }

        public static IEnumerable<AssemblyName> GetGacAssemblyFullNames()
        {
            IApplicationContext applicationContext;
            IAssemblyEnum assemblyEnum;
            IAssemblyName assemblyName;

            Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0);
            while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0)
            {
                uint nChars = 0;
                assemblyName.GetDisplayName(null, ref nChars, 0);

                StringBuilder name = new StringBuilder((int)nChars);
                assemblyName.GetDisplayName(name, ref nChars, 0);

                AssemblyName a = null;
                try
                {
                    a = new AssemblyName(name.ToString());
                }
                catch (Exception)
                {
                }

                if (a != null)
                {
                    yield return a;
                }
            }
        }
    }
}

May be you need GAC viewer something like this: 可能你需要这样的GAC查看器:

替代文字

Video: How to use Add GAC References Dialog 视频:如何使用“添加GAC引用”对话框

Hope this helps 希望这可以帮助

s 小号

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM