Convert c++ struct to c# struct. Error output - c#

My C++ dll has two struct.
C++:
typedef struct interface_info {
DWORD index;
DWORD ipv4Addr;
BYTE macAddr[6];
DWORD gateway;
}I_INFO;
typedef struct user_info {
DWORD uniqueid;
BYTE userid[32];
BYTE pwd[32];
BYTE info[5][64];
}U_INFO;
C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct I_INFO
{
public uint Index;
public uint ipv4Addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] macAddr;
public uint gateway;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct U_INFO
{
public uint uniqueid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] userid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] pwd;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 320)]
public byte[] info;
};
I'm calling this method:
C++:
void GetNetworkInfo(I_INFO *n); // Show interface Info
dword RegNewUser(U_INFO *u); // Save to test.ini
C#:
[DllImport("TestNet.dll")]
public static extern void GetNetworkInfo(ref I_INFO n);
[DllImport("TestNet.dll")]
public static extern uint RegNewUser(ref U_INFO u);
I'm calling this way:
C#:
I_INFO ii = new I_INFO();
GetNetworkInfo(ref ii);
MessageBox.Show(ii.index+ "\n"
+ Long2ip(ii.ipAddr) + "\n"
+ Long2ip(ii.gateway) + "\n"
+ Bytes2Str(ii.macAddr);
....
I_INFOuser = new I_INFO
{
uid = Encoding.ASCII.GetBytes("testUser"),
pwd = Encoding.ASCII.GetBytes("testPassword"),
info = Encoding.ASCII.GetBytes("hello world!")
};
try
{
RegNewUser(ref user);
}
catch(Exception err)
{
MessageBox.Show(err.ToString());
}
The output is:
GetNetworkInfo returns normal results, but RegNewUser outputs this error:
Error: Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout.
How do I fix this?

Related

C struct and use C dll in 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.

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???

Passing a struct by reference from managed to unmanaged code?

my struct looks like this in c code:
typedef struct _REQUEST {
char D [_D_MAX+1];
char M [_M_MAX+1];
char T [_T_MAX+1];
char ClRef [_CL_REF_MAX+1];
char load [_LOAD_MAX];
ULONG loadLen;
} _REQUEST, *LP_REQUEST;
in c# like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct _REQUEST_STRUCT {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string D;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string M;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string T;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string clRef;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1200)]
public string Load;
public UInt32 LoadLen;
}
the method I am calling is like this in c:
int
AJ_STDCALL
SimpTran (const char* svc_name,
const LP_REQUEST query,
LP_RESPONSE response)
{
//init stuff
//Validate input parameters
if ( query == NULL )
return(ERR_QUERY_NULL);
if (query->loadLen == 0)
return (ERR_QUERY_NULL);
}
in c#:
[DllImport(LINUXLIB, CallingConvention = CallingConvention.StdCall)]
public static extern int SimpTran(string serID, ref _REQUEST_STRUCT request, out IntPtr response);
and i am calling it like this:
p_request_struct = new _REQUEST_STRUCT();
// fill p_request_struct
// response
p_response_struct = new _RESPONSE_STRUCT();
// initialize unmanaged memory to hold return struct
IntPtr ptrRet = Marshal.AllocHGlobal(Marshal.SizeOf(p_response_struct));
Marshal.StructureToPtr(p_response_struct, ptrRet, false);
iRet = SimpTran(SerID, ref p_request_struct, out ptrRet);
I am getting null query! In C code query==null is true and it returns null. But I am populating the request struct?
Do I have to allocate memory for that as well?

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)

How can I correctly convert these to c#, marshall so I can pass these struct to DLL (c++)?

