如何将指向 C# 结构的指针传递给 C 中的方法

Posted

技术标签:

【中文标题】如何将指向 C# 结构的指针传递给 C 中的方法【英文标题】:how to pass a pointer to C# struct to a method in C 【发布时间】:2021-05-06 10:13:38 【问题描述】:

我想在 c# 中使用 c++ dll。我正在使用[DllImport] 来调用该方法。我在将结构传递给方法时遇到问题。

我有一个 C 结构体:

typedef struct

DWORD TopPoint;
DWORD EndPoint;
WORD dwCount;
MYFUNC_NUMERIC11 *pGetData;
 MYFUNC_BUFFERNORMAL;

MYFUNC_NYMERIC11 是另一个结构体。

typedef struct

BYTE Sign; // Sign ("±")
BYTE Integer[3]; // 3-digit integer (no zero suppression)
BYTE Period; // Decimal point (".")
BYTE Decimal[6]; // 6-digit decimal number
 MYFUNC_NUMERIC11;

我已经编写了一个 C# 结构来模仿这一点。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public unsafe struct MYFUNC_BUFFERNORMAL

    public uint TopPoint;
    public uint EndPoint;
    public ushort Count;
    public IntPtr pGetData;

指向结构的指针是方法中的参数。 C#函数是:

[DllImport("MYFUNC_DLL.dll", EntryPoint = "MYFUNC_GetData", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, ThrowOnUnmappableChar = true)]
public static extern int MYFUNC_GetData(IntPtr myfuncHandle, UInt32 dwIO, ref IntPtr pBufferNormal, Byte bccFlg);

这是C语言中的方法:

MYFUNC_STATUS MYFUNC_GetData(MYFUNC_HANDLE myfuncHandle, DWORD dwOut, MYFUNC_BUFFERNORMAL *pBufferNormal , BYTE bccFlg)

返回类型被强制转换为具有解释的枚举。结构参数无效。我尝试使用Marshal.AllocHGlobal(...)分配内存,但参数仍然无效,即编译时没有错误但返回的值不正确。

我已经为此花费了好几个小时,仍然无法弄清楚该怎么做。已经存在很多类似的问题,比如这里:How do I convert c struct from dll to C# 或这里:How to pass C# array to C++ and return it back to C# with additional items?,但不知何故,我仍然没有找到办法。

【问题讨论】:

这不是 "COM-interop" 而是 p-invoke Marshal.StructureToPtr 您正在使用 windows 约定而不是 c 语言进行调用:CallingConvention = CallingConvention.Cdecl ref IntPtr pBufferNormal 不正确,删除 ref。或者更好的是,将其设为 ref MYFUNC_BUFFERNORMAL,这样您就不必自己编组所有内容。 dwCount 成员强烈建议您可以使用 MYFUNC_NUMERIC11[] pGetData。 @DavidHeffernan 是的,我正在尝试使用它。不成功,我可以补充一下。 【参考方案1】:

这样的事情应该可以工作,至少在数组中有一个元素(它是一个数组吗?)。对于数组,您必须分配 sizeof * count 个元素并在每个元素的偏移量处编组 (StructureToPtr)。

var num = new MYFUNC_NUMERIC11();
num.Integer = new byte[]  1, 2, 3 ;
num.Decimal = new byte[]  4, 5, 6, 7, 8, 9 ;
num.Sign = 10;
num.Period = 11;

var buffer = new MYFUNC_BUFFERNORMAL();
buffer.Count = 1234;
buffer.EndPoint = 5678;
buffer.TopPoint = 9;
buffer.pGetData = Marshal.AllocCoTaskMem(Marshal.SizeOf(num));
try

    Marshal.StructureToPtr(num, buffer.pGetData, false);
    MYFUNC_GetData(Whatever, 0, ref buffer, 0);

finally

    Marshal.FreeCoTaskMem(buffer.pGetData);

有了这些定义。

[StructLayout(LayoutKind.Sequential)]
public struct MYFUNC_BUFFERNORMAL

    public uint TopPoint;
    public uint EndPoint;
    public ushort Count;
    public IntPtr pGetData;


[StructLayout(LayoutKind.Sequential)]
public struct MYFUNC_NUMERIC11

    public byte Sign;
    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] Integer;
    
    public byte Period;
    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] Decimal;


// check calling convention
[DllImport(@"MYFUNC_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern MYFUNC_STATUS  MYFUNC_GetData(IntPtr myfuncHandle, uint dwIO, ref MYFUNC_BUFFERNORMAL pBufferNormal, byte bccFlg);

【讨论】:

我实际上有一个元素数组,所以sizeof * count 是我要走的路。我对单个元素进行了尝试,到目前为止看起来不错。仅供参考:我的通话约定是StdCall。我正在使用的 DLL 不支持您建议的约定 Cdecl

以上是关于如何将指向 C# 结构的指针传递给 C 中的方法的主要内容,如果未能解决你的问题,请参考以下文章

如何将变量结构类型传递给 C 中的函数

C++成员函数指针指向全局函数指针

如何将 Stucture 的地址传递给 C# 中的 Void 指针 [void*] 到 VC++ Dll

如何将指针传递给字符串,指向子字符串的指针,并返回其他内容?

从 C# 调用 C 函数,传递包含指针的结构

我应该如何将指向成员函数的指针传递给 OpenCV 中的 setMouseCallback?