Merging a WPF application into a single EXE(WPF应用程序合并成单个Exe文件)
2014-02-24 00:34
357 查看
I always dislike handing off little applications to people. Not because I can’t, but because of the steps involved to make sure it all just works. Small apps are the most problematic because I never want to take the time to create a whole installer project for just a few assemblies, and packaging up a zip file must be accompanied by “Unzip this into a folder in your programs directory and create a shortcut…” which brings us back to the whole installer business we started with.
There are a few tools already out there such as ILMerge by Microsoft Research (Which works great for most .NET-y things, but chokes on WPF applications) and a few paid tools by third party vendors that you could fork over a few hundred for to get. But, I’m a developer, which means I want to do it the Hard Way™. I did a little research and found the following blog posts on setting up and merging in DLL’s as resources into the main assembly and then extracting and loading them into memory when you run your application.
Links:
http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/
http://blog.mahop.net/post/Merge-WPF-Assemblies.aspx
http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application
There were a few things I didn’t like about each solution. The first one (richarddingwall.name) ends up having you directly adding the .dll’s as resources directly. I hate maintaining things manually, especially when it will run fine on my machine but break when when I move it somewhere else because I forgot to update the resources when I added a new project. The one from blog.mahop.net builds on the previous one and changes the resolve location to a custom class with its own startup method. Better, because it resolves the resources earlier. Finally, the one from Daniel Chambers (digitallycreated.net) added in the final piece that automatically including the assemblies as resources. Unfortunately, the way he looks for culture specific assemblies didn’t work and I had to remove / change it to be closer to the one on mahop.net.
Final solution I’m currently using is as follows:
To the main executable project, unload and edit the .csproj file, and below the following line:
Add this XML to the project file, save, and load it back up.
It should look something like this when your done:
You’ll then add a new code file to the main project and add the following code to it (modified to fit how your application is named / structured, in a WPF application, a good place to put it would be
Finally, make sure you update the target method for your main application to be the main method for the project you just added:
And, that’s it!
When you build your application you’ll still see all the assemblies in the output directory, but you should be able to take just the executable, move it somewhere else, and run it just as it is.
There are a few tools already out there such as ILMerge by Microsoft Research (Which works great for most .NET-y things, but chokes on WPF applications) and a few paid tools by third party vendors that you could fork over a few hundred for to get. But, I’m a developer, which means I want to do it the Hard Way™. I did a little research and found the following blog posts on setting up and merging in DLL’s as resources into the main assembly and then extracting and loading them into memory when you run your application.
Links:
http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/
http://blog.mahop.net/post/Merge-WPF-Assemblies.aspx
http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application
There were a few things I didn’t like about each solution. The first one (richarddingwall.name) ends up having you directly adding the .dll’s as resources directly. I hate maintaining things manually, especially when it will run fine on my machine but break when when I move it somewhere else because I forgot to update the resources when I added a new project. The one from blog.mahop.net builds on the previous one and changes the resolve location to a custom class with its own startup method. Better, because it resolves the resources earlier. Finally, the one from Daniel Chambers (digitallycreated.net) added in the final piece that automatically including the assemblies as resources. Unfortunately, the way he looks for culture specific assemblies didn’t work and I had to remove / change it to be closer to the one on mahop.net.
Final solution I’m currently using is as follows:
To the main executable project, unload and edit the .csproj file, and below the following line:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Add this XML to the project file, save, and load it back up.
<Target Name="AfterResolveReferences"> <ItemGroup> <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'"> <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName> </EmbeddedResource> </ItemGroup> </Target>
It should look something like this when your done:
You’ll then add a new code file to the main project and add the following code to it (modified to fit how your application is named / structured, in a WPF application, a good place to put it would be
App.xaml.cs):
[STAThread] public static void Main() { AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly; App.Main(); // Run WPF startup code. } private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e) { var thisAssembly = Assembly.GetExecutingAssembly(); // Get the Name of the AssemblyFile var assemblyName = new AssemblyName(e.Name); var dllName = assemblyName.Name + ".dll"; // Load from Embedded Resources - This function is not called if the Assembly is already // in the same folder as the app. var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName)); if (resources.Any()) { // 99% of cases will only have one matching item, but if you don't, // you will have to change the logic to handle those cases. var resourceName = resources.First(); using (var stream = thisAssembly.GetManifestResourceStream(resourceName)) { if (stream == null) return null; var block = new byte[stream.Length]; // Safely try to load the assembly. try { stream.Read(block, 0, block.Length); return Assembly.Load(block); } catch (IOException) { return null; } catch(BadImageFormatException) { return null; } } } // in the case the resource doesn't exist, return null. return null; }
Finally, make sure you update the target method for your main application to be the main method for the project you just added:
And, that’s it!
When you build your application you’ll still see all the assemblies in the output directory, but you should be able to take just the executable, move it somewhere else, and run it just as it is.
相关文章推荐
- #708 – 将文件拖入到WPF应用程序中(Dragging a File Into a WPF Application)
- 用ILmerge工具将C#中的EXE和DLL文件合并成单个文件
- WPF中应用程序exe的自带config文件
- 用ILmerge工具将C#中的EXE和DLL文件合并成单个文件
- WPF(四)Application3:如何实现单实例的应用程序(Single Instance)
- 通过MageUi.exe修改通过ClickOnce发布过的WPF browser application 配置文件
- 嵌入资源第二讲:WPF调用嵌入的非.net的EXE资源文件
- Use py2exe compile python file into exe file(用py2exe把python文件编译成exe文件)
- 改造独立部署(SCD)模式下.NET Core应用程序 dotnet的exe文件启动过程
- 用wpf实现了多个excel文件的合并
- 【有效】关闭chm文件,hh.exe应用程序错误
- VS2012编译出来的程序,在XP上运行,出现“.exe 不是有效的 win32 应用程序” “not a valid win32 application”
- 【转载】用PyInstaller把Python代码打包成单个独立的exe可执行文件
- Wpf应用程序作为一个单独的可执行文件
- 【DEBUG笔记】执行exe文件时报错“应用程序无法正常启动(0x000007b)”
- MFC应用程序图标修改后exe文件没有立即生效问题
- WPF:将Office文档、任意类型文件嵌入到EXE可执行文件中
- WPF读写excel的完整示例-excel文件合并工具
- [C#]使用ILMerge来合并dll以及将dll合并进exe文件中
- 编译 asp 应用程序成为 exe 文件