如何在c#中编组打包结构的非托管缓冲区

时间:2009-02-03 07:14:41

标签: c# arrays struct marshalling buffer

我使用以下pinvoke签名(成功)在c#中调用Windows FilterSendMessage函数:

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

outBuffer 参数填充了任意数量的结构(一个接一个地打包),在C中定义为:

typedef struct _BAH_RECORD {

    int evt
    int len;
    WCHAR name[1];

} BAH_RECORD, *PBAH_RECORD;

name 字段被分配一个可变长度,以null结尾的unicode字符串。 len 字段描述结构的总大小(以字节为单位)(包括名称字符串)。我相信在未受管理的方面如何处理结构是没有错的。

当我尝试将outBuffer封送到BAH_RECORD结构的一个实例时出现问题,该结构在c#中定义为:

[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>

如果我尝试打印/查看/显示bah.name,我会得到垃圾......

为了确认outBuffer确实包含有效数据,我在c#中做了一些粗略的指针hackery来执行它,调用Marshal.ReadInt32两次(覆盖前2个struct字段),然后Marshal.ReadByte几次到填充一个byte []然后我将其用作Encoding.Unicode.GetString()的参数...字符串出来很好,所以它肯定在那里,我似乎无法让marshaller正确处理它(如果它甚至可以?)

任何帮助表示赞赏

史蒂夫

1 个答案:

答案 0 :(得分:1)

问题是C#BAH_RECORD结构中的'name'字符串被封送为指向字符串(WCHAR *)的指针,但在C端,它是内联WCHAR缓冲区。因此,当您编组结构时,运行时将缓冲区的前四个字节作为指针读取,然后尝试读取它指向的字符串。

不幸的是,运行时没有办法在结构体内自动编组可变大小的缓冲区,因此您需要使用手动编组(或者说“指针hackery”)。但是当你指针指向缓冲区时,你不需要单独读取字节,然后将它们转换为字符串 - 只需调用Marshal.PtrToStringUni。