You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将非托管C++静态嵌入托管C#包装器DLL中?

如何将非托管C++ DLL嵌入托管C# DLL,简化项目引用链路?

可以实现,下面提供两种主流优化方案,让项目C仅需引用B.dll即可正常运行:

方案一:将A.dll作为嵌入资源,运行时动态释放加载

这种方案无需额外项目,纯C#即可实现,核心思路是把非托管A.dll嵌入到B.dll的资源中,在B.dll初始化时将其提取到可访问目录并加载。

步骤:

  1. 在项目B中添加A.dll:右键项目B → 添加 → 现有项,选中A.dll;右键A.dll → 属性,将生成操作设置为「嵌入的资源」。
  2. 在B.dll中添加静态加载类,负责提取并加载A.dll:
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace YourBProjectNamespace
{
    internal static class NativeDllLoader
    {
        static NativeDllLoader()
        {
            // 资源名称格式:B项目命名空间 + A.dll,需与实际命名空间匹配
            var resourceName = $"{typeof(NativeDllLoader).Namespace}.A.dll";
            using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
            
            if (resourceStream == null)
                throw new InvalidOperationException("未能找到嵌入的A.dll资源,请检查资源名称是否正确");

            // 选择DLL释放路径:可使用程序运行目录或临时目录
            var dllOutputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "A.dll");

            // 仅当文件不存在或嵌入资源更新时写入
            if (!File.Exists(dllOutputPath) || File.GetLastWriteTime(dllOutputPath) < Assembly.GetExecutingAssembly().LastWriteTime)
            {
                using var fileStream = new FileStream(dllOutputPath, FileMode.Create, FileAccess.Write);
                resourceStream.CopyTo(fileStream);
            }

            // 加载DLL,确保后续DllImport能找到
            LoadLibrary(dllOutputPath);
        }

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern IntPtr LoadLibrary(string lpFileName);
    }

    // 在需要调用A.dll函数的类中,触发加载器初始化
    public class YourCallingClass
    {
        static YourCallingClass()
        {
            // 触发NativeDllLoader的静态构造函数,加载A.dll
            _ = typeof(NativeDllLoader);
        }

        [DllImport("A.dll")]
        public static extern int YourAFunction(int input);
    }
  1. 项目C只需引用B.dll即可,运行时B会自动处理A.dll的释放和加载。

注意事项:

  • 资源名称必须准确,可通过项目B的「资源」视图确认资源全名。
  • 若程序运行在受保护目录(如Program Files),写入程序运行目录可能需要管理员权限,建议改用临时目录。
  • 多实例运行时需处理文件占用问题,可通过添加唯一后缀或互斥锁避免冲突。

方案二:使用C++/CLI包装器,静态链接非托管代码

这种方案通过C++/CLI作为中间层,将非托管A的代码静态链接到托管包装DLL中,彻底消除对独立A.dll的依赖。

步骤:

  1. 将项目A改为静态库项目:右键项目A → 属性 → 配置属性 → 常规 → 配置类型:静态库(.lib),编译生成A.lib。
  2. 创建新的C++/CLI类库项目(项目D),作为非托管与托管代码的桥梁。
  3. 在项目D中配置A的依赖:
    • 右键项目D → 属性 → C/C++ → 常规 → 附加包含目录:添加项目A的头文件路径。
    • 右键项目D → 属性 → 链接器 → 输入 → 附加依赖项:添加A.lib的路径。
  4. 在项目D中编写托管包装类,封装A的非托管函数:
#pragma once

using namespace System;

namespace AWrapper
{
    public ref class NativeACaller
    {
    public:
        static int CallAFunction(int param)
        {
            // 直接调用项目A的非托管函数
            return ::AFunctionFromA(param);
        }
    };
}
  1. 在项目B中引用项目D的DLL,通过NativeACaller调用A的功能:
public class BClass
{
    public int UseAFunction(int input)
    {
        return AWrapper.NativeACaller.CallAFunction(input);
    }
}
  1. 项目C只需引用B.dll即可,项目D的DLL会随B的引用自动复制到C的输出目录,A的代码已静态链接到D.dll中,无需单独携带A.dll。

优势:

  • 无需手动处理DLL的释放和加载,稳定性更高。
  • 可在C++/CLI层完成复杂类型转换和异常捕获,隔离非托管代码风险,适合大型非托管库的封装。

原方案补充说明

你当前采用的「将A.dll作为现有项添加到C并设置较新则复制」的方案是可行的,但如果追求更简洁的引用链路,上述两种方案更优。

内容的提问来源于stack exchange,提问作者Ben Granger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 05:23:10