I am trying to serialize the following c# structure:
[Serializable]
[StructLayout(LayoutKind.Sequential, Size = 70, CharSet = CharSet.Ansi)]
public struct USSDContinueModel
{
[MarshalAs(UnmanagedType.U4)]
public uint Command_Length;
[MarshalAs(UnmanagedType.U4)]
public uint Command_ID;
[MarshalAs(UnmanagedType.U4)]
public uint Command_Status;
[MarshalAs(UnmanagedType.U4)]
public uint Sender_ID;
[MarshalAs(UnmanagedType.U4)]
public uint Receiver_ID;
[MarshalAs(UnmanagedType.U1)]
public uint Ussd_Version;
[MarshalAs(UnmanagedType.U1)]
public uint Ussd_Op_Type;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string MsIsdn;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string Service_Code;
[MarshalAs(UnmanagedType.U1)]
public uint Code_Scheme;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 182)]
public string Ussd_Content;
// Calling this method will return a byte array with the contents
// of the struct ready to be sent via the tcp socket.
public byte[] Serialize()
{
// allocate a byte array for the struct data
var buffer = new byte[Marshal.SizeOf(typeof(USSDContinueModel))];
// Allocate a GCHandle and get the array pointer
var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var pBuffer = gch.AddrOfPinnedObject();
// copy data from struct to array and unpin the gc pointer
Marshal.StructureToPtr(this, pBuffer, false);
gch.Free();
return buffer;
}
// this method will deserialize a byte array into the struct.
public void Deserialize(ref byte[] data)
{
var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
this = (USSDContinueModel)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(USSDContinueModel));
gch.Free();
}
}
when i try to serialize an instance of the structure say:
public USSDContinueModel continueModel;
continueModel.Command_Length = 174;
continueModel.Command_ID = 0x00000070;
continueModel.Command_Status = 0;
continueModel.Sender_ID = 0x01000005;
continueModel.Receiver_ID = 0x2900AB12;
continueModel.Ussd_Version = 0x20;
continueModel.Ussd_Op_Type = 0x01;
continueModel.MsIsdn = "08098765476";
continueModel.Service_Code = "*308";
continueModel.Code_Scheme = 0x44;
continueModel.Ussd_Content = "1. Continue if you are 18+ 2. Exit i";
I keep getting the error "Type: USSDcontinueModel cannot be marshalled as an unmanaged structure; no meaningful size or offset can be computed".
I noticed this happens when i set the Ussd_Version, Ussd_Op_Type and Code_Scheme as [MarshalAs(UnmanagedType.U1)] but it works fine with [MarshalAs(UnmanagedType.U4)].
Is [MarshalAs(UnmanagedType.U1)] unmarshallable? what do i do?
The problem is that the definition:
[MarshalAs(UnmanagedType.U1)]
public uint Ussd_Op_Type;
is ambiguous as to what size the members could be. If the interop marshal uses the member's size, it will calculate 4 bytes, whereas if it uses the MarshalAs attribute it will calculate 1 byte. Changing the member to use the correct size type,
[MarshalAs(UnmanagedType.U1)]
public byte Ussd_Op_Type;
should fix the issue.
So after taking #theB's recommendation of declaring the U1 data types as bytes. The problem was resolved. The updated structure definition follows:
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=1)]
public struct USSDContinueModel
{
[MarshalAs(UnmanagedType.U4)]
public uint Command_Length;
[MarshalAs(UnmanagedType.U4)]
public uint Command_ID;
[MarshalAs(UnmanagedType.U4)]
public uint Command_Status;
[MarshalAs(UnmanagedType.U4)]
public uint Sender_ID;
[MarshalAs(UnmanagedType.U4)]
public uint Receiver_ID;
[MarshalAs(UnmanagedType.U1)]
public byte Ussd_Version;
[MarshalAs(UnmanagedType.U1)]
public byte Ussd_Op_Type;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string MsIsdn;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string Service_Code;
[MarshalAs(UnmanagedType.U1)]
public byte Code_Scheme;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 182)]
public string Ussd_Content;
// Calling this method will return a byte array with the contents
// of the struct ready to be sent via the tcp socket.
public byte[] Serialize()
{
// allocate a byte array for the struct data
var buffer = new byte[Marshal.SizeOf(typeof(USSDContinueModel))];
// Allocate a GCHandle and get the array pointer
var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var pBuffer = gch.AddrOfPinnedObject();
// copy data from struct to array and unpin the gc pointer
Marshal.StructureToPtr(this, pBuffer, false);
gch.Free();
return buffer;
}
// this method will deserialize a byte array into the struct.
public void Deserialize(ref byte[] data)
{
var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
this = (USSDContinueModel)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(USSDContinueModel));
gch.Free();
}
}
Related
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???
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?
I'm trying to marshal the MIB_TCPTABLE_OWNER_MODULE struct from a P/Invoked' call into GetExtendedTcpTable, defined in iphlpapi.dll.
My P/Invoke signature is defined as this:
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);
According to the documentation on MSDN (and looking through the header files), this should set the pTcpTable parameter to the address of a MIB_TCPTABLE_OWNER_MODULE structure, who has a member which is an array of MIB_TCPROW_OWNER_MODULE structures. From tcpmib.h:
typedef struct _MIB_TCPTABLE_OWNER_MODULE
{
DWORD dwNumEntries;
MIB_TCPROW_OWNER_MODULE table[ANY_SIZE];
} MIB_TCPTABLE_OWNER_MODULE, *PMIB_TCPTABLE_OWNER_MODULE;
ANY_SIZE is defined to be 1.
Here is my problem; I've defined the MIB_TCPTABLE_OWNER_MODULE and MIB_TCPROW_OWNER_MODULE structs in C# like so:
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
public uint dwNumEntries;
MIB_TCPROW_OWNER_MODULE table;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPROW_OWNER_MODULE
{
public uint dwState;
public uint dwLocalAddr;
public uint dwLocalPort;
public uint dwRemoteAddr;
public uint dwRemotePort;
public uint dwOwningPid;
public ulong liCreateTimestamp;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TCPIP_OWNING_MODULE_SIZE)]
public ulong[] OwningModuleInfo;
}
Since I won't know the size of the returned MIB_TCPTABLE_OWNER_MODULE's table member at declaration, my plan was to increment the IntPtr and use Marshal.PtrToStructure to extract each array member from the table member.
The call to Marshal.PtrToStructure returns (no memory violation exceptions), but I wind up with garbage values in the struct members. Here is my complete code:
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPROW_OWNER_MODULE
{
public uint dwState;
public uint dwLocalAddr;
public uint dwLocalPort;
public uint dwRemoteAddr;
public uint dwRemotePort;
public uint dwOwningPid;
public ulong liCreateTimestamp;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TCPIP_OWNING_MODULE_SIZE)]
public ulong[] OwningModuleInfo;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
public uint dwNumEntries;
MIB_TCPROW_OWNER_MODULE table;
}
private const int TCPIP_OWNING_MODULE_SIZE = 16;
private const int AF_INET = 2;
private const int TCP_TABLE_OWNER_MODULE_ALL = 8;
public static void GetConnectionDetails()
{
var bufferSize = 0;
var ret = GetExtendedTcpTable(IntPtr.Zero, ref bufferSize, true, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
var tableBuffer = Marshal.AllocHGlobal(bufferSize);
try
{
ret = GetExtendedTcpTable(tableBuffer, ref bufferSize, true, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
if (ret != 0)
throw new Exception("Oh noes!");
var convertedTable = (MIB_TCPTABLE_OWNER_MODULE)Marshal.PtrToStructure(tableBuffer, typeof (MIB_TCPTABLE_OWNER_MODULE));
var finalTable = new MIB_TCPROW_OWNER_MODULE[convertedTable.dwNumEntries];
var rowPtr = (IntPtr) ((long) tableBuffer + Marshal.SizeOf(convertedTable.dwNumEntries));
for (int i = 0; i < convertedTable.dwNumEntries; i++)
{
var row = (MIB_TCPROW_OWNER_MODULE)Marshal.PtrToStructure(rowPtr, typeof (MIB_TCPROW_OWNER_MODULE));
finalTable[i] = row;
rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(row)); // Move to the next entry
}
foreach (var entry in finalTable)
{
// do something with each entry
Console.WriteLine(entry.dwState);
Console.WriteLine(entry.dwRemoteAddr);
}
}
finally
{
Marshal.FreeHGlobal(tableBuffer);
}
}
Comparing memory between this and an unmanaged version (that works properly), I do see some differences in the memory of the marshaled struct that I can't account for; there are a few bytes different.
Any assistance is most appreciated!
Consider this struct:
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
public uint dwNumEntries;
MIB_TCPROW_OWNER_MODULE table;
}
You are assuming that the offset of table is equal to the size of dwNumEntries. But you are forgetting about alignment. Since the largest type in MIB_TCPROW_OWNER_MODULE is 8 bytes wide, that type has alignment of 8. Which means that in order for it to be aligned it must be placed at an offset that is a multiple of 8. And hence there is padding between dwNumEntries and table. You need to allow for that padding.
So, at this point:
var rowPtr = (IntPtr) ((long) tableBuffer +
Marshal.SizeOf(convertedTable.dwNumEntries));
you add 4 to the address held in tableBuffer. You actually need to add 8. You should use Marshal.OffsetOf to calculate the offset:
var rowPtr = (IntPtr)((long)tableBuffer +
(long)Marshal.OffsetOf(typeof(MIB_TCPTABLE_OWNER_MODULE), "table"));
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?
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)