【问题标题】:Deploying C# Solution with Native Dll into single EXE将带有本机 Dll 的 C# 解决方案部署到单个 EXE 中
【发布时间】:2013-08-08 11:27:26
【问题描述】:

我有一个 C# Visual Studio 2012 解决方案,它依赖于我使用 PInvoke 访问的本机 dll。当我部署应用程序时,我必须确保此 Dll 位于应用程序文件夹中。

无论如何我可以将此 Dll 合并到可执行文件中吗?

也许是一种资源?

我听说过 ILMerge,但听说它无法处理原生代码。

任何帮助将不胜感激。

【问题讨论】:

  • 是的,这是可能的。每个人都将该可执行文件命名为“setup.exe”。肯定你以前用过一个。周围有 许多 安装程序创建实用程序,VS 提供 InstallShield LE。你得去购物了。
  • @HansPassant,极度贫穷/懒惰的人还有更多选择。它被称为Zip ;)
  • @Moo-Juice 虽然我们正在使用它,但 WinRAR 实际上更适合,它具有自执行结构和特殊的“设置”配置。
  • 这个 Dll 是用 DotNet 框架创建的吗?
  • @MohamedSalemyan,他明确表示它是本地的 :)

标签: c#


【解决方案1】:

您可以使用 Visual Studio 创建一个 Setup package 项目,将您的所有文件部署到正确的位置或使用其他第三方打包软件(如完整的 InstallShield 或 alternatives

但是,您的问题让我想起了 Open Hardware Monitor 项目,他们将驱动程序作为嵌入式资源包含在内,并在用户启动应用程序时提取它们。它的工作原理是这样的:他们在项目中添加了WinRing0.sysWinRing0x64.sys,并将他们的 Build Action 设置为 Embedded Resource,然后他们有一个方法可以提取来自资源的驱动程序:

private static bool ExtractDriver(string fileName) {
  string resourceName = "OpenHardwareMonitor.Hardware." +
    (OperatingSystem.Is64BitOperatingSystem() ? "WinRing0x64.sys" : 
    "WinRing0.sys");

  string[] names =
    Assembly.GetExecutingAssembly().GetManifestResourceNames();
  byte[] buffer = null;
  for (int i = 0; i < names.Length; i++) {
    if (names[i].Replace('\\', '.') == resourceName) {
      using (Stream stream = Assembly.GetExecutingAssembly().
        GetManifestResourceStream(names[i])) 
      {
          buffer = new byte[stream.Length];
          stream.Read(buffer, 0, buffer.Length);
      }
    }
  }

  if (buffer == null)
    return false;

  try {
    using (FileStream target = new FileStream(fileName, FileMode.Create)) {
      target.Write(buffer, 0, buffer.Length);
      target.Flush();
    }
  } catch (IOException) { 
    // for example there is not enough space on the disk
    return false; 
  }

  // make sure the file is actually writen to the file system
  for (int i = 0; i < 20; i++) {
    try {
      if (File.Exists(fileName) &&
        new FileInfo(fileName).Length == buffer.Length) 
      {
        return true;
      }
      Thread.Sleep(100);
    } catch (IOException) {
      Thread.Sleep(10);
    }
  }

  // file still has not the right size, something is wrong
  return false;
}

他们正在将资源读入缓冲区,将该缓冲区写入磁盘并等待文件刷新到磁盘。

【讨论】:

    【解决方案2】:

    我的解决方案在概念上类似于Wouter 提出的解决方案。

    这是我们在自己的应用程序中使用的,我们可以使用原生/混合模式和 c# dll 都嵌入在同一个 .exe 中。

    每次运行应用程序时,它都会将 dll 提取到临时目录中。显然你可能不想在生产版本中这样做,因为那里的 dll 是稳定的;您可能会在那里选择一个不同的目录(可能在 %AppData% 的某个地方)。不过,它将使用具有相同版本号的现有 dll(例如,它仅在启动计算机之间多次打开应用程序时第一次完成)。

    因为我们在做

    AppDomain.CurrentDomain.AssemblyResolve += (sender, args)
    

    只要系统试图解析 dll,就会调用此函数。而且由于它是在静态 Program 类中初始化的,所以它都是自动运行的。

    程序.cs:

    namespace MyApp
    {
        internal class Program
        {
            static Program()
            {
                LoadAssemblyResource.Initialize("MyApp");
            }
    
            //....
        }
    }  
    

    加载AssemblyResource.cs

    namespace MyAppStartup
    { 
        public static class LoadAssemblyResource
        {
            private readonly static String _version_string =
                Assembly.GetExecutingAssembly().GetName().Version.ToString();
    
            private readonly static String _dll_path = Path.GetTempPath()
                + "\\MyApp\\" + _version_string;
    
            static public String last_error_msg = null;
    
            public static bool WriteBytesToFile(string filename, byte[] bytes)
            {
                try
                {
                    var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
                    fs.Write(bytes, 0, bytes.Length);
                    fs.Close();
                    return true;
                }
                catch (Exception e)
                {
                    Console.WriteLine("Writing file failed. Exception: {0}", e.ToString());
                }
                return false;
            }
    
            public static Assembly LoadUnsafe(String assembly_name, Byte[] assembly)
            {
                if (!Directory.Exists(_dll_path))
                {
                    Directory.CreateDirectory(_dll_path);
                    Console.WriteLine("Created tmp path '" + _dll_path + "'.");
                }
    
                String fullpath = _dll_path + "\\" + assembly_name;
                if (!File.Exists(fullpath))
                {
                    Console.WriteLine("Assembly location: " + fullpath + ".");
                    if (!WriteBytesToFile(fullpath, assembly))
                         return null;
                }
    
                return Assembly.UnsafeLoadFrom(fullpath);
            }
    
            public static void Initialize(String exe_name)
            {
                AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
                {
                    String assembly_name = new AssemblyName(args.Name).Name + ".dll";
                    String resource_name = exe_name + "." + assembly_name;
    
                    using (var stream = 
                        Assembly.GetExecutingAssembly().GetManifestResourceStream(resource_name))
                    {
                        if (stream == null)
                            return null;
    
                        Byte[] assembly_data = new Byte[stream.Length];
                        stream.Read(assembly_data, 0, assembly_data.Length);
                        try
                        {
                            Assembly il_assembly = Assembly.Load(assembly_data);
                            return il_assembly;
                        }
                        catch (System.IO.FileLoadException ex)
                        {
                            // might have failed because it's an mixed-mode dll.
                            last_error_msg = ex.Message;
                        }
    
                        Assembly mixed_mode_assembly = LoadUnsafe(assembly_name, assembly_data);
                        return mixed_mode_assembly;
                    }
                };
            }
        }
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      • 2016-05-17
      • 2019-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多