如何将指向 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-invokeMarshal.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 中的方法的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Stucture 的地址传递给 C# 中的 Void 指针 [void*] 到 VC++ Dll