Click here to Skip to main content
15,881,898 members
Articles / Programming Languages / Visual Basic
Article

Marshaling Structures

Rate me:
Please Sign up or sign in to vote.
3.29/5 (14 votes)
14 Dec 20043 min read 145.9K   45   16
The article shows marshling of simple and complex structures and how to pass them in function parameters.

Introduction

This tutorial will show all about structures that are:

  1. Marshaling simple and complex structures
  2. Passing Structures as Input/Output Parameters in functions

Background

You can combine data items of different types to create a structure. A structure associates one or more members with each other and with the structure itself. When you declare a structure, it becomes a composite data type, and you can declare variables of that type. A structure is a generalization of the user-defined type (UDT) supported by previous versions of Visual Basic. In addition to fields, structures can expose properties, methods, and events. A structure can implement one or more interfaces, and you can declare individual accessibility for each field.

Structures are useful when you want a single variable to hold several related pieces of information. For example, you might want to keep an employee's name, telephone extension, and salary together. You could use several variables for this information, or you could define a structure and use it for a single employee variable. The advantage of the structure becomes apparent when you have many employees and therefore many instances of the variable. The members can be of any data type, but you must include at least one nonshared variable or an event. You can also define Function procedures, properties, and events in a structure. You can define one property as the default property, provided it takes at least one argument. You can handle an event with a Shared Sub procedure.

You can specify the accessibility of a structure using the Public, Protected, Friend, or Private keyword, or you can let it default to Public. You must declare every member and specify accessibility for it. If you use the Dim statement without any keywords, the accessibility defaults to Public.

Furthermore, if using them in unmanaged code then you must specify the Layout the structure will use. I will use System.Runtime.InteropServices.Marshal class.

