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

发布于 2024-11-27 18:12:09 字数 629 浏览 7 评论 0 原文

我正在开发一个使用 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 格式的表格,其中包含通过网络摄像头拍摄的照片(如果有)。谢谢大家!

I'm working on a C# program that uses iTextSharp.dll and WebCam_Capture.dll. When I build the program, it creates executable in the debug folder and it also copies these two dll's to the debug folder as expected. I want to merge them into a single executable, however I failed. These two libraries are visible in the references normally in the solution explorer. I also add them as resources. Executable size got bigger which equals the sum of three files, nevertheless the executable still requires these libraries in its directory... I played with "build action" property of the resource files but no change. I also tried ILmerge but it gave me an error. so what should I do?

Update: This is what I get from 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)

It is just a windows application by the way, a form to be filled and printed as pdf with a photo taken via webcam if available. Thanks all!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

空城缀染半城烟沙 2024-12-04 18:12:09
  1. 将 DLL 文件添加到 Visual Studio 项目中。
  2. 对于每个文件,转到“属性”并将其构建操作设置为“嵌入资源”。
  3. 在代码中,使用 GetManifestResourceStream(“DLL_Name_Here”) 检索资源,这将返回一个可加载的流。
  4. 编写一个“AssemblyResolve”事件处理程序来加载它。

这是代码:

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
        //

    }
}

希望有帮助,
巴勃罗

  1. Add the DLL files to your Visual Studio project.
  2. For each file go to "Properties" and set its Build Action to "Embedded Resource"
  3. On your code retrive the resource using the GetManifestResourceStream("DLL_Name_Here") this returns a stream that can be loadable.
  4. Write an "AssemblyResolve" event handler to load it.

Here is the code:

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
        //

    }
}

Hope it helps,
Pablo

烏雲後面有陽光 2024-12-04 18:12:09

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

ILMerge 页面还列出了 Jeffrey Richter 的博客作为解决您问题的另一种选择:

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

运行时,CLR将无法找到依赖的DLL
组件,这是一个问题。要解决此问题,当您的应用程序
初始化,向 AppDomain 注册回调方法
解决组装事件。代码应如下所示:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>; { 
   字符串资源名称=“AssemblyLoadingAndReflection。” + 
       new AssemblyName(args.Name).Name + ".dll"; 
   使用 (var 流 = Assembly.GetExecutingAssembly()
                               .GetManifestResourceStream(资源名称)) { 
      Byte[] assemblyData = new Byte[stream.Length]; 
      流.Read(程序集数据, 0, 程序集数据. 长度); 
      返回Assembly.Load(程序集数据); 
   }   
}; 

现在,线程第一次调用引用类型的方法时
依赖的 DLL 文件,将引发 AssemblyResolve 事件并且
上面显示的回调代码将找到所需的嵌入 DLL 资源
并通过调用 Assembly 的 Load 方法的重载来加载它
采用 Byte[] 作为参数。

You can use ILMerge to merge multiple assemblies together. You've already said you did this, and you've received an error. Though I don't know why, you can use an alternative: if the libraries are open source (and their licenses are compatible with yours), you can download the source code, add it to your project and compile. This will result in a single assembly.

The ILMerge page also lists Jeffrey Richter's blog as yet another alternative to solve your issue:

Many applications consist of an EXE file that depends on many DLL
files. When deploying this application, all the files must be
deployed. However, there is a technique that you can use to deploy
just a single EXE file. First, identify all the DLL files that your
EXE file depends on that do not ship as part of the Microsoft .NET
Framework itself. Then add these DLLs to your Visual Studio project.
For each DLL file you add, display its properties and change its
“Build Action” to “Embedded Resource.” This causes the C# compiler to
embed the DLL file(s) into your EXE file, and you can deploy this one
EXE file.

At runtime, the CLR won’t be able to find the dependent DLL
assemblies, which is a problem. To fix this, when your application
initializes, register a callback method with the AppDomain’s
ResolveAssembly event. The code should look something like this:

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); 
   }   
}; 

Now, the first time a thread calls a method that references a type in
a dependent DLL file, the AssemblyResolve event will be raised and the
callback code shown above will find the embedded DLL resource desired
and load it by calling an overload of Assembly’s Load method that
takes a Byte[] as an argument.

貪欢 2024-12-04 18:12:09

我稍微修改了巴勃罗的代码,它对我有用。
它没有正确获取 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;
}

I modified Pablo's code a little bit and it worked for me.
It was not getting the DLL's resource name correctly.

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;
}
殤城〤 2024-12-04 18:12:09

您正在寻找的答案:

// 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);
        }
    };

The answer you are looking for:

// 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);
        }
    };
转身泪倾城 2024-12-04 18:12:09

查看应用域上的 AssemblyResolve 事件。

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

Check out the AssemblyResolve event on the app domain.

I don't have a sample but you basically check what is asked for and stream back the resource DLL. I believe LinqPAD does this well - you could have a look at Joseph Albahari's implementation with a decompiler etc.

瀟灑尐姊 2024-12-04 18:12:09

将此匿名函数代码添加到我们的应用程序构造函数的顶部。这将从同一项目中的嵌入资源添加 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);
    }
};

Add this anonymous function code on the top of our application constructor. This will add dll from embedded resource in same project.

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);
    }
};
江城子 2024-12-04 18:12:09

我知道这个主题很旧,但我会为未来想要使用它的人编写它。

我基于userSteve的代码。

我建议改变这一点。

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

这样,

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

即使命名空间与程序集名称不同,它

也可以工作,如果您想使用目录中的 DLL,则可以像这样使用它(以目录 Resources 为例),

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

如果您仍然找不到将此代码放在 C# 表单中的位置应用程序将其粘贴到文件“Program.cs”上方行:

Application.Run(new Form_1());

和下方行:

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

I know that topic is old but i'll write it for future persons that will want to use it.

i base on code by userSteve.

i would suggest to change this.

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

into this

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

that way it would work even if namespace is different than assembly name

also if you want to use DLL from directory you can use it like that (directory Resources as Example)

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

if you still can't find where place this code in C# Form application paste it inside file "Program.cs" above line:

Application.Run(new Form_1());

and below lines:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
箜明 2024-12-04 18:12:09

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

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

You didn't reference using WPF, but if you are, this could be the cause of your error. If not, ILMerge should work fine for you. If you are using WPF, here is a solution that works well:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文