Structure with array unblittable? (C++ > VB.NET porting)

J

Jonathan Amend

I'm trying to port some C++ code to VB.NET but I have hit a snag. I need to
add the pointer of a structure which includes an array to an array and then
pass it to an API. Here are the two code snippets:

long inbuf[2]; // Array that gets passed to the API
struct cmbuf {
short cmds[2];
long cm2;
} cbuf; // Structure whos pointer must be added to inbuf
cbuf.cmds[0] = 0;
cbuf.cmds[1] = 4;
cbuf.cm2 = 2;
inbuf[0] = 2;
inbuf[1] = (long)&cbuf; // Add the pointer of cbuf to inbuf

--------

' The equivalent of cbuf
Private Structure Command
Dim Commands() As Short
Dim Command2 As Long
End Structure

' Function to get pointer of blittable object
Public Function VarPtr(ByVal o As Object) As Long
Dim GC As System.Runtime.InteropServices.GCHandle =
System.Runtime.InteropServices.GCHandle.Alloc(o,
System.Runtime.InteropServices.GCHandleType.Pinned)
Dim ret As Integer = GC.AddrOfPinnedObject.ToInt64
GC.Free()
Return ret
End Function

...
Dim InputBuffer(2) As Long ' inbuf
Dim CommandBuffer As Command ' cbuf
ReDim CommandBuffer.Commands(2)
CommandBuffer.Commands(0) = 4
CommandBuffer.Commands(1) = 4
CommandBuffer.Command2 = 0
InputBuffer(0) = 2
InputBuffer(1) = VarPtr(CommandBuffer) ' inbuf[1] = (long)&cbuf;

When I run it, I get the following error:

An unhandled exception of type 'System.ArgumentException' occurred in
mscorlib.dll

Additional information: Object contains non-primitive or non-blittable data.

I did some googling and found out that both structures and arrays are
blittable if they contain only primitives. Shouldn't my structure still be
blittable if it contains a blittable array? I tried making the structure
without the array and I didn't get the error but of course now the data no
longer fits the API.
 
D

David Browne

Jonathan Amend said:
I'm trying to port some C++ code to VB.NET but I have hit a snag. I need
to add the pointer of a structure which includes an array to an array and
then pass it to an API. Here are the two code snippets:

long inbuf[2]; // Array that gets passed to the API
struct cmbuf {
short cmds[2];
long cm2;
} cbuf; // Structure whos pointer must be added to inbuf
cbuf.cmds[0] = 0;
cbuf.cmds[1] = 4;
cbuf.cm2 = 2;
inbuf[0] = 2;
inbuf[1] = (long)&cbuf; // Add the pointer of cbuf to inbuf

--------

' The equivalent of cbuf
Private Structure Command
Dim Commands() As Short
Dim Command2 As Long
End Structure

By default array are marshaled using pointers.

You need to tell the framework how to marshal the structure using
attributes.


<StructLayout(LayoutKind.Sequential)> _
Public Structure Command
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _
Dim Commands() As Short
Dim Command2 As Long
End Structure

But this doesn't make the structure blittable to the unmanaged type. It
just tells the framework how to copy the managed type to unmanaged memory or
back.

Or, an easy workaround would be to define your struct as:

<StructLayout(LayoutKind.Sequential)> _
Private Structure Command
Dim Commands_0 As Short
Dim Commands_1 As Short
Dim Command2 As Long
End Structure


The real problem with the using an array as a struct member is that the
managed type is no longer 100% bitwise compatable with the unmanaged type.
Arrays are reference types and are always stored on the managed heap. An so
structure with an array member is only partially stored on stack. So every
time the type is marshalled it requires additional copying.

David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top