37

我有一个使用 C++ dll 进行数据 i/o 的 Windows C# 程序。我的目标是将应用程序部署为单个 EXE。

创建这样的可执行文件的步骤是什么?

4

8 回答 8

21

托管和非托管代码的单一程序集部署 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 就会被提取到它们自己的文件中,并准备好使用,就像您将它们部署为单独的文件一样。只要您对执行目录具有写入权限,这对您来说应该可以正常工作。至少对于原型代码,我认为这种单程序集部署方式非常方便。

享受!

http://weblogs.asp.net/ralfw/archive/2007/02/04/single-assembly-deployment-of-managed-and-unmanaged-code.aspx

于 2008-09-16T13:40:32.170 回答
3

试试boxedapp;它允许从内存中加载所有 DLL。此外,您甚至可以嵌入 .net 运行时。很好地创建一个真正独立的应用程序......

于 2008-09-25T19:55:30.110 回答
3

使用Fody.Costura nuget

  1. 打开您的解决方案 -> 项目 -> 管理 Nuget 包
  2. 搜索Fody.Costura
  3. 编译您的项目

就是这样 !

资料来源: http: //www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/

于 2017-06-30T20:32:10.570 回答
2

你试过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。

于 2008-09-16T13:44:50.973 回答
2

只需在 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/

于 2011-06-15T18:25:39.690 回答
0

Thinstall是一种解决方案。对于本机 Windows 应用程序,我建议将 DLL 作为二进制资源对象嵌入,然后在运行时在需要之前将其提取出来。

于 2008-09-16T13:40:44.197 回答
0

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 已经存在于那里。

如果程序集完全不受管理,您可以查看此链接链接以了解如何加载此类 dll。

于 2012-05-15T11:42:07.340 回答
-2

Xenocode的PostBuild可以将托管和非托管打包到单个 exe 中。

于 2010-04-10T19:58:15.177 回答