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

Exploring general functions of the Marshal class

Rate me:
Please Sign up or sign in to vote.
3.83/5 (35 votes)
24 Oct 20047 min read 158.1K   44   16
This article will explore general functions of the Marshal class - your bridge for managed and unmanaged code.

Introduction

The Marshal class has many members, but most of them are utilities that aid in interoperation with COM code. I will explain only those that are mostly used with managed code.

With .NET, Visual Basic has a lot of power to deal with Windows systems at a low level, and work with unmanaged code from external libraries. That power is found in three new tools: IntPtr, .NET's platform-dependent representation of a memory address; GCHandle, which helps you pin and retrieve the address of data in the managed memory heap; the Marshal class, the one-stop shop for all your memory allocation, cleanup, and manipulation needs.

If you decide to work with direct memory manipulation in VB.NET, the first thing you need to understand is the IntPtr type. IntPtr is a structure that represents both addresses and handles (most handles are pointers to Windows pointers). IntPtr instances are also platform-dependent (or independent, depending on your point of view). On a 32-bit system, IntPtr is 32-bit, while on a 64-bit system, IntPtr is 64-bit. The benefit is that you don't need to change or recompile your code for both platforms. Any function within the .NET Framework that exposes a way to work with addresses and handles, uses the IntPtr type. Such functions are marshaled to unmanaged code simply as the internal address number, meaning you can pass a variable of IntPtr type to any unmanaged code that expects a pointer. So the good news is that, although you can't use this in VB.NET:

VB
Dim MyPointer As Void *

this works brilliantly in VB.NET as:

VB
Dim MyPointer As IntPtr

Note that there was no IntPtr type in beta 1 — instead, functions that worked with pointers and handles used the Integer type. The IntPtr type has a ToInt32 method that converts the address to an Integer, but that can and will cause an Overflow exception on a 64-bit system. IntPtr also has a ToInt64 method, but you'll have to keep track of the platform if you want to do these conversions.

StrPtr() and VarPtr()  in VB.NET

Both undocumented VB 6 functions return address of a variable. Same functionality can obtained in VB.NET by GCHANDLE class.

Let's examine how you place the address of a variable into an IntPtr. You use the GCHandle class which has an AddrOfPinnedObject method that returns an IntPtr for a variable. You must "pin" the data before you can get this address. This prevents the garbage collector from moving the data inadvertently while you're referring to the original address. This code pins a variable, displays its address on the console window, and frees the handle:

VB
' dim managed variable
Dim MyString As String = "Adnan Samuel"
 ' pin variable and create
' GC handle instance
Dim gh As GCHandle = GCHandle.Alloc (MyString, GCHandleType.Pinned)
 ' get address of variable
Dim AddrOfMyString As IntPtr = gh.AddrOfPinnedObject()
 Console.WriteLine (AddrOfMyString.ToString())
 ' free the handle and unpin variable
gh.Free()

Explanation

AllocHGlobal and AllocCoTaskMem

AllocHGlobal, allocates memory on the native heap by using the GlobalAlloc function internally; and AllocCoTaskMem, which is similar but instead uses the COM memory manager (CoTaskMemAlloc). Both functions have one parameter, the number of bytes to allocate. Both functions return an IntPtr, the base address of the newly allocated buffer. To free the memory, you use the FreeHGlobal or FreeCoTaskMem methods, depending on which allocation method you used. Each of these functions has one parameter, the address to the newly allocated buffer returned by the allocation functions.

Use the WriteByte, WriteInt16, WriteInt32, or WriteInt64 methods to write simple numeric data to an unmanaged buffer. Each of the functions takes as parameters the destination address of the write operation and the numeric value you want to write. These functions are also overloaded to allow an optional third parameter indicating an offset from the address provided, which can be useful if you're attempting to fill memory with arrays of elements or fields of a structure, for instance:

VB
' allocate some memory and get its address
Dim MyPointer As IntPtr =   Marshal.AllocHGlobal(4)
 ' write the number 255 to that address
Marshal.WriteInt32(MyPointer, 255)
 ' some more code here (call to 
' unmanaged code for example)
 ' free memory
Marshal.FreeHGlobal(MyPointer)

You can also use the address provided by unmanaged code instead of allocating your own:

VB
Dim MyPointer As IntPtr = New IntPtr([insert integer address _
               from unmanaged code here])
 Marshal.WriteInt32(MyPointer, 255)

The reverse is also possible. You can read simple numeric data from an IntPtr address using the ReadByte, ReadInt16, ReadInt32, and ReadInt64 methods:

