2009-07-23 10 views
2

Je dois créer un tableau de type de structure dans VB.net. mais je reçois erreur tout en marshalling cette erreur. Je dois passer ce tableau de type de structure dans la fonction Dll.VB.net Marshalling Error

code: déclaration Structure:

<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 

Code de initiallization et marshalling:

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 

Je reçois l'erreur suivante:

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

Informations complémentaires: Type de dx_entry [ ] ne peut pas être marshalé comme une structure non gérée; aucune taille ou décalage significatif ne peut être calculé.

Répondre

2

J'ai regardé dans un peu, et le premier problème semble que vous passez un tableau dans la méthode Marshal.SizeOf (c'est cet appel qui lève l'exception). Comme tous les membres de votre structure ont une taille fixe, vous savez que toutes les instances de ce type auront la même taille. Ainsi, vous pouvez demander au lieu de retourner Marshal.SizeOf la taille pour un tel cas:

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

La prochaine chose est que Marshal.StructureToPtr copie les données d'une structure, pas un tableau de structures. Vous aurez donc besoin d'un autre moyen de copier votre tableau de structure dans la mémoire allouée. Il n'y a aucune méthode qui fera ceci pour vous dans un appel, mais ce que vous pouvez faire est de copier chacune des instances de structure dans votre tableau dans un tableau d'octets (en utilisant la méthode Marshal.Copy), puis en copiant ce tableau d'octets dans la mémoire pointé par le pointeur.

Ceci est plus facile à faire comme une fusée à deux étages. Tout d'abord, nous allons faire une méthode qui convertit le tableau de structures à un tableau d'octets:

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 

En second lieu, utiliser la méthode GetByteArray et copier le tableau d'octets retourné à la mémoire pointée par le pointeur:

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 

J'espère que cela fait ce que vous cherchez ...

En note; Puisque vous dans votre structure avez spécifié la taille fixe pour chacun des champs, vous n'avez pas besoin de remplir les valeurs avec des espaces dans votre code; ceci est pris en charge par le framework lors de la mise en place de vos données.

Mise à jour

Pour lire les données en arrière, vous devez essentiellement faire la même chose, mais à l'envers:

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 

appelé comme ça:

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) 

Remarque : Pour que tout fonctionne correctement, vous devrez modifier légèrement la structure: il semble que vous ayez besoin de augmentez le SizeConst par 1.Ceci est puisque les chaînes sont nulles terminés, donc il faut être une position de l'octet nul ainsi:

<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 
+0

Merci beaucoup Fredrik, S'il vous plaît laissez-moi savoir, si je dois retreive les données après la dll appel de fonction, comment je dois aller de l'avant pour le faire. En fait, il y a des valeurs dans cette structure qui seront mises à jour après l'appel de la fonction. – Yogi

+0

J'apprécie votre aide Fredrik. cette solution fonctionne pour moi. Je nedd vous aider, j'ai une Union qui contient une variable de type de structure en langage C. J'ai utilisé la méthode suivante pour rassembler dans VB.net Structure publique grpr_output_block1 DRG public Comme drg_grpr_block1 Fin Structure L'utilisation dans le code - passer comme pointeur dans la fonction Dim stpGob1 Comme grpr_output_block1 Dim pGob1Buf comme IntPtr = Marshal.AllocHGlobal (Marshal.SizeOfstpGob1) Marshal.StructureToPtr (stpGob1, pGob1Buf, Faux) Ezg_Block.pGob1 = pGob1Buf est-il juste Marshall? – Yogi