让我开始另一个问题,因为虽然我看到了很多类似的问题,但没有人真正谈论过这方面......我有一个 C++ DLL(没有源代码,但 .lib 和 .h),我编写了必要的托管包装器。这没有问题,问题是关于原始 C++ 代码中定义的结构和枚举,它们很多,都需要暴露给 C# 代码。教程和示例通常使用简单的数据类型,如浮点数和字符串,而不是复杂数据结构的现实世界场景。
我的托管 C++/CLI 包装器使用来自 DLL 的 .h 头文件的非托管结构。我包装的类成员函数一直在使用它们。因此,我需要在我的 C# 代码中使用相同的结构,传递它们并从 C++ 代码接收。很明显,我无法避免在 C# 中重新定义所有这些,但即便如此,使用它们也是有问题的。让我们举一个例子:一个由非托管代码中的函数使用的简单结构:
typedef struct INFO {
...
} INFO;
int GetInfo(INFO& out_Info);
Run Code Online (Sandbox Code Playgroud)
我在 C++/CLI 包装器代码中声明了相同的结构:
public ref struct INFO_WRAP {
...
};
int GetInfo(INFO_WRAP out_Info);
Run Code Online (Sandbox Code Playgroud)
包装器代码中的实现尝试将此新结构转换为原始结构,以使用旧的非托管代码:
int Namespace::Wrapper::GetInfo(INFO_WRAP out_Info) {
pin_ptr<INFO> pin_out_Info = out_Info;
return self->GetInfo(*(::INFO*)pin_out_Info);
}
Run Code Online (Sandbox Code Playgroud)
但这不会编译(无法在结构之间转换并且找不到合适的转换)。
是否有一种解决方案不涉及创建新的数据结构和手动来回复制所有结构成员?不仅是因为额外的工作和时间,而且确实有很多结构。
public ref struct INFO_WRAP
Run Code Online (Sandbox Code Playgroud)
您没有声明结构,这是 C# 术语中的类。古怪的 C++ 实现细节,一个 C++ 结构只是一个类,它的所有成员都是public。您需要value struct在 C++/CLI 中使用来声明 C# 结构的等效项。
int Namespace::Wrapper::GetInfo(INFO_WRAP out_Info)
Run Code Online (Sandbox Code Playgroud)
这也是错误的,因为 INFO_WRAP 实际上是一个引用类型,您必须始终使用 ^ 帽子声明它。或者用 % 通过引用传递它,这肯定是这里的意图。
基本障碍,你所要求的不是直接支持的。不允许托管编译器对托管结构的布局做出任何假设。无论如何,当你尝试它时会吠叫。出于一个很好的理由,它只是不可预测的。布局是一个强大的运行时实现细节,如果代码在不同的运行时上运行,布局可能会发生变化。像 32 位和 64 位一样,可能在一个中工作,但在另一个中不起作用。正如乔恩发现的那样。
一个一个地复制字段总是有效并且性能足够。只是不是程序员喜欢维护的代码。您可以要求框架为您做这件事,调用Marshal::PtrToStructure()或 StructureToPtr()。
作弊是可能的,而且在您编写 C++/CLI 代码时肯定会考虑到这一点。毕竟,该语言的重点是使互操作快速。您只是使保修无效,您必须在您打算支持的任何平台上彻底测试代码。一个简单的例子:
public value struct Managed {
int member1;
int member2;
};
struct Native {
int member1;
int member2;
};
void GetInfo(Managed% info) {
Native n = { 1, 2 };
pin_ptr<Managed> pinfo = &info;
memcpy(pinfo, &n, sizeof(n));
}
Run Code Online (Sandbox Code Playgroud)
在任何平台上都可以正常工作并且可预测地执行,结构很简单。当结构不简单或者你,比如说,修改 Native 并忘记修改 Managed 时,就会付出代价,堆栈和 GC 堆损坏是非常令人不快的不幸并且非常难以调试。