VB
Dim MyInteger As Integer = Marshal.ReadInt32(MyPointer)

String Functions

Reading and writing strings is similar to reading and writing simple numeric data, with one minor exception: you don't allocate memory first, then write a string to it. Instead, the act of creating a string in unmanaged memory allocates the space and returns the string's address. There are seven write methods: StringToBSTR, StringToCoTaskMemAnsi, StringToCoTaskMemUni, StringToCoTaskMemAuto, StringToHGlobalAnsi, StringToHGlobalUni, and StringToHGlobalAuto. The StringToCoTaskMemxxx functions write string data to COM allocated memory, while StringToHGlobalxxx functions write to the native unmanaged heap. Functions ending in Ansi write single-byte ANSI strings. Functions ending in Uni write double-byte Unicode strings. The functions ending in Auto write ANSI or Unicode strings, depending on the operating system: ANSI strings on Windows 98 and ME, Unicode strings on NT-based platforms (Windows NT 4.0, 2000, and XP). StringToBSTR writes an Automation BSTR, which is analogous to using the SysAllocString function. Each of these functions accepts a string as an input argument and returns a pointer to the resulting string:

VB
Dim MyStrPointer As IntPtr = Marshal.StringToHGlobalAuto("Hello World")

Four read methods — PtrToStringAnsi, PtrToStringUni, PtrToStringAuto, and PtrToStringBSTR — read the data at a given address and create a managed String object containing a copy of the characters. Use PtrToStringAnsi if the unmanaged string is ANSI, PtrToStringUni if the unmanaged string is Unicode, or PtrToStringBSTR if the unmanaged string is of BSTR type. PtrToStringAuto assumes the unmanaged string is ANSI on a Windows 98 or ME system, and Unicode on a Windows NT 4.0, 2000, or XP platform. PtrToStringAuto actually calls PtrToStringAnsi or PtrToStringUni, depending on the operating system. Each function is also overloaded to accept an optional number of characters to copy. If you don't provide the number of characters, the function looks for a terminating null character:

VB
' copy entire string
Dim MyString As String =   Marshal.PtrToStringAuto(MyPointer)
' copy first 5 characters
Dim MyString As String = Marshal.PtrToStringAuto (MyPointer, 5)

To free the unmanaged memory buffer holding a string, you call either the FreeHGlobal or FreeCoTaskMem member. To free a BSTR created with StringToBSTR, you call FreeBSTR, which in turn calls the FreeSysString function.

 StructureToPtr and PtrToStructure

You write a Structure (user-defined type) to unmanaged memory by using the StructureToPtr method. This method requires you to have a memory buffer allocated ahead of time. It takes three parameters: the Structure you wish to write, the address (IntPtr) of the memory buffer, and a delete flag. Setting the delete flag to True wipes and frees any existing data from the buffer. This is important, because you can cause memory leaks by failing to delete the existing buffer space. For example, if one of the fields of the structure is a reference to another structure or string, the data being referenced by the field won't be freed if the parameter is set to False. Also note that Structures are copied to unmanaged memory using specific formatting (which you can control optionally), and the unmanaged copy might not look exactly like the managed representation. Use the SizeOf method to determine the number of bytes required for the buffer:

VB
Dim MyVariable As Point
MyVariable.X = 100
MyVariable.Y = 250
Dim MyPointer As IntPtr = Marshal.AllocHGlobal   (Marshal.SizeOf(MyVariable))
Marshal.StructureToPtr(MyVariable, MyPointer, False)

Use the PtrToStructure method to reverse the process and read a structure from unmanaged memory. You can use this method either as a Function that returns a copy of the structure based on Type, or as a Sub that fills a structure parameter. Note that PtrToStructure returns an Object type reference, and with Option Strict On, you must cast to the structure type (by using CType, for example):

VB
Dim MyPoint As Point 
MyPoint = CType(Marshal.PtrToStructure (MyPointer, GetType(Point)), Point)

Copy Method

Reading and writing array data is especially valuable when you need to stream binary data. The Copy method does both reads and writes, depending on the parameters you pass to it. If you want to write the data, you need to allocate some buffer space first, just as you would with strings. Next, you call the Copy method, and pass the array itself, the index in the array of the element you want to start the copy with, the destination address (IntPtr resulting from the allocation), and the size of the buffer:

VB
Dim MyData(255) As Byte 
' insert code here to fill byte array 
Dim BufferAddress As IntPtr = Marshal.AllocHGlobal(256)
Marshal.Copy(MyData, 0, BufferAddress, 256)

