Click here to Skip to main content
Click here to Skip to main content
Go to top

Exploring general functions of the Marshal class

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

Dim MyPointer As Void *

this works brilliantly in VB.NET as:

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:

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

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

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:

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:

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:

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

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

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:

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:

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

Share

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

 
Questionpointer problem from vb.net to com? Pinmemberjeffery c19-Jul-13 14:17 
QuestionMarshal class usage PinmemberKevinBrady5723-Oct-12 15:07 
QuestionPointers Pinmemberkatela12-Jul-12 4:30 
How can you use pointers of pointers tp a variable in VB using the marshal, i am struggling with this code please help:
private unsafe void PutImage(ref System.IntPtr pPtr, int imageBufferSize, ADVANTAGELib.AdvImageType imageType)
       {
           byte** ppResBuffer = (byte**)pPtr;
           byte* pResBuffer = *ppResBuffer;
 
           int lPictureWidth = this.signatureImagePictureBox.Width;
           int lPictureHeight = this.signatureImagePictureBox.Height;
 
           using (MemoryStream imageStream = new MemoryStream(imageBufferSize))
           {
               using (BinaryWriter w = new BinaryWriter(imageStream))
               {
                   for (int i = 0; i < imageBufferSize; i++)
                   {
                       w.Write((byte)pResBuffer[i]);
                   }
 
                   Bitmap lPicture = new Bitmap(Image.FromStream(imageStream), lPictureWidth, lPictureHeight);
 
                   if (imageType == ADVANTAGELib.AdvImageType.AdvSignatureImage)
                   {
                       this.signatureImagePictureBox.Image = lPicture;
                   }
                   else
                   {
                       this.cardholderImagePictureBox.Image = lPicture;
                   }
 
                   System.Runtime.InteropServices.Marshal.FreeCoTaskMem((System.IntPtr)pResBuffer);
               }
           }
 
           System.GC.Collect();
       }

GeneralMy vote of 5 PinmemberDr Bob22-Nov-11 11:42 
GeneralGreat Article PinmemberShaneMcDonald4-Sep-10 14:54 
GeneralMy vote of 4 Pinmemberbboyse10-Aug-10 15:38 
GeneralMarshal Class and structure type PinmemberGoyo Taty2-Jun-10 2:27 
QuestionHow to retrieve pointer from ByRef? PinmemberDavis Tan15-Apr-10 21:34 
Questionwriting a float[] from managed code to unmanaged memory Pinmemberkamarchand13-Oct-09 9:57 
GeneralMarshalling: Using native DLLs in .NET Pinmembermeukjeboer29-Aug-08 4:28 

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
Web04 | 2.8.140926.1 | Last Updated 24 Oct 2004
Article Copyright 2004 by Adnan Samuel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid