Class VBUserDefinedTypeLayout
Where each field of a VBUserDefinedType sits in memory, and how much memory the whole type occupies — MS-VBAL §2.1's "linear concatenation of the aggregated data values possibly with implementation defined padding between data values".
public sealed class VBUserDefinedTypeLayout
- Inheritance
-
VBUserDefinedTypeLayout
- Inherited Members
Remarks
A UDT has two sizes, and MS-VBAL §6.1.2.11 says so where it defines Len
and LenB: "with user-defined types, Len returns the size as it will be written to the file",
while "LenB returns the in-memory size, including any implementation-specific padding between
elements". This is the second one, Size. MS-VBA's Len is not the size a Put statement
writes either, but the fields' memory widths with no padding, UnpaddedSize.
🎯 The padding is "implementation-specific" by the specification's own word, so RDCore is free to choose
it — and chooses MS-VBA's, because a UDT is exactly the kind of thing whose in-memory size a program
measures with LenB and then relies on. Each field is aligned to its own natural boundary, and the
type as a whole is padded up to the strictest boundary any of its fields asked for.
👉 Widths here are memory widths, which are not always the widths the same value takes in a file:
a variable-length String field is a pointer in memory and its characters in a record, and a
fixed-length String field is Unicode in memory and ANSI in a record. Len and LenB disagree
about a UDT for the padding, and for a fixed-length String field, which Len counts as its characters.
Fields
DefaultPointerWidth
The pointer width a layout assumes when the caller does not say — 32-bit, which is the width every
MS-VBA UDT is laid out for, and therefore the one a file or a LenB from MS-VBA agrees with.
public const int DefaultPointerWidth = 4
Field Value
Remarks
🚧 A 64-bit host lays the same declaration out differently (a pointer field, LongPtr and a
Variant all widen). Len and LenB pass the environment's own width; TODO the record's own
Size and memory image still lay it out for 32 bits.
MaxAlignment
The strictest boundary any field is aligned to, whatever its natural alignment would be.
public const int MaxAlignment = 8
Field Value
Remarks
An 8-byte boundary, which is what a Double, a Currency and a Date each ask for.
Nothing in a UDT asks for more: a Variant is sixteen bytes of data but is itself aligned to
eight, and a fixed-length String or a Byte to one.
Properties
Alignment
The boundary the type as a whole is aligned to: the strictest any of its fields asked for.
public int Alignment { get; }
Property Value
Fields
Each field, in declaration order, with the offset it sits at.
public IReadOnlyList<VBUserDefinedTypeField> Fields { get; }
Property Value
Size
The in-memory size of the whole type, padding included — what LenB reports.
public int Size { get; }
Property Value
UnpaddedSize
The fields' widths added up with no padding between or after them, a fixed-length String counted as its characters
and a nested record or an array's elements as their own unpadded size — what Len reports of a record.
public int UnpaddedSize { get; }
Property Value
Remarks
MS-VBAL §6.1.2.11.1.22 says Len of a record "returns the size as it will be written to the file"; MS-VBA reports this
instead, a variable-length String, an object or a resizable array field counting the pointer it is and not what it
points at, and RD-VBA does as MS-VBA does: Len of a record of a Long and a String is 12 in a 64-bit
host, whatever the String holds.
Methods
Of(VBUserDefinedType, int)
Lays type out.
public static VBUserDefinedTypeLayout Of(VBUserDefinedType type, int pointerWidth = 4)
Parameters
typeVBUserDefinedTypeThe type to lay out.
pointerWidthintThe host's pointer width in bytes; DefaultPointerWidth unless the caller knows better.
Returns
Remarks
A type does not know how many elements a fixed-size array field has: its bounds are reduced where a record of it is allocated, and a field laid out from the type alone has none. Of(VBUserDefinedTypeValue, int) lays out a record that has them.
Of(VBUserDefinedTypeValue, int)
Lays record out: as its type is, each fixed-size array field as wide as the elements the record's own has, and each
nested record as it is.
public static VBUserDefinedTypeLayout Of(VBUserDefinedTypeValue record, int pointerWidth = 4)
Parameters
recordVBUserDefinedTypeValueThe record to lay out.
pointerWidthintThe host's pointer width in bytes; DefaultPointerWidth unless the caller knows better.
Returns
OffsetOf(string)
The offset of the field named name, or null when the type has no such field.
public int? OffsetOf(string name)
Parameters
namestringThe field name, compared the way VBA compares identifiers.
Returns
- int?
WidthOf(VBType, int)
How many bytes a value of type occupies in memory, as a field of it would: what an element of an array of the type is wide, each
element being at the address of the one before it plus this (MS-VBA, recorded in Excel: 1 for a Byte, 2 for an Integer
or a Boolean, 4 for a Long, 8 for a Double or a Currency, a pointer for a String or an object, a VARIANT
for a Variant).
public static int WidthOf(VBType type, int pointerWidth)
Parameters
Returns
Remarks
A record is as wide as its type's layout, padding included; one whose fields are fixed-size arrays is as wide as a record of it says (Of(VBUserDefinedTypeValue, int)), which a type alone does not know.