C struct and use C dll in C# - c#
everyone, I'm newbie in C#, and I have a problem with using "C" dll in C#, below is the C side code which work fine.
// myCode.h
typedef struct _SubABC
{
unsigned short WordCount;
unsigned char *WordData;
unsigned char SpeedUp;
} SubABC;
typedef struct _ABC
{
unsigned char SubFontNum;
SubABC subABC[5];
} ABC
extern __declspec(dllimport) int __stdcall MY_SetSth(unsigned char SerialNum, ABC *pABC);
//myCode.c
ABC myFun = { '\0' };
unsigned char text_c[] =
{
0x00,0x27,0xff,0xA5,0xC3, 0x00,0x27,0xff,0xA6,0x26, 0x00,0x23,0xff,0xA7,0xAE, 0x00,0x27,0xff,0xBA,0x7E,
0x00,0x27,0xff,0xC1,0x52, 0x00,0x27,0xff,0xAC,0xF9, 0x00,0x27,0xff,0x20,0x00,0x27,0xff,0x31, 0x00,0x27,0xff,0x20, 0x00,0x27,0xff,0xA4,0xC0, 0x00,0x27,0xff,0xC4,0xC1,
};
myFun.SubFontNum = 1;
myFun.subMABC[0].WordCount = sizeof(text_c);
myFun.subMABC[0].WordData = text_c;
myFun.subMABC[0].SpeedUp = 0;
int retvalue = MY_SetSth(1, &myFun);
and my C# code is below
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SubABC
{
public ushort WordCount;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] WordData;
public byte SpeedUp;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ABC
{
public byte SubFontNum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public SubABC[] subABC;
}
[DllImport("myDLL.dll", CharSet = CharSet.Ansi)]
public extern static int MY_SetSth(byte SerialNum, ref ABC pABC);
ABC myFun = new ABC();
ABC.subABC = new SubABC[5];
unsigned char text_c[] =
{
0x00,0x27,0xff,0xA5,0xC3, 0x00,0x27,0xff,0xA6,0x26, 0x00,0x23,0xff,0xA7,0xAE, 0x00,0x27,0xff,0xBA,0x7E,
0x00,0x27,0xff,0xC1,0x52, 0x00,0x27,0xff,0xAC,0xF9, 0x00,0x27,0xff,0x20,0x00,0x27,0xff,0x31, 0x00,0x27,0xff,0x20, 0x00,0x27,0xff,0xA4,0xC0, 0x00,0x27,0xff,0xC4,0xC1,
};
myFun.SubFontNum = 1;
myFun.subMABC[0].WordCount = (ushort)text_c.Length;
myFun.subMABC[0].WordData = text_c;
myFun.subMABC[0].SpeedUp = 0;
int retvalue = MY_SetSth(1, ref myFun);
I always get TypeLoadException: Cannot marshal field 'WordData' of type 'subABC': Invalid managed/unmanaged type combination (Array fields must be paired with ByValArray or SafeArray), because WordData is unknown size, I can't use ByValArray, and SafeArray get AccessViolationException: Attempted to read or write protected memory
I try to use IntPtr like below
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SubABC
{
public ushort WordCount;
public IntPtr WordData;
public byte SpeedUp;
}
ABC.subABC[0].WordData = Marshal.AllocHGlobal(text_c.Length); ;
Marshal.Copy(text_c, 0, ABC.subABC[0].WordData, text_c.Length);
int retvalue = MY_SetSth(1, ref myFun);
I get error message AccessViolationException: Attempted to read or write protected memory
Can anyone help me? thank a lot.
Related
How to marshal c++ struct in c#
Below is the C++ struct. Its implementation is done in a C++ DLL: typedef char CusDate[sizeof("2023-02-02")]; typedef enum cycletype { NOES=0, LET=1, WARN=2, GRACE=3, EXP=4, } MYCYCLETYPE; typedef struct Info { MYCYCLETYPE midcycle; int type; int porotype; long sways; CusDate sedate; long days; CusDate elate; long edays; CusDate edate; const char* feature; const char* ver; const char* hid; }SINFO, *SINFO; int Initialize(SINFO lp) { lp->mycycle = NOES; ...... ..... lp->hid = "XYZ"; } Now, below is the C# code: [DllImport(#"abc.dll", CallingConvention = CallingConvention.Cdecl)] static extern int Initialize(ref SINFO lp); In C# main(): SINFO mydata; Initialize(ref mydata); Now, I am unable to access the struct data filled by C++ code in C#. How to access the C++ struct data in C#?
typedef struct Info { ... } SINFO, *SINFO; The above declaration is not valid C/C++, as you can't have the same identifier (SINFO) refer to two different types (Info and Info*). Perhaps you meant something more like this instead: typedef struct Info { ... } SINFO, *PSINFO; ^ Or, the more C++-ish style (as the above style originates from C): struct SINFO { ... }; typedef SINFO* PSINFO; // or: using PSINFO = SINFO*; Either way, Initialize() can then take either a SINFO& reference or a PSINFO pointer as its parameter, either of which would be compatible with ref SINFO on the C# side: int Initialize(SINFO& lp) int Initialize(PSINFO lp) Where the C# code would look something like this: public enum MYCYCLETYPE { NOES = 0, LET = 1, WARN = 2, GRACE = 3, EXP = 4, }; [StructLayout(LayoutKind.Sequential)] public struct SINFO { public MYCYCLETYPE midcycle; public int type; public int porotype; public long sways; // <-- or int, depending on the C++ compiler! [MarshalAs(UnmanagedType.ByValArray, SizeConst=11)] public byte[] sedate = new byte[11]; // alternatively: // [MarshalAs(UnmanagedType.ByValArray, SizeConst=11, ArraySubType=UnmanagedType.LPStr)] // public String sedate; public long days; // <-- or int, depending on the C++ compiler! [MarshalAs(UnmanagedType.ByValArray, SizeConst=11)] public byte[] elate = new byte[11]; // alternatively: // [MarshalAs(UnmanagedType.ByValArray, SizeConst=11, ArraySubType=UnmanagedType.LPStr)] // public String elate; public long edays; // <-- or int, depending on the C++ compiler! [MarshalAs(UnmanagedType.ByValArray, SizeConst=11)] public byte[] edate = new byte[11]; // alternatively: // [MarshalAs(UnmanagedType.ByValArray, SizeConst=11, ArraySubType=UnmanagedType.LPStr)] // public String edate; public IntPtr feature; // <-- use Marshal.PtrToStringAnsi() to read this // alternatively: // [MarshalAs(UnmanagedType.LPStr)] // public String feature; public IntPtr ver; // <-- use Marshal.PtrToStringAnsi() to read this // alternatively: // [MarshalAs(UnmanagedType.LPStr)] // public String ver; public IntPtr hid; // <-- use Marshal.PtrToStringAnsi() to read this // alternatively: // [MarshalAs(UnmanagedType.LPStr)] // public String hid; }; [DllImport("abc.dll", CallingConvention = CallingConvention.Cdecl)] static extern int Initialize(ref SINFO lp); Also, on a side note: you have Initialize() declared as returning an int, but the C++ code you have shown is not actually return'ing a value, which is undefined behavior.
How to correctly parse IntPtr to Object in C# when marshaling from C++
I'm invoking a C++ function from within C#. The function I am trying to invoke returns following C++ struct type result: LPWFSCIMNOTETYPELIST lpNoteTypeList; typedef struct _wfs_cim_note_type_list { USHORT usNumOfNoteTypes; LPWFSCIMNOTETYPE *lppNoteTypes; } WFSCIMNOTETYPELIST, *LPWFSCIMNOTETYPELIST; typedef struct _wfs_cim_note_type { USHORT usNoteID; CHAR cCurrencyID[3]; ULONG ulValues; USHORT usRelease; BOOL bConfigured; } WFSCIMNOTETYPE, *LPWFSCIMNOTETYPE; I found the suitable struct in C#: [StructLayout(LayoutKind.Sequential, Pack = XFSConstants.STRUCTPACKSIZE, CharSet = CharSet.Ansi)] public unsafe struct WfsCimNoteType { public ushort usNoteID; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public char[] cCurrencyID; public uint ulValues; public ushort usRelease; public byte bConfigured; } [StructLayout(LayoutKind.Sequential, Pack = XFSConstants.STRUCTPACKSIZE, CharSet = CharSet.Ansi)] public unsafe struct WfsCimNoteTypeList { public ushort usNumOfNoteTypes; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public WfsCimNoteType[] lppNoteTypes; } I used the following code to parse the result from invoked function: WfsCimNoteTypeList data = new WfsCimNoteTypeList(); XFSUtil.PtrToStructure<WfsCimNoteTypeList>(lpBuffer, ref data); (here lbBuffer is IntPtr) XFSUtil.PtrToStructure is defined like this: public static void PtrToStructure<T>(IntPtr ptr, ref T p) where T : struct { p = (T)Marshal.PtrToStructure(ptr, typeof(T)); } But after parsing from IntPtr to WfsCimNoteType object array, I am getting different values???
Sending array of struct from c++ to c#
I am trying to send an array of simple struct from c# to a c++ DLL and to get the size elements of the array. My c++ code is: struct TestStruct { int m_Int; }; unsigned int _declspec(dllexport) __stdcall GetSysData(LPSAFEARRAY FAR* ppsaFiles) { return (*ppsaFiles)->rgsabound[1].cElements; } My c# code is: [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct InnerStruct { public int m_Int; } [DllImport("DimMonitorDll.dll", EntryPoint = "GetSysData", CallingConvention = CallingConvention.StdCall)] public static extern int GetSysData([MarshalAs(UnmanagedType.LPArray)] ref InnerStruct[] x); public void run() { InnerStruct[] test_struct; test_struct = new InnerStruct[5]; var count = GetSysData(ref test_struct); } The problem is that I get count = 0 . I have tried many things I found on the web, but I didn't success to figure it out. I will appreciate any help, Thanks!
C# P/Invoke 2 dimensional array
I am trying to call a C function from C# code: typedef char IPTriggerNameType[256]; typedef unsigned long COMM_HANDLE; typedef BYTE GUID_TYPE[16]; typedef long LONGINT; typedef struct { GUID_TYPE Guid; IPTriggerNameType Name; }IPCAM_GENERIC_EVENT_ID; typedef struct { IPCAM_GENERIC_EVENT_ID EventId; LONGINT RelatedTriggerId; LONGINT ObsoleteEvent; }IPCAM_GENERIC_EVENT_INFO; typedef struct { LONGINT NumOfEvents; IPCAM_GENERIC_EVENT_INFO *GenericEventsList; }VID_CHANNEL_GENERIC_EVENTS_STRUCT; int __stdcall GetGenericEvents( /* Inputs: */ COMM_HANDLE Handle, LONGINT MaxNumOfChannelsInTable, LONGINT MaxNumOfEventsPerChannel, /* Outputs: */ LONGINT *NumOfChannels, VID_CHANNEL_GENERIC_EVENTS_STRUCT *ChannelsEventsTable); and the C# equivalent is as follows: [StructLayout(LayoutKind.Sequential)] public struct IPCAM_GENERIC_EVENT_ID { [MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] public byte[] Guid; [MarshalAs(UnmanagedType.LPArray, SizeConst = 256)] public char[] Name; }; [StructLayout(LayoutKind.Sequential)] public struct IPCAM_GENERIC_EVENT_INFO { public IPCAM_GENERIC_EVENT_ID EventId; public int RelatedTriggerId; public int ObsoleteEvent; } [StructLayout(LayoutKind.Sequential)] public struct VID_CHANNEL_GENERIC_EVENTS_STRUCT { public int NumOfEvents; [MarshalAs(UnmanagedType.LPArray, SizeConst = 100)] public IPCAM_GENERIC_EVENT_INFO[] GenericEventsList; } [DllImport(dllName)] public static extern int GetGenericEvents( /* Inputs: */ int Handle, int MaxNumOfChannelsInTable, int MaxNumOfEventsPerChannel, /* Outputs: */ out int NumOfChannels, out VID_CHANNEL_GENERIC_EVENTS_STRUCT[] ChannelsEventsTable); int numOfChannels = 16, actualNumOfChannels = 0; int maxNumOfEvents = 100; VID_CHANNEL_GENERIC_EVENTS_STRUCT[] genericEventsList = new VID_CHANNEL_GENERIC_EVENTS_STRUCT[numOfChannels]; for (int i = 0; i < numOfChannels; i++) { genericEventsList[i].GenericEventsList = new IPCAM_GENERIC_EVENT_INFO[maxNumOfEvents]; } GetGenericEvents(conn, numOfChannels, maxNumOfEvents, out actualNumOfChannels, out genericEventsList); when calling the C# method I get exception which crashes the application: Managed Debugging Assistant 'FatalExecutionEngineError' has detected a problem in 'My.exe'. Additional information: The runtime has encountered a fatal error. The address of the error was at 0x72a6cf41, on thread 0x5a98. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack. What am I doing wrong ?
The field: VID_CHANNEL_GENERIC_EVENTS_STRUCT.GenericEventsList is a pointer, declared as: IPCAM_GENERIC_EVENT_INFO *GenericEventsList But you are declaring it as an array, included in the struct: [MarshalAs(UnmanagedType.LPArray, SizeConst = 100)] public IPCAM_GENERIC_EVENT_INFO[] GenericEventsList; You may try to use an IntPtr and Marshal.AllocHGlobal. Check this out: How use pinvoke for C struct array pointer to C#
implement c function and parameter pointer struct to c#?
I Have a problem about implementint of C++ DLL to C# My Header File code is this : typedef struct{ uchar Command[4]; ushort Lc; uchar DataIn[512]; ushort Le; }CON_SEND; typedef struct{ ushort LenOut; uchar DataOut[512]; uchar outA; uchar outB; }CON_RESP; SDK_C_DLL_API int Csdk_Cmd(uchar port, CON_SEND * ConSend,CON_RESP * ConResp); SDK_C_DLL_API int Csdk_Open(uchar port); and my C# class [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct CON_SEND { public byte[] Command; public System.UInt16 Lc; public byte[] DataIn; public System.UInt16 Le; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct CON_RESP { public System.UInt16 LenOut; public byte[] DataOut; public byte outA; public byte outB; } [DllImport("SDK_C.dll")] public static extern System.Int16 Csdk_Cmd(System.Byte port, ref CON_SEND ConSend, ref CON_RESP ConResp); [DllImport("SDK_C.dll")] public static extern System.Int16 Csdk_Open(System.Byte port); now i use Csdk_Cmd; is working no problem this code : if(SDK_C.Csdk_Open(0x00)== 0) lblStatus.Text = "Gate 0 is busy"; else lblStatus.Text = "You can use Gate 0"; but when i try to use this code Csdk_Cmd given "NotSupportedException" CON_SEND SendCon = new CON_SEND(); SendCon.Command = new byte[] { 0xE0, 0xA0, 0xC4, 0x72 }; SendCon.DataIn = new byte[]{0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xA0}; SendCon.Lc = 0x0007; SendCon.Le = 0x0000; CON_RESP RespCon = new CON_RESP(); RespCon.DataOut = new byte[512]; SDK_C.Csdk_Cmd(0, ref SendCon, ref RespCon); // THIS LINE
Your struct declarations are not correct. The C code has inline byte arrays and simply put, they do not match the default marshalling for byte[]. The easiest way to fix it is to use MarshalAs(UnmanagedType.ByValArray). Like this: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct CON_SEND { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] Command; public ushort Lc; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] DataIn; public ushort Le; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct CON_RESP { public ushort LenOut; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] DataOut; public byte outA; public byte outB; } Your functions also have incorrect return value. A C int maps to C# int. So they declarations should be: [DllImport("SDK_C.dll")] public static extern int Csdk_Cmd(byte port, ref CON_SEND ConSend, ref CON_RESP ConResp); [DllImport("SDK_C.dll")] public static extern int Csdk_Open(byte port); The other thing to double check is the calling convention. The C# function above use the default of Stdcall. Are you sure that the C code also does? Since there is nothing specified between the return value and the function name, I suspect that the functions are actually Cdecl. In which case you need: [DllImport("SDK_C.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Csdk_Cmd(byte port, ref CON_SEND ConSend, ref CON_RESP ConResp); [DllImport("SDK_C.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Csdk_Open(byte port);
This is more of a guess really, but you are declaring your structs with inline (fixed) arrays in C++, so you'll need to tell c# to to do that too. You can do this with Fixed Size Buffers: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct CON_SEND { public fixed byte Command[4]; public System.UInt16 Lc; public fixed byte DataIn[512]; public System.UInt16 Le; } Warning: this code needs to be compiled with unsafe and appear in unsafe block. Alternatively, you could look into better defining the marshaling attributes for your struct: http://www.codeproject.com/Articles/66244/Marshaling-with-C-Chapter-2-Marshaling-Simple-Type (See #DavidHeffernan, answer below)