Class VBUserDefinedTypeImage
The bytes a VBUserDefinedTypeValue occupies in memory, laid out by
VBUserDefinedTypeLayout — what an LSet between two UDT variables copies
(MS-VBAL §5.4.3.6).
public static class VBUserDefinedTypeImage
- Inheritance
-
VBUserDefinedTypeImage
- Inherited Members
Remarks
The specification says twice that this is implementation-defined: "the data in expression (as
stored in memory in an implementation-defined manner) is copied into bound-variable-expression
in an implementation-defined manner". What it does not say, and what every VBA program using
LSet on a UDT relies on, is that the copy reinterprets: the source's bytes are laid out by
the source's own type and read back through the destination's, which is how VBA fakes a union. So this is
a byte image and not a field-by-field assignment — between two UDTs of the same shape the two would agree,
and between different shapes only the byte image does what the program meant.
👉 These are memory widths, the ones LenB counts. They are not the widths the same value
takes in a record: VBUserDefinedTypeLayout's own remarks say why the two differ, and
RecordDataFormat is the other one.
Methods
Copy(VBUserDefinedTypeValue, VBUserDefinedTypeValue)
Copies source into destination as bytes
(MS-VBAL §5.4.3.6), reinterpreting them through the destination's own layout.
public static void Copy(VBUserDefinedTypeValue source, VBUserDefinedTypeValue destination)
Parameters
sourceVBUserDefinedTypeValueThe value read.
destinationVBUserDefinedTypeValueThe value written, field by field, in place.
Remarks
Only the bytes both types have are copied, as a copy between records of different sizes must be: a destination field that does not fit entirely inside the source's image is left at its declared type's default rather than being filled with half a value.
Decode(VBType, ReadOnlySpan<byte>)
The value of type that bytes are the memory image of, as Encode(VBType, VBTypedValue, int, int) laid it out.
public static VBTypedValue Decode(VBType type, ReadOnlySpan<byte> bytes)
Parameters
typeVBTypeThe declared type.
bytesReadOnlySpan<byte>The bytes.
Returns
Encode(VBType, VBTypedValue, int, int)
The bytes a value of type occupies in memory, width of them, or null when a value of the type is a
pointer to its bytes rather than the bytes (HasBytesOfItsOwn(VBType)): what an element of an array is at its address.
public static byte[]? Encode(VBType type, VBTypedValue value, int width, int pointerWidth)
Parameters
typeVBTypeThe declared type, which says how the value is laid out.
valueVBTypedValueThe value.
widthintHow many bytes a value of the type occupies (WidthOf(VBType, int)).
pointerWidthintThe host's pointer width in bytes, which a record's layout depends on.
Returns
- byte[]
HasBytesOfItsOwn(VBType)
Whether a value of type is its bytes in memory, rather than a pointer to them: a number, a Boolean, a Date, a
fixed-length String, and a record of such fields are; a variable-length String, an object, a Variant and an array are not.
public static bool HasBytesOfItsOwn(VBType type)
Parameters
typeVBTypeThe declared type.
Returns
HoldsVariableLengthString(VBUserDefinedType)
Whether type has a variable-length String member, directly or in a user-defined type that it has as a member.
public static bool HoldsVariableLengthString(VBUserDefinedType type)
Parameters
typeVBUserDefinedTypeThe user-defined type to look into.
Returns
Remarks
A fixed-length String member is not one: its characters are in the record. A variable-length one is a pointer, and it is what
makes a byte copy of the record (LSet, MS-VBAL §5.4.3.6) unsafe in MS-VBA.
IsLaidOutByTheHost(VBType)
Whether a value of type is in memory what the host lays out, rather than bytes of its own: a variable-length String, an
object and a dynamic array are a pointer, a Variant a VARIANT. A fixed-size array is its elements, side by side.
public static bool IsLaidOutByTheHost(VBType type)
Parameters
typeVBTypeThe declared type.
Returns
Of(VBUserDefinedTypeValue)
The memory image of value: each field's bytes at the offset its layout assigns,
and zeroes in the padding between them.
public static byte[] Of(VBUserDefinedTypeValue value)
Parameters
valueVBUserDefinedTypeValueThe value to lay out.
Returns
- byte[]
Remarks
A field with no byte representation of its own — a variable-length String, an object, an array,
a Variant — is a pointer in memory, and a pointer's bytes mean nothing to a process
that did not allocate them. Those bytes are left zero here and the values are carried across by
Copy(VBUserDefinedTypeValue, VBUserDefinedTypeValue) instead, which is the one place the reinterpretation can be done safely.
Of(VBUserDefinedTypeValue, int)
The memory image of value, laid out for a host whose pointers are pointerWidth bytes wide: what the bytes at the
address of a record are (VarPtr), where a 64-bit host's pointer and Variant fields are wider than a 32-bit one's.
public static byte[] Of(VBUserDefinedTypeValue value, int pointerWidth)
Parameters
valueVBUserDefinedTypeValueThe value to lay out.
pointerWidthintThe host's pointer width in bytes.
Returns
- byte[]
Of(VBUserDefinedTypeValue, int, Func<int, byte[]?>?)
The memory image of value, laid out for a host whose pointers are pointerWidth bytes wide, with the bytes the host
lays out for each field that is no bytes of its own (IsLaidOutByTheHost(VBType)): a variable-length String the address of its characters
(StrPtr), an object the address of its interface (ObjPtr), a Variant its VARIANT.
public static byte[] Of(VBUserDefinedTypeValue value, int pointerWidth, Func<int, byte[]?>? hostBytes)
Parameters
valueVBUserDefinedTypeValueThe value to lay out.
pointerWidthintThe host's pointer width in bytes.
hostBytesFunc<int, byte[]>The bytes of the field the host lays out that begins at an offset of the image - in a nested record, and an element of a fixed-size array field, the offset is from the start of the outermost record. null, or a field it gives no bytes for, leaves those bytes zero.
Returns
- byte[]
WriteInto(VBUserDefinedTypeValue, ReadOnlySpan<byte>, int)
Reads an image back into destination, in place: each field that has bytes of its own takes what the image holds at its offset - what a
POKE at the address of a record changes. A field that is a pointer (a variable-length String, an object, a Variant, an array) keeps
the value it has.
public static void WriteInto(VBUserDefinedTypeValue destination, ReadOnlySpan<byte> image, int pointerWidth)
Parameters
destinationVBUserDefinedTypeValueThe record written.
imageReadOnlySpan<byte>The bytes, laid out as Of(VBUserDefinedTypeValue, int) lays them out.
pointerWidthintThe host's pointer width in bytes.