![]() |
Languages »
VB.NET »
Data Structures
Intermediate
Marshaling StructuresBy Adnan SamuelThe article shows marshling of simple and complex structures and how to pass them in function parameters. |
VB, Windows, .NET, Visual Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
This tutorial will show all about structures that are:
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.
Let us consider a simple structure. We will copy its contents to a byte array as follow:
Imports System.Runtime.InteropServices
Imports System.Text
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:
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)
Let us consider following types of structures:
Let us consider a structure (embedded) that contains a reference to other structures. We will copy its content to a byte array as:
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:
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)
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.
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
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
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 SubI 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�.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 14 Dec 2004 Editor: Sumalatha K.R. |
Copyright 2004 by Adnan Samuel Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |