Search code examples
c#pointerspinvoke

How do I allocate GCHandle to structure when structure contains bool


I have been trying to create a handle to a structure type because I need a pinned pointer to it, but I am getting the error "Object contains non-primitive or non-blittable data"

My structure looks like this:

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.U1)]
    public bool Test;
}

Now, when I call,

var mystruct = new MyStruct();
var handle = GCHandle.Alloc(mystruct, GCHandleType.Pinned);

I get the error "Object contains non-primitive or non-blittable data". Now I understand that the bool field is a non-blittable type. But I was under the impression that by adding the MarshalAs attribute, I could tell the marshaller how to convert the type. (I also tried UnmanagedType.Bool)

This structure has to be defined globally, because it is needed throughout my class. The only reason I need the pointer is because I have an unmanaged API that must pass this structure as a pointer. Then I have to get that structure in a callback and read/update members.

So this is the basic scenario.

  1. Structure is created globally in a managed class
  2. Pointer to structure is obtained
  3. Pointer to the structure is passed into the API
  4. The API calls a static method callback where I then need to get my structure and read/update members.

I tried to use Marshal.StructureToPtr but this only creates a copy, so if in my managed class I update the member, when the callback is raised, the updated value is not there.

Does anyone know how I can get a pinned pointer to my structure so I can read/modify the public members and have them available in the callback?

Thanks


Solution

  • You've got more than one problem here. Using a struct is highly inadvisable. It will get boxed before the GCHandle.Alloc() call and that boxed object gets pinned. You cannot see any updates to it through your mystruct variable. Use a class instead.

    And avoid bool, it is a non-blittable type due to its highly variable implementation. It is 4 bytes in C, 1 byte in C++, 2 bytes in COM. Just make it a byte instead. You can write a property to get it back to a bool.

    So:

    [StructLayout(LayoutKind.Sequential)]
    public class MyStruct
    {
        private byte _test;
        public bool Test {
           get { return _test != 0; }
           set { _test = value ? 1 : 0; }
        }
    }