Marshal结构包含在DLL中起作用的数组 [英] Marshal Structure containing arrays to function in DLL

查看:67
本文介绍了Marshal结构包含在DLL中起作用的数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我正在将一个VB6应用程序升级到VB.net,而我在调用一个

函数时出现问题DLL。


该函数获取一个结构的地址,它将用

值填充。


我收到一个错误:

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

发生类型''System.NullReferenceException''的未处理异常在

Project1.exe


附加信息:对象引用未设置为对象的实例。

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


基于UPGRADE_TODO等升级向导放入我的代码,我

怀疑问题是将VB结构封送到非托管的

函数。


下面是一个(相当长的)描述VB在VB6中的样子,是什么

向导把它变成了,函数的C签名,以及我试过的一些

的东西。


我会非常的有关如何正确传递结构

或在何处找到规则的良好解释的建议。谢谢!
VB6中的
,就像这样

''函数作为参数的结构

公共类型NifInterfaceInfo''接口Infor结构

interfaceName(NIF_NAME_LEN)As Byte

DeviceID(DEV_ID_SIZE)As Byte

结束类型


''采用NifInterfaceInfo结构的函数的声明

声明函数nifGetInterfaceList Lib" nifbstd" (ByVal session As Long,

ByRef numIntf As Integer,ByRef info As NifInterfaceInfo)As long


''调用函数的子程序示例

Private Sub OpenSess()

''打开现场总线会话

Ret(nifOpenSession(vbNull,SessionDesc))

>
''将最大接口号传递给函数'nifGetInterfaceList"

NoOfInterfaces = MAX_INTERFACES


''获取所有接口信息

Ret(nifGetInterfaceList(SessionDesc,NoOfInterfaces,InterfaceInfo(0)))

For i = 0 To NoOfInterfaces - 1

'' interfaceName和DeviceID是byte的数组,需要由函数getString转换为


ListInterface.AddItem getString(InterfaceInfo(i).interfaceName)

下一页


结束子


升级向导执行此操作:

公共结构NifInterfaceInfo' 'Int erface Infor结构

< VBFixedArray(NIF_NAME_LEN)> Dim interfaceName()As Byte

< VBFixedArray(DEV_ID_SIZE)> Dim DeviceID()As Byte


''UPGRADE_TODO:" Initialize"必须调用以初始化此结构的实例

。点击查看更多信息:

''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结构


''UPGRADE_WARNING:结构NifInterfaceInfo可能需要编组

属性作为此Declare语句中的参数传递。点击查看

more:''ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword =" vbup1050"''

声明函数nifGetInterfaceList Lib" nifbstd" (ByVal session As

整数,ByRef numIntf As Short,ByRef info as NifInterfaceInfo)As Integer

Private Sub OpenSess()

''打开现场总线会话

Ret((nifOpenSession(VariantType.Null,SessionDesc)))


''将最大接口号传递给函数nifGetInterfaceList

NoOfInterfaces = MAX_INTERFACES


''获取所有界面信息

Ret((nifGetInterfaceList(SessionDesc,NoOfInterfaces,

InterfaceInfo(0))))

对于i = 0到NoOfInterfaces - 1

''interfaceName和DeviceID是byte的数组,需要通过函数getString转换


ListInterface.Items.Add(getString(InterfaceInfo(i).interfaceName))

下一页


结束子


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


底层的C声明是这样的:

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);

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


根据UPGRADE_TODO vbup1026的建议,我添加了一个对Initialize()

的调用,如下所示:

Private Sub OpenSess()

''打开现场总线会话

Ret((nifOpenSession(VariantType.Null,SessionDesc)))


''将最大接口号传递给功能' ; nifGetInterfaceList"

NoOfInterfaces = MAX_INTERFACES


InterfaceInfo.Initialize()

For i = InterfaceInfo.GetLowerBound(0)To

InterfaceInfo.GetUpperBound(0)

InterfaceInfo(i).Initialize()

下一页


''获取所有界面信息

