May 15, 2013 in VBA
Generally speaking you don’t use pointers in VBA. There is no language-level syntax to explicitly obtain or dereference a pointer as in C or C++ (such as
int* somePtr = &someInt).
Within VBA there is almost no reason at all to use pointers. The VB runtime handles all the implementation details of allocating, using, and reclaiming memory for you. But sometimes you need (or want) to reach out to the power of an API function that actually requires a pointer and you have no choice but to take that plunge. Or maybe (like me) you’re just mischevious or curious, and want to dig into all the implementation details that language and runtime designers are constantly telling us programmers to ignore.
Either way, there are in fact ways to obtain and work with pointers in VBA. You can do some powerful things with them. You can also crash your whole program if you don’t know what you’re doing. Using pointers is a big topic, so in this post I’ll just present an overview and a description of the functions used to obtain them.
Mind the runtime
The VBA runtime manages memory for you
VBA has a runtime. That’s important. It means behind all your code, whether attached to a Microsoft Office file or a free-standing VB6 program, there is another program running all the time handling a bunch of utility stuff for you. Runtimes are also called virtual machines. In fact for most of its life the VB runtime was called “the VB runtime” while the dll that contained it was called msvbvmXX.dll, for “Microsoft Visual Basic Virtual Machine [version number].”
When you go in and start messing directly with memory and addresses it is easy to confuse the runtime, particularly when the runtime decides it needs to clean up memory. This theme will come up repeatedly in the rest of this article.
Types of variables, types of pointers
A variable is more than just an address. It also contains metadata including the data type and length. And when you essentially deference a variable’s pointer by directly reading the memory at that address you will get different things depending on the type of the of the variable. This means that you cannot naively ask for a “pointer for a variable” and go on you merry way operating “directly” on the “content” of that variable.
Effectively using pointers in VBA depends on understanding how variables of different types are stored. I’ve covered that in detail in another post: VBA Internals: What’s in a Variable. For the rest of this article I’ll assume you read or are otherwise familiar with what’s covered in that article.
Functions for Obtaining Pointers
VBA has three built-in functions for obtaining pointers. A fourth requires a
The declaration of
||Due to VBA’s syntax, there is no way to directly pass an array variable to
Here is the declaration for VarPtrArray for Office 2010+:
Public Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias _ "VarPtr" (Var() As Any) As LongPtr
For older versions of Office, use this declaration:
Public Declare Function VarPtrArray Lib "VBE6" Alias _ "VarPtr" (Var() As Any) As Long
Examples: Getting Pointers
Here are some quick examples of using the pointer functions in VBA. I don’t delve into the details of working with pointers to arrays and variants here. Those are substantial topics in themselves, and will be covered in future posts. In the examples below, note carefully the difference between the pointers to the variables themselves (obtained through VarPtr) and the pointers to the semantic content of the object, array, and variant variables.
Sub PtrExample() Dim lLong As Long Dim sString As String Dim oCollection As Collection Dim aDoubles(10) As Double Dim vDate As Variant lLong = 42 sString = "Hello" Set oCollection = New Collection aDoubles(0) = 3.14159 vDate = Now Dim ptrToLong As LongPtr Dim ptrToStringVar As LongPtr Dim ptrToBSTR As LongPtr Dim ptrToObjVar As LongPtr Dim ptrToObject As LongPtr Dim ptrToArray As LongPtr Dim ptrToArrayData As LongPtr Dim ptrToVarVar As LongPtr Dim ptrToDate As LongPtr ' Pointer to Long variable, which is also the actual Long value ptrToLong = VarPtr(lLong) ' Pointer to String variable ptrToStringVar = VarPtr(sString) ' Pointer to actual string content ptrToBSTR = StrPtr(sString) ' Pointer to Object variable ptrToObjVar = VarPtr(oCollection) ' Pointer to actual object in memory ptrToObject = ObjPtr(oCollection) ' Pointer to array variable ptrToArray = VarPtrArray(aDoubles) ' Pointer to the start of the actual array content ptrToArrayData = VarPtr(aDoubles(0)) ' Pointer to Variant variable ptrToVarVar = VarPtr(vDate) ' Pointer to actual Date value, at offset 8 of VARIANT struct ptrToDate = ptrToVarVar + 8 End Sub