首页 > 解决方案 > 如何将指向 C# 结构的指针传递给 C 中的方法

问题描述

我想在 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# or here:How to pass C# array to C++ and return it back to C# with Additional items?,但不知何故,我还没有想出办法。

标签: c#cpinvoke

解决方案


像这样的东西应该可以工作,至少在数组中有一个元素(它是一个数组吗?)。对于数组,您必须分配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);

推荐阅读