Перечисление сборок в GAC

Как я могу перечислять все доступные сборки в GAC в С#?

На самом деле я столкнулся с проблемой с глупым кодом - сборка, называемая Telerik.Web.UI.dll, передается и используется в проекте - эта конкретная DLL отсутствует в папке BIN. И приложение отлично работает на сервере - но на моей машине, очевидно, компилятор дает ошибку. Экс-разработчик настаивает на том, что он не находится в GAC на сервере и "это то, что есть". Теперь мне нужно проверить, зарегистрирована ли эта DLL в GAC или нет.

Помощь будет оценена.

Добрый день;

Ответ 1

Если у вас ограниченный доступ к серверу, это может сработать:

// 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/>");
        }
    }
}

В основном перечисляет необработанные папки GAC. Он работает на распределенном хосте DiscountASP, поэтому может работать в вашей среде хостинга.

Его можно было бы украсить, перечислив глубже в каждую папку сборки, чтобы вытащить номер версии и токен открытого ключа.

Ответ 2

Проверьте этот codeproject GAC API Interface. Это использует недокументированный файл fusion.dll для перечисления GAC. Но автор утверждает, что

Этот код, как известно, работает с .NET. 1.1 и 2.0. Обратите внимание, что DLL fusion.dll отличается в 1.1 и 2,0. Поэтому, если у вас установлены оба фреймворка, обязательно укажите код в нужную DLL. В противном случае большинство API-вызовы не удастся. По умолчанию реализация использует ту, которая найдена в каталог WINDOWS.

Ответ 3

Вам действительно не нужно писать что-то на С#, чтобы узнать, находится ли эта конкретная DLL в gac. Вы можете использовать gacutil.exe с параметром /l из командной строки, чтобы отобразить содержимое GAC.

Ответ 4

Если это простой сайт ASP.NET, а сборка не находится в папке bin или на сервере GAC (и в web.config нет ничего подозрительного), возможно, сайт является подсайтом какой-то тип и один из сайтов выше, содержит ссылку в папке bin (или что-то подозрительное в ее web.config, поскольку подсайты/папки наследуют веб файлы своих родителей)?

Теперь, если у вас есть среда, в которой файл загружается правильно (и это похоже на то, что вы делаете), вы можете просто спросить .NET, откуда происходит .dll, и отобразить это вместо этого, например:

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

Console.WriteLine(a.Location);

Будет загружаться сборка и отображать ее местоположение на диске, мой вывод: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Shell\2.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Shell.dll

Если он находится под "C:\Windows\assembly \", вы знаете это в GAC.

Вы также можете сделать это без вызова Assembly.Load, если вы уже ссылаетесь на сборку, вы можете перечислить сборки, загруженные в текущее приложение .domain, и просто распечатать (или визуализировать литеральный элемент управления или Response.Write и т.д.)....), каковы их свойства .Location.

Edit: Код для этого выглядит примерно так:

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

Edit: В полной среде доверия следующий код (консольное приложение) перечислит 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");
}

Ответ 5

простой способ перечислить 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();
}

и вы можете получить все файлы

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

Если вам нужно загрузить каждую сборку, вы получите исключение для разных версий во время выполнения. По этой причине вы можете загрузить сборку Assembly.ReflectionOnlyLoadFrom, чтобы загрузить сборку только в отражении. Затем Assembly не загружается в AppDomain и не генерирует исключение.

Ответ 6

Рисунок я мог бы также добавить мое решение на основе linq (удобно, если вы просто ищете один файл и не хотите перечислять через каждый файл)

чтобы найти файлы:

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

И для вызова (у меня не было Environment.SpecialFolder.Windows, поэтому вместо него использовалась Environment.SpecialFolder.System)

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

Кроме того, в качестве FYI этот метод перечисляет весь GAC, и вы можете найти дубликаты DLL в tmp/и temp/, для установки используется Tmp, а для удаления используется Temp.

Ответ 7

Проверьте, как fusion.dll используется в ILSpy для перечисления всех сборок GAC.

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

Главная

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