Marshal Structure containing arrays to function in DLL

G

Guest

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?
 
T

Tom Shelton

David - upgrade wizard is not all that great with api calls... That
said, you'll want to look at the System.Runtime.Interop namespace for
further infromation on marshaling to unmanaged code... Anyway, here is
a short bit of advice.
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

My first stab at it would look like:

Public Structure NifInterfaceInfo
<MarshalAs (UnmanagedType.ByValArray, SizeConst=NIF_NAME_LEN)> _
Public interfaceName As Byte ()
<MarshalAs (UnmanagedType.ByValArray, SizeConst=DEV_ID_SIZE)> _
Public DeviceID As Byte ()
End Structure
 
G

Guest

Tom Shelton said:
David - upgrade wizard is not all that great with api calls... That
said, you'll want to look at the System.Runtime.Interop namespace for
further infromation on marshaling to unmanaged code... Anyway, here is
a short bit of advice.


My first stab at it would look like:

Public Structure NifInterfaceInfo
<MarshalAs (UnmanagedType.ByValArray, SizeConst=NIF_NAME_LEN)> _
Public interfaceName As Byte ()
<MarshalAs (UnmanagedType.ByValArray, SizeConst=DEV_ID_SIZE)> _
Public DeviceID As Byte ()
End Structure

Thanks Tom,
I'm struggling with the info in the System.Runtime.Interop namespace help
documentation, but I'm trying. In an effort to reveal my VB and .Net
ignorance, I'd like to ask a few hopefully easy qustions:
1. by System.Runtime.Interop namespace, do you mean
System.Runtime.InteropServices?
2. in your "first stab" suggestion you proposed:
<MarshalAs (UnmanagedType.ByValArray, SizeConst=NIF_NAME_LEN)> _
Public interfaceName As Byte ()
That didn't compile for me - it says "Name 'SizeConst' is not declared."
I changed it to SizeConst:=NIF_NAME_LEN, and that compiles, but I don't
understand the difference between := and = (and I see places in the help that
use = as you did)
3. Should i keep (and use) the Initialize Sub that the Upgrade wizard added?

Assuming I made the correct choices about the above (yes, :=, and yes), then
I get the error
-------------------
An unhandled exception of type 'System.ExecutionEngineException' occured in
Project1.exe
-------------------

So, I've been trying to dig through the help info on Interop, and realize I
don't even have the vocabulary to understand it.

Is an Array of Structures containing 2 fixed length Arrays a blittable type?

Will VB use a memory layout that matches the unmanaged stuff, or is there a
magic layer that will convert from a VB Array of VB Structures of VB arrays
to the C-style array of C-style Struct of C-style arrays?

If there is such a layer, is that the "marshaller"?

Since the argument to the unmanaged function is the address of array of
structs containing fixed lenth arrays, how does the marshaller know how many
structs are in the array? for example, in VB, the instance of the array of
structures of arrays is like this:
Public InterfaceInfo(MAX_INTERFACES) As NifInterfaceInfo

NifInterfaceInfo is a Structure containing arrays, like this:
Public Structure NifInterfaceInfo
<MarshalAs (UnmanagedType.ByValArray, SizeConst=NIF_NAME_LEN)> _
Public interfaceName As Byte ()
<MarshalAs (UnmanagedType.ByValArray, SizeConst=DEV_ID_SIZE)> _
Public DeviceID As Byte ()
End Structure

and the call is like this:
nifGetInterfaceList(SessionDesc, NoOfInterfaces, InterfaceInfo(0))

NoOfInterfaces tells the function how big the array InterfaceInfo is, but I
don't see how a marshaller could figure that out. (but this works in VB6, so
there must be a way).

In VB, will the array of structures of arrays be layed out like it would in
C, or will it get a bunch of stuff added, like upper and lower bounds?

The Interop help has a lot of stuff about tlbimp and midl and stuff like
that, but I don't see any idl files in my VB6 project and when i run tlbimp
against the .dll or the .lib files provided, it says they aren't valid type
libraries. Using Dependency Walker on the .DLL, i can find the function and
see its ordinal and stuff like that, but how does VB know what the signature
is? Is it all up to the Declare that I put in my VB code? Or is there some
check against the DLL?

