Marshal Structure containing arrays to function in DLL

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



Hi,
I'm upgrading a VB6 app to VB.net and I'm having a problem with a call to a
function provided in a DLL.

The function takes the address of a structure which it will fill in with
values.

I get an error:
----------------
An unhandled exception of type 'System.NullReferenceException' occured in
Project1.exe

Additional Information: Object reference not set to an instance of an object.
----------------

Based on the UPGRADE_TODOs etc that the upgrade wizard put in my code, I
suspect the problem is with marshaling the VB struct to the unmanaged
function.

Below is a (rather long) description of how the VB looked in VB6, what the
wizard turned it into, the C signature of the function, and some of the
things I've tried.

I would be very grateful for suggestions on how to pass the structure
correctly or where to find a good explanation of the rules. Thanks!


in VB6, it was like this
' the structure that the function takes as a parameter
Public Type NifInterfaceInfo ' Interface Infor structure
interfaceName(NIF_NAME_LEN) As Byte
DeviceID(DEV_ID_SIZE) As Byte
End Type

' the declaration of the function that takes the NifInterfaceInfo struct
Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As Long,
ByRef numIntf As Integer, ByRef info As NifInterfaceInfo) As Long

' an example of a subroutine which invokes the function
Private Sub OpenSess()
' Open a fieldbus Session
Ret (nifOpenSession(vbNull, SessionDesc))

' Pass the max interface number to function "nifGetInterfaceList"
NoOfInterfaces = MAX_INTERFACES

' Get all the interface information
Ret (nifGetInterfaceList(SessionDesc, NoOfInterfaces, InterfaceInfo(0)))

For i = 0 To NoOfInterfaces - 1
' The interfaceName and DeviceID are array of byte, need to be
converted by function "getString"
ListInterface.AddItem getString(InterfaceInfo(i).interfaceName)
Next

End Sub

The upgrade wizard did this:
Public Structure NifInterfaceInfo ' Interface Infor structure
<VBFixedArray(NIF_NAME_LEN)> Dim interfaceName() As Byte
<VBFixedArray(DEV_ID_SIZE)> Dim DeviceID() As Byte

'UPGRADE_TODO: "Initialize" must be called to initialize instances
of this structure. Click for more:
'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="vbup1026"'
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure

'UPGRADE_WARNING: Structure NifInterfaceInfo may require marshalling
attributes to be passed as an argument in this Declare statement. Click for
more: 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="vbup1050"'
Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByRef info As NifInterfaceInfo) As Integer


Private Sub OpenSess()
' Open a fieldbus Session
Ret((nifOpenSession(VariantType.Null, SessionDesc)))

' Pass the max interface number to function "nifGetInterfaceList"
NoOfInterfaces = MAX_INTERFACES

' Get all the interface information
Ret((nifGetInterfaceList(SessionDesc, NoOfInterfaces,
InterfaceInfo(0))))

For i = 0 To NoOfInterfaces - 1
' The interfaceName and DeviceID are array of byte, need to be
converted by function "getString"
ListInterface.Items.Add(getString(InterfaceInfo(i).interfaceName))
Next

End Sub

--------------

The underlying C declarations are like this:
typedef struct nifInterfaceInfo_t {
char interfaceName[NIF_NAME_LEN];
char deviceID[DEV_ID_SIZE + 1];
} nifInterfaceInfo_t;

extern nifError_t nifGetInterfaceList(nifDesc_t ud, int16 *numInterfaces,
nifInterfaceInfo_t *info);
--------------

As suggested by the UPGRADE_TODO vbup1026, I added a call to Initialize()
like this:
Private Sub OpenSess()
' Open a fieldbus Session
Ret((nifOpenSession(VariantType.Null, SessionDesc)))

' Pass the max interface number to function "nifGetInterfaceList"
NoOfInterfaces = MAX_INTERFACES

InterfaceInfo.Initialize()
For i = InterfaceInfo.GetLowerBound(0) To
InterfaceInfo.GetUpperBound(0)
InterfaceInfo(i).Initialize()
Next

' Get all the interface information
Ret((nifGetInterfaceList(SessionDesc, NoOfInterfaces,
InterfaceInfo(0))))

For i = 0 To NoOfInterfaces - 1
' The interfaceName and DeviceID are array of byte, need to be
converted by function "getString"
ListInterface.Items.Add(getString(InterfaceInfo(i).interfaceName))
Next

End Sub

I examined InterfaceInfo in the debugger, and it looks ok following the
call(s) to Initialize().

The call to nifGetInterfaceList fails with an error
----------------
An unhandled exception of type 'System.NullReferenceException' occured in
Project1.exe

Additional Information: Object reference not set to an instance of an object.
----------------



if I add this
<StructLayout( LayoutKind.Sequential, CharSet:=CharSet.ANSI)>

to the structure declaration, I get the same error.


If I add MarshalAs attributes (as suggested by UPGRADE_WARNING: Structure
NifInterfaceInfo may require marshalling attributes to be passed as an
argument in this Declare statement. Click for more:
'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="vbup1050"):

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure NifInterfaceInfo ' Interface Infor structure
<VBFixedArray(NIF_NAME_LEN), MarshalAs(UnmanagedType.ByValTStr,
SizeConst:=NIF_NAME_LEN)> Dim interfaceName() As Byte
<VBFixedArray(DEV_ID_SIZE), MarshalAs(UnmanagedType.ByValTStr,
SizeConst:=DEV_ID_SIZE)> Dim DeviceID() As Byte

'UPGRADE_TODO: "Initialize" must be called to initialize instances
of this structure. Click for more:
'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="vbup1026"'
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure

then I get error:
----------------
An unhandled exception of type 'System.TypeLoadException' occured in
Project1.exe

Additional Information: Can not marshal field interfaceName of type
NifInterfaceInfo: This type can not be marshaled as a structure field
----------------




Anyone have suggestions for the right way to pass a VB.net struct containing
fixed length arrays to a DLL?


.



Relevant Pages