将C++结构编组到C#的最有效方法是什么?

sco*_*ttm 6 c# performance pinvoke marshalling

我即将开始阅读大量的二进制文件,每个文件有1000个或更多记录.不断添加新文件,因此我正在编写Windows服务来监视目录并处理收到的新文件.这些文件是用c ++程序创建的.我在c#中重新创建了结构定义并且可以很好地读取数据,但是我担心我这样做会最终导致我的应用程序死机.

using (BinaryReader br = new BinaryReader(File.Open("myfile.bin", FileMode.Open)))
{
    long pos = 0L;
    long length = br.BaseStream.Length;

    CPP_STRUCT_DEF record;
    byte[] buffer = new byte[Marshal.SizeOf(typeof(CPP_STRUCT_DEF))];
    GCHandle pin;

    while (pos < length)
    {
        buffer = br.ReadBytes(buffer.Length);
        pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        record = (CPP_STRUCT_DEF)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(CPP_STRUCT_DEF));
        pin.Free();

        pos += buffer.Length;

        /* Do stuff with my record */
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为我不需要使用GCHandle,因为我实际上并没有与C++应用程序通信,所有内容都是通过托管代码完成的,但我不知道另一种方法.

Dir*_*mar 8

使用Marshal.PtrToStructure相当慢.我在CodeProject上发现了以下文章,它比较(和基准测试)阅读二进制数据的不同方法非常有用:

用C#快速读取二进制文件


Jar*_*Par 6

对于您的特定应用,只有一件事会给您明确的答案:简介.

这里所说的是我在使用大型PInvoke解决方案时学到的经验教训.编组数据的最有效方法是编组可以闪现的字段.这意味着CLR可以简单地执行相当于memcpy的操作来在本机代码和托管代码之间移动数据.简单来说,从结构中获取所有非内联数组和字符串.如果它们存在于本机结构中,则使用IntPtr表示它们,并根据需要将值封送到托管代码中.

我没有描述使用Marshal.PtrToStructure与使用本机API取消引用值之间的区别.如果通过剖析将PtrToStructure显示为瓶颈,那么这可能是您应该投资的.

对于大型层次结构,按需编组,而不是一次将整个结构拉入托管代码.在处理大型树结构时,我遇到的问题最多.编组一个单独的节点非常快,如果它是快速的,并且性能明智,它只能在那一刻编组你需要的东西.