Should I be concentrating on the Marshaling attributes on the members of the
Structure declaration, or might I also need to put something on the argument
list of the Declare Function statement? (or even on the instance of the
array of structs)

I don't really understand the difference between MarshalAs and
MarshalAsAttribute. In the Interop Namespace documentation, they refer to
System.Runtime.InteropServices.MarshalAsAttribute, but the examples just say
MarshalAs. Does Attribute just mean it is in <> and not really part of the
name? (i realize this question should clearly identify me as a VB newbie).


Thanks for your help, I apologize for being overwhelmed by the interop help
info. I'd really appreciate any suggestions/comments on my additional
questions to help guide me through the maze.

Best regards,
David
 
T

Tom Shelton

David said:
Tom Shelton said:
David - upgrade wizard is not all that great with api calls... That
said, you'll want to look at the System.Runtime.Interop namespace for
further infromation on marshaling to unmanaged code... Anyway, here is
a short bit of advice.


My first stab at it would look like:

Public Structure NifInterfaceInfo
<MarshalAs (UnmanagedType.ByValArray, SizeConst=NIF_NAME_LEN)> _
Public interfaceName As Byte ()
<MarshalAs (UnmanagedType.ByValArray, SizeConst=DEV_ID_SIZE)> _
Public DeviceID As Byte ()
End Structure

David - I don't have time to cover all of these questions right now...
But, I will try and give a more complete answer latter. But, I will
try to answer some of them now...
Thanks Tom,
I'm struggling with the info in the System.Runtime.Interop namespace help
documentation, but I'm trying. In an effort to reveal my VB and .Net
ignorance, I'd like to ask a few hopefully easy qustions:
1. by System.Runtime.Interop namespace, do you mean
System.Runtime.InteropServices?

Yes - I ment InteropServices... Sorry about that.
2. in your "first stab" suggestion you proposed:
That didn't compile for me - it says "Name 'SizeConst' is not declared."
I changed it to SizeConst:=NIF_NAME_LEN, and that compiles, but I don't
understand the difference between := and = (and I see places in the help that
use = as you did)

It should be := in VB.NET. I mostly work in C#, and so sometimes I
mangle the VB syntax... Again Sorry.
3. Should i keep (and use) the Initialize Sub that the Upgrade wizard added?

You don't need it if you using the structure in interop scenarios.
Assuming I made the correct choices about the above (yes, :=, and yes), then
I get the error
-------------------
An unhandled exception of type 'System.ExecutionEngineException' occured in
Project1.exe
-------------------

So, I've been trying to dig through the help info on Interop, and realize I
don't even have the vocabulary to understand it.

Is an Array of Structures containing 2 fixed length Arrays a blittable type?

Blittable types are types that don't need special handling by the
marshaler. A one dimensional array of value types is normally a
blittable type - but, I'm not sure that would apply when used in side
of a structure because the marshaler has to allocate an array (base on
the SizeConst argument) when it is passed to a function.
Will VB use a memory layout that matches the unmanaged stuff, or is there a
magic layer that will convert from a VB Array of VB Structures of VB arrays
to the C-style array of C-style Struct of C-style arrays?

If there is such a layer, is that the "marshaller"?

The default memory layout should be fine most of the time. But, you
can change if by applying various attributes to the structure and the
fields. For example, by using the StructLayoutAttribute with the
LayoutKind.Explicit and the FieldOffsetAttribute you can essentially
imitate the passing of Unions to unmanaged code.
Since the argument to the unmanaged function is the address of array of
structs containing fixed lenth arrays, how does the marshaller know how many
structs are in the array? for example, in VB, the instance of the array of
structures of arrays is like this:
Public InterfaceInfo(MAX_INTERFACES) As NifInterfaceInfo

That would be the same in VB.NET.
NifInterfaceInfo is a Structure containing arrays, like this:

and the call is like this:
nifGetInterfaceList(SessionDesc, NoOfInterfaces, InterfaceInfo(0))

NoOfInterfaces tells the function how big the array InterfaceInfo is, but I
don't see how a marshaller could figure that out. (but this works in VB6, so
there must be a way).

The marshaller can tell because your array is an instance of
System.Array and there for will have a length argument to tell how many
elements it contains.
In VB, will the array of structures of arrays be layed out like it would in
C, or will it get a bunch of stuff added, like upper and lower bounds?

