Встраивание одной dll в другую в качестве встроенного ресурса, а затем вызов его из моего кода
у меня есть ситуация, когда у меня есть DLL, которую я создаю, которая использует другую стороннюю DLL, но я предпочел бы иметь возможность создавать стороннюю DLL в моей DLL вместо того, чтобы держать их вместе, если это возможно.
Это С C# и .NET 3.5.
Я хотел бы сделать это, сохранив стороннюю DLL в качестве встроенного ресурса, который я затем помещаю в соответствующее место во время выполнения первой DLL.
Я первоначально планировалось сделать это путем написания кода, чтобы поместить стороннюю DLL в место, указанное System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()
минус последний /nameOfMyAssembly.dll. Я могу успешно сохранить третью сторону .DLL в этом месте (где заканчивается
C:Documents и настройкиmyUserNameлокальные настройкиприложение
Данныесборкаdl3KXPPAX6Y.ZCYA1MZ1499.\E0115d44 91bb86eb_fe18c901 в 1TR
), но когда я добираюсь до части моего кода, требующей этой DLL, он не может найти оно.
кто-нибудь имеет представление о том, что мне нужно делать по-другому?
6 ответов:
после того, как вы внедрили стороннюю сборку в качестве ресурса, добавьте код для подписки на
AppDomain.AssemblyResolveсобытие текущего домена при запуске приложения. Это событие срабатывает всякий раз, когда подсистеме слияния среды 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(); } }наконец, как некоторые уже упоминали,ILMerge может быть еще один вариант для рассмотрения, хотя и несколько более сложным.
в конце концов я сделал это почти точно так, как предложил raboof (и похоже на то, что предложил dgvid), за исключением некоторых незначительных изменений и некоторых упущений. Я выбрал этот метод, потому что он был ближе всего к тому, что я искал в первую очередь и не требовал использования каких-либо сторонних исполняемых файлов и т. д. Он отлично работает!
вот как выглядел мой код:
EDIT: я решил переместить эту функцию в другую сборку, чтобы я мог повторно использовать ее несколько файлов (я просто передаю в сборке.GetExecutingAssembly()).
Это обновленная версия, которая позволяет передавать в сборку со встроенными DLL.
embeddedResourcePrefix-это строковый путь к встроенному ресурсу, обычно это имя сборки, за которым следует любая структура папок, содержащая ресурс (например, "MyComapny.MyProduct.Моя сборка.Ресурсы", если библиотека dll находится в папке Resources в проекте). Он также предполагает, что библиотека имеет .файл DLL.расширение ресурсов.
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 / ~mbarnett / ILMerge. aspx
тогда вы можете просто сделать событие сборки, подобное следующему.
Установить Путь="C:\Program Файлы\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
мне удалось сделать то, что вы описываете, но поскольку сторонняя DLL также является сборкой .NET, я никогда не записываю ее на диск, я просто загружаю ее из памяти.
Я получаю встроенную сборку ресурсов в виде массива байтов следующим образом:
Assembly resAssembly = Assembly.LoadFile(assemblyPathName); byte[] assemblyData; using (Stream stream = resAssembly.GetManifestResourceStream(resourceName)) { assemblyData = ReadBytesFromStream(stream); stream.Close(); }затем я загружаю данные с помощью сборки.Нагрузка.)(
наконец, я добавляю обработчик в AppDomain.CurrentDomain.AssemblyResolve возвращает мою загруженную сборку, когда загрузчик типов смотрит на нее.
посмотреть .NET Fusion Workshop для получения дополнительной информации.
вы можете достичь этого удивительно легко с помощью Netz, а .чистый исполняемые файлы компрессора и Пакер.
вместо записи сборки на диск вы можете попробовать сделать сборку.Загрузите (byte[] rawAssembly), где вы создаете rawAssembly из внедренного ресурса.
Comments