Встраивание одной библиотеки dll в другую в качестве встроенного ресурса и затем вызов ее из моего кода

StackOverflow https://stackoverflow.com/questions/96732

  •  01-07-2019
  •  | 
  •  

Вопрос

У меня ситуация, когда у меня есть библиотека DLL, которую я создаю, которая использует другую стороннюю библиотеку DLL, но я бы предпочел иметь возможность встроить стороннюю библиотеку DLL в свою библиотеку DLL вместо того, чтобы хранить их обе вместе, если это возможно.

Это касается C # и .NET 3.5.

Способ, которым я хотел бы это сделать, заключается в сохранении сторонней библиотеки DLL в качестве встроенного ресурса, который я затем размещаю в соответствующем месте во время выполнения первой библиотеки DLL.

Изначально я планировал сделать это, написав код для размещения сторонней библиотеки DLL в месте, указанном System.Reflection.Assembly.GetExecutingAssembly().Location.ToString() минус последний /nameOfMyAssembly.dll.Я могу успешно спасти третью сторону .DLL в этом месте (которое в конечном итоге становится

C:\Documents и Настройки\myUserName\Локальные настройки\Приложение Данные\ сборка\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901

), но когда я добираюсь до части моего кода, требующей этой библиотеки DLL, он не может ее найти.

У кого-нибудь есть какие-нибудь идеи относительно того, что мне нужно делать по-другому?

Это было полезно?

Решение

После того как вы внедрили стороннюю сборку в качестве ресурса, добавьте код для подписки на AppDomain.AssemblyResolve событие текущего домена во время запуска приложения.Это событие срабатывает всякий раз, когда подсистеме Fusion среды CLR не удается найти сборку в соответствии с действующим зондированием (политиками).В обработчике событий для AppDomain.AssemblyResolve, загрузить ресурс с помощью Assembly.GetManifestResourceStream и передайте его содержимое в виде массива байтов в соответствующий Assembly.Load перегрузка.Ниже показано, как могла бы выглядеть одна из таких реализаций на C#:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var resName = args.Name + ".dll";    
    var thisAssembly = Assembly.GetExecutingAssembly();    
    using (var input = thisAssembly.GetManifestResourceStream(resName))
    {
        return input != null 
             ? Assembly.Load(StreamToBytes(input))
             : null;
    }
};

где StreamToBytes может быть определен как:

static byte[] StreamToBytes(Stream input) 
{
    var capacity = input.CanSeek ? (int) input.Length : 0;
    using (var output = new MemoryStream(capacity))
    {
        int readLength;
        var buffer = new byte[4096];

        do
        {
            readLength = input.Read(buffer, 0, buffer.Length);
            output.Write(buffer, 0, readLength);
        }
        while (readLength != 0);

        return output.ToArray();
    }
}

Наконец, как некоторые из них уже упоминали, Ильмерге возможно, стоит рассмотреть еще один вариант, хотя и несколько более сложный.

Другие советы

В конце концов, я сделал это почти в точности так, как предложил raboof (и аналогично тому, что предложил dgvid), за исключением некоторых незначительных изменений и исправленных упущений.Я выбрал этот метод, потому что он был наиболее близок к тому, что я искал в первую очередь, и не требовал использования каких-либо сторонних исполняемых файлов и тому подобного.Это отлично работает!

Вот как в итоге выглядел мой код:

Редактировать:Я решил перенести эту функцию в другую сборку, чтобы я мог повторно использовать ее в нескольких файлах (я просто передаю Assembly.GetExecutingAssembly()).

Это обновленная версия, которая позволяет вам передавать сборку со встроенными библиотеками DLL.

embeddedResourcePrefix - это строковый путь к встроенному ресурсу, обычно это имя сборки, за которым следует любая структура папок, содержащая ресурс (например"MyComapny.MyProduct.MyAssembly.Ресурсы", если библиотека dll находится в папке с именем Resources в проекте).Это также предполагает, что библиотека dll имеет расширение .dll.resource.

   public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add =>
            try {
                string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource";
                using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) {
                    return input != null
                         ? Assembly.Load(StreamToBytes(input))
                         : null;
                }
            } catch (Exception ex) {
                _log.Error("Error dynamically loading dll: " + args.Name, ex);
                return null;
            }
        }; // Had to add colon
    }

    private static byte[] StreamToBytes(Stream input) {
        int capacity = input.CanSeek ? (int)input.Length : 0;
        using (MemoryStream output = new MemoryStream(capacity)) {
            int readLength;
            byte[] buffer = new byte[4096];

            do {
                readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length
                output.Write(buffer, 0, readLength);
            }
            while (readLength != 0);

            return output.ToArray();
        }
    }

Есть инструмент под названием ILMerge, который может это сделать: http://research.microsoft.com /~мбарнетт/ILMerge.aspx

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

Задать путь="C:\Program Files\Microsoft\ILMerge"

удаление /вывод: $(ProjectDir)\Deploy\LevelEditor.exe $(ProjectDir)\bin elease elease.exe $(ProjectDir)\bin elease\InteractLib.dll $(ProjectDir)\bin elease\SpriteLib.dll $(ProjectDir)\bin elease\LevelLibrary.dll

Я успешно выполнял то, что вы описываете, но поскольку сторонняя библиотека DLL также является сборкой .NET, я никогда не записываю ее на диск, я просто загружаю ее из памяти.

Я получаю встроенную сборку ресурсов в виде массива байтов следующим образом:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

        byte[] assemblyData;
        using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
        {
            assemblyData = ReadBytesFromStream(stream);
            stream.Close();
        }

Затем я загружаю данные с помощью Assembly.Load().

Наконец, я добавляю обработчик в AppDomain.CurrentDomain.AssemblyResolve возвращает мою загруженную сборку, когда загрузчик типов просматривает ее.

Смотрите на Семинар по слиянию .NET для получения дополнительной информации.

Вы можете добиться этого удивительно легко, используя Netz, компрессор и упаковщик исполняемых файлов .net NET.net.

Вместо записи сборки на диск вы можете попробовать выполнить сборку.Load(byte[] rawAssembly), где вы создаете rawAssembly из встроенного ресурса.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top