For information about Marshal class, see my article ‘Marshal Class Explained’ on this web site.

  1. Marshaling Structures

    Simple Structure

    Let us consider a simple structure. We will copy its contents to a byte array as follow:

    VB
    Imports System.Runtime.InteropServices
    Imports System.Text

    From Structure to byte array:

    VB
    Private Structure Test
      Dim Var1 As Short
      Dim Var2 As Short
    End Structure
    
    ‘Start here
    Dim Tst As Test ' Stuctuer variable
    Dim ByteArray() As Byte
    ' initialize Structure (Dummmy  Values)
    Tst.Var1 = 911
    Tst.Var2 = 7
    Dim Ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Tst))
    ReDim ByteArray(Marshal.SizeOf(Tst) - 1)
    'now copy strcutre to Ptr pointer 
    Marshal.StructureToPtr(Tst, Ptr, False)
    Marshal.Copy(Ptr, ByteArray, 0, Marshal.SizeOf(Tst))
    ‘now use ByteArray ready for use 
    Marshal.FreeHGlobal(Ptr)

    Now reverse of above is as simple as coded below:

    From Byte array to Structure

    Let us consider a byte array. We will copy its content to a simple Structure Test as coded below:

    VB
    Private Structure Test 
       Dim Var1 As Short
       Dim Var2 As Short
    End Structure
    
    ' Function to Create structure must be called first
    Private Function BulidStr(ByVal Buff() As Byte, _
               ByVal MyType As System.Type) As Object
      Dim MyGC As GCHandle = GCHandle.Alloc(Buff,  GCHandleType.Pinned)
     ‘Marshals data from an unmanaged block of memory 
     'to a newly allocated managed object of the specified type.
      Dim Obj As Object = _
          Marshal.PtrToStructure(MyGC.AddrOfPinnedObject, MyType)
      Return Obj
     ‘Free GChandle to avoid memory leaks
      MyGC.Free()
    End Function
    
    ‘Start here
     Dim Str As String = "Adnan Samuel"
     Dim Tst As Test ‘ Stuctuer variable
     Dim ByteArray () As Byte, i As Int16
     ‘convert to bytes
     ByteArray = Encoding.Default.GetBytes(Str)’ Convert string to bytes
     ‘Call BulidStr method to create structure and 
     'copy byte array data to structure 
     Tst = BulidStr(ByteArray, Tst.GetType) 
     ‘Now use it and print it
     Debug.WriteLine(Tst.Var1.ToString)
     Debug.WriteLine(Tst.Var2.ToString)

    Complex Structures

    Let us consider following types of structures:

    • Embedded structure.
    • Structure that contain a pointer (which point another structure).

    Let us consider a structure (embedded) that contains a reference to other structures. We will copy its content to a byte array as:

    From Structure to byte array

    VB
    Private Structure SAFEARRAYBOUND
            Dim cElements As Integer
            Dim lLbound As Integer
        End Structure
        Private Structure SAFEARRAY2D
            Dim cDims As Integer
            Dim fFeatures As Integer
            Dim cbElements As Integer
            Dim cLocks As Integer
            Dim pvData As Integer
            ‘reference to above structure
            Dim Bounds() As SAFEARRAYBOUND 
        End Structure
    
     ' start code here
     Dim SA As SAFEARRAY2D
     Dim StrtoBy() As Byte 'check with integer array for better result
     'Must initialize array to hold data
     ReDim SA.Bounds(1)
     'first initalize your stucture here (I just put dummy values )
     SA.cDims = 11 : SA.fFeatures = 12 : SA.cbElements = 9 : SA.cLocks = 12
     SA.pvData = 6 : SA.Bounds(0).cElements = 15 : SA.Bounds(1).lLbound = 16
     ' first get size of SA in pointer
     Dim ptr As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(SA))
     'Must initialize before use 
     ReDim StrtoBy(Marshal.SizeOf(SA) - 1)
     Marshal.Copy(ptr, StrtoBy, 0, Marshal.SizeOf(SA))
     Marshal.FreeCoTaskMem(ptr)
     'now use byte array as you like

    Now reverse of this (byte to structure) is easy however you have to manually put elements in Bound array as shown below:

    From Byte array to Structure

    VB
      Private Structure SAFEARRAYBOUND
          Dim cElements As Integer
          Dim lLbound As Integer
      End Structure
      Private Structure SAFEARRAY2D
          Dim cDims As Byte
          Dim fFeatures As Byte
          Dim cbElements As Byte
          Dim cLocks As Byte
          Dim pvData As Byte
      End Structure
    
         ' start code here
          Dim Str As String = "Adnan Samuel (adahmed911@hotmail.com)"
          Dim Tst As SAFEARRAY2D  ' Structure variable
          Dim Bounds() As SAFEARRAYBOUND ‘define array manually
          Dim ByteArray() As Byte, i As Int16
          'Convert to bytes
          ' Convert string to bytes
          ByteArray = Encoding.Default.GetBytes(Str)
          'Get array address and copy it to structure
          Dim MyGC As GCHandle = _
              GCHandle.Alloc(ByteArray, GCHandleType.Pinned)
         ‘This will copy byte array to SAFEARRAY2D elements only (0 to 4)
          Tst = CType(Marshal.PtrToStructure(MyGC.AddrOfPinnedObject, _
                GetType(SAFEARRAY2D)), SAFEARRAY2D)
          MyGC.Free()
          ‘now time for SAFEARRAYBOUND Structure members (5 & 6).
          ReDim Bounds(1)
          ‘Show array values just to check
          For i = 0 To 6
              Debug.WriteLine(ByteArray(i))
          Next
          'copy remaining bits manually in bound array
          Bounds(0).cElements = ByteArray(5)
          Bounds(1).lLbound = ByteArray(6)
          'now use both structure ready fro use
    

    Let us consider a structure that contains a pointer to a second structure as a member. I will show you only how to handle it.

    VB
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
    Public Structure Emp1
            Public first As String
            Public last As String
    End Structure 
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure Emp2
       Public Employee As IntPtr
       Public age As Integer
    End Structure 
         
    ‘Start code here
     Dim EmployeeName As Emp1
     EmployeeName.first = "Adnan"
     EmployeeName.last = "Samuel"
     Dim EmpAll As Emp2
     EmpAll.age = 26
     Dim buffer As IntPtr = Marshal.AllocCoTaskMem( _ 
               Marshal.SizeOf(EmployeeName))
     Marshal.StructureToPtr(EmployeeName, buffer, False)
     EmpAll.Employee = buffer
     Debug.WriteLine(ControlChars.CrLf & "Employee before call:")
     Debug.WriteLine("first = " & EmployeeName.first & vbCrLf & "last = " _
           & EmployeeName.last & vbCrLf & "age =" &                  
           EmpAll.age)
     'Now pass the Emp2 structure into unmanaged code (e.g. PinvokeLib.dll) _
     'as byRef then see the result
     ‘------
     Dim EmpRes As Emp1 = CType(Marshal.PtrToStructure(EmpAll.Employee, _
         GetType(Emp1)), Emp1)
     Marshal.FreeCoTaskMem(buffer)
     Debug.WriteLine(ControlChars.CrLf & "Employee after call:") 
     Debug.WriteLine("first = " & EmpRes.first & vbCrLf & "last = " & 
        EmpRes.last & vbCrLf & "age =" & EmpAll.age)
  2. Passing Structures as Parameters

    • Input parameters
    • Input/Output parameters

    Let us consider a simple structure we pass it as ByVal in function:

    VB
     <StructLayout(LayoutKind.Sequential)> _
        Public Structure SECURITY_ATTRIBUTES
            Public nLength As Integer
            Public lpSecurityDescriptor As Integer
            Public bInheritHandle As Integer
        End Structure
    
        Public Declare Function CreateDirectory Lib "kernel32" _
             Alias "CreateDirectoryA" (ByVal lpPathName As String, _
             ByVal lpSecurityAttributes As SECURITY_ATTRIBUTES) As Boolean
            '' start code here
            Dim security As New SECURITY_ATTRIBUTES
            If CreateDirectory("c:\Adnan", security) Then
                MsgBox("Directory created.", MsgBoxStyle.Information)
            Else
                MsgBox("Directory not created.", MsgBoxStyle.Information)
            End If

    Let us consider an example in which we can pass structure as Input/Output parameter or simply ByRef parameter in a function.

    1. Start a new project a name it what you want then you have a form in a project.
    2. Now add a class name OSVersionInfo and add code below:
      VB
      Imports System.Runtime.InteropServices
      <StructLayout(LayoutKind.Sequential)> _
      Public Class OSVersionInfo
        Public OSVersionInfoSize As Integer
        Public majorVersion As Integer
        Public minorVersion As Integer
        Public buildNumber As Integer
        Public platformId As Integer
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)> _
        Public versionString As String
        Public ServicePackMajor As Integer
        Public ServicePackMinor As Integer
      End Class
    3. Now add another class and name it API and add code below:
      VB
      Imports System.Runtime.InteropServices
      Imports System.Runtime.InteropServices
      Public Class APIDeclare Ansi Function GetVersionEx _ 
          Lib "kernel32" Alias "GetVersionExA" _ 
          (<[In](), Out()> ByVal osvi As OSVersionInfo) As Boolean
      
        ‘Now you have option to use above function with I/O parameters or use 
        'below with ByRef but use one at a time
        ' Declare Ansi Function GetVersionEx2 Lib "kernel32" Alias _
        ' "GetVersionExA" (ByRef osvi As OSVersionInfo) As Boolean
      End Class
    4. Now add a button to Form1 and add the code.
      VB
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal _ 
             e As System.EventArgs) Handles Button1.Click
       Dim versionInfo As New OSVersionInfo
       versionInfo.OSVersionInfoSize = Marshal.SizeOf(versionInfo)
       GetVersionEx(versionInfo)
       Debug.WriteLine("Major Version is: " & versionInfo.majorVersion)
       Debug.WriteLine("Minor Version is: " _
            & versionInfo.minorVersion.ToString)
       Debug.WriteLine("Bulid Number is: " _
            & versionInfo.buildNumber.ToString())
       Debug.WriteLine("Service Pack: " _
            & versionInfo.ServicePackMajor.ToString())
      
      End Sub

