![](/img/trans.png)
[英]C# PInvoke of C++ function that returns struct by value crashes if the struct is left uninitialized
[英]PInvoke Class by value for c++ struct
我有一个关于 P/Invoke 的小问题。 目前,我正在为原始 C# 实现糟糕的硬件设备实现 C API 的包装器。 我遇到的问题/不便如下:
API 实现了多个用于获取/设置设备设置的结构。 它们具有相似的结构,因此我给出了一个示例实现:
typedef struct _Struct1
{
bool b1;
unsigned int ui1;
unsigned int ui2;
unsigned int ui3;
unsigned int ui4;
unsigned int ui5;
} Struct1;
getter 和 setter 实现如下:
unsigned int SetSetting(bool b1, Struct1 s1);
unsigned int GetSetting(bool b1, Struct1 &s1);
C# DllImport 是:
[DllImport("api.dll", EntryPoint = "GetSetting", CallingConvention = CallingConvention.StdCall]
public static extern uint GetStatus(bool b1, CStruct1 s1);
[DllImport("api.dll", EntryPoint = "SetSetting", CallingConvention = CallingConvention.StdCall]
public static extern uint SetStatus(bool b1, SStruct1 s1);
C# 结构体实现是使用 LayoutKind.Sequential 完成的,并准确地表示 C 结构体,并且 P/Invoke 调用工作得很好,除了: 正如您可能注意到的,getter 和 setter DllImports 略有不同,因为 setter 使用 C# 结构(SStruct1 ) 和 getter 一个 C# 类 (CStruct1):
[StructLayout(LayoutKind.Sequential)]
public struct SStruct1
{
public bool b1;
public uint ui1;
public uint ui2;
public uint ui3;
public uint ui4;
public uint ui5;
}
[StructLayout(LayoutKind.Sequential)]
public class CStruct1 { /* same as struct */ }
我无法仅使用一个类或结构来使 P/Invoke 工作。
对于这个问题的更方便的解决方案,您有什么建议吗?
将结构更改为类(反之亦然)确实会产生 PInvokeStackImbalance 异常。
我猜这与 setter 期望一个 struct by value 和 getter by reference 的事实有关,因为它是 out-parameter-equivalent。 我已经尝试过任何我能想到的 Marshal 属性和参数定义,但我遇到了死胡同。 谷歌似乎没有帮助。
非常感谢任何帮助。
我弄错了:API 是 ANSI C,而不是 C++。
我已经尝试了建议的解决方案(用于 setter 的 SStruct,用于 getter 的 SStruct),尽管对于具有完全由 uint 组成的结构的函数工作正常,但它为所讨论的结构产生了完整的垃圾:
| Name | correct value | returned value |
| ---- | --------------- | -------------- |
| b1 | False | True |
| ui1 | 0 | 3355443223 |
| ui2 | 0 | 1677721600 |
| ui3 | 0 | 0 |
| ui4 | 0 | 0 |
| ui5 | 0 | 0 |
请记住:使用上述 DllImports 时,这工作得很好。
cdecl
,尝试在 DllImport 中指定它。基于@HansPassant 的评论,您希望将out SStruct
用于 getter 方法。 此外,您需要确保 dotnet 结构和编译后的 api.dll 结构中 bool 的大小相同(结构打包可能是 1 个字节,但不太可能在这里看到 :)。
此外,您使用CallingConvention=StdCall
。 使用错误调用约定的症状是堆栈不平衡异常。 如果您不知道,您想尝试使用CallingConvention.Cdecl
。
大多数情况下,在打开 dll 时,如果符号没有以@<some int>
结尾,则约定是Cdecl
,但不能保证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.