繁体   English   中英

按值为 C++ 结构 PInvoke 类

[英]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 时,这工作得很好。

  1. 检查 C# 项目的构建设置 x86 或 x64。
  2. 验证调用约定。 我认为在 VC++ 中默认是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM