Reputation: 1385
How to Marshal:
#define MAX_STRING_SIZE 255
typedef char T_STRING[MAX_STRING_SIZE];
#define MAX_STRING_LIST_SIZE 50
typedef T_STRING T_STRINGS[MAX_STRING_LIST_SIZE];
typedef struct
{
unsigned long m_ID;
T_STRING m_input;
T_STRINGS m_resultTexts;
} ResultList;
In C#?
I have tried:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ResultList
{
public uint m_ID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string m_input;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12750)]
public string[] m_resultTexts;
}
When the call is made it throws the error: Cannot marshal 'parameter #2': Internal limitation: structure is too complex or too large.
Parameter #2 is the ResultList struct.
This question is a "next step" from C# Marshal typedef char T_STRING[MAX_STRING_SIZE]
Upvotes: 0
Views: 405
Reputation: 1
What you need to do to use it for sending over a TCP/IP or a PIPESTREAM is the following.
Before each variable, just after you do your MarshalAs
[MarshalAs(UnmanagedType.U4 or BytValArray or I4 ... ETC ]
DO: [FieldOffset(Previous offset value + Previous Variable Size in byte)]
EXAMPLE
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
[FieldOffset(4)]
private char[] zSlotName;
public char[] _zSlotName
{
get { return zSlotName; }
set {
zSlotName = new char[9];
for (int i = 0; i < zSlotName.Length; i++)
{
if (value.Length > i)
zSlotName[i] = value[i];
else
zSlotName[i] = ' ';
}
}
}
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(13)]
private int nThreadId;
public int _nThreadId
{
get { return nThreadId; }
set { nThreadId = value; }
}
This will tell the compiler and the program that when you send the variable over TCP/IP Or PIPESTREAM, or want to write it binary where in the block to allocate the variable in correlation to the rest of the variables but also how to read it when returning from (STREAM)
I know it might sound like something is deeply wrong, but its due to the various size of int's and bytes and dword, words and what not variables we have to day in various languages i think C# needs to know specific where its variables are in memory or on file.
If you do this then you should have no problem in making your own communication protocol for game or what ever you want, please note its very easy to convert this to a class for storage in DB or for binary storage in a file if your not to keen on using SQL.
Kind regards
Crash override ;)
Upvotes: 0
Reputation: 612864
The marshaller will not deal with an array of fixed length strings. That's just too complex.
The struct declaration you have is incorrect anyway. The SizeConst
specifies array length rather than byte size. If you consider this more carefully you'll realise that you'd have to specify two dimensions, the array length, and the dimension of the array element type. But it doesn't matter. No amount of tweaking these attributes gets around the problem.
This means that you will need to do it by hand. You could do it using a custom marshaller. Or you could just allocate a block of memory with Marshal.AllocHGlobal
and pass that the the function.
I cannot tell which way the data flows, but either way is easy enough. To pass data to the unmanaged code, you need to write to the block of memory you allocated. Use Marshal.WriteInt32
to write the integer. Use Marshal.Copy
to write the strings. You'll need to to pointer arithmetic. And you'll need to use Encoding.GetBytes
to get the string data.
In the opposite direction, you'd use Marshal.ReadInt32
for the uint
. And the strings can be copied to byte arrays and decoded with Encoding.GetString
.
For the struct layout you'll need to consider alignment and padding. As it happens this struct currently has no internal padding. But it must be a multiple of 4 in size because of the integer's alignment requirements. Take care as you expand it.
Finally, if you control both managed and unmanaged code you might consider changing the data type on the unmanaged side to make marhsalling easier. Or you might contemplate using C++/CLI again to smooth the interop.
Upvotes: 2