Question

I'm doing an experiment as part of an R & D process. I need to be able to set values in a struct and retrieve and set them as a byte[].

Here's my struct:

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

Setting the values will obviously automatically set the byte[]:

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

Inspecting test in debug mode yields what I expect.

What I need is as follows:

  1. To be able to read the unmanaged fixed byte array as a 17 byte long managed array.
  2. To be able to set the unmanaged fixed byte array from a 17 byte managed array.

NOTES:

  1. If at all possible, I don't want to use marshalling as this is a time sensitive operation.
  2. I can't omit the fixed directive as that throws a runtime error due to the overlapping of objects and non-objects in the struct.
Was it helpful?

Solution

You're already using unsafe code, so why not simply get a pointer to the structure and pass it? Doesn't this work?

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

unsafe
{
  MyBuffer* pStruct = &bf;

  YourNativeMethod(pStruct);
}

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

To avoid all marshalling, you might have to write a C++/CLI wrapper, I'm not sure if .NET does marshalling even if you pass an unsafe pointer.

You don't even need the byte-array, the native method certainly doesn't care whether you're passing a pointer to a byte array or a structure. Everything is a byte array :D

EDIT: Since your case doesn't explicitly call a native method, we have to go around this.

The problem is, fixed byte[] isn't actually a byte array at all. It's simply a sequence of 17 bytes, nothing more. That's not enough for a .NET array. So we have to copy it to a new array (it might be worthwhile to keep "buffer" byte arrays ready and recycle them to avoid allocations and deallocations). This can be done either through Marshal.Copy or some unsafe pointer fun:

byte[] bytes = new byte[17];

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

This uses direct memory copying, but does some checks. In my testing, it's a great way to copy bigger arrays (for me the break-even point was somewhere around 50 bytes). If your array is smaller, the overhead of those checks gets higher compared to total copy time, so you might want to use byte-by-byte copying instead:

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

I hope I don't have to tell you to be careful around this kind of code :)

In any case, I wouldn't worry about the performance too much and I'd use the Marshal.Copy variant, simply because your DB call is going to be the bottle-neck anyway. The safer option is better :)

There's also a few tricks you can use to speed this up, for example copying a whole int or long at a time, which the CPU is much better, although it's trickier. Trying with a simple variant (a length with a multiple of 4, copying a whole int at a time) cut my test runtime by four. If your data length is not a multiple of four, you'd simply copy the remainder as bytes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top