views:

136

answers:

2

Consider the following struct to be sent over TCP to an unmanaged dll

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct FooMessage
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
    public string foo;

    //More fields...
}

Using the following function (credit to Cheeso):

public byte[] RawSerialize( T item )
{
    int rawSize = Marshal.SizeOf( typeof(T) );
    IntPtr buffer = Marshal.AllocHGlobal( rawSize );
    Marshal.StructureToPtr( item, buffer, false );
    byte[] rawData = new byte[ rawSize ];
    Marshal.Copy( buffer, rawData, 0, rawSize );
    Marshal.FreeHGlobal( buffer );
    return rawData;
}

Problem: The marshaller assumes foo is a null terminated string, whereas the unmanaged dll does not - and actually uses the last char (which always comes out null from the marshaller).

Any ideas ?

Clarification: I can't just change the SizeConst to 43, because I need to maintain the total size of the message, as well as the position of the next fields in the struct (according to an existing ICD)

A: 

You have two, and only two, choices:

  1. make the dll understand about NUL-terminated strings; or
  2. send a character count with the message and make the dll understand that count.

One or the other, take your pick.

-- b

Pete Wilson
The dll is out of my control. I actually have found a workaround, see OP. just wondering if there is a better way, though I doubt it.
ohadsc
+1  A: 

Since no other answer has been posted, here is the workaround I've found

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct FooMessage
{
    // use this for non-null-terminated strings
    // use default encoder to convert to and from string
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=42)]
    public char[] foo;

    //More fields...
}

Also a similar solution by TCP expert Stephen Cleary

ohadsc
It seems really strange to me that there is no Marshalling attribute that can help marshaling non null-terminated strings...
Paulo Manuel Santos
We ended up hiding (private) the char[] field and defining a public property on the struct with getter and setter that converts the char[] to string and vice-versa. It also applies Trim and Pad as necessary.
Paulo Manuel Santos