Встраивание DLL в .exe в Visual С# 2010
Я работаю над программой на С#, которая использует iTextSharp.dll и WebCam_Capture.dll. Когда я создаю программу, она создает исполняемый файл в папке отладки, а также копирует эти две библиотеки DLL в папку отладки, как и ожидалось. Я хочу объединить их в один исполняемый файл, однако я потерпел неудачу. Эти две библиотеки видны в ссылках, как правило, в проводнике решений. Я также добавляю их в качестве ресурсов. Исполняемый размер стал больше, что равно сумме трех файлов, тем не менее исполняемый файл по-прежнему требует наличия этих библиотек в своем каталоге... Я играл со свойством "build action" файлов ресурсов, но без изменений. Я также попробовал ILmerge, но это дало мне ошибку. так что мне делать?
Обновление: это то, что я получаю от ILmerge:
An exception occurred during merging:
Unresolved assembly reference not allowed: System.Core.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
Это просто приложение для Windows, кстати, форма, которую нужно заполнить и распечатать в формате pdf с фотографией, сделанной с помощью веб-камеры, если таковая имеется. Спасибо всем!
Ответы
Ответ 1
Вы можете использовать ILMerge для объединения нескольких сборок вместе. Вы уже сказали, что сделали это, и вы получили сообщение об ошибке. Хотя я не знаю, почему, вы можете использовать альтернативу: если библиотеки с открытым исходным кодом (и их лицензии совместимы с вашими), вы можете загрузить исходный код, добавить его в свой проект и скомпилировать. Это приведет к созданию одной сборки.
Страница ILMerge также содержит блог Джеффри Рихтера в качестве еще одной альтернативы для решения вашей проблемы:
Многие приложения состоят из EXE файла, который зависит от многих DLL файлы. При развертывании этого приложения все файлы должны быть развертывается. Тем не менее, существует метод, который можно использовать для развертывания просто один EXE файл. Сначала определите все файлы DLL, которые EXE файл зависит от того, который не поставляется как часть Microsoft.NET Сама по себе. Затем добавьте эти DLL в проект Visual Studio. Для каждого добавляемого DLL файла отобразите его свойства и измените его "Построить действие" на "Встроенный ресурс". Это приводит к тому, что компилятор С# вставьте DLL файл в ваш EXE файл, и вы можете развернуть этот файл EXE файл.
Во время выполнения CLR не сможет найти зависимую DLL сборок, что является проблемой. Чтобы исправить это, когда ваше приложение инициализирует, регистрирует метод обратного вызова с AppDomains Событие ResolveAssembly. Код должен выглядеть примерно так:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
String resourceName = "AssemblyLoadingAndReflection." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName)) {
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Теперь, когда поток вызывает метод, который ссылается на тип в зависимый DLL файл, событие AssemblyResolve будет поднято, а код обратного вызова, показанный выше, найдет желаемый ресурс библиотеки DLL и загрузите его, вызвав перегрузку метода Loads Loads, который принимает байт [] в качестве аргумента.
Ответ 2
- Добавьте файлы DLL в проект Visual Studio.
- Для каждого файла перейдите в "Свойства" и установите для его действия по сборке значение "Встроенный ресурс"
- На вашем коде вы можете восстановить ресурс с помощью GetManifestResourceStream ( "DLL_Name_Here" ), который возвращает поток, который может быть загружен.
- Запишите обработчик событий "AssemblyResolve", чтобы загрузить его.
Вот код:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
namespace WindowsForm
{
public partial class Form1 : Form
{
Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();
public Form1()
{
InitializeComponent();
AppDomain.CurrentDomain.AssemblyResolve += FindDLL;
}
private Assembly FindDLL(object sender, ResolveEventArgs args)
{
string keyName = new AssemblyName(args.Name).Name;
// If DLL is loaded then don't load it again just return
if (_libs.ContainsKey(keyName)) return _libs[keyName];
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespaceGoesHere." + keyName + ".dll")) // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
{
byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
Assembly assembly = Assembly.Load(buffer);
_libs[keyName] = assembly;
return assembly;
}
}
//
// Your Methods here
//
}
}
Надеюсь, что это поможет,
Pablo
Ответ 3
Я немного изменил код Пабло, и это сработало для меня.
Он не получал имя ресурса DLL правильно.
IDictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();
public Form1()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
InitializeComponent();
}
// dll handler
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string keyName = new AssemblyName(args.Name).Name;
// If DLL is loaded then don't load it again just return
if (_libs.ContainsKey(keyName)) return _libs[keyName];
using (Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(GetDllResourceName("itextsharp.dll"))) // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
{
byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
Assembly assembly = Assembly.Load(buffer);
_libs[keyName] = assembly;
return assembly;
}
}
private string GetDllResourceName(string dllName)
{
string resourceName = string.Empty;
foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
if (name.EndsWith(dllName))
{
resourceName = name;
break;
}
}
return resourceName;
}
Ответ 4
Ответ, который вы ищете:
// To embed a dll in a compiled exe:
// 1 - Change the properties of the dll in References so that Copy Local=false
// 2 - Add the dll file to the project as an additional file not just a reference
// 3 - Change the properties of the file so that Build Action=Embedded Resource
// 4 - Paste this code before Application.Run in the main exe
AppDomain.CurrentDomain.AssemblyResolve += (Object sender, ResolveEventArgs args) =>
{
String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
System.Reflection.AssemblyName embeddedAssembly = new System.Reflection.AssemblyName(args.Name);
String resourceName = thisExe + "." + embeddedAssembly.Name + ".dll";
using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return System.Reflection.Assembly.Load(assemblyData);
}
};
Ответ 5
Добавьте этот анонимный код функции в начало нашего конструктора приложений. Это добавит dll из встроенного ресурса в тот же проект.
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = new AssemblyName(args.Name).Name + ".dll";
string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Ответ 6
Просмотрите AssemblyResolve событие в домене приложения.
У меня нет образца, но вы в основном проверяете, что требуется, и потоковое восстановление DLL ресурсов. Я верю, что LinqPAD делает это хорошо - вы могли бы взглянуть на реализацию Джозефа Альбахари с декомпилятором и т.д.
Ответ 7
Я знаю, что тема старая, но я напишу ее будущим людям, которые захотят ее использовать.
i основывается на коде userSteve.
Я предлагаю изменить это.
String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
в этот
String thisExe = System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.Namespace;
таким образом он работал бы, даже если пространство имен отличается от имени сборки
также, если вы хотите использовать DLL из каталога, вы можете использовать его так (каталог "Ресурсы как пример" )
String resourceName = thisExe + ".Resources." + embeddedAssembly.Name + ".dll";
если вы все еще не можете найти, где разместить этот код в приложении С# Form, вставьте его внутри файла "Program.cs" над строкой:
Application.Run(new Form_1());
и ниже строк:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Ответ 8
Вы не ссылались на WPF, но если это так, это может быть причиной вашей ошибки. Если нет, ILMerge должен отлично работать для вас. Если вы используете WPF, вот решение, которое хорошо работает:
http://blogs.interknowlogy.com/2011/07/13/merging-a-wpf-application-into-a-single-exe/