我有一个使用 C++ dll 进行数据 i/o 的 Windows C# 程序。我的目标是将应用程序部署为单个 EXE。
创建这样的可执行文件的步骤是什么?
托管和非托管代码的单一程序集部署 2007 年 2 月 4 日,星期日
.NET 开发人员喜欢 XCOPY 部署。他们喜欢单一的装配组件。至少我总是觉得有点不安,如果我必须使用某个组件并且需要记住一个文件列表,还需要包含在该组件的主程序集中。因此,当我最近不得不开发一个托管代码组件并不得不使用来自 C DLL 的一些非托管代码来扩充它时(感谢 Marcus Heege 帮助我解决这个问题!),我想到了如何更轻松地部署这两个 DLL . 如果这只是两个程序集,我可以使用 ILmerge 将它们打包到一个文件中。但这不适用于具有托管和非托管 DLL 的混合代码组件。
所以这是我想出的解决方案:
我将我想要与组件的主程序集一起部署的任何 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");
}
...
在此示例中,我包含了两个 DLL 作为资源,一个是非托管代码 DLL,另一个是托管代码 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);
}
}
}
使用这样的托管代码程序集与往常一样 - 几乎。您在组件的主项目(此处:MyLib)中引用它(此处:ManagedService.dll),但将 Copy Local 属性设置为 false。此外,您将程序集链接为现有项目并将构建操作设置为嵌入式资源。
对于非托管代码(此处:UnmanagedService.dll),您只需将 DLL 作为现有项链接,并将构建操作设置为嵌入式资源。要访问其功能,请照常使用 DllImport 属性,例如
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
就是这样!一旦您使用静态 ctor 创建了该类的第一个实例,嵌入式 DLL 就会被提取到它们自己的文件中,并准备好使用,就像您将它们部署为单独的文件一样。只要您对执行目录具有写入权限,这对您来说应该可以正常工作。至少对于原型代码,我认为这种单程序集部署方式非常方便。
享受!
试试boxedapp;它允许从内存中加载所有 DLL。此外,您甚至可以嵌入 .net 运行时。很好地创建一个真正独立的应用程序......
使用Fody.Costura nuget
就是这样 !
资料来源: http: //www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/
你试过ILMerge吗?http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMerge 是一个实用程序,可用于将多个 .NET 程序集合并为一个程序集。它可从 Microsoft .NET Framework 开发人员中心的工具和实用程序页面免费使用。
如果您正在使用/clr
标志(全部或部分 C++/CLI)构建 C++ DLL,那么它应该可以工作:
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
但是,它不适用于普通的(本机)Windows DLL。
只需在 Visual Studio 中右键单击您的项目,选择 Project Properties -> Resources -> Add Resource -> Add Existing File... 并将以下代码包含到您的 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/
Thinstall是一种解决方案。对于本机 Windows 应用程序,我建议将 DLL 作为二进制资源对象嵌入,然后在运行时在需要之前将其提取出来。
Smart Assembly可以做到这一点,甚至更多。如果您的 dll 具有非托管代码,它不会让您将 dll 合并到单个程序集中,而是可以将所需的依赖项作为资源嵌入到您的主 exe 中。它的另一面,它不是免费的。
您可以通过将 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);
};
}
这里的关键是将字节写入文件并从其位置加载。为了避免先有鸡还是先有蛋的问题,您必须确保在访问程序集之前声明处理程序,并且不要在加载(程序集解析)部分中访问程序集成员(或实例化任何必须处理程序集的东西)。还要注意确保GetMyApplicationSpecificPath()
不是任何临时目录,因为临时文件可能会被其他程序或您自己尝试删除(不是说它会在您的程序访问 dll 时被删除,但至少它很麻烦。AppData 很好地点)。另请注意,您每次都必须写入字节,您不能从位置加载,因为 dll 已经存在于那里。
Xenocode的PostBuild可以将托管和非托管打包到单个 exe 中。