将托管数组复制到非托管固定大小的数组中

时间:2014-02-20 14:07:11

标签: c# arrays marshalling unmanaged unsafe

我正在做一个实验,作为R& S的一部分。 D过程。我需要能够在结构中设置值并检索并将它们设置为byte []。

这是我的结构:

[StructLayout(LayoutKind.Explicit, Size = 17)]
unsafe internal struct MyBuffer
{
    [FieldOffset(0)]
    internal fixed byte Bytes[17];

    [FieldOffset(0)]
    internal long L1;

    [FieldOffset(8)]
    internal long L2;

    [FieldOffset(16)]
    internal byte B;
}

设置值显然会自动设置字节[]:

MyBuffer test = new MyBuffer();
test.L1 = 100;
test.L2 = 200;
test.B = 150;

在调试模式下检查测试会产生我期望的结果。

我需要的是:

  1. 能够将非托管固定字节数组读取为17字节长的托管数组。
  2. 能够从17字节的托管数组中设置非托管固定字节数组。
  3. 注意:

    1. 如果可能的话,我不想使用编组,因为这是一个时间敏感的操作。
    2. 我不能省略fixed指令,因为结构中的对象和非对象重叠会引发运行时错误。

1 个答案:

答案 0 :(得分:2)

您已经在使用不安全的代码了,为什么不简单地获取指向结构的指针并传递它?这不行吗?

MyBuffer bf = new MyBuffer();
bf.L1 = 23;

unsafe
{
  MyBuffer* pStruct = &bf;

  YourNativeMethod(pStruct);
}

[DllImport]
static extern void YourNativeMethod(MyBuffer* pStruct);

为了避免所有编组,您可能必须编写C ++ / CLI包装器,即使您传递了不安全的指针,我也不确定.NET是否会进行编组。

您甚至不需要字节数组,本机方法当然不关心您是否将指针传递给字节数组或结构。一切都是字节数组:D

编辑:由于你的案例没有明确地调用本机方法,我们必须解决这个问题。

问题是,固定byte []实际上根本不是字节数组。它只是一个17字节的序列,仅此而已。这对于.NET数组来说还不够。所以我们必须将它复制到一个新的数组(保持“缓冲区”字节数组准备好并回收它们以避免分配和解除分配可能是值得的)。这可以通过Marshal.Copy或一些不安全的指针乐趣来完成:

byte[] bytes = new byte[17];

unsafe
{
  IntPtr srcPtr = new IntPtr(bf.Bytes);
  {
    Marshal.Copy(srcPtr, bytes, 0, 17);
  }
}

这使用直接内存复制,但会进行一些检查。在我的测试中,这是一个很好的方法来复制更大的数组(对我来说,收支平衡点大约是50字节)。如果您的阵列较小,那么与总复制时间相比,这些检查的开销会更高,因此您可能希望使用逐字节复制:

byte[] bytes = new byte[17];
unsafe
{
  byte* srcPtr = bf.Bytes;
  fixed (byte* bPtr = bytes)
  {
    var j = 0;
    while (j++ < 17)
    {
      *(bPtr + j) = *(srcPtr++);
    }
  }
}

我希望我不必告诉你要小心这种代码:)

在任何情况下,我都不会过分担心性能而且我会使用Marshal.Copy变体,因为无论如何你的数据库调用都将成为瓶颈。更安全的选择更好:)

还可以使用一些技巧来加快速度,例如一次复制整个intlong,虽然它比较复杂但CPU要好得多。尝试使用一个简单的变体(长度为4的倍数,一次复制整个int)会将我的测试运行时间减少4。如果您的数据长度不是4的倍数,则只需将余数复制为字节。