• WPF程序将DLL嵌入到EXE的两种方法


    WPF程序将DLL嵌入到EXE的两种方法

    这一篇可以看作是《Visual Studio 版本转换工具WPF版开源了》的续,关于《Visual Studio 版本转换工具WPF版开源了》可以参看地下地址(两篇是一样的):

    1. 开源中国 http://my.oschina.net/chinesedragon/blog/308336
    2. CNBLOGS http://www.cnblogs.com/luoshupeng/p/3946635.html

    ** 博客园的Markdown编辑器真的不行,还是麻烦大家稳步到http://my.oschina.net/chinesedragon/blog/309223观看吧 **

    引言

    前几一写了一个小工具————《Visual Studio版本转换工具》,由于使用了WPF做为界面,因此这个小程序运行必须附带两个DLL:Microsoft.Expression.Interactions.dll和System.Windows.Interactivity.dll,同时由于自己也写了一个库,一个小程序需要附带3个DLL,这种体验真的很不爽,于是就着手把DLL嵌入到EXE中去。

    挫折

    对于C#程序,要把DLL嵌入到EXE中去,最权威和最常见的方法就是使用ILMerge这个工具,这是个命令行工具,有很多参数,可以将DLL很完美的嵌入到EXE中去,如果嫌命令行麻烦,也有人开源开发了图形界面ILMergeGUI,这两个工具的下载和帮助地址如下:

    1. ILMerge http://www.microsoft.com/en-us/download/details.aspx?id=17630
    2. ILMerge-GUI http://ilmergegui.codeplex.com/
      于是下载了这两个工具,却出现将DLL嵌入到EXE中错误的情况,在网上查了下原因,竟然是ILMerge不支持WPF程序,我勒了个去,微软,你让我说你什么好呢?
      ILMerge可以将Winform程序的DLL完美地嵌入到EXE中去(这一点我亲自测试了下,很不,点赞),但对WPF却不支持,原因是WPF的DLL中含有资源无法解决,微软,你好意思说这个工具是你开发的吗?

    Winform程序将DLL嵌入到EXE中(一)——使用命令行 ##

    下载ILMerge或者同时下载ILMerge-GUI,使用图形界面和使用命令行是同一个道理,只是图形界面简单些,所以这里以命令行说明。
    我是下载ILMerge安装后,把ILMerge.exe复制到C:Windows目录下去了,这样可以直接在命令行中使用而不用去设置环境变量,不管怎样,只要能够在命令行下使用这个工具就行。
    ILMerge有很多参数,其中有几个比较重要:

    1. /target:目标,有library和Winexe两种选择,当将多个DLL整合成一个DLL时可以使用library,当要整合为EXE时,应该使用Winexe.
    2. /out:输出,最终生成文件的路径和名称.
    3. /log:输入,如果输入是EXE时可以不使用此参数直接写,而如果输入时DLL时,最好使用此参数
      还有其它一些参数,使用时可以百度或者google一下,这是我测试的一张图片:

    Winform程序将DLL嵌入到EXE中(二)——使用ILMerge.MSBuild.Tasks

    ILMerge也使用Nuget发布了工具,使用Nuget的好处想必大家都知道,所以推荐使用这种方法
    第一步, 使用Nuget图形或Nuget命令下载ILMerge.MSBuild.Tasks

    PM> Install-Package ILMerge.MSBuild.Tasks   
    

    第二步, 把VS项目文件记事本或者其它文本编辑工具打开,我使用的是Sublime Text 3,并按照如下格式根据实际情况修改:

    <!-- Code to merge the assemblies into one:setup.exe -->  
    <UsingTask TaskName="ILMerge.MSBuild.Tasks.ILMerge" AssemblyFile="$(SolutionDir)packagesILMerge.MSBuild.Tasks.1.0.0.3	oolsILMerge.MSBuild.Tasks.dll" />  
    <Target Name="AfterBuild">  
        <ItemGroup>
          <MergeAsm Include="$(OutputPath)$(TargetFileName)" />
          <MergeAsm Include="$(OutputPath)LIB1_To_MERGE.dll" /> <!-- 这儿改成需要做嵌入的dll名 -->
          <MergeAsm Include="$(OutputPath)LIB2_To_MERGE.dll" />
        </ItemGroup>
        <PropertyGroup>
          <MergedAssembly>$(ProjectDir)$(OutDir)MERGED_ASSEMBLY_NAME.exe</MergedAssembly><!-- 这儿改成需要做输出的exe名 -->
        </PropertyGroup>
    	<Message Text="ILMerge @(MergeAsm) -&gt; $(MergedAssembly)" Importance="high" />
    	<ILMerge InputAssemblies="@(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" />
    </Target>
    

    这样编译后就可以了。

    WPF程序将DLL嵌入到EXE中(一)——将DLL自动转换为嵌入资源

    第一步,修改项目文件,将DLL自动转换为嵌入资源。
    把VS项目文件记事本或者其它文本编辑工具打开,我使用的是Sublime Text 3,并将下面内容添加到文件末尾:

    <Target Name="AfterResolveReferences">
        <ItemGroup>
          <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
            <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
          </EmbeddedResource>
        </ItemGroup>
    </Target>
    

    第二步,修改App.xaml文件,在程序启动时加载资源

    public partial class App : Application
    {
        private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
        {
            Assembly executingAssembly = Assembly.GetExecutingAssembly();
            var executingAssemblyName = executingAssembly.GetName();
            var resName = executingAssemblyName.Name + ".resources";
    
            AssemblyName assemblyName = new AssemblyName(args.Name); string path = "";
            if (resName == assemblyName.Name)
            {
                path = executingAssemblyName.Name + ".g.resources"; ;
            }
            else
            {
                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);
            }
        }
    
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
        }
    }
    

    第三步,dll嵌入exe后,目录中的dll就没用了,配置Post buid 脚本自动删除dll:

    cd $(TargetDir)
    del *.dll
    

    有些情况下,以上方法也不行,那么可以尝试 Eazfuscator.NET
    Eazfuscator.NET以前免费,现在已经变成收费软件了,不过找个免费的3.3版本也可以支持VS2010和VS2012

    WPF程序将DLL嵌入到EXE中(二)——使用LibZ Container

    LibZ是ILMerge的另外一个选择,它同样可以把DLL嵌入到EXE中去,在我的测试中它可以完成WPF程序的DLL嵌入到EXE中去,但好像这个组件使用的人不是很多。
    LibZ Container的项目主页是http://libz.codeplex.com/
    LibZ同样提供了Nuget下载,使用Nuget有很多好处,所以推荐使用这种方式
    使用Nuget图形或者命令下载LibZ.Bootstrap

    Install-Package LibZ.Bootstrap
    

    然后,配置Post buid 脚本:

    set LIBZ=$(SolutionDir)packagesLibZ.Bootstrap.1.1.0.2	oolslibz.exe
    %LIBZ% inject-dll --assembly VSConverter.WPF.exe --include *.dll --move
    

    编译通过后就可以了。这里需要注意的是--assembly后的参数是项目生成的文件名.
    LibZ还有很多用法,可以到项目文档学习。

    参考资料

    1. Combining multiple assemblies into a single EXE for a WPF application
    2. DLL嵌入exe中
    3. LibZ项目

    Nuget是个十分强大的工具,使用Nuget在很多时候可以使解决方法变得简单,给Nuget赞一个!
    再做下广告,Visual Studio版本转换工具WPF版的代码托管地址是:http://git.oschina.net/shupengluo/VSConverter,欢迎交流。

    最后,再小小地鄙视下微软,_

  • 相关阅读:
    vue中使用ztree时报找不到init方法错的解决方案
    element-ui 自定义表单验证 , 但是不出现小红心解决方案
    iframe子页面与父页面之间通信
    GSS系列题解——最大子段和系列
    平衡树 区间树 学习
    数据结构:从入门到精通
    后缀数组
    2019元旦总结,迎接2019
    网络流+费用流 学习
    SLT学习——leafes tree扩展 【文艺平衡树】
  • 原文地址:https://www.cnblogs.com/luoshupeng/p/3951597.html
Copyright © 2020-2023  润新知