使用ilmerge实现.net程序静态链接

当编写规模稍大一点的程序时,需要进行模块划分。我们一般是对每个模块建一个工程,输出一个类库,这样就形成了一个exe加多个类库的解放方案。这样本身是没有什么问题的,就是发布上稍微有点麻烦,需要发布一个exe加多个dll文件。特别是那些需要经常更新的程序,其实本身可能并不大,一共也就几百kb,但每次还要发布三四个dll确实令人厌烦,做成安装包就更没必要了。

在C++中,我们一般可以通过静态链接解决这种问题,但在.net中却不行,每个类库只能输出dll,VisualStudio官方也没有提供静态链接的选项,其实从理论上讲要想合并似乎也不难,ildasm能够把.NET程序反编译成il,那么把两个il合并到一个il当中再用il编译就可以了。虽然M$却没有直接提供这种功能,但M$的一位大牛提供了一个工具ILMerge可以实现这种功能,可以在http://research.microsoft.com/~mbarnett/ILMerge.aspx下载。

网上也有一些文章对这个工具进行了介绍,margiex的blog中的说明就介绍的较简明扼要,这里转录一下:

ilmerge /ndebug /target:exe /targetplatform:v1.1 /out:newclient.exe oldclient.exe /log AutoUpdater.dll DockingSuite.dll DocumentManager.dll FileHelpers.dll OutlookBar.dll SandBar.dll ICSharpCode.SharpZipLib.dll

解释如下:

  1. target: 指定输出组件的类型,有DLL/EXE/WINEXE; (如果是EXE,则程序在执行的时候将出现一个DOS窗口(即使是WINFORM的程序). 因此应该设置target为winexe)
  2. targetplatform: 输出组件的.net运行版本;
  3. out: 合并后的组件全名;
    后面是主要的被合并的组件名称, 及相关所有的其它DLL;
    上面的示例只是合并为EXE, 也可以多个DLL合并成一个DLL使用, 各自的命名空间不会改变;
  • 不能合并interop这种由COM转换过来的DLL; (可以合并,但相关依赖的DLL必须也一起合并, 第一次合并的时候只有Excel.dll,总是报错,以为是interop的缘故,后来才发现是没有合并vbide.dll的缘故,复制到目录再一起合并,一切OK.)
  • 如果有资源DLL, 应该将要被合并的oldclient.exe先改为别的名称,然后合并后的输出命名为:oldclient.exe,因为资源文件名是:oldclient.resources.dll,而ILMERGE不能合并资源文件,否则在导出后将找不到此资源文件。(如果哪位知道如何合并资源文件,请指教,谢谢)
  • 虽然合并后的EXE比较大,但在用户那里只有一个EXE,这样直观的多,也容易升级维护.

原文地址:http://www.cnblogs.com/margiex/archive/2008/06/24/302329.html

具体的详细使用文档请参看它的说明文档,还是很简单的。

另外,也可以通过编程的方式来调用ILMerge的库来实现简化合并,这里转录一下CodeProject上的一篇文章Post build step static linking tool for C#, using ILMerge的代码:

using System;
using System.Text;
using System.Collections;

namespace HTMerge
{
    class Program
    {   
        static void Main(string[] args)
        {
            try
            {
                String strDir = “”;
                if (args.Length != 1)
                {
                    Console.WriteLine(“Usage: HTMerge directoryName”);
                    return;
                }
                else
                {
                    strDir = args[0];
                }

                String[] exeFiles = System.IO.Directory.GetFiles(strDir, “*.exe”);
                String[] dllFiles = System.IO.Directory.GetFiles(strDir, “*.dll”);

                ArrayList ar = new ArrayList();

                Boolean bAdded = false;

                //there might be more than 1 exe file, 
                //we go for the first one that isn’t the vshost exe
                foreach (String strExe in exeFiles)
                {
                    if (!strExe.Contains(“vshost”))
                    {
                        ar.Add(strExe);
                        bAdded = true;
                        break;
                    }
                }

                if (!bAdded)
                {
                    Console.WriteLine(“Error: No exe could be found”);
                    //I know multiple returns are bad�
                    return;
                }

                bAdded = false;

                foreach (String strDLL in dllFiles)
                {
                    ar.Add(strDLL);
                    bAdded = true;
                }

                //no point merging if nothing to merge with!
                if (!bAdded)
                {
                    Console.WriteLine(“Error: No DLLs could be found”);
                    //I know multiple returns are bad�
                    return;
                }

                //You will need to add a reference to ILMerge.exe from Microsoft
                //See http://research.microsoft.com/~mbarnett/ILMerge.aspx
                ILMerging.ILMerge myMerge = new ILMerging.ILMerge();

                String[] files = (String[])ar.ToArray(typeof(string));

                String strTargetDir = strDir + “\\Merged”;

                try
                {
                    System.IO.Directory.CreateDirectory(strTargetDir);
                }
                catch
                {
                }

                //Here we get the first file name 
                //(which was the .exe file) and use that
                // as the output
                String strOutputFile = System.IO.Path.GetFileName(files[0]);

                myMerge.OutputFile = strTargetDir + “\\” + strOutputFile;
                myMerge.SetInputAssemblies(files);

                myMerge.Merge();
            }
            catch (Exception ex)
            {
                Console.WriteLine(String.Format(“Error :{0}”,ex.Message));
            }

        }
    }
}

此条目发表在C#分类目录,贴了, 标签。将固定链接加入收藏夹。

发表评论