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

如何将C#实现接口的结构体传递给非托管C++作为函数参数

实现方案

C#托管类型和C++非托管类型不存在原生的类继承/接口实现对应关系,不能直接跨互操作边界传递面向对象结构,需要通过平面化内存结构 + 手动适配层实现需求,具体步骤如下:

步骤1:统一两侧跨边界传递的内存布局

首先定义C++和C#内存完全对齐的纯数据结构体(不能带虚函数、不能包含引用类型字段),作为跨边界传递的载体:

  • C++侧定义纯数据结构体,不要包含任何成员函数、虚函数表指针:
// C++ 侧统一跨边界数据结构
enum AnimalType { TYPE_DOG = 1, TYPE_CAT = 2 };
struct AnimalData {
    AnimalType type; // 标记具体子类类型,用于两侧识别
    int age;
    char name[32];
    // 子类独有字段放在共用体避免内存浪费
    union {
        struct { char breed[20]; } dog_ext;
        struct { bool is_indoor; } cat_ext;
    } ext_data;
};
  • C#侧定义完全对齐的结构体,标注布局特性保证内存顺序一致:
// C# 侧对齐数据结构定义
public enum AnimalType { Dog = 1, Cat = 2 }

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DogExt
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string Breed;
}

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct AnimalExt
{
    [FieldOffset(0)]
    public DogExt Dog;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct AnimalData
{
    public AnimalType Type;
    public int Age;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string Name;
    public AnimalExt Ext;
}

步骤2:C++侧导出C风格接口适配层

C++抽象类不能直接作为导出函数的参数对外暴露,需要导出C风格的扁平函数,接收统一结构体参数后内部转成现有抽象类派生类实例使用:

// C++ 导出函数必须加extern "C"避免名字修饰,C#才能正常识别入口
extern "C" __declspec(dllexport) int ProcessAnimal(const AnimalData* data)
{
    // 根据类型标记构造你现有C++抽象类的派生类实例
    Animal* animal_obj = nullptr;
    if (data->type == TYPE_DOG) {
        animal_obj = new Dog(data->age, data->name, data->ext_data.dog_ext.breed);
    }
    // 其他子类类型的构造逻辑...
    
    if (!animal_obj) return -1;
    // 调用你原本接收Animal抽象类指针的业务函数
    int ret = YourNativeAnimalProcessFunc(animal_obj);
    
    delete animal_obj;
    return ret;
}

注意:不要直接导出C类成员函数,也不要直接把C对象指针传递给C#侧操作,避免内存管理和虚表解析异常。

步骤3:C#侧声明导入函数并传递实例

C#侧按照对齐的结构体声明导入函数,直接传递封装好的结构体即可:

// C# 侧DllImport声明
[DllImport("你的C++ DLL文件名.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int ProcessAnimal(ref AnimalData data);

// 调用示例
public void PassDogToCpp()
{
    var dog = new AnimalData
    {
        Type = AnimalType.Dog,
        Age = 3,
        Name = "阿黄",
        Ext = new AnimalExt
        {
            Dog = new DogExt { Breed = "柴犬" }
        }
    };
    
    int result = ProcessAnimal(ref dog);
}

扩展场景补充

如果需要传递带方法回调的对象,可以用函数指针表方案:C#侧把委托封送成函数指针,和数据一起打包成结构体传给C++,C++侧把函数指针填充到虚函数表中,就可以直接封装成抽象类实例使用,该方案实现复杂度更高,普通数据传递场景用上述结构体方案足够。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 15:27:03