4

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)

1
  • 1
    Using a char[] is indeed the way to do it. Commented Aug 17, 2010 at 11:41

3 Answers 3

2

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

Sign up to request clarification or add additional context in comments.

3 Comments

It seems really strange to me that there is no Marshalling attribute that can help marshaling non null-terminated strings...
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.
public string Foo { get => new string(this.foo); set => this.foo = value.ToCharArray(); } Add a property like this to make things less painful.
2

You could use StructLayout(LayoutKind.Explicit ...) and tag each field with [FieldOffset(n)]. This will allow you to increase the SizeConst value to 43, and still mark the next field as starting at offset 42. The marshaller will marshal the 42 character string and ignore the 43rd byte appending a null-terminator instead.

Comments

0

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

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.