Click here to Skip to main content
Click here to Skip to main content

Marshaling Structures

, 14 Dec 2004
Rate this:
Please Sign up or sign in to vote.
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:

    Imports System.Runtime.InteropServices
    Imports System.Text

    From Structure to byte array:

    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:

    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

    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

        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.

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

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

About the Author

Adnan Samuel
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 PinmemberYassar Arafath Thacharakkal14-Dec-09 6:32 
QuestionVB 6 screen issue Pinmemberkams2-Jan-08 0:59 
GeneralFree unmanaged memory Pinmembersampdoria19-Nov-07 22:55 
QuestionStructure to byte array Pinmemberbgm229125-Aug-07 2:57 
AnswerRe: Structure to byte array Pinmemberpsmartt112-May-08 20:58 
GeneralPtrToStructure not work on compact framework Pinmembersyedcodeproject4-Feb-07 21:06 
QuestionRe: PtrToStructure not work on compact framework PinmemberNasenbaaer10-Apr-07 5:29 
QuestionSystem.AccessViolationException was unhandled PinmemberEddieMac1-Oct-06 2:04 
QuestionStructureToPtr doesn't work with complex structures including arrays Pinmemberjfalcon138727-Jul-06 12:35 
Generala bug with MyGC.Free() - never executes Pinmembertridy6-Jul-06 1:22 
GeneralRe: a bug with MyGC.Free() - never executes Pinmembercwcwcwcwcw31-Oct-06 23:48 
QuestionHow to Convert Byte Array to Structure? [modified] Pinmemberjeyjobs8-Jun-06 0:37 
GeneralMarshal Structure Array Pinmemberjcarasick116-Dec-05 1:39 
GeneralMarshal.StructureToPtr problem PinmemberYongFei26-Aug-05 4:01 
GeneralRe: Marshal.StructureToPtr problem PinmemberAdnan Samuel29-Aug-05 8:27 
GeneralRe: Marshal.StructureToPtr problem PinmemberYongFei29-Aug-05 14:14 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 14 Dec 2004
Article Copyright 2004 by Adnan Samuel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid