在Visual C#2010中将DLL嵌入到.exe中

时间:2011-08-03 02:10:50

标签: c# dll embed itextsharp

我正在使用一个使用iTextSharp.dll和WebCam_Capture.dll的C#程序。当我构建程序时,它会在调试文件夹中创建可执行文件,并且它还会按预期将这两个dll复制到调试文件夹中。我想将它们合并为一个可执行文件,但是我失败了。通常在解决方案资源管理器中的参考中可以看到这两个库。我还将它们添加为资源。可执行文件大小变得更大,等于三个文件的总和,但可执行文件仍然需要在其目录中使用这些库...我使用资源文件的“构建操作”属性但没有更改。我也试过ILmerge,但它给了我一个错误。那我该怎么办?

更新:这是我从ILmerge获得的:

An exception occurred during merging:
Unresolved assembly reference not allowed: System.Core.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
   at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)

顺便说一下,它只是一个Windows应用程序,一个表格将被填写并以pdf格式打印,并通过网络摄像头拍照(如果有的话)。谢谢大家!

8 个答案:

答案 0 :(得分:9)

您可以使用ILMerge将多个程序集合并在一起。你已经说过你这么做了,而且你收到了一个错误。虽然我不知道为什么,但您可以使用另一种选择:如果库是开源的(并且它们的许可证与您的许可证兼容),您可以下载源代码,将其添加到项目中并进行编译。这将导致单个组件。

ILMerge页面还列出了Jeffrey Richter's blog作为解决问题的另一种方法:

  

许多应用程序都包含一个依赖于许多DLL的EXE文件   文件。部署此应用程序时,所有文件必须是   部署。但是,您可以使用一种技术进行部署   只是一个EXE文件。首先,确定您的所有DLL文件   EXE文件取决于不作为Microsoft .NET的一部分提供的文件   框架本身。然后将这些DLL添加到Visual Studio项目中。   对于您添加的每个DLL文件,显示其属性并更改其属性   “构建操作”到“嵌入式资源”。这会导致C#编译器   将DLL文件嵌入到您的EXE文件中,您可以部署这个文件   EXE文件。

     

在运行时,CLR将无法找到依赖DLL   组件,这是一个问题。要解决这个问题,请在申请时   初始化,使用AppDomain注册回调方法   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资源   并通过调用Assembly的Load方法的重载来加载它   将Byte []作为参数。

答案 1 :(得分:7)

  1. 将DLL文件添加到Visual Studio项目中。
  2. 对于每个文件,请转到“属性”并将其“构建操作”设置为“嵌入式资源”
  3. 在您的代码上使用GetManifestResourceStream(“DLL_Name_Here”)检索资源,这会返回一个可加载的流。
  4. 编写一个“AssemblyResolve”事件处理程序来加载它。
  5. 以下是代码:

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.IO;
    
    namespace WindowsForm
    {
        public partial class Form1 : Form
        {
            Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();            
    
            public Form1()
            {
                InitializeComponent();
                AppDomain.CurrentDomain.AssemblyResolve += FindDLL;
            }
    
            private Assembly FindDLL(object sender, ResolveEventArgs args)
            {
                string keyName = new AssemblyName(args.Name).Name;
    
                // If DLL is loaded then don't load it again just return
                if (_libs.ContainsKey(keyName)) return _libs[keyName];
    
    
                using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespaceGoesHere." + keyName + ".dll"))  // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
                {
                    byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
                    Assembly assembly = Assembly.Load(buffer);
                    _libs[keyName] = assembly;
                    return assembly;
                }
            }
    
            //
            // Your Methods here
            //
    
        }
    }
    

    希望它有所帮助, 巴勃罗

答案 2 :(得分:3)

我稍微修改了Pablo的代码,它对我有用 它没有正确获取DLL的资源名称。

IDictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();

public Form1()
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    InitializeComponent();
}

// dll handler
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string keyName = new AssemblyName(args.Name).Name;

    // If DLL is loaded then don't load it again just return
    if (_libs.ContainsKey(keyName)) return _libs[keyName];

    using (Stream stream = Assembly.GetExecutingAssembly()
           .GetManifestResourceStream(GetDllResourceName("itextsharp.dll")))  // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
    {
        byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
        Assembly assembly = Assembly.Load(buffer);
        _libs[keyName] = assembly;
        return assembly;
    }
}

private string GetDllResourceName(string dllName)
{
    string resourceName = string.Empty;
    foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
    {
        if (name.EndsWith(dllName))
        {
            resourceName = name;
            break;
        }
    }

    return resourceName;
}

答案 3 :(得分:3)

您正在寻找的答案:

// To embed a dll in a compiled exe:
// 1 - Change the properties of the dll in References so that Copy Local=false
// 2 - Add the dll file to the project as an additional file not just a reference
// 3 - Change the properties of the file so that Build Action=Embedded Resource
// 4 - Paste this code before Application.Run in the main exe
AppDomain.CurrentDomain.AssemblyResolve += (Object sender, ResolveEventArgs args) =>
    {
        String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
        System.Reflection.AssemblyName embeddedAssembly = new System.Reflection.AssemblyName(args.Name);
        String resourceName = thisExe + "." + embeddedAssembly.Name + ".dll";

        using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            return System.Reflection.Assembly.Load(assemblyData);
        }
    };

答案 4 :(得分:1)

在我们的应用程序构造函数的顶部添加此匿名函数代码。这将从同一项目中的嵌入资源中添加dll。

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    string resourceName = new AssemblyName(args.Name).Name + ".dll";
    string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));

    using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
    {
        Byte[] assemblyData = new Byte[stream.Length];
        stream.Read(assemblyData, 0, assemblyData.Length);
        return Assembly.Load(assemblyData);
    }
};

答案 5 :(得分:0)

查看应用域中的AssemblyResolve事件。

我没有样本,但您基本上检查了要求的内容并重新流回资源DLL。我相信LinqPAD做得很好 - 你可以看一下Joseph Albahari用反编译器等的实现。

答案 6 :(得分:0)

我知道这个话题已经过时了,但是我会把它写给想要使用它的未来的人。

我的代码基于 userSteve

我建议改变这一点。

String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

进入这个

String thisExe = System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.Namespace;

即使名称空间与程序集名称

不同,它也会起作用

如果你想使用目录中的DLL,你可以像那样使用它(目录资源作为示例)

String resourceName = thisExe + ".Resources." + embeddedAssembly.Name + ".dll";

如果您仍然无法找到C#表单应用程序中此代码的位置,请将其粘贴到文件&#34; Program.cs&#34;上面一行:

Application.Run(new Form_1());

及以下行:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

答案 7 :(得分:-1)

您没有使用WPF引用,但如果您使用WPF,则可能是导致错误的原因。如果没有,ILMerge应该可以正常工作。如果您使用的是WPF,这是一个运行良好的解决方案:

http://blogs.interknowlogy.com/2011/07/13/merging-a-wpf-application-into-a-single-exe/