Mr. JWolf
Mr. JWolf

Reputation: 1385

C# Marshal typedef T_STRING T_STRINGS[MAX_STRING_SIZE]

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

Answers (2)

Crashoverridee2002
Crashoverridee2002

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

David Heffernan
David Heffernan

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

Related Questions