2

我必须在 VB.net 中创建一个结构类型的数组。但我在编组此错误时遇到错误。我必须将这个结构类型数组传递给 Dll 函数。

代码:结构声明:

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
    Public Structure dx_entry
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=10)> _
        Public dx As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
        Public type As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1)> _
        Public narray As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1)> _
        Public ctier As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1)> _
        Public poa As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1)> _
        Public poa_rsvd As String
       <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=81)> _
        Public filler As String
    End Structure

初始化和编组代码:

Dim stpDx(2) As dx_entry
stpDx(1).dx = "5939" & Space(6)
        stpDx(1).type = "BK" & Space(1)
        stpDx(1).narray = Space(1)
        stpDx(1).ctier = Space(1)
        stpDx(1).poa = "Y"
        stpDx(1).poa_rsvd = Space(1)
        stpDx(1).filler = Space(81)
        stpDx(2).dx = "1231" & Space(6)
        stpDx(2).type = "BF" & Space(1)
        stpDx(2).narray = Space(1)
        stpDx(2).ctier = Space(1)
        stpDx(2).poa = "Y"
        stpDx(2).poa_rsvd = Space(1)
        stpDx(2).filler = Space(81)
        Dim pDxBuf As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(stpDx))
        Marshal.StructureToPtr(stpDx, pDxBuf, False)
        ezg_Block.pDx = pDxBuf

我收到以下错误:

An unhandled exception of type 'System.ArgumentException' occurred in Audit_Demo_2307.exe

附加信息:类型 dx_entry[] 不能被编组为非托管结构;无法计算出有意义的大小或偏移量。

4

1 回答 1

2

我对此进行了一些研究,第一个问题似乎是将数组传递给Marshal.SizeOf方法(正是这个调用引发了异常)。由于结构的所有成员都具有固定大小,因此您知道该类型的所有实例都将具有相同的大小。因此,您可以改为要求Marshal.SizeOf返回一个此类实例的大小:

Dim structSize As Integer = Marshal.SizeOf(GetType(dx_entry))

接下来是Marshal.StructureToPtr从结构中复制数据,而不是从结构数组中复制数据。所以你需要一些其他的方法来将你的结构数组复制到分配的内存中。没有一种方法可以在一次调用中为您执行此操作,但是您可以做的是将数组中的每个结构实例复制到一个字节数组中(使用该Marshal.Copy方法),然后将该字节数组复制到指向的内存中通过指针。

作为两步火箭,这是最容易做到的。首先,让我们创建一个将结构数组转换为字节数组的方法:

Private Shared Function GetByteArray(ByVal array As dx_entry()) As Byte()
    Dim structSize As Integer = Marshal.SizeOf(GetType(dx_entry))
    Dim size As Integer = structSize * array.Length
    Dim dataBuffer As Byte() = New Byte(size - 1) {}
    For i As Integer = 0 To array.Length - 1
        Dim pTemp As IntPtr = Marshal.AllocHGlobal(structSize)
        Marshal.StructureToPtr(array(i), pTemp, True)
        Marshal.Copy(pTemp, dataBuffer, 0 + (i * structSize), structSize)
        Marshal.FreeHGlobal(pTemp)
    Next
    Return dataBuffer
End Function

二、使用GetByteArray方法,将返回的字节数组复制到指针指向的内存中:

Dim data As Byte() = GetByteArray(stpDx)
Dim pDxBuf As IntPtr = Marshal.AllocHGlobal(data.Length)
Marshal.Copy(data, 0, pDxBuf, data.Length)
ezg_Block.pDx = pDxBuf

我希望这能满足您的需求...

作为旁注;由于您在结构中为每个字段指定了固定大小,因此您无需在代码中用空格填充值;这在编组数据时由框架处理。

更新

要读回数据,您基本上需要做同样的事情,但反过来:

Private Shared Function GetStructArray(ByVal dataBuffer() As Byte) As dx_entry()
    Dim structSize As Integer = Marshal.SizeOf(GetType(dx_entry))
    If dataBuffer.Length Mod structSize <> 0 Then
        Throw New ArgumentException("Argument is of wrong length", "dataBuffer")
    End If
    Dim elementCount As Integer = Convert.ToInt32(dataBuffer.Length / structSize)
    Dim size As Integer = structSize * elementCount
    Dim result() As dx_entry = New dx_entry(elementCount - 1) {}
    For i As Integer = 0 To elementCount - 1
        Dim pTemp As IntPtr = Marshal.AllocHGlobal(structSize)
        Marshal.Copy(dataBuffer, 0 + (i * structSize), pTemp, structSize)
        result(i) = DirectCast(Marshal.PtrToStructure(pTemp, GetType(dx_entry)), dx_entry)
        Marshal.FreeHGlobal(pTemp)
    Next
    Return result
End Function

像这样调用:

Dim structSize As Integer = Marshal.SizeOf(GetType(dx_entry))        
Dim newdata() As Byte = New Byte(structSize * numberOfElements -1) {}
Marshal.Copy(ezg_Block.pDx, newdata, 0, newdata.Length)
Dim newstruct() As dx_entry = GetStructArray(data)

注意:要使所有这些正常工作,您需要稍微调整结构:似乎您需要将 SizeConst 增加 1。这是因为字符串以空值结尾,因此需要为空字节也是:

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure dx_entry
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=11)> _
    Public dx As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=4)> _
    Public type As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=2)> _
    Public narray As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=2)> _
    Public ctier As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=2)> _
    Public poa As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=2)> _
    Public poa_rsvd As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=82)> _
     Public filler As String
End Structure
于 2009-07-23T13:41:41.777 回答