The RTL has the function let access a Varant
array as a SAFEARRAY
:
function VarArrayAsPSafeArray(const V: Variant): PSafeArray;
I wanted to document how to do the reverse.
Variant is a structure
In Delphi a Variant
is an opaque blob. But internally it is really the TVarData
structure (aka the Windows VARIANT
structure). A variant can hold different types of data. You indicate which type through the VType
member. The value of the VType
member tells you how to interpret the rest of the structure:
a 32-bit Integer (VT_I4
)
- Variant
VType: Word = VT_I4;
//3
VInteger: Integer;
a IUnknown interface (VT_UNKNOWN
)
- Variant
VType: Word = VT_UNKNOWN;
//13
VUnknown: Pointer;
//actually IUnknown
an BSTR (aka WideString in Delphi)
- Variant
VType: Word = VT_BSTR;
//8
VOleStr: PWideChar;
In the case that the variant is a SAFEARRAY of 32-bit integers:
- Variant
VType: Word = (VT_ARRAY or VT_I4);
VArray: PVarArray;
And then VArray
points to a SAFEARRAY
strucuture:
- Variant
VType: Word = (VT_ARRAY or VT_I4);
VArray: PVarArray;
cDims: Word;
fFeatures: Word;
cbElements: LongWord;
cLocks: LongWord;
pvData: Pointer;
rgsabound: array[0..0] of TSafeArrayBound;
What if we start with a SAFEARRAY
There are times, particularly when interacting with COM or .NET that you:
- have to supply a
PSafeArray
,
- or are given a
PSafeArray
.
You can construct a SafeArray
easily enough, if you use Delphi's functions to create a variant array. Delphi does the heavy lifting to creating the underlying SafeArray that your "variant array" actually is.
But we want to go the other way; we are given a PSafeArray
, and we want to wrap it up inside a Delphi Variant variable, so that it handles all the ugliness and lifetime.
assemblies: PSafeArray;
assemblies := DefaultAppDomain.GetAssemblies;
How can we deal with this pointer to a SAFEARRAY
?
function PSafeArrayToVariant(psa: PSafeArray): OleVariant;
begin
TVarData(v).VType = (VT_ARRAY or VT_xxxx);
TVarData(v).VArray := PVarArray(psa);
end;
except we need to know what the SafeArray contains; we need to fill in the VT_xxxx in the above code.
Fortunately, one of the members of the SAFEARRAY structure tells what VType the members of the array are:
fFeatures: Word;
- FADF_BSTR: It is an array of BSTRs (
VT_BSTR
)
- FADF_UNKNOWN: It is an array of IUnknown (
VT_UNKNOWN
)
- FADF_DISPATCH: It is an array of IDispatch (
VT_DISPATCH
)
- FADF_VARIANT: It is an array of Variants (
VT_VARIANT
)
- FADF_HAVEVARTYPE: You can get the type using SafeArrayGetVartype
Final function
function SafeArrayGetVartype(psa: PSafeArray): TVarType; safecall; external 'OleAut32.dll';
function PSafeArrayToVariant(psa: PSafeArray): OleVariant;
var
features: Word;
vt: TVarType;
const
FADF_HAVEVARTYPE = $80;
begin
features := psa^.fFeatures;
if (features and FADF_UNKNOWN) = FADF_UNKNOWN then
vt := VT_UNKNOWN
else if (features and FADF_DISPATCH) = FADF_DISPATCH then
vt := VT_DISPATCH
else if (features and FADF_VARIANT) = FADF_VARIANT then
vt := VT_VARIANT
else if (features and FADF_BSTR) <> 0 then
vt := VT_BSTR
else if (features and FADF_HAVEVARTYPE) <> 0 then
vt := SafeArrayGetVartype(psa)
else
vt := VT_UI4; //assume 4 bytes of *something*
TVarData(Result).VType := VT_ARRAY or vt;
TVarData(Result).VArray := PVarArray(psa);
end;
ArrayTicks
is an array but perhaps it's not. – WarderArrayTicks
being passed byvar
? – Rior