To read an array from unmanaged memory, call the Copy method, and pass the address of the buffer (IntPtr), the array you want to fill with the data from the buffer, the index of the array you want to start copying into, and the size of the data you want to copy:

VB
Dim MyData(255) As Byte
Marshal.Copy(BufferAddress, MyData, 0, 256)

The Marshal class has many more methods, but most of them are utilities that aid in interoperation with COM code.

Conclusion

These functions are quite helpful in marshaling managed data as well as a good replacement for unmanaged memory functions such as CopyMemory also known as RtlMoveMomry.

I am always willing to help, so if you have any questions, or 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

 
PraiseThanks A LOT !. Pin
mmx524-Jul-18 0:25
mmx524-Jul-18 0:25 
Questionpointer problem from vb.net to com? Pin
jeffery c19-Jul-13 14:17
jeffery c19-Jul-13 14:17 
QuestionMarshal class usage Pin
KevinBrady5723-Oct-12 15:07
KevinBrady5723-Oct-12 15:07 
QuestionPointers Pin
katela12-Jul-12 4:30
katela12-Jul-12 4:30 
GeneralMy vote of 5 Pin
Dr Bob22-Nov-11 11:42
Dr Bob22-Nov-11 11:42 
GeneralGreat Article Pin
ShaneMcDonald4-Sep-10 14:54
ShaneMcDonald4-Sep-10 14:54 
GeneralMy vote of 4 Pin
bboyse10-Aug-10 15:38
bboyse10-Aug-10 15:38 
GeneralMarshal Class and structure type Pin
Goyo Taty2-Jun-10 2:27
Goyo Taty2-Jun-10 2:27 
QuestionHow to retrieve pointer from ByRef? Pin
Davis Tan15-Apr-10 21:34
Davis Tan15-Apr-10 21:34 
Questionwriting a float[] from managed code to unmanaged memory Pin
kamarchand13-Oct-09 9:57
kamarchand13-Oct-09 9:57 
GeneralMarshalling: Using native DLLs in .NET Pin
meukjeboer29-Aug-08 4:28
meukjeboer29-Aug-08 4:28 
QuestionMarshalling in VB .net How to? Pin
Rajashree Rajadhyax26-Jun-08 3:10
Rajashree Rajadhyax26-Jun-08 3:10 
I'm integrating with an SDK from my VB .net application. One of the function call requires passing a BSTR and structure pointer. It also has two other parameters that are enumerations. I don't know the datatype for these Enumerations. How do I make the call. My code is someting like this:

Dim lChannel As New SmIa.SInputChannel
Dim lStructSize As Integer

lStructSize = Marshal.SizeOf(GetType(SmIa.SInputChannel))

With lChannel
.bw = SmIa.Bandwidth.bwBroad
.res = SmIa.Resolution.reHigh
.type = SmIa.InputChannelId.icMicrophone
End With

Dim lPtr As IntPtr = Marshal.AllocHGlobal(lStructSize)

Marshal.StructureToPtr(lChannel, lPtr, False)

Dim lResChannel As SmIa.SInputChannel = CType(Marshal.PtrToStructure(lPtr, GetType(SmIa.SInputChannel)), SmIa.SInputChannel)

Dim lStrUser As String = "r"
Dim lStrPtr As IntPtr = Marshal.StringToBSTR(lStrUser)
Dim lResUserName As String = Marshal.PtrToStringBSTR(lStrPtr)
Dim lLang As Integer = SmIa.LanguageId.lngUK

cTraining.Open(lResUserName, lResChannel, lLang, lLang)
Marshal.FreeBSTR(lStrPtr)

Of these SmIa.SInputChannel, SmIa.LanguageId are the structure and enum from the SDK.

Thanks for the help in advance
Generalso how does this int ptr work in conjunction with API's Pin
MartyK200717-Jul-07 6:10
MartyK200717-Jul-07 6:10 
General[MARSHALLING PROBLEM] i've mailed you too please check that and help me [modified] Pin
K edar V25-Jun-06 20:26
K edar V25-Jun-06 20:26 
AnswerRe: [MARSHALLING PROBLEM] i've mailed you too please check that and help me Pin
richardwest17-Apr-07 2:04
richardwest17-Apr-07 2:04 
QuestionRe: [MARSHALLING PROBLEM] i've mailed you too please check that and help me Pin
Matelin12-May-07 23:10
Matelin12-May-07 23:10 

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.