Подтвердить что ты не робот

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

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

Это с С# и .NET 3.5.

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

То, как я изначально планировал это сделать, - написать код, чтобы поместить стороннюю DLL в место, указанное System.Reflection.Assembly.GetExecutingAssembly(). Location.ToString() минус последнее /nameOfMyAssembly.dll. Я могу успешно сохранить стороннее .DLL в этом месте (которое заканчивается (C:\Documents and Settings\myUserName\Local Settings\Application Data\assembly\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901), но когда я добираюсь до части моего кода, требующего этой DLL, он не может ее найти.

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

embeddedResourcePrefix - это строковый путь к встроенному ресурсу, обычно это имя сборки, за которой следует любая структура папок, содержащая ресурс (например, "MyComapny.MyProduct.MyAssembly.Resources", если DLL находится в папке "Ресурсы" в проекте). Он также предполагает, что 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();
        }
    }

Ответ 3

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

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

Установить путь = "C:\Program Files\Microsoft\ILMerge"

ilmerge/out:$(ProjectDir)\Deploy\LevelEditor.exe $(ProjectDir)\bin\Release\release.exe $(ProjectDir)\bin\Release\InteractLib.dll $(ProjectDir)\bin\Release\SpriteLib.dll $(ProjectDir)\bin\Release\LevelLibrary.dll

Ответ 4

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

Ответ 5

У меня был успех, делающий то, что вы описываете, но поскольку сторонняя 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 Fusion Workshop.

Ответ 6

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