如何将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
相关产品推荐
相关产品推荐