Ret((nifGetInterfaceList(SessionDesc,NoOfInter)面孔,

InterfaceInfo(0))))

对于i = 0到NoOfInterfaces - 1

''interfaceName和DeviceID是字节数组,需要通过函数getString转换为


ListInterface.Items.Add(getString(InterfaceInfo(i).interfaceName))

下一页


结束子


我在调试器中检查了InterfaceInfo,它看起来没问题

调用Initialize()。


调用nifGetInterfaceList失败并显示错误

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

类型''System.NullReferenceException''未处理的异常发生在

Project1.exe


附加信息:对象引用未设置为对象的实例。

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


如果我将这个

< StructLayout(LayoutKind.Sequential,CharSet:= CharSet.ANSI)>


添加到结构声明中,我得到同样的错误。

如果我添加MarshalAs属性(由UPGRADE_WARNING建议:结构

NifInterfaceInfo可能需要在此Declare语句中将编组属性作为

参数传递。点击查看更多信息:

''ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword =" vbup1050"):


< StructLayout(LayoutKind.Sequential,CharSet:= CharSet.Ansi)> _

公共结构NifInterfaceInfo''接口Infor结构

< 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"必须调用以初始化此结构的实例

。点击查看更多信息:

''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结构


然后我收到错误:

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



Project1.exe中出现了未处理的'System.TypeLoadException'类型的异常。
附加信息:无法封送类型为

NifInterfaceInfo:此类型无法作为结构字段封送

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



任何人都有建议将正确的方法将包含

定长数组的VB.net结构传递给DLL吗?

解决方案



David - 使用api调用升级向导并不是那么好......那个
说,你会想看看System.Runtime .Interop命名空间

进一步信息化编组到非托管代码......无论如何,这里是

a一些建议。


David Fort(donotspam)写道:


我正在将VB6应用程序升级到VB.net,我遇到了调用
DLL中提供的函数。

该函数获取一个结构的地址,它将用
值填充。

我收到错误:
----------------
在Project1.exe中发生了'System.NullReferenceException'类型的未处理异常

>附加信息:对象引用未设置为对象的实例。
----------------

基于UPGRADE_TODO等,升级向导放入我的代码中,我怀疑问题是将VB结构编组到非托管的
函数。

下面是一个(相当长的)VB的描述看看VB6,
向导是什么把它改成了,函数的C签名,以及我尝试过的一些东西。

我非常感谢有关如何通过结构的建议正确或在哪里找到规则的良好解释。谢谢!
VB6中的
,就像这样
''函数作为参数的结构
公共类型NifInterfaceInfo''接口Infor结构
interfaceName( NIF_NAME_LEN)As Byte
DeviceID(DEV_ID_SIZE)As Byte
结束类型

''采用NifInterfaceInfo结构的函数的声明
声明函数nifGetInterfaceList Lib" nifbstd" (ByVal session As Long,
ByRef numIntf As Integer,ByRef info as NifInterfaceInfo)As

