一个C ++的Windows DLL如何被合并到一个C#应用程序的EXE?
我有一个Windows C#程序,使用C + + DLL的数据I / O。 我的目标是将应用程序部署为单个EXE。
有什么步骤来创build这样的可执行文件?
托pipe代码和非托pipe代码的单一部署部署2007年2月4日,星期日
.NET开发人员喜欢XCOPY部署。 他们喜欢单一的组装部件。 至less我总是觉得有点不自在,如果我不得不使用某个组件,并且需要记住一个文件列表,并且包含该组件的主要组件。 所以当我最近不得不开发一个托pipe代码组件,并且必须用C DLL中的一些非托pipe代码来扩充它(thx到Marcus Heege来帮助我!),我想到了如何更容易地部署这两个DLL 。 如果这只是两个程序集,我可以用ILmerge把它们打包成一个文件。 但是,这对于混合代码组件与托pipeDLL以及非托pipeDLL不起作用。
所以这就是我提出的一个解决scheme:
我将包含我想要部署的任何DLL与我的组件主要程序集作为embedded式资源。 然后,我build立了一个类构造函数来提取下面这些DLL。 这个类在每个AppDomain中只调用一次,所以我觉得它是一个可以忽略的开销。
namespace MyLib { public class MyClass { static MyClass() { ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll"); ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll"); } ...
在这个例子中,我包含了两个DLL作为资源,一个是非托pipe代码DLL,一个是托pipe代码DLL(仅用于演示目的),以显示这种技术对于这两种代码是如何工作的。
将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); } } }
像这样使用托pipe代码组合几乎是一样的。 您在组件的主项目(这里:MyLib)中引用它(在这里:ManagedService.dll),但将复制本地属性设置为false。 此外,您还可以将程序集链接为Existing Item,并将Build Action设置为Embedded Resource。
对于非托pipe代码(这里是:UnmanagedService.dll),您只需将DLL链接为Existing Item并将Build Action设置为Embedded Resource。 为了访问它的function,像往常一样使用DllImport属性,例如
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
而已! 只要用静态ctor创build类的第一个实例,embedded式DLL就会被解压缩到自己的文件中,并可以像使用单独文件一样部署它们。 只要你有执行目录的写权限,这应该适合你。 至less对于原型代码,我认为这种单一程序集部署方式是相当方便的。
请享用!
尝试boxedapp ; 它允许从内存加载所有的DLL。 另外,它似乎甚至可以embedded.net运行时。 很好地创build一个真正独立的应用程序…
你尝试过ILMerge吗? http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMerge是一个可用于将多个.NET程序集合到一个程序集中的实用工具。 它可以从Microsoft .NET Framework开发人员中心的工具和实用程序页面免费使用。
如果使用/clr
标志(全部或部分为C ++ / CLI)构buildC ++ DLL,那么它应该可以工作:
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
但是,它不会与普通的(本机)Windows DLL一起工作。
只需在Visual Studio中右键单击您的项目,select“项目属性” – >“资源” – >“添加资源” – >“添加现有文件…”,并将下面的代码包含到您的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); }
这是我原来的博客文章: http : //codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
使用Fody.Costura nuget
- 打开你的解决scheme – >项目 – >pipe理Nuget包
- searchFody.Costura
- 编译你的项目 。
而已 !
资料来源: http : //www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/
Thinstall是一个解决scheme。 对于本地Windows应用程序,我build议将DLL作为二进制资源对象embedded,然后在运行之前在需要的时候将其解压缩。
智能assembly可以做到这一点,更多。 如果你的DLL有非托pipe代码,它不会让你把dll合并到一个程序集中,而是可以将所需的依赖作为资源embedded到你的主要exe文件中。 它的倒装,它不是免费的。
您可以通过将dllembedded资源,然后依靠AppDomain的Assembly ResolveHandler
手动执行此操作。 说到混合模式DLL,我发现ResolveHandler
方法的许多变体和风格都不适用于我(所有将dll字节读入内存并从中读取的方法)。 他们都为受pipe理的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); }; }
这里的关键是将字节写入文件并从其位置加载。 为了避免鸡和鸡蛋的问题,你必须确保你在访问程序集之前声明处理程序,并且不要在加载(程序集parsing)部分访问程序集成员(或实例化任何需要处理程序集的东西)。 还要注意确保GetMyApplicationSpecificPath()
不是临时目录,因为临时文件可能会被其他程序或自己删除(而不是在程序访问dll时会被删除,但至less是一个麻烦。 AppData是很好的位置)。 另外请注意,你必须每次写入字节,你不能从位置加载'因为DLL已经驻留在那里。
如果程序集是完全不受pipe理的,你可以看到这个链接或这个如何加载这样的DLL。
来自Xenocode的PostBuild可以将托pipe和unmanged打包成一个exe文件。