6.1.2.13 Hidden Module
Note
The _HiddenModule is not one of the modules MS-VBAL §6.1.2
specifies, and has no section of its own there. It is the module of the VBA library that its type library marks hidden, which an object browser lists under that name;
Array is documented, the other members are not.
The platform declares it as it does the modules the specification names, because the language needs it all the same.
The _HiddenModule module is represented in the SDK by the type
IStdHiddenModule, and implemented by RDCore.Runtime.StdLib.StdHidden.
A hidden member is left out of a completion list and resolves anyway, being a member of a standard module like any other. The module is declared hidden
(StdLibModuleAttribute.IsHidden), and so are its members; the flag is the hidden bit (SymbolProperties.HiddenMemberFlag, 0x40) of the symbol's
SymbolProperties.MemberFlags. ObjPtr, StrPtr and VarPtr are hidden members of the module on their own account.
| Member | Implemented |
|---|---|
Array(ParamArray arglist()) |
✅ See below. |
Input(Number, FileNumber), Input$(Number, FileNumber) |
✅ The characters read from a file opened for Input or Binary, with no parsing: a quotation mark or a comma is a character. Error 62 when the file ends first, 52 when the file number is not open, 54 when it was opened in a mode that cannot be read this way (RD-VBAL §5.4.5.1). |
InputB(Number, FileNumber), InputB$(Number, FileNumber) |
The bytes of the file as they are, not the characters they encode: a String of Number bytes (LenB is Number), which StrConv(…, vbUnicode) reads as characters. It reads at the same position Input does, on a file opened For Input or For Binary; on any other, error 54; past the end of the file, 62; a negative Number, 5. As MS-VBA has it (recorded in Excel). |
ObjPtr(Ptr As Unknown) |
✅ The address of an interface of an object in the session's memory, a LongPtr. See below. |
StrPtr(Ptr As String) |
✅ The address of the characters of a String in the session's memory, a LongPtr. See below. |
VarPtr(Ptr As Any) |
✅ The address of a variable in the session's memory, a LongPtr. See below. |
Width(FileNumber, Width) |
✅ The member the Width statement is the syntax of: error 5 outside 0–255, 52 and 54 as for any file statement. |
Array
Array(<element>, ...) yields a Variant holding a resizable array of Variant, with an element for each argument, in order. With no arguments it is zero-length: its upper bound is one below its lower, and
UBound(Array()) is -1 (which is not the error 9 of an array that has no dimensions at all).
The language has two of it, and which one a call is depends on whether the name is qualified:
| Written | Is | Lower bound |
|---|---|---|
Array(1, 2, 3) |
The Array keyword: ArrayExpressionNode |
The Option Base of the module the expression is written in: Option Base 1 makes LBound(Array(1, 2, 3)) 1. |
VBA.Array(1, 2, 3) |
A call of the library's member of the hidden module | Always 0, whatever Option Base says. |
👉 This is the classic gotcha of the language, and it is modelled rather than papered over. Like LBound and UBound (MS-VBAL §3.3.5.2, the special forms; see RD-VBAL §2.5.2.1.2),
the keyword is let through by the grammar as an identifier, and the parse listener recognizes it by its token when it is written without a qualifier and with positional arguments: it builds an ArrayExpressionNode
rather than an IndexExpressionNode. The qualified form reaches the listener as a member access, which stays what it is and resolves to the library's member like any other call of one. A name written [Array], or
typed, or a member called that, is an ordinary name; arguments that are named or omitted leave the call an index expression for the rules about calls to say what is wrong with it.
- Static semantics. The expression is a
Variant; each element is an expression like any other, and can be wrong in its own right. - Runtime semantics. Each element is evaluated and held as a
Variant(RuntimeExpressionEvaluator.EvaluateArray); an object in one is referenced by its cell, as it is by any variable. The lower bound is the executing frame'sOption Base, like theReDimstatement's omitted lower bound (RD-VBAL §5.4.3.3).
VarPtr
VarPtr is declared as the VBA type library declares it, VarPtr(Ptr As Any) As LongPtr: its parameter is ByRef … As Any, as a Declare's can be
(StdLibAsAnyAttribute). It takes a variable of any type and gives its address in
the session's memory, which is where a PEEK or a POKE reads and writes it (RD-VBAL §2.3.1.2 Session Services,
which has what is at every address in one place).
The address space begins above the first 64 KiB, so that no address is 0 or a small number.
As in MS-VBA (recorded in Excel):
| Argument | VarPtr gives |
|---|---|
A variable - a local, a parameter, a Static local, a variable of a module bare or qualified, a field of the class in its own code by its bare name |
Its address: never 0, aligned to its size, and the same for as long as the variable exists, whatever is assigned to it (a String, a Variant, an Object set and set to Nothing). |
A ByRef parameter |
The address of the variable it was given; a ByVal parameter has its own. |
The name of a Function or a Property Get, in its own code |
The address of its result variable (MS-VBAL §5.3.1): one of its own, which no local has, the same for the whole call whatever is assigned to it. What is at it is what the call returns. |
A Variant ByRef parameter given a variable of another type |
The address of a VARIANT of its own, the same for the whole call, which is not the variable's. |
| An element of an array | Its address in the block the array's elements are laid out in, side by side: the first element's plus its index times the width of an element (1 for a Byte, 2 for an Integer or a Boolean, 4 for a Long, 8 for a Double, a Currency or a Date, a pointer for a String or an object, a VARIANT for a Variant, a record's in-memory size for a record), the first subscript varying fastest. The block is the array's for as long as the array is: every ReDim makes another, Preserve or not, and so does an array assigned another. Error 9 for an array that has no elements. |
An element of a fixed-size array field of a record (r.N(2)) |
Inline, in the record: the record's address, plus the field's offset, plus the element's. |
A field of a record (r.X, a(1).X, r.Inner.X) |
The record's address plus the field's offset, in MS-VBA's layout for the session's pointer width: each field aligned to its own size, a nested record to its strictest field's. A fixed-length String, a variable or a field, is passed as a String made of it: a temporary. |
What is no variable: a literal, an expression, (x), a constant, what a function returns, a public variable of an object named through it (h.Value, Me.Value) |
The address of a temporary, which is freed when the statement is over: two temporaries of one statement have two addresses, so VarPtr(1) = VarPtr(1) is False. |
ByVal n |
The bytes n is stored in, read as a whole number: a whole number or a Boolean is itself, a Double, a Date or a Currency its eight bytes, a Variant its VARIANT's type (VarPtr(ByVal Null) is 1). A String or an object is passed in a temporary. |
| An array | Compile error VBC09310 Type mismatch, as it is for a Declare's As Any; an element of it is valid. |
Note
The bytes at an element's or a record's address are what it holds, laid out as MS-VBA lays them out: a number, a Boolean, a Date and a
fixed-length String are their bytes, a variable-length String the address of its characters (StrPtr), an object the address of its interface
(ObjPtr), a Variant its VARIANT (below), and a record its fields' at their offsets, padding zeroed. A POKE there writes the element
or the field the byte is part of (RD-VBAL §2.3.1.2 Session Services).
Note
🚧 Not implemented yet: a Single passed ByVal has its four bytes and zeros above them, where MS-VBA leaves whatever was there.
SAFEARRAY
A dynamic array - a field of a record, an element of a Variant array - and a Variant holding an array, is the address of a SAFEARRAY, 0 for an array
that has no dimensions. As in MS-VBA (recorded in Excel), the descriptor is the number of dimensions (2 bytes), its features (2 bytes), the width of an
element (4), its locks (4), the address of the elements (a pointer, eight bytes in on 64-bit), then each dimension's count of elements and lower bound,
4 bytes each, the last dimension first.
| Elements | Features |
|---|---|
A number, a Boolean, a Date, a fixed-length String |
&H80 (FADF_HAVEVARTYPE) |
A String |
&H180, BSTRs |
A Variant |
&H880, VARIANTs |
An object of a class, Object |
&H440, interfaces |
IUnknown |
&H240 |
| A record | 0 |
The address of the elements is the block they are laid out in (VarPtr), and the locks are the For Each statements over the array and the calls
it is passed an element to. A dynamic array's descriptor is the same for as long as it holds an array - a ReDim moves the elements, not the descriptor - and
is freed when it holds none, after an Erase; a Variant's is the array's, and another array assigned to it has another.
A POKE into a lower bound moves the elements to other subscripts, where they are; into a count, makes the array shorter. A count that makes it longer than
it is would be memory it is not, and is refused, as is a POKE into anything else of the descriptor. A POKE into the pointer a dynamic array is makes it
the array of the descriptor it points at, which the two then hold - an element written through either is the other's - and 0 makes it an array of no
dimensions; the descriptor it pointed at is not freed.
Variant
A Variant is a VARIANT, 24 bytes in a 64-bit session and 16 in a 32-bit one, as in MS-VBA (recorded in Excel): its first two bytes are what VarType
says it holds - an array's being vbArray and its elements' - and its data is eight bytes in. A number, a Boolean, a Date and a Currency are their
bytes, a String the address of its characters, an object the address of its class's interface, an array the address of its SAFEARRAY, and a
Decimal is a DECIMAL over the whole of it but its type. A POKE into the type or the data makes it what they say; into the data of a String, an object
or an array, it is the pointer it is, as a variable's is. A type changed to or from one of those is refused: what it would point at is nothing the session has.
StrPtr
StrPtr is declared as the VBA type library declares it, StrPtr(Ptr As String) As LongPtr, and is given where its argument is
(StdLibByAddressAttribute): it gives the address of the characters of the very
String it is passed. Those are in a BSTR of the session's memory, as MS-VBA lays one out: a Long of the length in bytes just before them
(StrPtr(s) - 4), the characters two bytes each - and the odd byte of a String that has one - then a null character
(RD-VBAL §2.3.1.2 Session Services). The bytes of a String variable, at VarPtr(s), are this address.
As in MS-VBA (recorded in Excel):
| Argument | StrPtr gives |
|---|---|
vbNullString, a String variable nothing was assigned to |
0. A zero-length String, "", is not 0. |
A String variable, an element of a String array, a String field of a record |
The address of its characters, the same until a String is assigned to it - any String: the same text, and s = s itself, are another. t = s gives t characters of its own, and so does a ByVal parameter; a ByRef one is the variable's. |
A Variant holding a String |
The address of the Variant's own characters. |
What is no String variable: a literal, an expression, a fixed-length String, a number |
The address of the characters of a String made of it, a temporary freed when the statement is over. |
A POKE into the characters changes the String, in place: StrPtr(s) is where it was. So does the Mid statement, which writes the characters of its
target where they are.
The bytes of the BSTR are its own, as in MS-VBA (recorded in Excel):
A POKE into |
Makes |
|---|---|
The length, StrPtr(s) - 4 |
The String as long as the length says, at the same address: LenB and Len are what it says, and the characters past it stay where they are - the length written back is the String it was. A length of 0 is a zero-length String, not vbNullString. A length that goes on past the BSTR is refused, as reading memory it is not. |
A String's pointer, VarPtr(t) - a variable, an element, a field |
0: vbNullString; the BSTR it pointed at is not freed, and pointing at it again is the String again. StrPtr(s): the String of s, which the two then hold - a change to the characters through either, a POKE or the Mid statement, is the other's. A pointer is written a byte at a time, and one that points at no BSTR yet leaves the String as it was, its bytes as they were written. |
ObjPtr
ObjPtr is declared as the VBA type library declares it, ObjPtr(Ptr As Unknown) As LongPtr, and is given where its argument is
(StdLibByAddressAttribute): it gives the address of the interface of the object
that the variable it is passed is declared as. An object has one for each interface it is asked for through, made the first time its address is asked for and
the same for as long as the object lives; it is freed with the object. The bytes of an object variable, at VarPtr(o), are this address.
As in MS-VBA (recorded in Excel):
| Argument | ObjPtr gives |
|---|---|
Nothing |
0. |
A variable of the object's class, of Object, a Variant holding the object |
The address of the class's own interface: one address for the three. |
A variable of an interface the class implements, of IUnknown |
The address of another interface, one for each. |
An object variable declared As New that holds nothing yet |
The new object's: passing it is using it. |
What is no variable: New Collection, what a function returns |
The address of an interface of the object, which lives as long as something holds it. |
The first pointer at the address is the address of the table of the class's interface, which every object of the class shares and an object of another class
does not; what is past it reads as zeros. A POKE there is refused: the interface is the session's.
A POKE into an object variable's pointer makes it the object whose interface the pointer is the address of - without a reference of its own, as MS-VBA
counts none: the weak reference programs make so - and 0 makes it Nothing. A pointer that points at no interface yet leaves the variable as it was, its
bytes as they were written.
Note
🚧 Not implemented yet: the table of an interface holds no address a program can call through.
⏮️ RD-VBAL §6.1.2.12 SystemColorConstants | ⏭️ RD-VBAL §6.1.3 Predefined Class Modules