C++
#define FIELD_SIZE_MSGID 24
#define FIELD_SIZE_TIME 12
#define FIELD_SIZE_ADMIN 256
typedef struct
{
char MsgId[FIELD_SIZE_MSGID+1];
char SendTime[FIELD_SIZE_TIME+1];
char ReceiptTime[FIELD_SIZE_TIME+1];
} AdminDataM0;
typedef struct
{
int Type;
union
{
AdminDataM0 M0;
char Data[FIELD_SIZE_ADMIN + 1];
} AdData;
char Unknown[FIELD_SIZE_ADMIN + 1];
} AdminData;
C#:
[DllImport("Receiver.dll",
CallingConvention = CallingConvention.Cdecl,
ExactSpelling = false,
SetLastError = false,
CharSet = CharSet.Ansi,
EntryPoint = "SendMessage")]
[return: MarshalAs(UnmanagedType.I4)]
protected static extern int SendMessage(
[MarshalAs(UnmanagedType.Struct)] ref AdminData ptrAdminData,
);
protected const Int32 FIELD_SIZE_MSGID = 24;
protected const Int32 FIELD_SIZE_TIME = 12;
protected const Int32 FIELD_SIZE_ADMIN = 256;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct AdminDataM0
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_MSGID + 1)]
public char[] MsgId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_TIME + 1)]
public char[] SendTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_TIME + 1)]
public char[] ReceiptTime;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
protected struct AdminData
{
[MarshalAs(UnmanagedType.I4)]
public Int32 nType;
[MarshalAs(UnmanagedType.Struct)]
public Data AdminData_Data;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Data
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public AdminDataM0 M0; //135
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_ADMIN + 1)]
public char[] Data_FldSizeAdmin;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_ADMIN + 1)]
public char[] Unknown;
}
MAIN:
AdminData oAdminData = new AdminData();
oAdminData.AdminData_Data = new oAdminData.Data();
oAdminData.AdminData_Data.M0 = new oAdminDataM0();
oAdminData.AdminData_Data.M0.MsgId = new char[FIELD_SIZE_MSGID + 1];
oAdminData.AdminData_Data.M0.SendTime = new char[FIELD_SIZE_TIME + 1];
oAdminData.AdminData_Data.M0.ReceiptTime = new char[FIELD_SIZE_TIME + 1];
oAdminData.AdminData_Data.Data_FldSizeAdmin = new char[FIELD_SIZE_ADMIN + 1];
oAdminData.Unknown = new char[FIELD_SIZE_ADMIN + 1];
string M0_MsgId = "MsgId";
string M0_SendTime = "Send Time";
string M0_ReceiptTime = "ReceiptTime";
string unknown = "Unknown";
M0_MsgId.ToCharArray().CopyTo(oAdminData.AdminData_Data.M0.MsgId, 0);
M0_SendTime.ToCharArray().CopyTo(oAdminData.AdminData_Data.M0.SendTime, 0);
M0_ReceiptTime.ToCharArray().CopyTo(oAdminData.AdminData_Data.M0.ReceiptTime, 0);
// function to DLL
SendMessage(ref oAdminData);
Problem:
Only the MsgId and the DataData_FldSizeAdmin have values which is the same value.
I think it's because they are sharing the same memory address.
UNKNOWN, SENDTIME and RECEIPTIME don't have values.
In stead of MarshalAs[UnmanagedType.ByValArray], you should use MashalAs[UnmanagedType.ByValTStr] inside the declaration of your struct:
as in:
...
public struct AdminDataM0
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FIELD_SIZE_MSGID + 1)]
public string MsgId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FIELD_SIZE_TIME + 1)]
public string SendTime;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FIELD_SIZE_TIME + 1)]
public string ReceiptTime;
}
...
See the documentation:
ByValTStr: Used for in-line,
fixed-length character arrays that
appear within a structure. The
character type used with ByValTStr is
determined by the
System.Runtime.InteropServices.CharSet
argument of the
System.Runtime.InteropServices.StructLayoutAttribute
applied to the containing structure.
Always use the
MarshalAsAttribute.SizeConst field
to indicate the size of the array.
.NET Framework ByValTStr types behave
like C-style, fixed-size strings
inside a structure (for example, char
s[5]). The behavior in managed code
differs from the Microsoft Visual
Basic 6.0 behavior, which is not null
terminated (for example, MyString As
String * 5).
The documentation for ´ByValArray´ sais (emphasis mine):
When MarshalAsAttribute.Value is set
to ByValArray, the SizeConst must be
set to indicate the number of elements
in the array. The ArraySubType field
can optionally contain the
UnmanagedType of the array elements
when it is necessary to differentiate
among string types. You can only use
this UnmanagedType on an array that
appear as fields in a structure.
So I think for your code to work using ByValArray, you should also add a ArraySubType of LPStr to the MarshalAs attribute.

Categories

Resources