Search code examples
vb.netdllstructunmanagedtyped-arrays

How to create a VB.NET unmanaged structure which contains an array of structures?


I'm trying to call a DLL function (written in C) that expects a pointer to an Outer structure, which in turn contains an array of Inner structures. The C structures look like this:

typedef struct Inner {
    int x;
    int y;
} Inner;

typedef struct Outer {
    Inner ArrayOfInners[20];
    unsigned char b;
} Outer;

I defined the VB.NET structures as follows:

<StructLayout(LayoutKind.Sequential)> _
Public Structure Inner
    Public x As Integer
    Public y As Integer
End Structure

<StructLayout(LayoutKind.Sequential)> _
Public Structure Outer
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=20)> Public ArrayOfInners() As Inner
    Public b As Byte
End Structure

However, when I instantiate an Outer and try to access the nested Inner array like this ...

Dim s As Outer
s.ArrayOfInners(2).x = 5

... the VS editor complains Variable 'ArrayOfInners' is used before it has been assigned a value. A null reference exception could result at runtime. And indeed, at runtime I see that the value of s.ArrayOfInners is Nothing -- vs. what I expected: a nested array of Inner structures.

What's the problem here, and how can I instantiate an Outer structure that's compatible with the DLL?


Solution

  • In case someone else runs into this issue, here's the solution (thanks to user Ry):

    Dim s As Outer
    s.ArrayOfInners = New Inner(19) {}  ' Must manually create every array in structure!
    s.ArrayOfInners(2).x = 5
    

    Apparently it's necessary to explicitly create every array within s because the .NET structure s is not what is passed to the DLL function. Instead, when the DLL function is called, VB will automatically allocate memory for a DLL-compatible struct and then copy all of s's members (including its referenced, non-contiguous ArrayOfInners) to the struct. The struct is passed to the DLL function and, when the function returns, VB will copy all of the struct's members back to s.

    VB.NET cannot do these scatter/gather operations unless ArrayOfInners has first been explicitly created, because Dim s As Outer only initializes ArrayOfInners to Nothing (effectively a NULL pointer), and it's not possible to copy Nothing to/from a DLL-compatible struct.