Можно ли встроить уже существующую DLL в скомпилированный исполняемый файл C# (чтобы у вас был только один файл для распространения)? Если возможно, как бы это сделать?
Обычно мне нравится просто оставлять библиотеки DLL снаружи, а программа установки все обрабатывает, но на работе было несколько человек, которые спрашивали меня об этом, а я, честно говоря, не знаю.
@ PawełDyda: Вы можете встроить необработанные двоичные данные в PE-образ (см. RCDATA). Никаких преобразований не требуется (или рекомендуется).
Помимо ILMerge, если вы не хотите возиться с переключателями командной строки, я действительно рекомендую ILMerge-Gui. Это действительно хороший проект с открытым исходным кодом!
Я бы порекомендовал вам попробовать утилиту .NETZ, которая также сжимает сборку по выбранной вами схеме: http://madebits.com/netz/help.php#single





Вы можете добавить библиотеки DLL в качестве встроенных ресурсов, а затем попросить вашу программу распаковать их в каталог приложения при запуске (после проверки, есть ли они уже там).
Однако файлы установки создать настолько легко, что я не думаю, что это того стоит.
Обновлено: этот метод будет легко использовать со сборками .NET. С библиотеками DLL, отличными от .NET, было бы намного больше работы (вам нужно было бы выяснить, где распаковать файлы, зарегистрировать их и т. д.).
Вот отличная статья, объясняющая, как это сделать: codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resou rce
Если они действительно управляемые сборки, вы можете использовать ILMerge. Для собственных DLL вам придется немного поработать.
Смотрите также:Как можно объединить dll Windows C++ с exe-файлом приложения C#?
Меня интересует слияние Native DLL, есть ли материалы?
См. Также: stackoverflow.com/questions/108971/…
@BaiyanHuang посмотрите на github.com/boxedapp/bxilmerge, идея состоит в том, чтобы сделать "ILMerge" для нативных Dll.
Разработчики VB NET вроде меня не пугаются этого C++ по ссылке. ILMerge также очень легко работает с VB NET. Смотрите здесь https://github.com/dotnet/ILMerge. Спасибо @ Shog9
Возможно, но не так просто, создать гибридную собственную / управляемую сборку на C#. Если бы вы использовали C++ вместо этого, было бы намного проще, поскольку компилятор Visual C++ может создавать гибридные сборки так же легко, как и все остальное.
Если у вас нет строгих требований к созданию гибридной сборки, я согласен с MusiGenesis, что на самом деле это не стоит того, чтобы делать с C#. Если вам нужно это сделать, возможно, лучше подумайте о переходе на C++ / CLI.
Еще один продукт, который может с этим элегантно справиться - SmartAssembly, по адресу SmartAssembly.com. Этот продукт, помимо слияния всех зависимостей в единую DLL, (необязательно) обфусцирует ваш код, удаляет лишние метаданные, чтобы уменьшить итоговый размер файла, а также может фактически оптимизировать IL для повышения производительности во время выполнения.
Существует также какая-то функция глобальной обработки исключений / отчетов, которую он добавляет в ваше программное обеспечение (при желании), что может быть полезно. Я считаю, что у него также есть API командной строки, поэтому вы можете сделать его частью процесса сборки.
Как правило, для выполнения слияния сборок, как вы описываете, вам понадобится какой-то инструмент для пост-сборки. Существует бесплатный инструмент под названием Eazfuscator (eazfuscator.blogspot.com/), предназначенный для изменения байт-кода, который также обрабатывает слияние сборок. Вы можете добавить это в командную строку после сборки с помощью Visual Studio, чтобы объединить ваши сборки, но ваш опыт будет варьироваться из-за проблем, которые возникнут в любых сценариях слияния нетривиальных сборок.
Вы также можете проверить, имеет ли NANT возможность объединять сборки после сборки, но я недостаточно хорошо знаком с NANT, чтобы сказать, встроена ли эта функция или нет.
Также существует множество подключаемых модулей Visual Studio, которые будут выполнять слияние сборок как часть сборки приложения.
В качестве альтернативы, если вам не нужно, чтобы это выполнялось автоматически, существует ряд инструментов, таких как ILMerge, которые объединят сборки .net в один файл.
Самая большая проблема, с которой я столкнулся при слиянии сборок, - это то, используют ли они какие-либо похожие пространства имен. Или, что еще хуже, ссылаться на разные версии одной и той же dll (мои проблемы обычно были с файлами dll NUnit).
Eazfuscator просто позвонит IlMerge, AFAIK.
+1 Бобби. Я должен был помнить об этом. Практически все, что делает для вас Eazfucator, - это абстрагирование фактических вызовов ILMerge с помощью более общего файла конфигурации.
Да, можно объединять исполняемые файлы .NET с библиотеками. Для выполнения этой работы доступно несколько инструментов:
Вдобавок это можно комбинировать с Моно линкер, который удаляет неиспользуемый код и, следовательно, уменьшает размер итоговой сборки.
Другая возможность - использовать .NETZ, который не только позволяет сжимать сборку, но также может упаковывать библиотеки DLL прямо в exe. Отличие от вышеупомянутых решений в том, что .NETZ не объединяет их, они остаются отдельными сборками, а упаковываются в один пакет.
.NETZ is a open source tool that compresses and packs the Microsoft .NET Framework executable (EXE, DLL) files in order to make them smaller.
NETZ, кажется, ушел
Вау - я думал, что наконец нашел, потом прочитал этот комментарий. Кажется, он полностью ушел. Есть ли вилки?
Ну, он только что переместился на GitHub, и на него больше нет ссылок на веб-сайте ... так что "полностью исчез" - это преувеличение. Скорее всего, он больше не поддерживается, но он все еще есть. Обновил ссылку.
Просто щелкните свой проект в Visual Studio правой кнопкой мыши, выберите «Свойства проекта» -> «Ресурсы» -> «Добавить ресурс» -> «Добавить существующий файл…». И включите приведенный ниже код в свой App.xaml.cs или аналогичный.
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Вот мой оригинальный пост в блоге: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
Вы можете настроить такое поведение прямо из коробки. Посмотрите мой ответ stackoverflow.com/a/20306095/568266
Также важно отметить НЕВЕРОЯТНО полезный комментарий к вашему блогу от AshRowe: если у вас установлена настраиваемая тема, она попытается разрешить сборку PresentationFramework.Theme, которая вылетает и горит! Согласно предложению AshRowe, вы можете просто проверить, содержит ли dllName PresentationFramework следующим образом: if (dllName.ToLower (). Contains ("presentationframework")) return null;
Два комментария по этому поводу. Первый: вы должны проверить, является ли bytes нулевым, и если да, вернуть там ноль. В конце концов, возможно, что dll - это нет в ресурсах. Два: это работает только в том случае, если сам класс не имеет «использования» для чего-либо из этой сборки. Для инструментов командной строки мне пришлось переместить реальный программный код в новый файл и создать небольшую новую основную программу, которая просто делает это, а затем вызывает исходный основной код в старом классе.
В связи с этим, это можно использовать для компиляции решения с двумя проектами в один exe-файл ... все, что вам нужно сделать, это сделать так, чтобы сценарии предварительной сборки проекта встраивания перезаписывали dll, используемую в качестве ресурса, со встроенной dll из встроенный проект, и заставьте сценарии после сборки внедренного проекта удалить библиотеки DLL из выходной папки решения.
Еще одно полезное замечание: если вы используете byte[] bytes = rm.GetObject(dllName) as byte[];, вы не можете получить там ошибки приведения, опять же, если система каким-то образом случайно выбрала существующий ресурс другого типа.
Где я могу найти App.xaml.cs или аналогичный в Winforms? Это файл, который мне нужно создать?
Итак, ваш ответ предназначен для WPF. У меня получилось работать с Winforms. После добавления ресурса, как вы сказали, просто поместите строку AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve); перед строкой InitializeComponent(); в конструкторе формы. Затем поместите весь метод System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) куда угодно. Скомпилируйте и запустите. Вот и все. Это делает ваше решение даже проще, чем ответ с наивысшей оценкой, поскольку нет сторонней утилиты для загрузки.
@DanW: В приложениях с нормальной формой просто есть файл program.cs, который создает фактический объект формы и запускает его. Вы можете просто поместить его туда перед созданием объекта формы. В любом случае, там больше ничего не должно быть. Ваше решение может не работать, поскольку фактический класс, содержащий этот код, не может использовать загруженный таким образом материал сборки, поскольку разрешение сборки выполняется до выполнения конструктора.
Достоинством этого подхода является то, что он не полагается на установку каких-либо внешних библиотек для достижения желаемой функциональности. Обратной стороной этого подхода является то, что он полезен только когда дело доходит до управляемых библиотек DLL - библиотеки взаимодействия (по крайней мере, в рамках моего тестирования) не запускают событие assemblyresolve, и даже если они выполнили Assembly.Load (<bytes of some interop .dll>) не дает желаемого эффекта в будущем. stackoverflow.com/questions/13113131/… Просто мой 2с по этому поводу
На всякий случай, если кто-нибудь столкнется с моей проблемой: если имя .dll содержит дефисы (например, twenty-two.dll), они также будут заменены подчеркиванием (например, twenty_two.dll). Вы можете изменить эту строку кода на это: dllName = dllName.Replace(".", "_").Replace("-", "_");
Когда я это сделаю, мое решение не будет компилироваться. Выдает ошибку везде, где я использовал класс из dll
@ Notts90 Вам все равно нужно ссылаться на библиотеку для компиляции решения
ILMerge может объединять сборки в одну сборку при условии, что сборка имеет только управляемый код. Вы можете использовать приложение командной строки или добавить ссылку на exe и программно слить. Для версии с графическим интерфейсом есть Eazfuscator, а также .Netz, оба из которых бесплатны. Платные приложения включают BoxedApp и SmartAssembly.
Если вам нужно объединить сборки с неуправляемым кодом, я бы предложил SmartAssembly. У меня никогда не было икоты с SmartAssembly, но со всеми остальными. Здесь он может встроить необходимые зависимости в качестве ресурсов в ваш основной исполняемый файл.
Вы можете сделать все это вручную, не беспокоясь о том, управляется ли сборка или в смешанном режиме, путем встраивания dll в свои ресурсы и последующего использования сборки ResolveHandler AppDomain. Это универсальное решение, основанное на худшем случае, то есть сборках с неуправляемым кодом.
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
Ключевым моментом здесь является запись байтов в файл и загрузка из его местоположения. Чтобы избежать проблем с курицей и яйцом, вы должны убедиться, что вы объявили обработчик перед доступом к сборке и что вы не имеете доступа к элементам сборки (или не создаете экземпляры чего-либо, что имеет дело со сборкой) внутри части загрузки (разрешения сборки). Также убедитесь, что GetMyApplicationSpecificPath() не является временным каталогом, поскольку временные файлы могут быть удалены другими программами или вами (не то, чтобы они были удалены, пока ваша программа обращается к dll, но, по крайней мере, это неприятно. хорошее расположение). Также обратите внимание, что вам нужно записывать байты каждый раз, вы не можете загружать из местоположения только потому, что dll уже находится там.
Для управляемых dll вам нужно не записывать байты, а загружать напрямую из местоположения dll или просто читать байты и загружать сборку из памяти. Примерно так:
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
return Assembly.Load(data);
}
//or just
return Assembly.LoadFrom(dllFullPath); //if location is known.
Если сборка полностью неуправляемая, вы можете увидеть этот связь или это относительно того, как загружать такие библиотеки DLL.
Обратите внимание, что для ресурса «Build Action» необходимо установить значение «Embedded Resource».
@Mavamaarten Не обязательно. Если он заранее добавлен в файл Resources.resx проекта, в этом нет необходимости.
EAZfuscator теперь коммерческий.
Ни подход ILMerge, ни обработка события AssemblyResolve Ларсом Холмом Дженсеном не будут работать для хоста плагина. Скажем, исполняемый файл ЧАС динамически загружает сборку п и обращается к ней через интерфейс IP, определенный в отдельной сборке. Чтобы встроить IP в ЧАС, потребуется небольшая модификация кода Ларса:
Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{ Assembly resAssembly;
string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if ( !loaded.ContainsKey( dllName ) )
{ if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
resAssembly = System.Reflection.Assembly.Load(bytes);
loaded.Add(dllName, resAssembly);
}
else
{ resAssembly = loaded[dllName]; }
return resAssembly;
};
Уловка для обработки повторяющихся попыток разрешить одну и ту же сборку и вернуть существующую вместо создания нового экземпляра.
Обновлено: Чтобы не испортить сериализацию .NET, убедитесь, что возвращено значение null для всех сборок, не встроенных в вашу, что приведет к стандартному поведению по умолчанию. Вы можете получить список этих библиотек:
static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{ IncludedAssemblies.Add(resources[i]); }
и просто верните null, если переданная сборка не принадлежит IncludedAssemblies.
Извините за то, что разместил это как ответ, а не как комментарий. Я не имею права комментировать чужие ответы.
отрывок Джеффри Рихтера очень хорошо. Короче говоря, добавьте библиотеку как встроенные ресурсы и добавьте обратный вызов прежде всего. Вот версия кода (найдена в комментариях к его странице), которую я поместил в начале метода Main для консольного приложения (просто убедитесь, что все вызовы, использующие библиотеку, находятся в другом методе, чем Main).
AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
{
String dllName = new AssemblyName(bargs.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null; // Not found, maybe another handler will find it
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Немного изменил, сделал работу, приятель!
Проект libz.codeplex.com использует этот процесс, но он также будет делать некоторые другие вещи, например, управлять обработчиком событий для вас и некоторым специальным кодом, чтобы не нарушать "Каталоги управляемой платформы расширяемости" (который сам по себе этот процесс сломал бы)
Замечательно!! Спасибо @Steve
Я настоятельно рекомендую использовать Costura.Fody - безусловно, лучший и простой способ встраивать ресурсы в вашу сборку. Он доступен как пакет NuGet.
Install-Package Costura.Fody
После добавления его в проект он автоматически вставит все ссылки, которые копируются в выходной каталог, в вашу сборку основной. Возможно, вы захотите очистить встроенные файлы, добавив цель в свой проект:
Install-CleanReferencesTarget
Вы также сможете указать, следует ли включать pdb, исключать определенные сборки или извлекать сборки на лету. Насколько мне известно, поддерживаются и неуправляемые сборки.
Обновлять
В настоящее время некоторые люди пытаются добавить поддержка DNX.
Обновление 2
Для последней версии Fody вам потребуется MSBuild 16 (то есть Visual Studio 2019). Fody версии 4.2.1 поддерживает MSBuild 15. (ссылка: Fody поддерживается только в MSBuild 16 и выше. Текущая версия: 15)
Спасибо за это замечательное предложение. Установите пакет, и все готово. По умолчанию он даже сжимает сборки.
Ненавижу быть «я тоже», но и я тоже - это избавило меня от головной боли! Спасибо за рекомендацию! Это позволило мне упаковать все, что мне нужно для распространения, в один exe, и теперь он меньше, чем исходный exe и библиотеки, которые были объединены ... Я использую это всего несколько дней, поэтому я не могу сказать, что я ' Я прошел через это испытание, но если не произойдет чего-нибудь плохого, я вижу, что это стало обычным инструментом в моем наборе инструментов. Просто работает!
Это круто. Но есть недостаток: сборка, созданная в Windows, больше не двоично совместима с моно Linux. Это означает, что вы не можете развернуть сборку в Linux mono напрямую.
У Costura, похоже, нет крючков для автоматического обновления через Интернет. См. Мой вопрос: stackoverflow.com/questions/28323709/…
Создание чистого выходного каталога Существует также командлет PowerShell для автоматической установки этой цели в ваш проект. В консоли диспетчера пакетов введите: PM> Install-CleanReferencesTarget
Он отлично работал с Visual Studio 2013, но я больше не работаю с VS 2015 (Enterprise) - у кого-нибудь такая же проблема!?!?
@CeOnSql У меня такая же проблема в Windows 8.1 и 10 с сообществом VS 2015. Если у кого-то есть решение, дайте мне знать!
@ alexy13: Поскольку у меня такая репутация благодаря этому посту, я чувствую ответственность за его обновление. К сожалению, сейчас я не могу это проверить. Я был бы очень признателен, если бы один из вас создал здесь проблему github.com/Fody/Costura/issues.
@Siva: Спасибо за подсказку. Добавил в пост.
Могу ли я включить текстовый файл в exe ?? У меня тоже есть родная библиотека dll и txt. Могу ли я сделать с помощью Costura.Fody только один exe-файл ??
Я нашел эти два: exepack.codeplex.com И visualstudiogallery.msdn.microsoft.com/…
@Ciprian на самом деле я не использовал костуру.
Кто-нибудь хотел бы предоставить образец того, как вы сливаете это запутанным образом? Я имею в виду, что .exe запутывается перед слиянием.
Просто попробовал с VS 2015 R3 и сработал. Уменьшение общего размера файла для загрузки на 40%. Жаль, что имя NuGet так сложно запомнить (никогда бы не нашел его, если бы не этот ответ).
Просто голова вверх .. У меня были серьезные проблемы с установкой Costura.Fody с использованием всех обновлений VS 2013. Очевидно, последняя сборка 1.4.1 не работает с 2013 годом. Я успешно установил 1.4 из консоли nuget, но у меня были ошибки компилятора. Я закончил тем, что вручную добавил их в качестве ресурсов проекта, поскольку подход Fody в конце концов потратил мое время. Я могу обновить его, как только закончу свой проект, потому что простота и сжатие звучат УДИВИТЕЛЬНО
Кстати ... У меня был проект с использованием Fody в Visual Studio 2012. Все работало КРАСИВО! Теперь я обновился до Visual Studio 2017, и Fody полностью разочаровался; Совсем не работает.
@CeOnSql, а alexy13 отлично работает в VS2015 обновление 3, 14.0.254431.01
Спасатель. Для людей, которые не знают, как установить Costura.Fody, просто зайдите в VS, в Инструменты-> Диспетчер пакетов NuGet-> Консоль диспетчера пакетов. Итак, вы переходите к команде «Install-Package Costura.Fody». Перестройте приложение, и все будет хорошо. Просто как тот! Не будучи здесь высокомерным, мне тоже пришлось разобраться в себе.
он не встраивает "SQLite.Interop.dll" из библиотеки System.Data.SQLite.Core
Это прекрасно! Если вы используете vs2018, не забудьте, что файл FodyWeavers.xml должен находиться в корне вашего проекта.
Это действительно прекрасно и в 2018 году! :) Очень полезно!
В качестве дополнения к последнему комментарию: добавьте в свой проект файл FodyWeavers.xml следующего содержания: <? Xml version = "1.0" encoding = "utf-8"?> <Weavers VerifyAssembly = "true"> <Costura /> </Weavers>
Все еще работает в 2018 году, но вам нужно следовать руководству из readme.md здесь github.com/Fody/Costura
это работа со ссылками, добавленными из NUGETPackages. Но не работает, если я добавлю решение существующего проекта в качестве ссылки на него. Может кто-нибудь помочь, почему это?
Для тех, у кого ошибка MSBuild: stackoverflow.com/a/55843973/6674014
Для меня это не решение - Symantec пометила этот пакет Nuget как вирус и автоматически удалила его.
Команда консоли диспетчера пакетов Install-CleanReferencesTarget больше недействительна и завершится ошибкой. В текущих версиях это автоматизировано. Также для Visual Studio 2017 (с использованием MSBuild 15) установите Nugets Fody 4.2.1 и Costura.Fody 3.3.3 для успешной компиляции.
Это может показаться упрощенным, но WinRar дает возможность сжать кучу файлов в самораспаковывающийся исполняемый файл. Он имеет множество настраиваемых параметров: конечный значок, извлечение файлов по заданному пути, файл для выполнения после извлечения, собственный логотип / тексты для всплывающих окон во время извлечения, отсутствие всплывающих окон, текст лицензионного соглашения и т. д. Может быть полезно в некоторых случаях.
В самой Windows есть аналогичный инструмент под названием iexpress. Вот учебник
Чтобы развернуть на @ Ответ Бобби выше. Вы можете отредактировать свой .csproj, чтобы использовать Ил-Репак для автоматической упаковки всех файлов в одну сборку при сборке.
Install-Package ILRepack.MSBuild.TaskВот простой пример, который объединяет ExampleAssemblyToMerge.dll с выходными данными вашего проекта.
<!-- ILRepack -->
<Target Name = "AfterBuild" Condition = "'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include = "$(OutputPath)$(AssemblyName).exe" />
<InputAssemblies Include = "$(OutputPath)\ExampleAssemblyToMerge.dll" />
</ItemGroup>
<ILRepack
Parallel = "true"
Internalize = "true"
InputAssemblies = "@(InputAssemblies)"
TargetKind = "Exe"
OutputFile = "$(OutputPath)$(AssemblyName).exe"
/>
</Target>
Синтаксис IL-Repack изменился, проверьте README.md, который находится в связанном репозитории github (github.com/peters/ILRepack.MSBuild.Task). Этот способ был единственным, который у меня сработал, и я смог использовать подстановочный знак, чтобы сопоставить все библиотеки DLL, которые я хотел включить.
Я использую компилятор csc.exe, вызываемый из сценария .vbs.
В вашем скрипте xyz.cs добавьте следующие строки после директив (мой пример для Renci SSH):
using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly
//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"
Теги ref, res и ico будут взяты приведенным ниже сценарием .vbs для формирования команды csc.
Затем добавьте вызывающий преобразователь сборки в Main:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
.
... и добавьте сам резолвер где-нибудь в классе:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
String resourceName = 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);
}
}
Я называю сценарий vbs, чтобы он соответствовал имени файла .cs (например, ssh.vbs ищет ssh.cs); это многократно упрощает выполнение сценария, но если вы не такой идиот, как я, то общий сценарий может выбрать целевой файл .cs путем перетаскивания:
Dim name_,oShell,fso
Set oShell = CreateObject("Shell.Application")
Set fso = CreateObject("Scripting.fileSystemObject")
'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME
'################################################
name_ = Split(wscript.ScriptName, ".")(0)
'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE
'#######################################################
Const OPEN_FILE_FOR_READING = 1
Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1)
'READ EVERYTHING INTO AN ARRAY
'#############################
inputData = Split(objInputFile.ReadAll, vbNewline)
For each strData In inputData
if left(strData,7) = "//+ref>" then
csc_references = csc_references & " /reference:" & trim(replace(strData,"//+ref>","")) & " "
end if
if left(strData,7) = "//+res>" then
csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " "
end if
if left(strData,7) = "//+ico>" then
csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " "
end if
Next
objInputFile.Close
'COMPILE THE FILE
'################
oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2
WScript.Quit(0)
Эта функция включается при использовании следующего свойства в файле проекта (.csproj):
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
Это делается без каких-либо внешних инструментов.
См. Мой ответ на этот вопрос для получения дополнительной информации.
Следующий метод НЕ используйте внешние инструменты и АВТОМАТИЧЕСКИ включить все необходимые DLL (никаких ручных действий не требуется, все делается при компиляции)
Я читал здесь много ответов, в которых говорилось об использовании метода ILMerge, ILRepack или Джеффри Ритчер, но ни один из них не работал с Приложения WPF и не был простым в использовании.
Когда у вас много DLL, может быть сложно вручную включить ту, которая вам нужна в исполняемый файл. Лучший метод, который я нашел, был объяснен Wegged здесь, в StackOverflow.
Скопируйте сюда его ответ для ясности (все кредиты Wegged)
.csproj:<Target Name = "AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include = "@(ReferenceCopyLocalPaths)" Condition = "'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
Program.cs выглядел так:[STAThreadAttribute]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
App.Main();
}
OnResolveAssembly:private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
var path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null) return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
Это возможно, но вы получите большой исполняемый файл (Base64 будет использоваться для кодирования вашей dll).