当前位置: 代码网 > it编程>编程语言>Asp.net > C# DLL跨语言调用的两种实现方法

C# DLL跨语言调用的两种实现方法

2026年03月02日 Asp.net 我要评论
你想知道如何编写能被其他编程语言(如c++、python、java等)调用的c# dll,这需要解决不同语言间的互操作(interop)问题,核心是让c# dll符合跨语言调用的标准。一、核心思路:让

你想知道如何编写能被其他编程语言(如c++、python、java等)调用的c# dll,这需要解决不同语言间的互操作(interop)问题,核心是让c# dll符合跨语言调用的标准。

一、核心思路:让c# dll支持com/非托管调用

普通c# dll基于.net托管代码,非.net语言(如c++原生、python)无法直接调用,需要通过以下两种主流方式实现跨语言调用:

  1. 方式1:将c# dll注册为com组件(兼容绝大多数语言:c++、vb6、python、java等);
  2. 方式2:通过p/invoke封装非托管导出函数(主要适配c/c++等原生语言)。

下面优先讲解最通用的com组件方式,再补充p/invoke方式。

方式1:创建可注册为com的c# dll(通用跨语言)

步骤1:创建类库项目并配置com兼容

  1. 打开visual studio,创建「类库(.net framework)」项目(.net core/.net 5+需额外配置,推荐先用.net framework做兼容),命名为cominteropdll
  2. 右键项目→「属性」→「应用程序」→「程序集信息」,勾选「使程序集com可见」。
  3. 右键项目→「属性」→「生成」,勾选「为com互操作注册」(仅开发环境需要,发布时手动注册)。

步骤2:编写com兼容的c#代码

com要求必须通过接口 暴露方法,且需要给接口/类添加guid(唯一标识),示例代码如下:

using system;
using system.runtime.interopservices;

// 1. 为接口添加唯一guid(可通过vs工具生成:工具→创建guid→选注册表格式,去掉{})
[guid("12345678-1234-1234-1234-1234567890ab")]
// 2. 标记为com可见的接口
[comvisible(true)]
public interface icalculator
{
    // com接口方法不能有重载,参数/返回值尽量用基础类型(int、string、double等)
    int add(int a, int b);
    string reversestring(string input);
}

// 3. 为实现类添加唯一guid
[guid("87654321-4321-4321-4321-ba0987654321")]
[comvisible(true)]
public class calculator : icalculator
{
    public int add(int a, int b)
    {
        return a + b;
    }

    public string reversestring(string input)
    {
        if (string.isnullorempty(input))
            throw new argumentnullexception(nameof(input));
        
        char[] arr = input.tochararray();
        array.reverse(arr);
        return new string(arr);
    }
}

步骤3:编译并注册com组件

按f6编译项目,生成cominteropdll.dllcominteropdll.tlb(类型库文件,供其他语言识别)。

注册com组件(管理员权限运行cmd):

# 替换为你的dll路径(release版建议用release目录)
regasm /codebase "c:\cominteropdll\bin\debug\cominteropdll.dll"

卸载com组件(如需):regasm /u "你的dll路径"

步骤4:不同语言调用该com dll示例

示例1:python调用(使用pywin32库)

# 先安装依赖:pip install pywin32
import win32com.client

# 创建com对象(参数是接口/类的progid,或直接用guid)
# 方式1:用类名(需确保项目属性中配置了progid,或手动指定)
calculator = win32com.client.dispatch("cominteropdll.calculator")

# 调用方法
sum_result = calculator.add(10, 25)
reverse_result = calculator.reversestring("python call c# dll")

print(f"两数之和:{sum_result}")  # 输出:35
print(f"反转字符串:{reverse_result}")  # 输出:lld #c llac nohtyp

示例2:c++原生调用(使用com接口)

#include <iostream>
#include <windows.h>
// 导入类型库(替换为你的tlb文件路径)
#import "c:\cominteropdll\bin\debug\cominteropdll.tlb" no_namespace

