Memory layout of VBA variable types
In VBA the Dim statement declares variables and allocates memory space. This is a quick overview of the main variable types and memory layout.
Some differences between 32- and 64-bit VBA:
- In 64-bit VBA every pointer is 8 bytes - in 32-bit 4 bytes
-
LongLongis only valid in 64-bit VBA together with its type-character (^) -
LongPtris not a real datatype: compiles toLongorLongLongon 32/64-bit VBA - 64-bit
Variantis larger and 64-bit SAFEARRAY structure is larger
Scalar variable types
- The native or primitive datatypes for the CPU/FPU: integers and floating-point numbers IEEE 754.
-
Booleanis Integer with value restrictions. -
DateandCurrencyare composed datatype structures based on underlying COM/Automation.
LongLong (signed 64-bit integer) +--+--+--+--+--+--+--+--+
Double (64-bit floating-point) | 8 bytes |
Date and Currency +--+--+--+--+--+--+--+--+
+--+--+--+--+
Long (signed 32-bit integer) | 4 bytes |
Single (32-bit floating-point) +--+--+--+--+
+--+--+
Integer (signed 16-bit integer) | 2 |
Boolean (0 = False, -1 = True) +--+--+
+--+
Byte (unsigned 8-bit integer) | 1|
+--+
Declaring any of these datatypes with Dim allocates (aligned) n-bytes and initializes the memory with zeroes. So the default value zero = False for Boolean, #12:00:00 AM# for Date and 0 for Currency. It is also the default value for Functions of these types.
Pointer variable types
These VBA variable types store pointers:
- String variable
- Object types (all Object, Collection, Class, etc.)
- Dynamic Array variable
0 4 8 +-----------+-----------+ | pointer 64-bit | +-----------+-----------+ Dim str as String ---> to BSTR STRUCTURE Dim o as Object ---> to IDispatch Dim varr() ---> to SAFEARRAY STRUCTURE
After Dim and for functions returning these types the default value is zero, a null-ptr:
-
StrPtr()is zero for String -
ObjPtr()is zero = Nothing for Object types - No such thing for Dynamic Arrays
VBA Variant
Speciality of VBA/COM is the Variant data type. Variant can store all variable types, including arrays. Conversion is implicite. It is a structure of tagVARIANT in Oaidl.h, which compiles to different sizes on 32- or 64-bit VBA and operated by COM routines:
0 2 8 16 +--+--+-----+-----+-----+-----------------------+ |TYPE | reserved | 8-byte data | 32-bit VBA Variant: 16 bytes +--+--+-----+-----+-----+-----------------------+ 0 2 8 16 24 +--+--+-----+-----+-----+-----------------------+-----------------------+ |TYPE | reserved | 8-byte data | | 64-bit VBA Variant: 24 bytes +--+--+-----+-----+-----+-----------------------+-----------------------+
Every Dim and each elements of Variant Arrays allocate 16- or 24 bytes.
The reason for larger size is this structure in tagVARIANT for UDT-s, when it stores two 64-bit pointers (for vbUserDefinedType Variant):
struct {
PVOID pvRecord;
IRecordInfo *pRecInfo;
} __VARIANT_NAME_4;
But Variant storing UDT-s is possible only for Types from external libraries, f. ex. mscorlib. User Types defined in a standard VBA Module or Class cannot be converted to Variant (VBA doesn't create an instance for IRecordInfo). This means that the last qword of all 64-bit Variant in normal VBA code is usually zero and unused.
Variant type-mapping and memory layout:
enum VARENUM VbVarType VarType() |TYPE | RESERVED | DATA | 64-bit VBA only | VT_EMPTY=0 vbEmpty 0 |00 00|00 00 00 00 00 00|00 00 00 00 00 00 00 00|00 00 00 00 00 00 00 00| VT_NULL=1 vbNull 1 |01 00|00 00 00 00 00 00|00 00 00 00 00 00 00 00|00 00 00 00 00 00 00 00| VT_I2=2 vbInteger 2 |02 00|00 00 00 00 00 00|XX XX 00 00 00 00 00 00|00 00 00 00 00 00 00 00| VT_I4=3 vbLong 3 |03 00|00 00 00 00 00 00|XX XX XX XX 00 00 00 00|00 00 00 00 00 00 00 00| VT_R4=4 vbSingle 4 |04 00|00 00 00 00 00 00|XX XX XX XX 00 00 00 00|00 00 00 00 00 00 00 00| VT_R8=5 vbDouble 5 |05 00|00 00 00 00 00 00|XX XX XX XX XX XX XX XX|00 00 00 00 00 00 00 00| VT_CY=6 vbCurrency 6 |06 00|00 00 00 00 00 00|XX XX XX XX XX XX XX XX|00 00 00 00 00 00 00 00| VT_DATE=7 vbDate 7 |07 00|00 00 00 00 00 00|XX XX XX XX XX XX XX XX|00 00 00 00 00 00 00 00| VT_BSTR=8 vbString 8 |08 00|00 00 00 00 00 00|XX XX XX XX 64 64 64 64|00 00 00 00 00 00 00 00| VT_DISPATCH=9 vbObject 9 |09 00|00 00 00 00 00 00|XX XX XX XX 64 64 64 64|00 00 00 00 00 00 00 00| VT_ERROR=10 vbError 10 |0A 00|00 00 00 00 00 00|XX XX XX XX 00 00 00 00|00 00 00 00 00 00 00 00| VT_BOOL=11 vbBoolean 11 |0B 00|00 00 00 00 00 00|XX XX 00 00 00 00 00 00|00 00 00 00 00 00 00 00| VT_VARIANT=12 vbVariant 12 VT_UNKNOWN=13 vbDataObject 13 VT_DECIMAL=14 vbDecimal 14 |0E 00|XX XX XX XX XX XX|XX XX XX XX XX XX XX XX|00 00 00 00 00 00 00 00| VT_UI1=17 vbByte 17 |11 00|00 00 00 00 00 00|XX 00 00 00 00 00 00 00|00 00 00 00 00 00 00 00| VT_I8=20 vbLongLong 20 |14 00|00 00 00 00 00 00|XX XX XX XX XX XX XX XX|00 00 00 00 00 00 00 00| VT_RECORD=36 vbUserDefinedType 36 |24 00|00 00 00 00 00 00|XX XX XX XX XX XX XX XX|64 64 64 64 64 64 64 64| VT_ARRAY=0x2000 vbArray 8192 |XX 20|00 00 00 00 00 00|XX XX XX XX 64 64 64 64|00 00 00 00 00 00 00 00| VT_BYREF=0x4000 - - |XX 40|00 00 00 00 00 00|XX XX XX XX 64 64 64 64|00 00 00 00 00 00 00 00|
The default value for Variant after Dim is all zero, vbEmpty.
-
VT_ARRAY = vbArrayis a flag. These are Variants of array type and store a Pointer to SAFEARRAY structure (PSA). -
VT_BYREFis a flag used by VBA to pass other data types to procedures as ByRef Variant. The conversion is implicite.VT_BYREFstores a pointer and its operation is invisible for the programmer: callee automatically dereferences the pointer each time of access (see details in Calling mechanisms). -
vbVariantis used only together with thevbArrayflag (Variant of array of Variant) -
vbString,vbArrayandvbObjectVariant stores a pointer to BSTR structure, SAFEARRAY structure and IDispatch Interface respectively.
These data types live only inside a Variant by conversion and are handled by COM routines:
-
vbDecimalby CDec() -
vbErrorby CVErr()
CDec(number) returns Variant/Decimal. DECIMAL maps to the Variant structure and uses the reserved bytes for a 14-byte structure:
+---+---+-------+-------+-------+-------------------------------+
|VARTYPE| reserved | 8-byte data | 32-bit VBA Variant
+---+---+-------+-------+-------+-------------------------------+
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| res |sc | S | Hi32 | Lo32 | Mid32 | DECIMAL
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
typedef struct tagDEC {
WORD wReserved;
BYTE scale;
BYTE sign;
ULONG Hi32;
ULONGLONG Lo64;
} DECIMAL;
CVErr(code) returns Variant/Error and store a 32-bit HRESULT value (see details in Error handling). The argument is 32-bit Long with acceptable range of 0-65535:
CVErr(0) ---> 0A00-000000000000-0000000000000000-0000000000000000 64-bit Variant/Error (S=0 success) CVErr(1..65535) ---> 0A00-000000000000-xxxx0A8000000000-0000000000000000 64-bit Variant/Error (S=1 failure)
Passing variables and expressions as Byref Variant
Any variable, expression can be passed as Byref Variant and it is very common for several built-in functions as well.
Here Sub (v) receives one pointer, an address of a Variant and defers the actual value and type at run-time:
Sub (v)
Pass:
_______________
+---> |_xxxx____xx____| Variant variable Dim myVar As Variant
| _______________
| |_0800___->BSTR_| Variant/String variable Dim myStr As Variant
| _______________
| |_xx20___->SA___| Variant/Array variable Dim myArr() As Variant
|
|
| or TEMP Variant made by caller:
| _______________
+---> |_0200____100___| Variant/Integer 100
| _______________
| |_0300___60000__| Variant/Long 60000
| _______________
_________ | |_0800___->BSTR_| Variant/String to BSTR "hi"
|_________|->--+ _______________
v | |_xx20___->SA___| Variant/Array to SAFEARRAY Array(1,2,3)
address of |
Variant |
| or for Non-Variant variable
| using TEMP VT_BYREF-Variant:
| _______________ ____
+---> |_xx40____ptr___| ------> |____| Integer Dim I As Integer
VT_BYREF-Variant _________
|_________| Long Dim L As Long
_________
|__->BSTR_| String Dim str As String
^ _________
| |__->SA___| Array Dim varr()
|
VarPtr(v)
Note that VarPtr(v) will always return the address in v itself. Something to keep in mind when Non-Variant variables are passed.
No comments:
Post a Comment