It will be laid out as it is in C.
The Interop help has a lot of stuff about tlbimp and midl and stuff like
that, but I don't see any idl files in my VB6 project and when i run tlbimp
against the .dll or the .lib files provided, it says they aren't valid type
libraries. Using Dependency Walker on the .DLL, i can find the function and
see its ordinal and stuff like that, but how does VB know what the signature
is? Is it all up to the Declare that I put in my VB code? Or is there some
check against the DLL?

It's up to the declare. You only need the tlbimp and midl stuff if
your working with a COM object library.
Should I be concentrating on the Marshaling attributes on the members of the
Structure declaration, or might I also need to put something on the argument
list of the Declare Function statement? (or even on the instance of the
array of structs)

Sometimes :) It depends. I didn't look to closely at the declare. I
probably should have. I will try and go back and take a closer look at
that latter (if someone doesn't beat me to it).
I don't really understand the difference between MarshalAs and
MarshalAsAttribute. In the Interop Namespace documentation, they refer to
System.Runtime.InteropServices.MarshalAsAttribute, but the examples just say
MarshalAs. Does Attribute just mean it is in <> and not really part of the
name? (i realize this question should clearly identify me as a VB newbie).

MarshalAs and MarshalAsAttribute are the same thing. The compiler will
automatically change MarshalAs to MarshalAsAttribute. It's just
shortcut.
 
G

Guest

here are 4 interesting (in a geeklike way) new observations which lead me to
think we are close.

1. The DLL function is actually working a little bit before it throws the
exception.
2. The first byte of the first array in the structure is 0, and it shouldn't
be, but the rest of the bytes look ok
3. At the point of the exception, the Length of the first array contained
within the structure has been changed to 32, but the rest of them still say
33. (same for the other array contained with the struct)
4. Initialize, which simply does a ReDim DeviceID(DEV_ID_SIZE) (where
DEV_ID_SIZE is 32) creates an array of {Length=33}.

The function should have filled in 2 elements of the array, and it only
filled in one before it threw the exception.

A little more detail:
this is the function:
Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByRef info As NifInterfaceInfo) As Integer

this is the type of the argument (as in "First Stab")
Public Structure NifInterfaceInfo
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=NIF_NAME_LEN)> _
Public interfaceName As Byte()
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=DEV_ID_SIZE)> _
Public DeviceID As Byte()
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure

this is the instance:
Public InterfaceInfo(MAX_INTERFACES) As NifInterfaceInfo

and this is the function invocation:
nifGetInterfaceList(SessionDesc, NoOfInterfaces, InterfaceInfo(0))

So InterfaceInfo(0) gets filled in with expected data.
InterfaceInfo(1) is unchanged (and should have changed, because
NoOfInterfaces comes back as a 2)

InterfaceInfo(0).DeviceID is filled in correctly except for the first byte,
which is a 0.
The other values are shifted down. ie. the first byte of the DeviceID is in
InterfaceInfo(0).DeviceID(1).
InterfaceInfo(0).DeviceID {Length=32}
InterfaceInfo(1).DeviceID {Length=33}
 
G

Guest

David Fort said:
2. The first byte of the first array in the structure is 0, and it shouldn't
be, but the rest of the bytes look ok
....
InterfaceInfo(0).DeviceID {Length=32}
InterfaceInfo(1).DeviceID {Length=33}

changing the structure declaration to:
Public Structure NifInterfaceInfo
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=NIF_NAME_LEN + 1)> _
Public interfaceName As Byte()
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=DEV_ID_SIZE + 1)> _
Public DeviceID As Byte()
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure

(note the +1 on the SizeConst)
seems to make the data line up in the right spots in the structure at the
first element of the array.

I'm still not getting anything in the remaining structures in the array, so
it feels like only the first element gets marshalled, then when the DLL
function tries to fill in the second element, it generates an exception
because the second element isn't there.

Does that sound like barking up the right tree? If so, how do you tell the
marshaller how many elements are in the array actually being passed?
 
G

Guest

Don't burn any calories on this question for now.

I just talked with National Instruments, the provider of the DLL, and they
have a .Net version ready for Beta to release in the next few days. I'll see
if that reveals the secret of how to marshal this array of structs.

News as it develops.
david
 
T

Tom Shelton

