Объединение библиотек в единое.exe с wpf
В настоящее время я работаю над проектом, в котором у нас много зависимостей. Я хотел бы скомпилировать все ссылки dll в .exe очень похоже на то, что вы сделали бы со встроенными ресурсами. Я пробовал ILMerge , но он не может справиться .ресурсы xaml.
Итак, мой вопрос: есть ли способ объединить проект WPF с несколькими зависимостями в один?отлично?
9 ответов:
.NET reactor имеет особенность слияния сборок и его не очень дорого.
Это сработало как заклинание для меня :) и совершенно бесплатно.
Добавление кода на случай, если блог когда-нибудь исчезнет.
1) Добавьте это в свой файл.csproj
:<Target Name="AfterResolveReferences"> <ItemGroup> <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'"> <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName> </EmbeddedResource> </ItemGroup> </Target>
2) Сделайте свой основной
Program.cs
таким образом:[STAThreadAttribute] public static void Main() { AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly; App.Main(); }
3) Добавить
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); } }
Costura Fody-это инструмент с открытым исходным кодом, предназначенный для обработки слияния сборок wpf.
{smartassembly} является одним из таких продуктов. Он может обсфуцировать или встраивать ваши библиотеки DLL.
Попробуйте это: http://www.smartassembly.com/
Вы также можете сделать много улучшений в вашем приложении, чтобы оно работало быстрее.
И да. Вы можете использовать его для WPF.
Обновление 8/06/2015: ILRepack 2.0.0 (который является альтернативой ILMerge с открытым исходным кодом) теперь поддерживает большинство случаев слияния WPF: https://twitter.com/Gluckies/status/607680149157462016
Как размещено на веб-сайте ILMerge, рассматривайте эти библиотеки DLL как ресурсы, начиная с Джеффри Рихтера здесь :
Многие приложения состоят из EXE-файла, который зависит от многих DLL файлы. При развертывании этого приложения все файлы должны быть развертываемый. Однако существует техника, которую можно использовать для развертывания только один исполняемый файл. Во-первых, определите все DLL-файлы, которые ваш EXE файл зависит от того, что не поставляется в составе Microsoft .NET Сама структура. Затем добавьте эти библиотеки DLL в проект Visual Studio. Для каждого добавляемого файла DLL отобразите его свойства и измените их "Построить действие" для " встроенного ресурса."Это приводит компилятор C# к вставьте DLL-файл (ы) в свой EXE-файл, и вы можете развернуть этот файл файл EXE. Во время выполнения среда CLR не сможет найти зависимый объект Сборки DLL, что является проблемой. Чтобы исправить это, когда ваше приложение инициализирует, регистрирует метод обратного вызова в домене приложения. Событие 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 и загрузить его, вызвав перегрузку метода загрузки сборки, который принимает байт[] в качестве аргумента.
Используйте Costura.Fody - он доступен в виде Nuget Pkg для лучшего и простого способа встраивания ресурсов в вашу сборку.
Install-Package Costura.Fody
После добавления его в проект, он будет автоматически вставлять все добавленные ссылки на вашу основную сборку.
Попробуй .Нец (http://madebits.com/netz/) - это бесплатно (как в пиве)и делает некоторые приятные вещи, если вы цель является exe.
Вот измененная версия цитируемого кода из Matthieu, которая не требует знания пространства имен для извлечения кода. Для WPF поместите это в код события запуска приложения.
AppDomain.CurrentDomain.AssemblyResolve += (s, args) => { // Note: Requires a using statement for System.Reflection and System.Diagnostics. Assembly assembly = Assembly.GetExecutingAssembly(); List<string> embeddedResources = new List<string>(assembly.GetManifestResourceNames()); string assemblyName = new AssemblyName(args.Name).Name; string fileName = string.Format("{0}.dll", assemblyName); string resourceName = embeddedResources.Where(ern => ern.EndsWith(fileName)).FirstOrDefault(); if (!string.IsNullOrWhiteSpace(resourceName)) { using (var stream = assembly.GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); var test = Assembly.Load(assemblyData); string namespace_ = test.GetTypes().Where(t => t.Name == assemblyName).Select(t => t.Namespace).FirstOrDefault(); #if DEBUG Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", fileName, namespace_)); #endif return Assembly.Load(assemblyData); } } return null; };
Чтобы сделать их доступными во время компиляции, я создаю папку с именем ExternalDLLs и копирую библиотеки DLL туда и устанавливаю их в EmbeddedResource, как отмечалось выше. Чтобы использовать их в своем коде, вам все равно нужно установить ссылку на них, но установить Copy local в False. Чтобы получить код для компиляции чисто без ошибки также необходимо задать с помощью statments в коде пространства имен библиотек DLL.
Вот небольшая утилита, которая прокручивает имена встроенных ресурсов и отображает их пространства имен в окне вывода.
private void getEmbeddedResourceNamespaces() { // Note: Requires a using statement for System.Reflection and System.Diagnostics. Assembly assembly = Assembly.GetExecutingAssembly(); List<string> embeddedResourceNames = new List<string>(assembly.GetManifestResourceNames()); foreach (string resourceName in embeddedResourceNames) { using (var stream = assembly.GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); try { var test = Assembly.Load(assemblyData); foreach (Type type in test.GetTypes()) { Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", type.Name, type.Namespace)); } } catch { } } } }
- Добавьте это к .файл csprofj:
>
<Target Name="AfterResolveReferences"> <ItemGroup> <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'"> <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName> </EmbeddedResource> </ItemGroup> </Target>
Щелкните правой кнопкой мыши проект / свойства / приложение / объект запуска / выберите Sinhro.Программа
Добавьте это в свою программу.cs-файл:
Использование Системы.Отражение; использование System.IO; использование системы.Глобализация;
[STAThreadAttribute] static void Main() { AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly; ... private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); AssemblyName assemblyName = new AssemblyName(args.Name); string 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; byte[] assemblyRawBytes = new byte[stream.Length]; stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length); return Assembly.Load(assemblyRawBytes); } }