int main()
{
    // 初始化com库
    coinitialize(null);

    // 创建com对象
    icalculator* pcalculator = null;
    hresult hr = cocreateinstance(
        __uuidof(calculator),  // 类的guid
        null,
        clsctx_inproc_server,
        __uuidof(icalculator), // 接口的guid
        (void**)&pcalculator
    );

    if (succeeded(hr))
    {
        // 调用方法
        int sum = 0;
        pcalculator->add(100, 200, &sum);
        
        bstr input = sysallocstring(l"c++ call c# com dll");
        bstr reversed = null;
        pcalculator->reversestring(input, &reversed);

        // 输出结果
        std::cout << "sum: " << sum << std::endl; // 输出:300
        wprintf(l"reversed: %s\n", reversed);    // 输出:lld moc #c llac ++c

        // 释放资源
        sysfreestring(input);
        sysfreestring(reversed);
        pcalculator->release();
    }

    // 释放com库
    couninitialize();
    return 0;
}

方式2:p/invoke导出非托管函数(适配c/c++)

如果仅需给c/c++调用,可通过dllexport库直接导出非托管函数(c#原生不支持直接导出函数,需第三方库)。

步骤1:安装dllexport库

在nuget包管理器中安装:unmanagedexports(注意:仅支持.net framework)。

步骤2:编写导出函数的c#代码

using system;
using system.runtime.interopservices;
using rgiesecke.dllexport;

namespace pinvokedll
{
    public class myexports
    {
        // 导出add函数,供c/c++调用
        [dllexport("add", callingconvention = callingconvention.cdecl)]
        public static int add(int a, int b)
        {
            return a + b;
        }

        // 导出反转字符串函数(注意字符编码适配)
        [dllexport("reversestring", callingconvention = callingconvention.cdecl)]
        public static intptr reversestring([marshalas(unmanagedtype.lpstr)] string input)
        {
            if (string.isnullorempty(input))
                return intptr.zero;
            
            char[] arr = input.tochararray();
            array.reverse(arr);
            string result = new string(arr);
            
            // 分配非托管内存并返回(调用方需释放)
            return marshal.stringtohglobalansi(result);
        }

        // 提供释放内存的函数,供调用方使用
        [dllexport("freestring", callingconvention = callingconvention.cdecl)]
        public static void freestring(intptr ptr)
        {
            if (ptr != intptr.zero)
                marshal.freehglobal(ptr);
        }
    }
}

步骤3:c++调用该dll

#include <iostream>
#include <windows.h>

// 声明导入的函数
extern "c" __declspec(dllimport) int add(int a, int b);
extern "c" __declspec(dllimport) char* reversestring(const char* input);
extern "c" __declspec(dllimport) void freestring(char* ptr);

int main()
{
    // 调用add函数
    int sum = add(5, 8);
    std::cout << "add result: " << sum << std::endl; // 输出:13

    // 调用reversestring函数
    char* reversed = reversestring("hello c++");
    std::cout << "reversed: " << reversed << std::endl; // 输出:++c olleh
    freestring(reversed); // 释放内存

    return 0;
}

关键注意事项

  1. 数据类型兼容:跨语言调用时,优先使用基础数据类型(int、double、string),避免复杂自定义类型;字符串需注意编码(ansi/utf-8/utf-16)。
  2. 框架选择
    • .net framework:对com/p/invoke支持最好,兼容老语言;
    • .net core/.net 5+:需用comhostnativeaot编译为原生dll,步骤更复杂。
  3. 错误处理:跨语言调用时,c#中的异常无法直接传递给非托管语言,建议返回错误码而非抛出异常。
  4. 64位/32位适配:编译dll和调用程序时,需保持位数一致(均为x86或x64),否则会调用失败。

总结

  1. 通用跨语言方案:将c# dll注册为com组件,适配python、java、c++等绝大多数语言,核心是通过comvisibleguid暴露接口,并注册com。
  2. c/c++专属方案:使用unmanagedexports库导出非托管函数,通过p/invoke调用,更轻量但仅适配c/c++。
  3. 核心要点:保持数据类型简单、位数一致、处理好内存释放(尤其是字符串),是跨语言调用的关键。

以上就是c# dll跨语言调用的两种实现方法的详细内容,更多关于c# dll跨语言调用的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com