Встраивание одной библиотеки dll в другую в качестве встроенного ресурса и затем вызов ее из моего кода
Вопрос
У меня ситуация, когда у меня есть библиотека 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 из встроенного ресурса.