''调用函数的子例程示例
Private Sub OpenSess()
''打开现场总线会话
Ret(nifOpenSession(vbNull,SessionDesc))
'''将最大接口号传递给函数'nifGetInterfaceList"
NoOfInterfaces = MAX_INTERFACES

''获取所有界面信息
Ret(nifGetInterfaceList(SessionDesc,NoOfInterfaces,InterfaceInfo(0)))

对于i = 0到NoOfInterfaces - 1
''interfaceName和DeviceID是byte的数组,需要按函数getString进行转换。
ListInterface.AddItem getString(InterfaceInfo(i).interfaceName)
下一步

End Sub

升级向导执行了此操作:
公共结构NifInterfaceInfo''接口Infor结构
&l吨; VBFixedArray(NIF_NAME_LEN)GT; Dim interfaceName()As Byte
< VBFixedArray(DEV_ID_SIZE)> Dim DeviceID()As Byte

''UPGRADE_TODO:" Initialize"必须调用以初始化此结构的实例。点击查看更多信息:
''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
结束结构


我对它的第一次尝试看起来像是:


公共结构NifInterfaceInfo

< MarshalAs(UnmanagedType.ByValArray,SizeConst = NIF_NAME_LEN)> _

Public interfaceName As Byte()

< MarshalAs(UnmanagedType.ByValArray,SizeConst = DEV_ID_SIZE)> _

Public DeviceID As Byte()

结束结构

从声明的loogs,这应该对你有用:)



-

Tom Shelton [MVP]





Tom Shelton写道:


大卫 - 升级向导对于api调用并不是那么好......那说
说,你会想看看系统.Runtime.Interop命名空间,以便进一步了解编组到非托管代码...无论如何,这里有一点建议。

David Fort(donotspam)写道:< blockquote class =post_quotes>
我正在将VB6应用程序升级到VB.net,我在调用DLL中提供的
函数时出现问题。 />
该函数获取一个结构的地址,它将用
值填充。

我收到一个错误:
------- ---------
Project1.exe中出现未处理的System.NullReferenceException类型异常

附加信息:对象引用未设置为一个对象的实例。
----------------
我第一次尝试它看起来像:

公共结构NifInterfaceInfo
< MarshalAs(Un managedType.ByValArray,SizeConst = NIF_NAME_LEN)> _
Public interfaceName As Byte()
< MarshalAs(UnmanagedType.ByValArray,SizeConst = DEV_ID_SIZE)> _
公共DeviceID As Byte()
结束结构

从声明的loogs,这应该适合你:)



- 汤姆谢尔顿[MVP]




谢谢Tom,

我''我正在努力解决System.Runtime.Interop命名空间中的信息帮助

文档,但我正在尝试。为了揭示我的VB和.Net

无知,我想问一些有希望的简单问题:

1.由System.Runtime.Interop命名空间,你的意思是

System.Runtime.InteropServices?

2.在你的第一次刺中你提出的建议:< MarshalAs(UnmanagedType.ByValArray,SizeConst = NIF_NAME_LEN)> _
Public interfaceName As Byte()
那个没有为我编译 - 它说Name'SizeConst''没有声明。

我改变了它来自SizeConst:= NIF_NAME_LEN,并且编译,但我不知道
理解:=和=之间的区别(我看到帮助中的地方

use =就像你做的那样)

3.我应该保留(并使用)升级向导添加的Initialize Sub吗?


假设我做出了正确的选择关于以上(是的,= =,是),然后

我收到错误

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

类型''System.ExecutionEngineException''未处理的异常发生在

Project1.exe

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


所以,我一直在努力挖掘Interop上的帮助信息,并意识到我

甚至没有词汇来理解它。


是一个结构数组,包含2个固定长度的数组,一个blittable typ e?


VB会使用与非托管内容相匹配的内存布局吗,还是有一个

魔法图层将从VB结构的VB数组转换VB数组

到C风格的C风格数组结构的C风格数组?


如果有这样一层,那就是marshaller?


由于非托管函数的参数是包含固定lenth数组的

结构数组的地址,编组人员如何知道如何很多

结构都在阵列中?例如,在VB中,数组

数组结构的实例是这样的:

Public InterfaceInfo(MAX_INTERFACES)As NifInterfaceInfo


NifInterfaceInfo是一个包含数组的结构,如下所示: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()
结束结构




,调用如下:

nifGetInterfaceList( SessionDesc,NoOfInterfaces,InterfaceInfo(0))

NoOfInterfaces告诉函数数组InterfaceInfo有多大,但我不知道
怎么看marshaller可以搞清楚。 (但这在VB6中有效,所以

必须有办法)。

在VB中,数组的结构数组是否会像它一样布局会在

C,还是会增加一些东西,比如上限和下限?


Interop帮助有很多关于tlbimp的东西和midl以及像

这样的东西,但我在VB6项目中看不到任何idl文件,当我对.dll或.lib运行tlbimp

时提供的文件,它说它们不是有效的类型

库。在.DLL上使用Dependency Walker,我可以找到函数和

看到它的序数和类似的东西,但VB如何知道签名

是什么?这完全取决于我放入VB代码的Declare吗?或者是否有一些

检查DLL?


我应该专注于

成员的Marshaling属性结构声明,或者我可能还需要在参数上添加一些内容

声明函数声明的列表? (或者甚至是

阵列结构的实例)


我真的不明白MarshalAs和
$ b之间的区别$ b MarshalAsAttribute。在Interop Namespace文档中,它们引用了

System.Runtime.InteropServices.MarshalAsAttribute,但这些示例只是说

MarshalAs。属性是否意味着它在<>中而不是

名称的真正部分? (我意识到这个问题应该清楚地将我识别为一个VB新手)。

感谢您的帮助,我为因互操作帮助而感到惋惜而感到抱歉

info。我非常感谢您对我的额外

问题的任何建议/意见,以帮助我完成迷宫。


祝你好运,

David




David Fort(donotspam)写道:

" Tom Shelton"写道:


大卫 - 升级向导对于api调用并不是那么好......那说
说,你会想看看系统.Runtime.Interop命名空间,以便进一步了解编组到非托管代码...无论如何,这里有一点建议。

David Fort(donotspam)写道:< blockquote class =post_quotes>
我正在将VB6应用程序升级到VB.net,我在调用DLL中提供的
函数时出现问题。 />
该函数获取一个结构的地址,它将用
值填充。

我收到一个错误:
------- ---------
Project1.exe中出现未处理的System.NullReferenceException类型异常

附加信息:对象引用未设置为一个对象的实例。
----------------
我第一次尝试它看起来像:

公共结构NifInterfaceInfo
< MarshalAs(Unm anagedType.ByValArray,SizeConst = NIF_NAME_LEN)> _
Public interfaceName As Byte()
< MarshalAs(UnmanagedType.ByValArray,SizeConst = DEV_ID_SIZE)> _
公共DeviceID As Byte()
结束结构

从声明的loogs,这应该适合你:)



- 汤姆谢尔顿[MVP]




大卫 - 我没有时间现在就涵盖所有这些问题...

但是,我会尽力给出更完整的答案。但是,我会现在尝试回答其中的一些......

谢谢汤姆,
我正在努力解决System.Runtime中的问题.Interop命名空间帮助
文档,但我正在尝试。为了揭示我的VB和.Net
无知,我想问一些有希望的简单问题:
1.通过System.Runtime.Interop命名空间,你的意思是
System.Runtime.InteropServices?


是的 - 我对InteropServices感到抱歉......很抱歉。

2.在你的第一次刺中你提出的建议:

< MarshalAs(UnmanagedType.ByValArray,SizeConst = NIF_NAME_LEN)> _
Public interfaceName As Byte()我没有为我编译 - 它说Name'SizeConst''未声明。
我将其更改为SizeConst:= NIF_NAME_LEN,并且编译,但我不明白:=和=之间的区别(我在帮助中看到的地方使用了=就像你做的那样)




它应该是:=在VB.NET中。我主要在C#工作,所以有时候我会用b语法来修改VB语法...再次抱歉。

3.我应该保留(并使用)初始化Sub,即添加了升级向导?


如果在互操作场景中使用该结构,则不需要它。

假设我对上述内容做出了正确的选择(是的,:=,是),然后
我收到错误
-------------------
未处理的类型异常在Project1.exe中出现''System.ExecutionEngineException'
-------------------

所以,我我一直试图深入了解Interop的帮助信息,并意识到我甚至没有词汇来理解它。

是一个包含2个固定长度的结构数组数组是一个blittable类型?


Blittable类型是不需要由

封送者进行特殊处理的类型。一个值类型的一维数组通常是一个

blittable类型 - 但是,我不确定在结构中使用时是否适用,因为编组器当它传递给函数时,必须分配一个数组(基于

的SizeConst参数)。

VB是否会使用与非托管内容匹配的内存布局,或者是有一个
魔术层,它将VB VB数组的VB结构转换为C风格的C风格阵列C风格阵列?

如果有这样一层,那就是marshaller?

默认的内存布局应该在大多数时候都很好。但是,如果通过对结构和

字段应用各种属性,你可以改变
。例如,通过使用带有

LayoutKind.Explicit和FieldOffsetAttribute的StructLayoutAttribute,您基本上可以模仿将Unions传递给非托管代码。

由于非托管函数的参数是包含固定lenth数组的
结构数组的地址,编组器如何知道数组中有多少个结构?例如,在VB中,数组结构数组的实例是这样的:
Public InterfaceInfo(MAX_INTERFACES)As NifInterfaceInfo


那将是在VB.NET中也一样。

NifInterfaceInfo是一个包含数组的结构,如下所示:

公共结构NifInterfaceInfo
< MarshalAs(UnmanagedType.ByValArray,SizeConst = NIF_NAME_LEN )> _
Public interfaceName As Byte()
< MarshalAs(UnmanagedType.ByValArray,SizeConst = DEV_ID_SIZE)> _
公共DeviceID As Byte()
结束结构
并且调用如下:
nifGetInterfaceList(SessionDesc,NoOfInterfaces,InterfaceInfo(0))

> NoOfInterfaces告诉函数InterfaceInfo数组有多大,但是我没有看到编组人员如何解决这个问题。 (但这可以在VB6中运行,所以
必须有一种方法)。




编组可以判断,因为你的数组是
的一个实例
System.Array和那里将有一个长度参数来告诉它包含多少

元素。

在VB中,数组的结构数组是像在C中那样布局,或者它会增加一些东西,比如上限和下限?


它将在C中布局。

Interop帮助有很多关于tlbimp和midl的东西和
之类的东西,但我在VB6项目中看不到任何idl文件,当我运行tlbimp <对于提供的.dll文件或.lib文件,它说它们不是有效的类型库。在.DLL上使用Dependency Walker,我可以找到函数并看到它的序数和类似的东西,但VB如何知道签名是什么?这完全取决于我放入VB代码的Declare吗?或者是否有一些针对DLL的检查?


这取决于声明。你只需要使用tlbimp和midl的东西,如果你使用COM对象库就可以获得


我是否应该专注于
成员的Marshaling属性结构声明,或者我是否还需要在Declare Function语句的参数列表中添加一些内容? (或者甚至是结构阵列的实例)


有时:)这取决于。我没有密切关注宣言。我可能应该有
。我会尝试回去再仔细看看

后者(如果有人不打我的话)。

我真的不明白MarshalAs和
MarshalAsAttribute之间的区别。在Interop Namespace文档中,它们引用了System.Runtime.InteropServices.MarshalAsAttribute,但这些例子只是说MarshalAs。属性是否意味着它在<>中而不是真正的名称的一部分? (我意识到这个问题应该清楚地将我识别为VB新手)。


MarshalAs和MarshalAsAttribute是一回事。编译器将自动将MarshalAs更改为MarshalAsAttribute。它只是

的快捷方式。

感谢您的帮助,我为互操作帮助
信息而感到抱歉。我非常感谢您对我的其他问题的任何建议/意见,以帮助我完成迷宫。

致以最诚挚的问候,
David




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?

解决方案


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.

David Fort (donotspam) wrote:

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
From the loogs of the declare, this should work for you :)



--
Tom Shelton [MVP]




"Tom Shelton" wrote:


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.

David Fort (donotspam) wrote:

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.
----------------
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

From the loogs of the declare, this should work for you :)



--
Tom Shelton [MVP]



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



David Fort (donotspam) wrote:

"Tom Shelton" wrote:


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.

David Fort (donotspam) wrote:

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.
----------------
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

From the loogs of the declare, this should work for you :)



--
Tom Shelton [MVP]



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:

<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)



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:

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).



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.

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




这篇关于Marshal结构包含在DLL中起作用的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