views:

1511

answers:

2

Hello

I am (successfully) calling the Windows FilterSendMessage function in c# using the following pinvoke signature:

[DllImport("fltlib.dll")]
    public static extern IntPtr FilterSendMessage(
        IntPtr hPort,
        IntPtr inBuffer,
        UInt32 inBufferSize,
        IntPtr outBuffer,
        UInt32 outBufferSize,
        out UInt32 bytesReturned);

The outBuffer parameter is populated with an arbitrary number of structs (packed one after the other), defined in C as:

typedef struct _BAH_RECORD {

    int evt
    int len;
    WCHAR name[1];

} BAH_RECORD, *PBAH_RECORD;

The name field is assigned a variable length, null-terminated unicode string. The len field describes the total size of the struct in bytes (including the name string). I am confident there's nothing wrong with how the structs are being handled in the unmanaged side of things.

My problem arises when I try and marshal the outBuffer to an instance of the BAH_RECORD struct, defined in c# as:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct BAH_RECORD
{
    public UInt32 evt;
    public UInt32 len;
    public string name;
}

IntPtr outBuffer = Marshal.AllocHGlobal(OUT_BUFFER_SIZE);

hResult = Win32.FilterSendMessage(hPortHandle, inBuffer, IN_BUFFER_SIZE, outBuffer, OUT_BUFFER_SIZE, out bytesReturned);

BAH_RECORD bah = (BAH_RECORD)Marshal.PtrToStructure(outBuffer, typeof(BAH_RECORD));

<snip>

If I try and print/view/display bah.name, I get garbage...

To confirm that outBuffer does actually contain valid data, I did some crude pointer hackery in c# to step though it, calling Marshal.ReadInt32 twice (to cover the first 2 struct fields), and then Marshal.ReadByte a few times to populate a byte[] which I then use as an argument to Encoding.Unicode.GetString()...the string comes out fine, so it's definitely in there, I just can't seem to get the marshaller to handle it correctly (if it even can?)

Any help appreciated

Steve

A: 

The problem is that the 'name' string in your C# BAH_RECORD struct is marshaled as a pointer to a string (WCHAR*) but on the C side it is an inline WCHAR buffer. So when you marshal your struct the runtime reads the first four bytes of the buffer as a pointer and then attempts to read the string that it points to.

Unfortunately, there is no way for the runtime to automatically marshal variable sized buffers inside structs so you will need to use manual marshaling (or as you say "pointer hackery"). But when you advance the pointer to point at the buffer you don't need to read in the bytes individually and then convert them to a string - just call Marshal.PtrToStringUni.

Stephen Martin
PtrToStringUni works great. Thank you for the suggestion :)
TheLearningCurve
A: 

Can you shouw the code example using Marshal.PtrToStringUni?

I have a similar issue.

I am guessing that this line: BAH_RECORD bah = (BAH_RECORD)Marshal.PtrToStructure(outBuffer, typeof(BAH_RECORD));

is going to be replaced.