简体   繁体   English

C#将指向struct(包含非blittable类型)的指针传递给非托管C ++ DLL

[英]C# pass pointer to struct (containing non-blittable types) to unmanaged C++ DLL

I try to use a C++ DLL in my C# code. 我尝试在我的C#代码中使用C ++ DLL。
All data that is read must be passed to the dll as a pointer to a struct. 必须将读取的所有数据作为指向结构的指针传递给dll。 My first idea was to just reserve some unmanaged memory, pass the pointer to the function and extract the data afterwards. 我的第一个想法是只保留一些非托管内存,将指针传递给函数并随后提取数据。 The problem is that the functions will only returns an error code that translates to "An argument is not valid". 问题是函数只会返回一个错误代码,转换为“参数无效”。

From the DLLs Header (Result and BusPortId shortened) 从DLL标题(结果和BusPortId缩短)

typedef enum {
    BUS_COM1 = 0x01,  // 2 to 15 ommited
    BUS_COM16 = 0x10,
    BUS_USB1 = 0x101,  // 2 to 15 ommited
    BUS_USB16 = 0x110
} BusPortId;

typedef enum {
    BUS_SUCCESS    = 0,     //!< The operation was completed successfully
    BUS_ERROR      = 0x100,  //!< An error occured
    BUS_INVALIDARG = 0x1000, //!< An argument is not valid
} Result


struct BusPortInfo
{
    ULONG       portInfoSize;
    CHAR        portText[64];
    BOOL        portLocked;
    BusPortId   portId;
};

Result BUSDRV_API busGetAvailablePortCount( ULONG *retCount );

Result BUSDRV_API busGetAvailablePort( ULONG index, BusPortInfo *portInfo );

The relevant parts of my C# program so far 到目前为止我的C#程序的相关部分

enum BusPortId
{
    BUS_COM1 = 0x01,  // 2 to 15 ommited
    BUS_COM16 = 0x10,
    BUS_USB1 = 0x101,  // 2 to 15 ommited
    BUS_USB16 = 0x110
};

public enum Result
{
    BUS_SUCCESS = 0,       //!< The operation was completed successfully
    BUS_ERROR = 0x100,  //!< An error occured
    BUS_INVALIDARG = 0x1000, //!< An argument is not valid
};

struct BusPortInfo
{
    public ULONG portInfoSize;
    unsafe public fixed char portText[64];
    public BOOL portLocked;
    public BusPortId portId;
}

[DllImport(DLL_Path)]
unsafe static extern Result busGetAvailablePortCount(ULONG* retCount);
[DllImport(DLL_Path)]
unsafe static extern Result busGetAvailablePort(ULONG index, BusPortInfo* portInfo);

ulong count= 0;
Result res = busGetAvailablePortCount(&count);

ulong index = 0;
BusPortInfo info = new BusPortInfo();
Result res = busGetAvailablePort(0, &info);

The call to busGetAvailablePortCount (and other similar functions) works without any problems. 对busGetAvailablePortCount(和其他类似函数)的调用没有任何问题。 But when I call busGetAvailablePort I get the following in my console output 但是当我调用busGetAvailablePort时,我在控制台输出中得到以下内容

Cannot marshal 'parameter #2': Pointers cannot reference marshaled structures. Use ByRef instead.

The Problem is that I can change my struct in C# so that I can pass the pointer, but then the function from the DLL also returns "An argument is not valid" 问题是我可以在C#中更改我的结构,以便我可以传递指针,但是DLL中的函数也会返回“参数无效”

What do I have to do to my struct so I can pass a pointer to it to a function while still getting accepted by the DLL? 我该怎么做我的结构,所以我可以将指针传递给一个函数,同时仍然被DLL接受?

PS Sorry for the bad english, I'm not a native speaker. PS抱歉英语不好,我不是母语人士。

There are a lot of problems with these declarations, tends to happen when the programmer keeps hacking the code to try to make the pinvoke call work. 这些声明存在很多问题,当程序员不断破解代码以试图使pinvoke调用工作时,往往会发生这些问题。 The most likely correct declaration for the structure is: 该结构最可能的正确声明是:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct BusPortInfo {
        public int portInfoSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string portText;
        public bool portLocked;
        public BusPortId portId;
    }

Emphasizing that an ULONG in native code is a 32-bit type and fixed just isn't necessary and is pretty awkward. 强调本机代码中的ULONG是32位类型并且仅fixed不是必需的并且非常笨拙。 This struct is not blittable due to the bool and string member, nothing much to fret about. 由于bool和string成员,这个结构不是blittable,没什么可担心的。

The [DllImport] declaration needs to declare the 2nd argument correctly. [DllImport]声明需要正确声明第二个参数。 The CallingConvention property always matters a great deal and we can't see what BUSDRV_API means. CallingConvention属性总是很重要,我们无法看到BUSDRV_API的含义。 Punting: 划船:

    [DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)]
    static extern Result busGetAvailablePortCount(out int retCount );

    [DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)]
    static extern Result busGetAvailablePort(int index, 
                            [In, Out] ref BusPortInfo portInfo);

And the call does not look correct. 电话看起来不正确。 When a struct has a "size" member then the api contract normally demands that it is set before the call. 当struct具有“size”成员时,api契约通常要求在调用之前设置它。 It is a safety measure, it ensures that the api cannot corrupt memory when the caller uses the wrong structure declaration. 这是一种安全措施,它确保当调用者使用错误的结构声明时,api不会破坏内存。 And when set wrong, an "Invalid argument" error is the expected outcome. 如果设置错误,“无效参数”错误是预期结果。 So write it similar to: 所以写它类似于:

int count;
Result res = busGetAvailablePortCount(out count);
if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString());

for (int ix = 0; ix < count; ++ix) {
    BusPortInfo info;
    info.portInfoSize = Marshal.SizeOf(typeof(BusPortInfo));   // Important!
    res = busGetAvailablePort(ix, ref info);
    if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString());
    // etc...
}

Not tested of course, should be in the ballpark. 当然没有经过测试,应该在球场。 If you still have problems then verify that sizeof(BusPortInfo) in native code matches the value of Marshal.SizeOf(typeof(BusPortInfo)) in C#. 如果仍有问题,请验证本机代码中的sizeof(BusPortInfo)是否与C#中的Marshal.SizeOf(typeof(BusPortInfo))的值匹配。 And if all fails then use C++/CLI instead so you can use the native declarations directly. 如果全部失败则使用C ++ / CLI,这样您就可以直接使用本机声明。 And talk to the owner of the DLL for proper usage instructions, preferably he'll write a pinvoke sample for you. 并与DLL的所有者讨论正确的使用说明,最好是他会为你编写一个pinvoke样本。

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

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