I am always willing to help, so if you have any questions, suggestions about my article, feel free to email me. You can also reach me on msn messenger with screen name “Maxima”.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Pakistan Pakistan
I am software engineer and working from last four years in the information technology field. I love to do intresting tasks. I love to see diffrent places and listening muzik.

Comments and Discussions

 
Generalusing structure as argument in function Pin
Yassar Arafath Thacharakkal14-Dec-09 6:32
Yassar Arafath Thacharakkal14-Dec-09 6:32 
QuestionVB 6 screen issue Pin
kams2-Jan-08 0:59
kams2-Jan-08 0:59 
GeneralFree unmanaged memory Pin
sampdoria19-Nov-07 22:55
sampdoria19-Nov-07 22:55 
QuestionStructure to byte array Pin
bgm229125-Aug-07 2:57
bgm229125-Aug-07 2:57 
Your example on Structure to Byte Array and Byte Array to Structure suits my current project which requires to sent and receive data from an external device. I need to use every byte in the structure to calculate a checksum for verification purposes. However, I could not seems to get it working when the structure contains a string data type. It seems that the structure contains a 4 byte field containing a pointer to the string. Would appreciate if you could provide an example such as shown below.

Public Structure structTest
Public devName As String '10 bytes long
Public Weight As Long
Public percent As Double
End Structure

