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)

Categories

Resources