[
StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public class Request{
public UInt16 FirstIndex = 0; //Patient filters public bool use_ID = false;[
MarshalAs(UnmanagedType.BStr)] public string ID; public bool use_FN = false;}
I would like to marshal (convert) this class to an array of bytes that can be sent over a socket and then be used by a C++ program.
The C++ program will interpret the data as following
1. read 2 bytes for index
2. read 1 byte as bool
3. read 2 bytes as length of string
4. read X bytes (strlength)
5. read 1 byte as bool
So if a Request package looks like this:
firstIndex = 33;
use_ID = true;
ID = "test";
use_FN = true
Id like the byte-array to look like this:
[0] = 33
[1] = 00
[2] = 1 (true)
[3] = 2 (stlen byte 1)
[4] = 0 (stlen byte 2)
[5] = 't'
[6] = 'e'
[7] = 's'
[8] = 't'
[9] = 1 (true)
How do I convert the class? Marshal.StructToPtr? I have tried but failed :(
AlanPosted Aug 9, 2007, 6:39 AM
I don't think that there is a way to use Marshal.StructureToPtr() here for the simple reason that your class contains a string (i.e. a reference type). This will inevitably be marshalled to an unmanaged pointer (4 bytes on Win32) containing the address of where the BStr is actually stored on the unmanaged heap, whereas what you're looking for is for the BStr to be stored 'inline' with the other class fields.
I think you'll see what I mean if you run this code. Notice, incidentally that the bools need to be marshalled to UnmanagedType.I1 (one byte) otherwise they'll come out by default as BOOLs which are 4 bytes long.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class Request
{
public UInt16 FirstIndex = 0;
//Patient filters
[MarshalAs(UnmanagedType.I1)]
public bool use_ID = false;
[MarshalAs(UnmanagedType.BStr)]
public string ID;
[MarshalAs(UnmanagedType.I1)]
public bool use_FN = false;
}
.........
Request r = new Request();
r.FirstIndex = 33;
r.use_ID = true;
r.ID = "test";
r.use_FN = true;
int size = Marshal.SizeOf(r);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(r, ptr, true);
byte[] ba = new byte[size];
Marshal.Copy(ptr,ba, 0, size);
Marshal.FreeHGlobal(ptr);
// check what was written
Console.WriteLine("Size is {0} bytes:\n", size);
for(int i = 0; i < size ; i++)
{
Console.WriteLine("[{0}] = {1}", i, ba[i]);
}
Notice that the size is 8 bytes rather than 10 and the 4 bytes allocated to the string represent an address rather than the actual bytes themselves.
Stefan JohanssoPosted Aug 9, 2007, 1:13 AM
Well, I wanted to utilize the marshalling function.
Your example is good ol´ C-mangling and that was not what I was looking for :(
There must be some way to use the Marshal.StructureToPtr...
AlanPosted Aug 8, 2007, 12:05 PM
I'd be inclined to just marshal it by hand:
Request r = new Request();
r.firstIndex = 33;
r.use_ID = true;
r.ID = "test";
r.use_FN = true;
byte[] ba = new byte[6 + r.ID.Length];
int len = ba.Length;
ba[0] = (byte)(r.firstIndex % 256);
ba[1] = (byte)(r.firstIndex / 256);
if (r.use_ID) ba[2] = 1; // default is 0
ba[3] = (byte)(r.ID.Length % 256);
ba[4] = (byte)(r.ID.Length / 256);
for (int i = 5; i < len - 1 ; i++)
{
ba[i] = (byte)r.ID[i - r.ID.Length - 1];
}
if (r.use_FN) ba[len - 1] = 1; // default is 0
// check it worked
for(int i = 0; i < len ; i++)
{
if (i > 4 && i < len - 1)
Console.WriteLine("[{0}] = '{1}'", i, (char)ba[i]);
else
Console.WriteLine("[{0}] = {1}", i, ba[i]);
}