如何将 C++ windows dll 合并到 C# 应用程序 exe 中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/72264/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can a C++ windows dll be merged into a C# application exe?
提问by Noah
I have a Windows C# program that uses a C++ dll for data i/o. My goal is to deploy the application as a single EXE.
我有一个 Windows C# 程序,它使用 C++ dll 进行数据 i/o。我的目标是将应用程序部署为单个 EXE。
What are the steps to create such an executable?
创建这样的可执行文件的步骤是什么?
采纳答案by Nick
Single Assembly Deployment of Managed and Unmanaged Code Sunday, February 4, 2007
托管和非托管代码的单一程序集部署 2007 年 2 月 4 日,星期日
.NET developers love XCOPY deployment. And they love single assembly components. At least I always feel kinda uneasy, if I have to use some component and need remember a list of files to also include with the main assembly of that component. So when I recently had to develop a managed code component and had to augment it with some unmanaged code from a C DLL (thx to Marcus Heege for helping me with this!), I thought about how to make it easier to deploy the two DLLs. If this were just two assemblies I could have used ILmerge to pack them up in just one file. But this doesn′t work for mixed code components with managed as well as unmanaged DLLs.
.NET 开发人员喜欢 XCOPY 部署。他们喜欢单个装配组件。至少我总是觉得有点不安,如果我必须使用某个组件并且需要记住一个文件列表,该列表也包含在该组件的主要组件中。因此,当我最近不得不开发一个托管代码组件,并且不得不使用来自 C DLL 的一些非托管代码对其进行扩充时(感谢 Marcus Heege 帮助我解决这个问题!),我考虑如何使部署这两个 DLL 变得更容易. 如果这只是两个程序集,我可以使用 ILmerge 将它们打包到一个文件中。但这不适用于具有托管和非托管 DLL 的混合代码组件。
So here′s what I came up with for a solution:
所以这是我想出的解决方案:
I include whatever DLLs I want to deploy with my component′s main assembly as embedded resources. Then I set up a class constructor to extract those DLLs like below. The class ctor is called just once within each AppDomain so it′s a neglible overhead, I think.
我将我想与我的组件的主程序集一起部署的任何 DLL 作为嵌入式资源包括在内。然后我设置了一个类构造函数来提取这些 DLL,如下所示。类 ctor 在每个 AppDomain 中只调用一次,所以我认为这是一个可以忽略不计的开销。
namespace MyLib
{
public class MyClass
{
static MyClass()
{
ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
}
...
In this example I included two DLLs as resources, one being an unmanaged code DLL, and one being a managed code DLL (just for demonstration purposes), to show, how this technique works for both kinds of code.
在这个例子中,我包含了两个 DLL 作为资源,一个是非托管代码 DLL,一个是托管代码 DLL(仅用于演示目的),以展示这种技术如何适用于这两种代码。
The code to extract the DLLs into files of their own is simple:
将 DLL 提取到它们自己的文件中的代码很简单:
public static class ResourceExtractor
{
public static void ExtractResourceToFile(string resourceName, string filename)
{
if (!System.IO.File.Exists(filename))
using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create))
{
byte[] b = new byte[s.Length];
s.Read(b, 0, b.Length);
fs.Write(b, 0, b.Length);
}
}
}
Working with a managed code assembly like this is the same as usual - almost. You reference it (here: ManagedService.dll) in your component′s main project (here: MyLib), but set the Copy Local property to false. Additionally you link in the assembly as an Existing Item and set the Build Action to Embedded Resource.
使用像这样的托管代码程序集与往常一样 - 几乎。您在组件的主项目(此处:MyLib)中引用它(此处:ManagedService.dll),但将 Copy Local 属性设置为 false。此外,您将程序集中作为现有项目链接,并将构建操作设置为嵌入式资源。
For the unmanaged code (here: UnmanagedService.dll) you just link in the DLL as an Existing Item and set the Build Action to Embedded Resource. To access its functions use the DllImport attribute as usual, e.g.
对于非托管代码(此处:UnmanagedService.dll),您只需将 DLL 作为现有项链接,并将构建操作设置为嵌入式资源。要访问其功能,请像往常一样使用 DllImport 属性,例如
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
That′s it! As soon as you create the first instance of the class with the static ctor the embedded DLLs get extracted into files of their own and are ready to use as if you deployed them as separate files. As long as you have write permissions for the execution directory this should work fine for you. At least for prototypical code I think this way of single assembly deployment is quite convenient.
就是这样!一旦您使用静态构造函数创建类的第一个实例,嵌入的 DLL 就会被提取到它们自己的文件中,并准备好使用,就像您将它们部署为单独的文件一样。只要您对执行目录有写权限,这应该适合您。至少对于原型代码,我认为这种单程序集部署方式非常方便。
Enjoy!
享受!
回答by titanae
回答by Raithlin
Have you tried ILMerge? http://research.microsoft.com/~mbarnett/ILMerge.aspx
你试过ILMerge吗?http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMerge is a utility that can be used to merge multiple .NET assemblies into a single assembly. It is freely available for use from the Tools & Utilities page at the Microsoft .NET Framework Developer Center.
ILMerge 是一个实用程序,可用于将多个 .NET 程序集合并为一个程序集。它可以从 Microsoft .NET Framework 开发人员中心的工具和实用程序页面免费使用。
If you're building the C++ DLL with the /clr
flag (all or partially C++/CLI), then it should work:
如果您使用/clr
标志(全部或部分 C++/CLI)构建 C++ DLL ,那么它应该可以工作:
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
It will not work with an ordinary (native) Windows DLL however.
但是,它不适用于普通(本机)Windows DLL。
回答by Raithlin
回答by Joel Lucsy
回答by Lars Holm Jensen
Just right-click your project in Visual Studio, choose Project Properties -> Resources -> Add Resource -> Add Existing File… And include the code below to your App.xaml.cs or equivalent.
只需在 Visual Studio 中右键单击您的项目,选择项目属性 -> 资源 -> 添加资源 -> 添加现有文件...并将以下代码包含到您的 App.xaml.cs 或等效文件中。
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Here's my original blog post: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
这是我的原始博客文章:http: //codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
回答by nawfal
Smart Assemblycan do this and more. If your dll has unmanaged code, it wont let you merge the dlls to a single assembly, instead it can embed the required dependencies as resources to your main exe. Its flip-side, its not free.
Smart Assembly可以做到这一点以及更多。如果您的 dll 具有非托管代码,它不会让您将 dll 合并到单个程序集,而是可以将所需的依赖项作为资源嵌入到您的主 exe 中。它的另一面,它不是免费的。
You can do this manually by embedding dll to your resources and then relying on AppDomain's Assembly ResolveHandler
. When it comes to mixed mode dlls, I found many of the variants and flavours of ResolveHandler
approach to not work for me (all which read dll bytes to memory and read from it). They all worked for managed dlls. Here is what worked for me:
您可以通过将 dll 嵌入到您的资源中,然后依赖 AppDomain 的 Assembly 来手动执行此操作ResolveHandler
。当涉及到混合模式 dll 时,我发现许多变体和风格的ResolveHandler
方法对我不起作用(所有这些都将 dll 字节读取到内存并从中读取)。它们都适用于托管 dll。这是对我有用的:
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
The key here is to write the bytes to a file and load from its location. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath()
is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just 'cos the dll already resides there.
这里的关键是将字节写入文件并从其位置加载。为避免先有鸡还是先有蛋的问题,您必须确保在访问程序集之前声明处理程序,并且您不访问加载(程序集解析)部件内的程序集成员(或实例化任何必须处理程序集的内容)。还要注意确保GetMyApplicationSpecificPath()
不是任何临时目录,因为临时文件可能会被其他程序或您自己尝试删除(不是在您的程序访问 dll 时它会被删除,但至少它是一个麻烦。AppData 是好的地点)。另请注意,您每次都必须写入字节,您无法从位置加载,因为 dll 已经驻留在那里。
If the assembly is fully unmanaged, you can see this linkor thisas to how to load such dlls.
回答by automan
Use Fody.Costuranuget
使用Fody.Costuranuget
- Open your solution -> Project -> Manage Nuget Packages
- Search for Fody.Costura
- Compileyour project.
- 打开您的解决方案 -> 项目 -> 管理 Nuget 包
- 搜索Fody.Costura
- 编译您的项目。
That's it !
就是这样 !
Source: http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/
资料来源:http: //www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/