David said:
changing the structure declaration to:
Public Structure NifInterfaceInfo
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=NIF_NAME_LEN + 1)> _
Public interfaceName As Byte()
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=DEV_ID_SIZE + 1)> _
Public DeviceID As Byte()
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure

(note the +1 on the SizeConst)
seems to make the data line up in the right spots in the structure at the
first element of the array.

I'm still not getting anything in the remaining structures in the array, so
it feels like only the first element gets marshalled, then when the DLL
function tries to fill in the second element, it generates an exception
because the second element isn't there.

Your original declare was:

Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByRef info As NifInterfaceInfo) As
Integer

That should be changed to:

Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByVal info() As NifInterfaceInfo) As
Integer

When you call it, don't pass just the first element like in VB6 - just
pass the array... In other words, don't do:

nifGetInterfaceList (....., info (0))

do:

nifGetInterfaceList (...., info)
 
G

Guest

Tom Shelton said:
Your original declare was:

Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByRef info As NifInterfaceInfo) As
Integer

That should be changed to:

Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByVal info() As NifInterfaceInfo) As
Integer

When you call it, don't pass just the first element like in VB6 - just
pass the array... In other words, don't do:

nifGetInterfaceList (....., info (0))

do:

nifGetInterfaceList (...., info)

I thought so too, and when i tried that, the function completes, but the
array is unchanged.
So I thought maybe this was the ticket: (same idea with 1 dim array, but
ByRef)
Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, ByRef info() As NifInterfaceInfo) As Integer

and that doesn't throw an exception, but the program exits at that point.
It doesn't give much of a hint why:
The program '[3444] Project1.exe' has exited with code 0 (0x0).

I'll see if I can get my hands on the .Net Beta version of this DLL and
hopefully the secrets will be revealed.

Thanks,
David
 
G

Guest

Here is the magic syntax:

<StructLayout(LayoutKind.Sequential)> _
Public Structure NifInterfaceInfo
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=NIF_NAME_LEN + 1)> _
Public interfaceName As Byte()
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=DEV_ID_SIZE + 1)> _
Public DeviceID As Byte()
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure


Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, <Out()> ByVal info As NifInterfaceInfo()) As
Integer

Public InterfaceInfo(MAX_INTERFACES) As NifInterfaceInfo


and call it like this:
nifGetInterfaceList(SessionDesc, NoOfInterfaces, InterfaceInfo)


So, the only change from Tom's last suggestion was the addition of <Out()>
in the function declaration on the third argument.

In summary, changes from what the Upgrade Wizard produced:
Add <StructLayout(LayoutKind.Sequential)> _
to the Structure declaration
Add <MarshalAs(UnmanagedType.ByValArray, SizeConst:=NIF_NAME_LEN +
1)> _
to the fields in the structure that are contained arrays
Add <Out()> to the third argument
change ByRef to ByVal in third argument
Change info to info() in the declaration of the function (or put the () on
the type name: <Out()> info as NifInterfaceInfo())
Change call of function to pass the whole array: fn(arrayname) instead of
passing the first element, as you might in C: fn(arrayname(0))


voila! success.

Thanks for your help, Tom.

Best regards,
David Fort
 
T

Tom Shelton

David said:
Here is the magic syntax:

<StructLayout(LayoutKind.Sequential)> _
Public Structure NifInterfaceInfo
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=NIF_NAME_LEN + 1)> _
Public interfaceName As Byte()
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=DEV_ID_SIZE + 1)> _
Public DeviceID As Byte()
Public Sub Initialize()
ReDim interfaceName(NIF_NAME_LEN)
ReDim DeviceID(DEV_ID_SIZE)
End Sub
End Structure


Declare Function nifGetInterfaceList Lib "nifbstd" (ByVal session As
Integer, ByRef numIntf As Short, <Out()> ByVal info As NifInterfaceInfo()) As
Integer

Public InterfaceInfo(MAX_INTERFACES) As NifInterfaceInfo


and call it like this:
nifGetInterfaceList(SessionDesc, NoOfInterfaces, InterfaceInfo)


So, the only change from Tom's last suggestion was the addition of <Out()>
in the function declaration on the third argument.

David - I'm glad you got this figured out. I've been busy the last
couple of days, and wasn't able to respond.
 

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

Similar Threads

Translating code from VB6 2

Top