I want to use a c++ dll in c#. I'm using [DllImport]
to call the method. I'm having trouble passing struct to a method.
I have a C struct:
typedef struct
{
DWORD TopPoint;
DWORD EndPoint;
WORD dwCount;
MYFUNC_NUMERIC11 *pGetData;
} MYFUNC_BUFFERNORMAL;
MYFUNC_NYMERIC11
is another struct.
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;
I have written a C# struct to mimic this.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public unsafe struct MYFUNC_BUFFERNORMAL
{
public uint TopPoint;
public uint EndPoint;
public ushort Count;
public IntPtr pGetData;
}
A pointer to the struct is an argument in a method. C# function is:
[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);
This is the method in C:
MYFUNC_STATUS MYFUNC_GetData(MYFUNC_HANDLE myfuncHandle, DWORD dwOut, MYFUNC_BUFFERNORMAL *pBufferNormal , BYTE bccFlg)
The return type is cast to an enum, which has an interpretation. The struct parameter is invalid. I've tried to allocate memory using Marshal.AllocHGlobal(...)
, but the parameter is still invalid, i.e. there is no error during compilation but the value returned is incorrect.
I've spent quite a few hours on this, still unable to figure out what to do. A lot of similar questions exist already, like here: 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?, but I, somehow, still haven't figured out a way.
Something like this should work, at least with one element in the array (is it an array?). For an array, you will have to allocate sizeof * count
of elements and marshal (StructureToPtr) each element at its offset.
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);
}
With these definitions.
[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);