The size of the byte array to store the structure should be 26 but my testing using your code example indicates 20. The problems lies with how to get the 10 char devname into a byte array of size 10. The code I use are listed below

Dim Tst As structTest
Dim ByteArray() As Byte

Tst.UserId = "1234567890"
Tst.Weight = 23
Tst.percent = 50.1

Dim Ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Tst))
ReDim ByteArray(Marshal.SizeOf(Tst) - 1)
'now copy strcutre to Ptr pointer
Marshal.StructureToPtr(Tst, Ptr, False)
Marshal.Copy(Ptr, ByteArray, 0, Marshal.SizeOf(Tst))
'now use ByteArray ready for use
Marshal.FreeHGlobal(Ptr)
AnswerRe: Structure to byte array Pin
psmartt112-May-08 20:58
psmartt112-May-08 20:58 
GeneralPtrToStructure not work on compact framework Pin
syedcodeproject4-Feb-07 21:06
syedcodeproject4-Feb-07 21:06 
QuestionRe: PtrToStructure not work on compact framework Pin
Nasenbaaer10-Apr-07 5:29
Nasenbaaer10-Apr-07 5:29 
QuestionSystem.AccessViolationException was unhandled Pin
EddieMac1-Oct-06 2:04
EddieMac1-Oct-06 2:04 
QuestionStructureToPtr doesn't work with complex structures including arrays Pin
jfalcon138727-Jul-06 12:35
jfalcon138727-Jul-06 12:35 
Generala bug with MyGC.Free() - never executes Pin
tridy6-Jul-06 1:22
tridy6-Jul-06 1:22 
GeneralRe: a bug with MyGC.Free() - never executes Pin
cwcwcwcwcw31-Oct-06 23:48
cwcwcwcwcw31-Oct-06 23:48 
QuestionHow to Convert Byte Array to Structure? [modified] Pin
jeyjobs8-Jun-06 0:37
jeyjobs8-Jun-06 0:37 
GeneralMarshal Structure Array Pin
jcarasick116-Dec-05 1:39
jcarasick116-Dec-05 1:39 
GeneralMarshal.StructureToPtr problem Pin
YongFei26-Aug-05 4:01
YongFei26-Aug-05 4:01 
GeneralRe: Marshal.StructureToPtr problem Pin
Adnan Samuel29-Aug-05 8:27
Adnan Samuel29-Aug-05 8:27 
GeneralRe: Marshal.StructureToPtr problem Pin
YongFei29-Aug-05 14:14
YongFei29-Aug-05 14:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.