简体   繁体   English

按值为 C++ 结构 PInvoke 类

[英]PInvoke Class by value for c++ struct

I've got a small issue regarding P/Invoke.我有一个关于 P/Invoke 的小问题。 Currently, I am implementing a wrapper for a C API for a hardware device whose original C# implementation sucks.目前,我正在为原始 C# 实现糟糕的硬件设备实现 C API 的包装器。 The issue/inconvenience I am having is the following:我遇到的问题/不便如下:

The API implements multiple structs for getting/setting device settings. API 实现了多个用于获取/设置设备设置的结构。 These are of similar structure, thus I am giving an example implementation:它们具有相似的结构,因此我给出了一个示例实现:

typedef struct _Struct1
{
    bool b1;
    unsigned int ui1;
    unsigned int ui2;
    unsigned int ui3;
    unsigned int ui4;
    unsigned int ui5;
} Struct1;

The getter and setter are implemented as follows: getter 和 setter 实现如下:

unsigned int SetSetting(bool b1, Struct1 s1);
unsigned int GetSetting(bool b1, Struct1 &s1);

The C# DllImport is: 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);

The C# struct implementation is done using LayoutKind.Sequential and represents the C struct exactly and the P/Invoke calls work quite well except for: As you may noticed, the getter and setter DllImports differ slightly in that the setter uses a C# struct (SStruct1) and the getter a C# class (CStruct1): 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 */ }

I have not been able to get P/Invoke working using just one, either a class or a struct.我无法仅使用一个类或结构来使 P/Invoke 工作。

Do you have any tips regarding a more convenient solution for this problem?对于这个问题的更方便的解决方案,您有什么建议吗?

Changing the struct to class and vice versa does give a PInvokeStackImbalance exception.将结构更改为类(反之亦然)确实会产生 PInvokeStackImbalance 异常。

I am guessing this has to do with the fact that the setter is expecting a struct by value and the getter by reference, given it's out-parameter-equivalent.我猜这与 setter 期望一个 struct by value 和 getter by reference 的事实有关,因为它是 out-parameter-equivalent。 I've tried any Marshal attribute and parameter definitions, I could think of, but am hitting a dead end.我已经尝试过任何我能想到的 Marshal 属性和参数定义,但我遇到了死胡同。 Google does not seem to help.谷歌似乎没有帮助。

Any help is greatly apprechiated.非常感谢任何帮助。

Edit编辑

I've been mistaken: The API is ANSI C, not C++.我弄错了:API 是 ANSI C,而不是 C++。

Update更新

I've tried the solution suggested (SStruct for setter, out SStruct for getter) and although working fine for a function with a struct consisting entirely of uints, it produces complete garbage for the struct in question:我已经尝试了建议的解决方案(用于 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              |

Remember: When using using aforementioned DllImports, this works just fine.请记住:使用上述 DllImports 时,这工作得很好。

  1. Check your build settings x86 or x64 for C# projects.检查 C# 项目的构建设置 x86 或 x64。
  2. Verify calling convention.验证调用约定。 I think that in VC++ default is cdecl , try to specify it in DllImport.我认为在 VC++ 中默认是cdecl ,尝试在 DllImport 中指定它。

Building on the comment from @HansPassant, you want to use out SStruct for the getter method.基于@HansPassant 的评论,您希望将out SStruct用于 getter 方法。 Also, you need to make sure that the size of a bool is the same both in the dotnet struct and the compiled api.dll struct (the struct packing might be 1 byte but it is very unlikely see here :).此外,您需要确保 dotnet 结构和编译后的 api.dll 结构中 bool 的大小相同(结构打包可能是 1 个字节,但不太可能在这里看到 :)。

Also, you use CallingConvention=StdCall .此外,您使用CallingConvention=StdCall The symptom of using the wrong calling convention is a stack imbalance exception.使用错误调用约定的症状是堆栈不平衡异常。 You want to try using CallingConvention.Cdecl if you don't know it.如果您不知道,您想尝试使用CallingConvention.Cdecl

Most often, when opening the dll in depends, if symbols don't end-up with @<some int> , the convention is Cdecl with no guarantee though.大多数情况下,在打开 dll 时,如果符号没有以@<some int>结尾,则约定是Cdecl ,但不能保证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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