fpc/rtl/inc/variants.pp
2011-09-27 20:22:40 +00:00

4651 lines
131 KiB
ObjectPascal

{
This include file contains the variants
support for FPC
This file is part of the Free Pascal run time library.
Copyright (c) 2001-2005 by the Free Pascal development team
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$IFDEF fpc}
{$mode objfpc}
{$ENDIF}
{$h+}
{ Using inlining for small system functions/wrappers }
{$inline on}
{$define VARIANTINLINE}
unit variants;
interface
uses
sysutils,sysconst,rtlconsts,typinfo;
type
EVariantParamNotFoundError = class(EVariantError);
EVariantInvalidOpError = class(EVariantError);
EVariantTypeCastError = class(EVariantError);
EVariantOverflowError = class(EVariantError);
EVariantInvalidArgError = class(EVariantError);
EVariantBadVarTypeError = class(EVariantError);
EVariantBadIndexError = class(EVariantError);
EVariantArrayLockedError = class(EVariantError);
EVariantNotAnArrayError = class(EVariantError);
EVariantArrayCreateError = class(EVariantError);
EVariantNotImplError = class(EVariantError);
EVariantOutOfMemoryError = class(EVariantError);
EVariantUnexpectedError = class(EVariantError);
EVariantDispatchError = class(EVariantError);
EVariantRangeCheckError = class(EVariantOverflowError);
EVariantInvalidNullOpError = class(EVariantInvalidOpError);
TVariantRelationship = (vrEqual, vrLessThan, vrGreaterThan, vrNotEqual);
TNullCompareRule = (ncrError, ncrStrict, ncrLoose);
TBooleanToStringRule = (bsrAsIs, bsrLower, bsrUpper);
Const
OrdinalVarTypes = [varSmallInt, varInteger, varBoolean, varShortInt,
varByte, varWord,varLongWord,varInt64];
FloatVarTypes = [
{$ifndef FPUNONE}
varSingle, varDouble,
{$endif}
varCurrency];
{ Variant support procedures and functions }
function VarType(const V: Variant): TVarType; inline;
function VarTypeDeRef(const V: Variant): TVarType; overload;
function VarTypeDeRef(const V: TVarData): TVarType; overload; inline;
function VarAsType(const V: Variant; aVarType: TVarType): Variant;
function VarIsType(const V: Variant; aVarType: TVarType): Boolean; overload; inline;
function VarIsType(const V: Variant; const AVarTypes: array of TVarType): Boolean; overload;
function VarIsByRef(const V: Variant): Boolean; inline;
function VarIsEmpty(const V: Variant): Boolean; inline;
procedure VarCheckEmpty(const V: Variant); inline;
function VarIsNull(const V: Variant): Boolean; inline;
function VarIsClear(const V: Variant): Boolean; inline;
function VarIsCustom(const V: Variant): Boolean; inline;
function VarIsOrdinal(const V: Variant): Boolean; inline;
function VarIsFloat(const V: Variant): Boolean; inline;
function VarIsNumeric(const V: Variant): Boolean; inline;
function VarIsStr(const V: Variant): Boolean;
function VarToStr(const V: Variant): string;
function VarToStrDef(const V: Variant; const ADefault: string): string;
function VarToWideStr(const V: Variant): WideString;
function VarToWideStrDef(const V: Variant; const ADefault: WideString): WideString;
function VarToUnicodeStr(const V: Variant): UnicodeString;
function VarToUnicodeStrDef(const V: Variant; const ADefault: UnicodeString): UnicodeString;
{$ifndef FPUNONE}
function VarToDateTime(const V: Variant): TDateTime;
function VarFromDateTime(const DateTime: TDateTime): Variant;
{$endif}
function VarInRange(const AValue, AMin, AMax: Variant): Boolean;
function VarEnsureRange(const AValue, AMin, AMax: Variant): Variant;
function VarSameValue(const A, B: Variant): Boolean;
function VarCompareValue(const A, B: Variant): TVariantRelationship;
function VarIsEmptyParam(const V: Variant): Boolean; inline;
procedure VarClear(var V: Variant);{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
procedure VarClear(var V: OleVariant);{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
procedure SetClearVarToEmptyParam(var V: TVarData);
function VarIsError(const V: Variant; out AResult: HRESULT): Boolean;
function VarIsError(const V: Variant): Boolean; inline;
function VarAsError(AResult: HRESULT): Variant;
function VarSupports(const V: Variant; const IID: TGUID; out Intf): Boolean;
function VarSupports(const V: Variant; const IID: TGUID): Boolean;
{ Variant copy support }
procedure VarCopyNoInd(var Dest: Variant; const Source: Variant);
{ Variant array support procedures and functions }
function VarArrayCreate(const Bounds: array of SizeInt; aVarType: TVarType): Variant;
function VarArrayCreate(const Bounds: PVarArrayBoundArray; Dims : SizeInt; aVarType: TVarType): Variant;
function VarArrayOf(const Values: array of Variant): Variant;
function VarArrayAsPSafeArray(const A: Variant): PVarArray;
function VarArrayDimCount(const A: Variant) : LongInt;
function VarArrayLowBound(const A: Variant; Dim : LongInt) : LongInt;
function VarArrayHighBound(const A: Variant; Dim : LongInt) : LongInt;
function VarArrayLock(const A: Variant): Pointer;
procedure VarArrayUnlock(const A: Variant);
function VarArrayRef(const A: Variant): Variant;
function VarIsArray(const A: Variant): Boolean; inline;
function VarIsArray(const A: Variant; AResolveByRef: Boolean): Boolean;
function VarTypeIsValidArrayType(const aVarType: TVarType): Boolean;
function VarTypeIsValidElementType(const aVarType: TVarType): Boolean;
{ Variant <--> Dynamic Arrays }
procedure DynArrayToVariant(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer);
procedure DynArrayFromVariant(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer);
{ Global constants }
function Unassigned: Variant; // Unassigned standard constant
function Null: Variant; // Null standard constant
var
EmptyParam: OleVariant;
{ Custom Variant base class }
type
TVarCompareResult = (crLessThan, crEqual, crGreaterThan);
TCustomVariantType = class(TObject, IInterface)
private
FVarType: TVarType;
protected
function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; virtual; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
function _AddRef: Integer; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
function _Release: Integer; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
procedure SimplisticClear(var V: TVarData);
procedure SimplisticCopy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean = False);
procedure RaiseInvalidOp;
procedure RaiseCastError;
procedure RaiseDispError;
function LeftPromotion(const V: TVarData; const Operation: TVarOp; out RequiredVarType: TVarType): Boolean; virtual;
function RightPromotion(const V: TVarData; const Operation: TVarOp; out RequiredVarType: TVarType): Boolean; virtual;
function OlePromotion(const V: TVarData; out RequiredVarType: TVarType): Boolean; virtual;
procedure DispInvoke(Dest: PVarData; const Source: TVarData; CallDesc: PCallDesc; Params: Pointer); virtual;
procedure VarDataInit(var Dest: TVarData);
procedure VarDataClear(var Dest: TVarData);
procedure VarDataCopy(var Dest: TVarData; const Source: TVarData);
procedure VarDataCopyNoInd(var Dest: TVarData; const Source: TVarData);
procedure VarDataCast(var Dest: TVarData; const Source: TVarData);
procedure VarDataCastTo(var Dest: TVarData; const Source: TVarData; const aVarType: TVarType); overload;
procedure VarDataCastTo(var Dest: TVarData; const aVarType: TVarType); overload;
procedure VarDataCastToOleStr(var Dest: TVarData);
procedure VarDataFromStr(var V: TVarData; const Value: string);
procedure VarDataFromOleStr(var V: TVarData; const Value: WideString);
function VarDataToStr(const V: TVarData): string;
function VarDataIsEmptyParam(const V: TVarData): Boolean;
function VarDataIsByRef(const V: TVarData): Boolean;
function VarDataIsArray(const V: TVarData): Boolean;
function VarDataIsOrdinal(const V: TVarData): Boolean;
function VarDataIsFloat(const V: TVarData): Boolean;
function VarDataIsNumeric(const V: TVarData): Boolean;
function VarDataIsStr(const V: TVarData): Boolean;
public
constructor Create; overload;
constructor Create(RequestedVarType: TVarType); overload;
destructor Destroy; override;
function IsClear(const V: TVarData): Boolean; virtual;
procedure Cast(var Dest: TVarData; const Source: TVarData); virtual;
procedure CastTo(var Dest: TVarData; const Source: TVarData; const aVarType: TVarType); virtual;
procedure CastToOle(var Dest: TVarData; const Source: TVarData); virtual;
procedure Clear(var V: TVarData); virtual; abstract;
procedure Copy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean); virtual; abstract;
procedure BinaryOp(var Left: TVarData; const Right: TVarData; const Operation: TVarOp); virtual;
procedure UnaryOp(var Right: TVarData; const Operation: TVarOp); virtual;
function CompareOp(const Left, Right: TVarData; const Operation: TVarOp): Boolean; virtual;
procedure Compare(const Left, Right: TVarData; var Relationship: TVarCompareResult); virtual;
property VarType: TVarType read FVarType;
end;
TCustomVariantTypeClass = class of TCustomVariantType;
TVarDataArray = array of TVarData;
IVarInvokeable = interface
['{1CB65C52-BBCB-41A6-9E58-7FB916BEEB2D}']
function DoFunction(var Dest: TVarData; const V: TVarData;
const Name: string; const Arguments: TVarDataArray): Boolean;
function DoProcedure(const V: TVarData; const Name: string;
const Arguments: TVarDataArray): Boolean;
function GetProperty(var Dest: TVarData; const V: TVarData;
const Name: string): Boolean;
function SetProperty(const V: TVarData; const Name: string;
const Value: TVarData): Boolean;
end;
TInvokeableVariantType = class(TCustomVariantType, IVarInvokeable)
protected
procedure DispInvoke(Dest: PVarData; const Source: TVarData;
CallDesc: PCallDesc; Params: Pointer); override;
public
{ IVarInvokeable }
function DoFunction(var Dest: TVarData; const V: TVarData;
const Name: string; const Arguments: TVarDataArray): Boolean; virtual;
function DoProcedure(const V: TVarData; const Name: string;
const Arguments: TVarDataArray): Boolean; virtual;
function GetProperty(var Dest: TVarData; const V: TVarData;
const Name: string): Boolean; virtual;
function SetProperty(const V: TVarData; const Name: string;
const Value: TVarData): Boolean; virtual;
end;
IVarInstanceReference = interface
['{5C176802-3F89-428D-850E-9F54F50C2293}']
function GetInstance(const V: TVarData): TObject;
end;
TPublishableVariantType = class(TInvokeableVariantType, IVarInstanceReference)
protected
{ IVarInstanceReference }
function GetInstance(const V: TVarData): TObject; virtual; abstract;
public
function GetProperty(var Dest: TVarData; const V: TVarData;
const Name: string): Boolean; override;
function SetProperty(const V: TVarData; const Name: string;
const Value: TVarData): Boolean; override;
end;
function FindCustomVariantType(const aVarType: TVarType;
out CustomVariantType: TCustomVariantType): Boolean; overload;
function FindCustomVariantType(const TypeName: string;
out CustomVariantType: TCustomVariantType): Boolean; overload;
type
TAnyProc = procedure (var V: TVarData);
TVarDispProc = procedure (Dest: PVariant; const Source: Variant;
CallDesc: PCallDesc; Params: Pointer); cdecl;
Const
CMaxNumberOfCustomVarTypes = $0EFF;
CMinVarType = $0100;
CMaxVarType = CMinVarType + CMaxNumberOfCustomVarTypes;
CIncVarType = $000F;
CFirstUserType = CMinVarType + CIncVarType;
var
NullEqualityRule: TNullCompareRule = ncrLoose;
NullMagnitudeRule: TNullCompareRule = ncrLoose;
NullStrictConvert: Boolean = true;
NullAsStringValue: string = '';
PackVarCreation: Boolean = True;
{$ifndef FPUNONE}
OleVariantInt64AsDouble: Boolean = False;
{$endif}
VarDispProc: TVarDispProc;
ClearAnyProc: TAnyProc; { Handler clearing a varAny }
ChangeAnyProc: TAnyProc; { Handler to change any to Variant }
RefAnyProc: TAnyProc; { Handler to add a reference to an varAny }
InvalidCustomVariantType : TCustomVariantType;
procedure VarCastError;
procedure VarCastError(const ASourceType, ADestType: TVarType);
procedure VarCastErrorOle(const ASourceType: TVarType);
procedure VarInvalidOp;
procedure VarInvalidOp(const aLeft, aRight: TVarType; aOpCode: TVarOp);
procedure VarInvalidOp(const aRight: TVarType; aOpCode: TVarOp);
procedure VarInvalidNullOp;
procedure VarBadTypeError;
procedure VarOverflowError;
procedure VarOverflowError(const ASourceType, ADestType: TVarType);
procedure VarBadIndexError;
procedure VarArrayLockedError;
procedure VarNotImplError;
procedure VarOutOfMemoryError;
procedure VarInvalidArgError;
procedure VarInvalidArgError(AType: TVarType);
procedure VarUnexpectedError;
procedure VarRangeCheckError(const AType: TVarType);
procedure VarRangeCheckError(const ASourceType, ADestType: TVarType);
procedure VarArrayCreateError;
procedure VarResultCheck(AResult: HRESULT);{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
procedure VarResultCheck(AResult: HRESULT; ASourceType, ADestType: TVarType);
procedure HandleConversionException(const ASourceType, ADestType: TVarType);
function VarTypeAsText(const AType: TVarType): string;
function FindVarData(const V: Variant): PVarData;
const
VarOpAsText : array[TVarOp] of string = (
'+', {opAdd}
'-', {opSubtract}
'*', {opMultiply}
'/', {opDivide}
'div', {opIntDivide}
'mod', {opModulus}
'shl', {opShiftLeft}
'shr', {opShiftRight}
'and', {opAnd}
'or', {opOr}
'xor', {opXor}
'', {opCompare}
'-', {opNegate}
'not', {opNot}
'=', {opCmpEq}
'<>', {opCmpNe}
'<', {opCmpLt}
'<=', {opCmpLe}
'>', {opCmpGt}
'>=', {opCmpGe}
'**' {opPower}
);
{ Typinfo unit Variant routines have been moved here, so as not to make TypInfo dependent on variants }
Function GetPropValue(Instance: TObject; const PropName: string): Variant;
Function GetPropValue(Instance: TObject; const PropName: string; PreferStrings: Boolean): Variant;
Procedure SetPropValue(Instance: TObject; const PropName: string; const Value: Variant);
Function GetVariantProp(Instance: TObject; PropInfo : PPropInfo): Variant;
Function GetVariantProp(Instance: TObject; const PropName: string): Variant;
Procedure SetVariantProp(Instance: TObject; const PropName: string; const Value: Variant);
Procedure SetVariantProp(Instance: TObject; PropInfo : PPropInfo; const Value: Variant);
{$IFDEF DEBUG_VARIANTS}
var
__DEBUG_VARIANTS: Boolean = False;
{$ENDIF}
implementation
uses
Math,
VarUtils;
var
customvarianttypes : array of TCustomVariantType;
customvarianttypelock : trtlcriticalsection;
customvariantcurrtype : LongInt;
const
{ all variants for which vType and varComplexType = 0 do not require
finalization. }
varComplexType = $BFE8;
procedure DoVarClearComplex(var v : TVarData); forward;
procedure DoVarCopy(var Dest : TVarData; const Source : TVarData); forward;
procedure DoVarCast(var aDest : TVarData; const aSource : TVarData; aVarType : LongInt); forward;
procedure DoVarClear(var v : TVarData); inline;
begin
if v.vType and varComplexType <> 0 then
DoVarClearComplex(v)
else
v.vType := varEmpty;
end;
procedure DoVarClearIfComplex(var v : TVarData); inline;
begin
if v.vType and varComplexType <> 0 then
DoVarClearComplex(v);
end;
function AlignToPtr(p : Pointer) : Pointer;inline;
begin
{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
Result:=align(p,SizeOf(p));
{$ELSE FPC_REQUIRES_PROPER_ALIGNMENT}
Result:=p;
{$ENDIF FPC_REQUIRES_PROPER_ALIGNMENT}
end;
{ ---------------------------------------------------------------------
String Messages
---------------------------------------------------------------------}
ResourceString
SErrVarIsEmpty = 'Variant is empty';
SErrInvalidIntegerRange = 'Invalid Integer range: %d';
{ ---------------------------------------------------------------------
Auxiliary routines
---------------------------------------------------------------------}
Procedure VariantError (Const Msg : String); inline;
begin
Raise EVariantError.Create(Msg);
end;
Procedure NotSupported(Meth: String);
begin
Raise EVariantError.CreateFmt('Method %s not yet supported.',[Meth]);
end;
type
TVariantArrayIterator = object
Bounds : PVarArrayBoundArray;
Coords : PVarArrayCoorArray;
Dims : SizeInt;
constructor Init(aDims: SizeInt; aBounds : PVarArrayBoundArray);
destructor Done;
function Next : Boolean;
{ returns true if the iterator reached the end of the variant array }
function AtEnd: Boolean;
end;
{$push}
{$r-}
constructor TVariantArrayIterator.Init(aDims: SizeInt; aBounds : PVarArrayBoundArray);
var
i : sizeint;
begin
Dims := aDims;
Bounds := aBounds;
GetMem(Coords, SizeOf(SizeInt) * Dims);
{ initialize coordinate counter }
for i:= 0 to Pred(Dims) do
Coords^[i] := Bounds^[i].LowBound;
end;
function TVariantArrayIterator.Next: Boolean;
var
Finished : Boolean;
procedure IncDim(Dim : SizeInt);
begin
if Finished then
Exit;
Inc(Coords^[Dim]);
if Coords^[Dim] >= Bounds^[Dim].LowBound + Bounds^[Dim].ElementCount then begin
Coords^[Dim]:=Bounds^[Dim].LowBound;
if Dim > 0 then
IncDim(Pred(Dim))
else
Finished := True;
end;
end;
begin
Finished := False;
IncDim(Pred(Dims));
Result := not Finished;
end;
function TVariantArrayIterator.AtEnd: Boolean;
var
i : sizeint;
begin
result:=true;
for i:=0 to Pred(Dims) do
if Coords^[i] < Bounds^[i].LowBound + Bounds^[i].ElementCount then
begin
result:=false;
exit;
end;
end;
{$pop}// {$r-} for TVariantArrayIterator
destructor TVariantArrayIterator.done;
begin
FreeMem(Coords);
end;
type
tdynarraybounds = array of SizeInt;
tdynarraycoords = tdynarraybounds;
tdynarrayelesize = tdynarraybounds;
tdynarraypositions = array of Pointer;
tdynarrayiter = object
Bounds : tdynarraybounds;
Coords : tdynarraycoords;
elesize : tdynarrayelesize;
positions : tdynarraypositions;
Dims : SizeInt;
data : Pointer;
constructor init(d : Pointer;p : pdynarraytypeinfo;_dims: SizeInt;b : tdynarraybounds);
function next : Boolean;
destructor done;
end;
constructor tdynarrayiter.init(d : Pointer;p : pdynarraytypeinfo;_dims: SizeInt;b : tdynarraybounds);
var
i : sizeint;
begin
Bounds:=b;
Dims:=_dims;
SetLength(Coords,Dims);
SetLength(elesize,Dims);
SetLength(positions,Dims);
positions[0]:=d;
{ initialize coordinate counter and elesize }
for i:=0 to Dims-1 do
begin
Coords[i]:=0;
if i>0 then
positions[i]:=Pointer(positions[i-1]^);
{ skip kind and name }
inc(Pointer(p),ord(pdynarraytypeinfo(p)^.namelen)+2);
p:=AlignToPtr(p);
elesize[i]:=psizeint(p)^;
{ skip elesize }
inc(Pointer(p),SizeOf(sizeint));
p:=pdynarraytypeinfo(ppointer(p)^);
end;
data:=positions[Dims-1];
end;
function tdynarrayiter.next : Boolean;
var
Finished : Boolean;
procedure incdim(d : SizeInt);
begin
if Finished then
exit;
inc(Coords[d]);
inc(Pointer(positions[d]),elesize[d]);
if Coords[d]>=Bounds[d] then
begin
Coords[d]:=0;
if d>0 then
begin
incdim(d-1);
positions[d]:=Pointer(positions[d-1]^);
end
else
Finished:=true;
end;
end;
begin
Finished:=False;
incdim(Dims-1);
data:=positions[Dims-1];
Result:=not(Finished);
end;
destructor tdynarrayiter.done;
begin
Bounds:=nil;
Coords:=nil;
elesize:=nil;
positions:=nil;
end;
{ ---------------------------------------------------------------------
VariantManager support
---------------------------------------------------------------------}
procedure sysvarinit(var v : Variant);
begin
TVarData(V).vType := varEmpty;
end;
procedure sysvarclear(var v : Variant);
begin
if TVarData(v).vType and varComplexType <> 0 then
VarClearProc(TVarData(V))
else
TVarData(v).vType := varEmpty;
end;
function Sysvartoint (const v : Variant) : Integer;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varInt64)
else
Result := 0
else
Result := VariantToLongInt(TVarData(V));
end;
function Sysvartoint64 (const v : Variant) : Int64;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varInt64)
else
Result := 0
else
Result := VariantToInt64(TVarData(V));
end;
function sysvartoword64 (const v : Variant) : QWord;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varQWord)
else
Result := 0
else
Result := VariantToQWord (TVarData(V));
end;
function sysvartobool (const v : Variant) : Boolean;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varBoolean)
else
Result := False
else
Result := VariantToBoolean(TVarData(V));
end;
{$ifndef FPUNONE}
function sysvartoreal (const v : Variant) : Extended;
var Handler: TCustomVariantType;
dest: TVarData;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varDouble)
else
Result := 0
{ TODO: performance: custom variants must be handled after standard ones }
else if FindCustomVariantType(TVarData(v).vType, Handler) then
begin
VariantInit(dest);
Handler.CastTo(dest, TVarData(v), varDouble);
Result := dest.vDouble;
end
else
Result := VariantToDouble(TVarData(V));
end;
{$endif}
function sysvartocurr (const v : Variant) : Currency;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varCurrency)
else
Result := 0
else
Result := VariantToCurrency(TVarData(V));
end;
function CustomVarToLStr(const v: TVarData; out s: AnsiString): Boolean;
var
handler: TCustomVariantType;
temp: TVarData;
begin
result := FindCustomVariantType(v.vType, handler);
if result then
begin
VariantInit(temp);
handler.CastTo(temp, v, varString);
{ out-semantic ensures that s is finalized,
so just copy the pointer and don't finalize the temp }
Pointer(s) := temp.vString;
end;
end;
procedure sysvartolstr (var s : AnsiString; const v : Variant);
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varString)
else
s := NullAsStringValue
{ TODO: performance: custom variants must be handled after standard ones }
else if not CustomVarToLStr(TVarData(v), s) then
S := VariantToAnsiString(TVarData(V));
end;
procedure sysvartopstr (var s; const v : Variant);
var
tmp: AnsiString;
begin
sysvartolstr(tmp, v);
ShortString(s) := tmp;
end;
procedure sysvartowstr (var s : WideString; const v : Variant);
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varOleStr)
else
s := NullAsStringValue
else
S := VariantToWideString(TVarData(V));
end;
procedure sysvartointf (var Intf : IInterface; const v : Variant);
begin
case TVarData(v).vType of
varEmpty:
Intf := nil;
varNull:
if NullStrictConvert then
VarCastError(varNull, varUnknown)
else
Intf := nil;
varUnknown:
Intf := IInterface(TVarData(v).vUnknown);
varUnknown or varByRef:
Intf := IInterface(TVarData(v).vPointer^);
varDispatch:
Intf := IInterface(TVarData(v).vDispatch);
varDispatch or varByRef:
Intf := IInterface(TVarData(v).vPointer^);
varVariant, varVariant or varByRef: begin
if not Assigned(TVarData(v).vPointer) then
VarBadTypeError;
sysvartointf(Intf, Variant(PVarData(TVarData(v).vPointer)^) );
end;
else
VarCastError(TVarData(v).vType, varUnknown);
end;
end;
procedure sysvartodisp (var Disp : IDispatch; const v : Variant);
begin
case TVarData(v).vType of
varEmpty:
Disp := nil;
varNull:
if NullStrictConvert then
VarCastError(varNull, varDispatch)
else
Disp := nil;
varUnknown:
if IInterface(TVarData(v).vUnknown).QueryInterface(IDispatch, Disp) <> S_OK then
VarCastError(varUnknown, varDispatch);
varUnknown or varByRef:
if IInterface(TVarData(v).vPointer^).QueryInterface(IDispatch, Disp) <> S_OK then
VarCastError(varUnknown or varByRef, varDispatch);
varDispatch:
Disp := IDispatch(TVarData(v).vDispatch);
varDispatch or varByRef:
Disp := IDispatch(TVarData(v).vPointer^);
varVariant, varVariant or varByRef: begin
if not Assigned(TVarData(v).vPointer) then
VarBadTypeError;
sysvartodisp(Disp, Variant(PVarData(TVarData(v).vPointer)^) );
end;
else
VarCastError(TVarData(v).vType, varDispatch);
end;
end;
{$ifndef FPUNONE}
function sysvartotdatetime (const v : Variant) : TDateTime;
begin
if VarType(v) = varNull then
if NullStrictConvert then
VarCastError(varNull, varDate)
else
Result := 0
else
Result:=VariantToDate(TVarData(v));
end;
{$endif}
function DynamicArrayIsRectangular(p : Pointer;TypeInfo : Pointer) : Boolean;
var
arraysize,i : sizeint;
begin
Result := False;
{ get TypeInfo of second level }
{ skip kind and name }
inc(Pointer(TypeInfo),ord(pdynarraytypeinfo(TypeInfo)^.namelen)+2);
TypeInfo:=AlignToPtr(TypeInfo);
TypeInfo:=ppointer(TypeInfo+SizeOf(sizeint))^;
{ check recursively? }
if assigned(pdynarraytypeinfo(TypeInfo)) and (pdynarraytypeinfo(TypeInfo)^.kind=byte(tkDynArray)) then
begin
{ set to dimension of first element }
arraysize:=psizeint(ppointer(p)^-SizeOf(sizeint))^;
{ walk through all elements }
for i:=1 to psizeint(p-SizeOf(sizeint))^ do
begin
{ ... and check dimension }
if psizeint(ppointer(p)^-SizeOf(sizeint))^<>arraysize then
exit;
if not(DynamicArrayIsRectangular(ppointer(p)^,TypeInfo)) then
exit;
inc(p,SizeOf(Pointer));
end;
end;
Result:=true;
end;
procedure sysvartodynarray (var dynarr : Pointer; const v : Variant; TypeInfo : Pointer);
begin
DynArrayFromVariant(dynarr, v, TypeInfo);
end;
procedure sysvarfrombool (var Dest : Variant; const Source : Boolean);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varBoolean;
vBoolean := Source;
end;
end;
procedure VariantErrorInvalidIntegerRange(Range: LongInt);
begin
VariantError(Format(SErrInvalidIntegerRange,[Range]));
end;
procedure sysvarfromint (var Dest : Variant; const Source, Range : LongInt);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do
if PackVarCreation then
case Range of
-4 : begin
vType := varInteger;
vInteger := Source;
end;
-2 : begin
vType := varSmallInt;
vSmallInt := Source;
end;
-1 : Begin
vType := varShortInt;
vshortint := Source;
end;
1 : begin
vType := varByte;
vByte := Source;
end;
2 : begin
vType := varWord;
vWord := Source;
end;
4 : Begin
vType := varLongWord;
{use vInteger, not vLongWord as the value came passed in as an Integer }
vInteger := Source;
end;
else
VariantErrorInvalidIntegerRange(Range);
end
else begin
vType := varInteger;
vInteger := Source;
end;
end;
procedure sysvarfromint64 (var Dest : Variant; const Source : Int64);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varInt64;
vInt64 := Source;
end;
end;
procedure sysvarfromword64 (var Dest : Variant; const Source : QWord);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varQWord;
vQWord := Source;
end;
end;
{$ifndef FPUNONE}
procedure sysvarfromreal (var Dest : Variant; const Source : Extended);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varDouble;
vDouble := Source;
end;
end;
procedure sysvarfromsingle (var Dest : Variant; const Source : single);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varSingle;
vSingle := Source;
end;
end;
procedure sysvarfromdouble (var Dest : Variant; const Source : double);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varDouble;
vDouble := Source;
end;
end;
{$endif}
procedure sysvarfromcurr (var Dest : Variant; const Source : Currency);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varCurrency;
vCurrency := Source;
end;
end;
{$ifndef FPUNONE}
procedure sysvarfromtdatetime (var Dest : Variant; const Source : TDateTime);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varDate;
vDate := Source;
end;
end;
{$endif}
procedure sysvarfrompstr (var Dest : Variant; const Source : ShortString);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varString;
vString := nil;
AnsiString(vString) := Source;
end;
end;
procedure sysvarfromlstr (var Dest : Variant; const Source : AnsiString);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varString;
vString := nil;
AnsiString(vString) := Source;
end;
end;
procedure sysvarfromwstr (var Dest : Variant; const Source : WideString);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vType := varOleStr;
vOleStr := nil;
WideString(Pointer(vOleStr)) := Source;
end;
end;
procedure sysvarfromintf(var Dest : Variant; const Source : IInterface);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vUnknown := nil;
IInterface(vUnknown) := Source;
vType := varUnknown;
end;
end;
procedure sysvarfromdisp(var Dest : Variant; const Source : IDispatch);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vUnknown := nil;
IDispatch(vDispatch) := Source;
vType := varDispatch;
end;
end;
type
TCommonType = (ctEmpty,ctAny,ctError,ctLongInt,ctBoolean,
{$ifndef FPUNONE}
ctFloat,ctDate,ctCurrency,
{$endif}
ctInt64,ctNull,ctWideStr,ctString);
TCommonVarType = varEmpty..varQWord;
const
{$ifdef FPUNONE}
ctFloat = ctError;
ctDate = ctError;
ctCurrency = ctError;
{$endif}
{ get the basic type for a Variant type }
VarTypeToCommonType : array[TCommonVarType] of TCommonType =
(ctEmpty, // varEmpty = 0;
ctNull, // varNull = 1;
ctLongInt, // varSmallInt = 2;
ctLongInt, // varInteger = 3;
ctFloat, // varSingle = 4;
ctFloat, // varDouble = 5;
ctCurrency, // varCurrency = 6;
ctDate, // varDate = 7;
ctWideStr, // varOleStr = 8;
ctError, // varDispatch = 9;
ctError, // varError = 10;
ctBoolean, // varBoolean = 11;
ctError, // varVariant = 12;
ctError, // varUnknown = 13;
ctError, // ??? 15
ctError, // varDecimal = 14;
ctLongInt, // varShortInt = 16;
ctLongInt, // varByte = 17;
ctLongInt, // varWord = 18;
ctInt64, // varLongWord = 19;
ctInt64, // varInt64 = 20;
ctInt64 // varQWord = 21;
);
{ map a basic type back to a Variant type }
{ Not used yet
CommonTypeToVarType : array[TCommonType] of TVarType =
(
varEmpty,
varany,
varError,
varInteger,
varDouble,
varBoolean,
varInt64,
varNull,
varOleStr,
varDate,
varCurrency,
varString
);
}
function MapToCommonType(const vType : TVarType) : TCommonType;
begin
case vType of
Low(TCommonVarType)..High(TCommonVarType):
Result := VarTypeToCommonType[vType];
varString:
Result:=ctString;
varAny:
Result:=ctAny;
else
Result:=ctError;
end;
end;
const
FindCmpCommonType : array[TCommonType, TCommonType] of TCommonType = (
{ ctEmpty ctAny ctError ctLongInt ctBoolean ctFloat ctDate ctCurrency ctInt64 ctNull ctWideStr ctString }
({ ctEmpty } ctEmpty, ctEmpty, ctError, ctEmpty, ctEmpty, {$ifndef FPUNONE}ctEmpty, ctEmpty, ctEmpty, {$endif}ctEmpty, ctEmpty, ctEmpty, ctEmpty ),
({ ctAny } ctEmpty, ctAny, ctError, ctAny, ctAny, {$ifndef FPUNONE}ctAny, ctAny, ctAny, {$endif}ctAny, ctAny, ctAny, ctAny ),
({ ctError } ctError, ctError, ctError, ctError, ctError, {$ifndef FPUNONE}ctError, ctError, ctError, {$endif}ctError, ctError, ctError, ctError ),
({ ctLongInt } ctEmpty, ctAny, ctError, ctLongInt, ctBoolean, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctInt64, ctNull, ctFloat, ctFloat ),
({ ctBoolean } ctEmpty, ctAny, ctError, ctLongInt, ctBoolean, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctInt64, ctNull, ctWideStr, ctString ),
{$ifndef FPUNONE}
({ ctFloat } ctEmpty, ctAny, ctError, ctFloat, ctFloat, ctFloat, ctDate, ctCurrency, ctFloat, ctNull, ctFloat, ctFloat ),
({ ctDate } ctEmpty, ctAny, ctError, ctDate, ctDate, ctDate, ctDate, ctDate, ctDate, ctNull, ctDate, ctDate ),
({ ctCurrency } ctEmpty, ctAny, ctError, ctCurrency, ctCurrency, ctCurrency,ctDate, ctCurrency, ctCurrency, ctNull, ctCurrency, ctCurrency ),
{$endif}
({ ctInt64 } ctEmpty, ctAny, ctError, ctInt64, ctInt64, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctInt64, ctNull, ctFloat, ctFloat ),
({ ctNull } ctEmpty, ctAny, ctError, ctNull, ctNull, {$ifndef FPUNONE}ctNull, ctNull, ctNull, {$endif}ctNull, ctNull, ctNull, ctNull ),
({ ctWideStr } ctEmpty, ctAny, ctError, ctFloat, ctWideStr, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctFloat, ctNull, ctWideStr, ctWideStr ),
({ ctString } ctEmpty, ctAny, ctError, ctFloat, ctString, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctFloat, ctNull, ctWideStr, ctString )
);
function DoVarCmpSimple (const Left, Right, Common: TCommonType) : ShortInt; inline;
begin
if Left = Common then
if Right = Common then
Result := 0
else
Result := -1
else
Result := 1;
end;
function DoVarCmpAny(const Left, Right: TVarData; const OpCode: TVarOp) : ShortInt;
begin
VarInvalidOp(Left.vType, Right.vType, OpCode);
Result:=0;
end;
function DoVarCmpLongInt(const Left, Right: LongInt): ShortInt; inline;
begin
if Left < Right then
Result := -1
else if Left > Right then
Result := 1
else
Result := 0;
end;
{$ifndef FPUNONE}
function DoVarCmpFloat(const Left, Right: Double; const OpCode: TVarOp): ShortInt;
begin
if Left = Right then
Result := 0
else if (OpCode in [opCmpEq, opCmpNe]) or (Left < Right) then
Result := -1
else
Result := 1;
end;
{$endif}
function DoVarCmpInt64(const Left, Right: Int64): ShortInt;
begin
if Left < Right then
Result := -1
else if Left > Right then
Result := 1
else
Result := 0;
end;
function DoVarCmpNull(const Left, Right: TCommonType; const OpCode: TVarOp) : ShortInt;
const
ResultMap: array [Boolean, opCmpEq..opCmpGe] of ShortInt =
( ( -1, 0, 0, 1, 0, -1 ), ( 0, -1, -1, -1, 1, 1 ) );
begin
if OpCode in [opCmpEq, opCmpNe] then
case NullEqualityRule of
ncrError: VarInvalidNullOp;
ncrStrict: Result := ResultMap[False, OpCode];
ncrLoose: Result := ResultMap[(Left = Right) xor (OpCode = opCmpNe), OpCode];
end
else
case NullMagnitudeRule of
ncrError: VarInvalidNullOp;
ncrStrict: Result := ResultMap[False, OpCode];
ncrLoose: Result := DoVarCmpSimple(Left, Right, ctNull);
end;
end;
function DoVarCmpCurr(const Left, Right: Currency): ShortInt;
begin
if Left < Right then
Result := -1
else if Left > Right then
Result := 1
else
Result := 0;
end;
function DoVarCmpWStrDirect(const Left, Right: Pointer; const OpCode: TVarOp): ShortInt; inline;
begin
{ we can do this without ever copying the string }
if OpCode in [opCmpEq, opCmpNe] then
if Length(WideString(Left)) <> Length(WideString(Right)) then
Exit(-1);
Result := WideCompareStr(
WideString(Left),
WideString(Right)
);
end;
function DoVarCmpWStr(const Left, Right: TVarData; const OpCode: TVarOp): ShortInt;
begin
{ keep the temps away from the main proc }
Result := DoVarCmpWStrDirect(Pointer(VariantToWideString(Left)),
Pointer(VariantToWideString(Right)), OpCode);
end;
function DoVarCmpLStrDirect(const Left, Right: Pointer; const OpCode: TVarOp): ShortInt; inline;
begin
{ we can do this without ever copying the string }
if OpCode in [opCmpEq, opCmpNe] then
if Length(AnsiString(Left)) <> Length(AnsiString(Right)) then
Exit(-1);
Result := CompareStr(
AnsiString(Left),
AnsiString(Right)
);
end;
function DoVarCmpLStr(const Left, Right: TVarData; const OpCode: TVarOp): ShortInt;
begin
{ keep the temps away from the main proc }
Result := DoVarCmpLStrDirect(Pointer(VariantToAnsiString(Left)),
Pointer(VariantToAnsiString(Right)), OpCode);
end;
function DoVarCmpComplex(const Left, Right: TVarData; const OpCode: TVarOp): ShortInt;
var Handler: TCustomVariantType;
CmpRes: boolean;
begin
if FindCustomVariantType(Left.vType, Handler) then
CmpRes := Handler.CompareOp(Left, Right, OpCode)
else if FindCustomVariantType(Right.vType, Handler) then
CmpRes := Handler.CompareOp(Left, Right, OpCode)
else
VarInvalidOp(Left.vType, Right.vType, OpCode);
case OpCode of
opCmpEq:
if CmpRes then
Result:=0
else
Result:=1;
opCmpNe:
if CmpRes then
Result:=1
else
Result:=0;
opCmpLt,
opCmpLe:
if CmpRes then
Result:=-1
else
Result:=1;
opCmpGt,
opCmpGe:
if CmpRes then
Result:=1
else
Result:=-1;
end;
end;
function DoVarCmp(const vl, vr : TVarData; const OpCode : TVarOp) : ShortInt;
var
lct: TCommonType;
rct: TCommonType;
begin
{ as the function in cvarutil.inc can handle varByRef correctly we simply
resolve the final type }
lct := MapToCommonType(VarTypeDeRef(vl));
rct := MapToCommonType(VarTypeDeRef(vr));
{$IFDEF DEBUG_VARIANTS}
if __DEBUG_VARIANTS then begin
WriteLn('DoVarCmp $', IntToHex(Cardinal(@vl),8), ' ', GetEnumName(TypeInfo(TVarOp), Ord(OpCode)) ,' $', IntToHex(Cardinal(@vr),8));
DumpVariant('DoVarCmp/vl', vl);
WriteLn('lct ', GetEnumName(TypeInfo(TCommonType), Ord(lct)));
DumpVariant('DoVarCmp/vr', vr);
WriteLn('rct ', GetEnumName(TypeInfo(TCommonType), Ord(rct)));
WriteLn('common ', GetEnumName(TypeInfo(TCommonType), Ord(FindCmpCommonType[lct, rct])));
end;
{$ENDIF}
case FindCmpCommonType[lct, rct] of
ctEmpty: Result := DoVarCmpSimple(lct, rct, ctEmpty);
ctAny: Result := DoVarCmpAny(vl, vr, OpCode);
ctLongInt: Result := DoVarCmpLongInt(VariantToLongInt(vl), VariantToLongInt(vr));
{$ifndef FPUNONE}
ctFloat: Result := DoVarCmpFloat(VariantToDouble(vl), VariantToDouble(vr), OpCode);
{$endif}
ctBoolean: Result := DoVarCmpLongInt(LongInt(VariantToBoolean(vl)), LongInt(VariantToBoolean(vr)));
ctInt64: Result := DoVarCmpInt64(VariantToInt64(vl), VariantToInt64(vr));
ctNull: Result := DoVarCmpNull(lct, rct, OpCode);
ctWideStr:
if (vl.vType = varOleStr) and (vr.vType = varOleStr) then
Result := DoVarCmpWStrDirect(Pointer(vl.vOleStr), Pointer(vr.vOleStr), OpCode)
else
Result := DoVarCmpWStr(vl, vr, OpCode);
{$ifndef FPUNONE}
ctDate: Result := DoVarCmpFloat(VariantToDate(vl), VariantToDate(vr), OpCode);
ctCurrency: Result := DoVarCmpCurr(VariantToCurrency(vl), VariantToCurrency(vr));
{$endif}
ctString:
if (vl.vType = varString) and (vr.vType = varString) then
Result := DoVarCmpLStrDirect(Pointer(vl.vString), Pointer(vr.vString), OpCode)
else
Result := DoVarCmpLStr(vl, vr, OpCode);
else
Result := DoVarCmpComplex(vl, vr, OpCode);
end;
end;
function syscmpop (const Left, Right : Variant; const OpCode : TVarOp) : Boolean;
var
CmpRes : ShortInt;
begin
CmpRes:=DoVarCmp(TVarData(Left),TVarData(Right),OpCode);
case OpCode of
opCmpEq:
Result:=CmpRes=0;
opCmpNe:
Result:=CmpRes<>0;
opCmpLt:
Result:=CmpRes<0;
opCmpLe:
Result:=CmpRes<=0;
opCmpGt:
Result:=CmpRes>0;
opCmpGe:
Result:=CmpRes>=0;
else
VarInvalidOp;
end;
end;
const
FindOpCommonType : array[TCommonType,TCommonType] of TCommonType = (
{ ctEmpty ctAny ctError ctLongInt ctBoolean ctFloat ctDate ctCurrency ctInt64 ctNull ctWideStr ctString }
({ ctEmpty } ctEmpty, ctAny, ctError, ctEmpty, ctEmpty, {$ifndef FPUNONE}ctEmpty, ctEmpty, ctEmpty, {$endif}ctEmpty, ctEmpty, ctEmpty, ctEmpty ),
({ ctAny } ctAny, ctAny, ctError, ctAny, ctAny, {$ifndef FPUNONE}ctAny, ctAny, ctAny, {$endif}ctAny, ctAny, ctAny, ctAny ),
({ ctError } ctError, ctError, ctError, ctError, ctError, {$ifndef FPUNONE}ctError, ctError, ctError, {$endif}ctError, ctError, ctError, ctError ),
({ ctLongInt } ctEmpty, ctAny, ctError, ctLongInt, ctBoolean, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctInt64, ctNull, ctFloat, ctFloat ),
({ ctBoolean } ctEmpty, ctAny, ctError, ctLongInt, ctBoolean, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctInt64, ctNull, ctBoolean, ctBoolean ),
{$ifndef FPUNONE}
({ ctFloat } ctEmpty, ctAny, ctError, ctFloat, ctFloat, ctFloat, ctDate, ctCurrency, ctFloat, ctNull, ctFloat, ctFloat ),
({ ctDate } ctEmpty, ctAny, ctError, ctDate, ctDate, ctDate, ctDate, ctDate, ctDate, ctNull, ctDate, ctDate ),
({ ctCurrency } ctEmpty, ctAny, ctError, ctCurrency, ctCurrency, ctCurrency, ctDate, ctCurrency, ctCurrency, ctNull, ctCurrency, ctCurrency ),
{$endif}
({ ctInt64 } ctEmpty, ctAny, ctError, ctInt64, ctInt64, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctInt64, ctNull, ctFloat, ctFloat ),
({ ctNull } ctEmpty, ctAny, ctError, ctNull, ctNull, {$ifndef FPUNONE}ctNull, ctNull, ctNull, {$endif}ctNull, ctNull, ctNull, ctNull ),
({ ctWideStr } ctEmpty, ctAny, ctError, ctFloat, ctBoolean, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctFloat, ctNull, ctWideStr, ctWideStr ),
({ ctString } ctEmpty, ctAny, ctError, ctFloat, ctBoolean, {$ifndef FPUNONE}ctFloat, ctDate, ctCurrency, {$endif}ctFloat, ctNull, ctWideStr, ctString )
);
procedure DoVarOpFloat(var vl :TVarData; const vr : TVarData; const OpCode : TVarOp);
{$ifndef FPUNONE}
var
l, r : Double;
begin
l := VariantToDouble(vl);
r := VariantToDouble(vr);
case OpCode of
opAdd : l := l + r;
opSubtract : l := l - r;
opMultiply : l := l * r;
opDivide : l := l / r;
opPower : l := l ** r;
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
DoVarClearIfComplex(vl);
vl.vType := varDouble;
vl.vDouble := l;
{$else}
begin
VarInvalidOp(vl.vType, vr.vType, OpCode);
{$endif}
end;
procedure DoVarOpAny(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
begin
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
procedure DoVarOpLongInt(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
var
l, r: LongInt;
begin
l := VariantToLongint(vl);
r := VariantToLongint(vr);
case OpCode of
opIntDivide : l := l div r;
opModulus : l := l mod r;
opShiftLeft : l := l shl r;
opShiftRight : l := l shr r;
opAnd : l := l and r;
opOr : l := l or r;
opXor : l := l xor r;
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
DoVarClearIfComplex(vl);
vl.vType := varInteger;
vl.vInteger := l;
end;
procedure DoVarOpInt64(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
var
l, r : Int64;
Overflow : Boolean;
begin
l := VariantToInt64(vl);
r := VariantToInt64(vr);
Overflow := False;
case OpCode of
{$push}
{$R+}{$Q+}
opAdd..opMultiply,opPower: try
case OpCode of
opAdd : l := l + r;
opSubtract : l := l - r;
opMultiply : l := l * r;
{$ifndef FPUNONE}
opPower : l := l ** r;
{$endif}
end;
except
on E: SysUtils.ERangeError do
Overflow := True;
on E: SysUtils.EIntOverflow do
Overflow := True;
end;
{$pop}
opIntDivide : l := l div r;
opModulus : l := l mod r;
opShiftLeft : l := l shl r;
opShiftRight : l := l shr r;
opAnd : l := l and r;
opOr : l := l or r;
opXor : l := l xor r;
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
if Overflow then
DoVarOpFloat(vl,vr,OpCode)
else begin
DoVarClearIfComplex(vl);
vl.vType := varInt64;
vl.vInt64 := l;
end;
end;
procedure DoVarOpInt64to32(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
begin
{ can't do this well without an efficent way to check for overflows,
let the Int64 version handle it and check the Result if we can downgrade it
to integer }
DoVarOpInt64(vl, vr, OpCode);
with vl do
if (vType = varInt64) and (vInt64 >= Low(LongInt)) and (vInt64 <= High(LongInt)) then begin
vInteger := vInt64;
vType := varInteger;
end;
end;
procedure DoVarOpBool(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
var
l,r: Boolean;
begin
l := VariantToBoolean(vl);
r := VariantToBoolean(vr);
case OpCode of
opAnd : l := l and r;
opOr : l := l or r;
opXor : l := l xor r;
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
DoVarClearIfComplex(vl);
vl.vType := varBoolean;
vl.vBoolean := l;
end;
procedure DoVarOpNull(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
begin
if (OpCode = opAnd) or (OpCode = opOr) then
if vl.vType = varNull then begin
if vr.vType = varNull then begin
{both null, do nothing }
end else begin
{Left null, Right not}
if OpCode = opAnd then begin
if not VariantToBoolean(vr) then
VarCopyProc(vl, vr);
end else {OpCode = opOr} begin
if VariantToBoolean(vr) then
VarCopyProc(vl, vr);
end;
end;
end else begin
if vr.vType = varNull then begin
{Right null, Left not}
if OpCode = opAnd then begin
if VariantToBoolean(vl) then begin
DoVarClearIfComplex(vl);
vl.vType := varNull;
end;
end else {OpCode = opOr} begin
if not VariantToBoolean(vl) then begin
DoVarClearIfComplex(vl);
vl.vType := varNull;
end;
end;
end else begin
{ both not null, shouldn't happen }
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
end
else begin
DoVarClearIfComplex(vl);
vl.vType := varNull;
end;
end;
procedure DoVarOpWStrCat(var vl : TVarData; const vr : TVarData);
var
ws: WideString;
begin
ws := VariantToWideString(vl) + VariantToWideString(vr);
DoVarClearIfComplex(vl);
vl.vType := varOleStr;
{ transfer the WideString without making a copy }
Pointer(vl.vOleStr) := Pointer(ws);
{ prevent the WideString from being freed, the reference has been transfered
from the local to the variant and will be correctly finalized when the
variant is finalized. }
Pointer(ws) := nil;
end;
procedure DoVarOpLStrCat(var vl: TVarData; const vr : TVarData);
var
s: AnsiString;
begin
s := VariantToAnsiString(vl) + VariantToAnsiString(vr);
DoVarClearIfComplex(vl);
vl.vType := varString;
{ transfer the AnsiString without making a copy }
Pointer(vl.vString) := Pointer(s);
{ prevent the AnsiString from being freed, the reference has been transfered
from the local to the variant and will be correctly finalized when the
variant is finalized. }
Pointer(s) := nil;
end;
procedure DoVarOpDate(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
{$ifndef FPUNONE}
var
l, r : TDateTime;
begin
l := VariantToDate(vl);
r := VariantToDate(vr);
case OpCode of
opAdd : l := l + r;
opSubtract : l := l - r;
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
DoVarClearIfComplex(vl);
vl.vType := varDate;
vl.vDate := l;
{$else}
begin
VarInvalidOp(vl.vType, vr.vType, OpCode);
{$endif}
end;
procedure DoVarOpCurr(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp; const lct, rct : TCommonType);
{$ifndef FPUNONE}
var
c : Currency;
d : Double;
begin
case OpCode of
opAdd:
c := VariantToCurrency(vl) + VariantToCurrency(vr);
opSubtract:
c := VariantToCurrency(vl) - VariantToCurrency(vr);
opMultiply:
if lct = ctCurrency then
if rct = ctCurrency then {both Currency}
c := VariantToCurrency(vl) * VariantToCurrency(vr)
else {Left Currency}
c := VariantToCurrency(vl) * VariantToDouble(vr)
else
if rct = ctCurrency then {rigth Currency}
c := VariantToDouble(vl) * VariantToCurrency(vr)
else {non Currency, error}
VarInvalidOp(vl.vType, vr.vType, OpCode);
opDivide:
if lct = ctCurrency then
if rct = ctCurrency then {both Currency}
c := VariantToCurrency(vl) / VariantToCurrency(vr)
else {Left Currency}
c := VariantToCurrency(vl) / VariantToDouble(vr)
else
if rct = ctCurrency then begin {rigth Currency}
d := VariantToCurrency(vl) / VariantToCurrency(vr);
DoVarClearIfComplex(vl);
vl.vType := varDouble;
vl.vDouble := d;
Exit;
end else {non Currency, error}
VarInvalidOp(vl.vType, vr.vType, OpCode);
opPower:
if lct = ctCurrency then
if rct = ctCurrency then {both Currency}
c := VariantToCurrency(vl) ** VariantToCurrency(vr)
else {Left Currency}
c := VariantToCurrency(vl) ** VariantToDouble(vr)
else
if rct = ctCurrency then {rigth Currency}
c := VariantToDouble(vl) ** VariantToCurrency(vr)
else {non Currency, error}
VarInvalidOp(vl.vType, vr.vType, OpCode);
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
DoVarClearIfComplex(vl);
vl.vType := varCurrency;
vl.vCurrency := c;
{$else}
begin
VarInvalidOp(vl.vType, vr.vType, OpCode);
{$endif}
end;
procedure DoVarOpComplex(var vl : TVarData; const vr : TVarData; const OpCode : TVarOp);
var Handler: TCustomVariantType;
begin
if FindCustomVariantType(vl.vType, Handler) then
Handler.BinaryOp(vl, vr, OpCode)
else if FindCustomVariantType(vr.vType, Handler) then
Handler.BinaryOp(vl, vr, OpCode)
else
VarInvalidOp(vl.vType, vr.vType, OpCode);
end;
procedure SysVarOp(var Left : Variant; const Right : Variant; OpCode : TVarOp);
var
lct: TCommonType;
rct: TCommonType;
{$IFDEF DEBUG_VARIANTS}
i: Integer;
{$ENDIF}
begin
{ as the function in cvarutil.inc can handle varByRef correctly we simply
resolve the final type }
lct := MapToCommonType(VarTypeDeRef(Left));
rct := MapToCommonType(VarTypeDeRef(Right));
{$IFDEF DEBUG_VARIANTS}
if __DEBUG_VARIANTS then begin
WriteLn('SysVarOp $', IntToHex(Cardinal(@TVarData(Left)),8), ' ', GetEnumName(TypeInfo(TVarOp), Ord(OpCode)) ,' $', IntToHex(Cardinal(@TVarData(Right)),8));
DumpVariant('SysVarOp/TVarData(Left)', TVarData(Left));
WriteLn('lct ', GetEnumName(TypeInfo(TCommonType), Ord(lct)));
DumpVariant('SysVarOp/TVarData(Right)', TVarData(Right));
WriteLn('rct ', GetEnumName(TypeInfo(TCommonType), Ord(rct)));
WriteLn('common ', GetEnumName(TypeInfo(TCommonType), Ord(FindOpCommonType[lct, rct])));
end;
{$ENDIF}
case FindOpCommonType[lct, rct] of
ctEmpty:
case OpCode of
opDivide:
Error(reZeroDivide);
opIntDivide, opModulus:
Error(reDivByZero);
else
DoVarClear(TVarData(Left));
end;
ctAny:
DoVarOpAny(TVarData(Left),TVarData(Right),OpCode);
ctLongInt:
case OpCode of
opAdd..opMultiply,opPower:
DoVarOpInt64to32(TVarData(Left),TVarData(Right),OpCode);
opDivide:
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode);
else
DoVarOpLongInt(TVarData(Left),TVarData(Right),OpCode);
end;
{$ifndef FPUNONE}
ctFloat:
if OpCode in [opAdd,opSubtract,opMultiply,opDivide] then
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode)
else
DoVarOpInt64to32(TVarData(Left),TVarData(Right),OpCode);
{$endif}
ctBoolean:
case OpCode of
opAdd..opMultiply, opPower:
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode);
opIntDivide..opShiftRight:
DoVarOpLongInt(TVarData(Left),TVarData(Right),OpCode);
opAnd..opXor:
DoVarOpBool(TVarData(Left),TVarData(Right),OpCode);
else
VarInvalidOp(TVarData(Left).vType, TVarData(Right).vType, OpCode);
end;
ctInt64:
if OpCode <> opDivide then
DoVarOpInt64(TVarData(Left),TVarData(Right),OpCode)
else
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode);
ctNull:
DoVarOpNull(TVarData(Left),TVarData(Right),OpCode);
ctWideStr:
case OpCode of
opAdd:
DoVarOpWStrCat(TVarData(Left),TVarData(Right));
opSubtract..opDivide,opPower:
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode);
opIntDivide..opXor:
DoVarOpInt64to32(TVarData(Left),TVarData(Right),OpCode);
else
VarInvalidOp(TVarData(Left).vType, TVarData(Right).vType, OpCode);
end;
{$ifndef FPUNONE}
ctDate:
case OpCode of
opAdd:
DoVarOpDate(TVarData(Left),TVarData(Right),OpCode);
opSubtract: begin
DoVarOpDate(TVarData(Left),TVarData(Right),OpCode);
if lct = rct then {both are date}
TVarData(Left).vType := varDouble;
end;
opMultiply, opDivide:
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode);
else
DoVarOpInt64to32(TVarData(Left),TVarData(Right),OpCode);
end;
ctCurrency:
if OpCode in [opAdd..opDivide, opPower] then
DoVarOpCurr(TVarData(Left),TVarData(Right),OpCode, lct, rct)
else
DoVarOpInt64to32(TVarData(Left),TVarData(Right),OpCode);
{$endif}
ctString:
case OpCode of
opAdd:
DoVarOpLStrCat(TVarData(Left),TVarData(Right));
opSubtract..opDivide,opPower:
DoVarOpFloat(TVarData(Left),TVarData(Right),OpCode);
opIntDivide..opXor:
DoVarOpInt64to32(TVarData(Left),TVarData(Right),OpCode);
else
VarInvalidOp(TVarData(Left).vType, TVarData(Right).vType, OpCode);
end;
else
{ more complex case }
DoVarOpComplex(TVarData(Left),TVarData(Right),OpCode);
end;
end;
procedure DoVarNegAny(var v: TVarData);
begin
VarInvalidOp(v.vType, opNegate);
end;
procedure DoVarNegComplex(var v: TVarData);
begin
{ custom variants? }
VarInvalidOp(v.vType, opNegate);
end;
procedure sysvarneg(var v: Variant);
const
BoolMap: array [Boolean] of SmallInt = (0, -1);
begin
with TVarData(v) do case vType of
varEmpty: begin
vSmallInt := 0;
vType := varSmallInt;
end;
varNull:;
varSmallint: vSmallInt := -vSmallInt;
varInteger: vInteger := -vInteger;
{$ifndef FPUNONE}
varSingle: vSingle := -vSingle;
varDouble: vDouble := -vDouble;
varCurrency: vCurrency := -vCurrency;
varDate: vDate := -vDate;
varOleStr: sysvarfromreal(v, -VariantToDouble(TVarData(v)));
{$else}
varOleStr: sysvarfromint64(v, -VariantToInt64(TVarData(v)));
{$endif}
varBoolean: begin
vSmallInt := BoolMap[vBoolean];
vType := varSmallInt;
end;
varShortInt: vShortInt := -vShortInt;
varByte: begin
vSmallInt := -vByte;
vType := varSmallInt;
end;
varWord: begin
vInteger := -vWord;
vType := varInteger;
end;
varLongWord:
if vLongWord and $80000000 <> 0 then begin
vInt64 := -vLongWord;
vType := varInt64;
end else begin
vInteger := -vLongWord;
vType := varInteger;
end;
varInt64: vInt64 := -vInt64;
varQWord: begin
if vQWord and $8000000000000000 <> 0 then
VarRangeCheckError(varQWord, varInt64);
vInt64 := -vQWord;
vType := varInt64;
end;
varVariant: v := -Variant(PVarData(vPointer)^);
else {with TVarData(v) do case vType of}
case vType of
{$ifndef FPUNONE}
varString: sysvarfromreal(v, -VariantToDouble(TVarData(v)));
{$else}
varString: sysvarfromint64(v, -VariantToInt64(TVarData(v)));
{$endif}
varAny: DoVarNegAny(TVarData(v));
else {case vType of}
if (vType and not varTypeMask) = varByRef then
case vType and varTypeMask of
varSmallInt: begin
vSmallInt := -PSmallInt(vPointer)^;
vType := varSmallInt;
end;
varInteger: begin
vInteger := -PInteger(vPointer)^;
vType := varInteger;
end;
{$ifndef FPUNONE}
varSingle: begin
vSingle := -PSingle(vPointer)^;
vType := varSingle;
end;
varDouble: begin
vDouble := -PDouble(vPointer)^;
vType := varDouble;
end;
varCurrency: begin
vCurrency := -PCurrency(vPointer)^;
vType := varCurrency;
end;
varDate: begin
vDate := -PDate(vPointer)^;
vType := varDate;
end;
varOleStr: sysvarfromreal(v, -VariantToDouble(TVarData(v)));
{$else}
varOleStr: sysvarfromint64(v, -VariantToInt64(TVarData(v)));
{$endif}
varBoolean: begin
vSmallInt := BoolMap[PWordBool(vPointer)^];
vType := varSmallInt;
end;
varShortInt: begin
vShortInt := -PShortInt(vPointer)^;
vType := varShortInt;
end;
varByte: begin
vSmallInt := -PByte(vPointer)^;
vType := varSmallInt;
end;
varWord: begin
vInteger := -PWord(vPointer)^;
vType := varInteger;
end;
varLongWord:
if PLongWord(vPointer)^ and $80000000 <> 0 then begin
vInt64 := -PLongWord(vPointer)^;
vType := varInt64;
end else begin
vInteger := -PLongWord(vPointer)^;
vType := varInteger;
end;
varInt64: begin
vInt64 := -PInt64(vPointer)^;
vType := varInt64;
end;
varQWord: begin
if PQWord(vPointer)^ and $8000000000000000 <> 0 then
VarRangeCheckError(varQWord, varInt64);
vInt64 := -PQWord(vPointer)^;
vType := varInt64;
end;
varVariant:
v := -Variant(PVarData(vPointer)^);
else {case vType and varTypeMask of}
DoVarNegComplex(TVarData(v));
end {case vType and varTypeMask of}
else {if (vType and not varTypeMask) = varByRef}
DoVarNegComplex(TVarData(v));
end; {case vType of}
end; {with TVarData(v) do case vType of}
end;
procedure DoVarNotAny(var v: TVarData);
begin
VarInvalidOp(v.vType, opNot);
end;
procedure DoVarNotOrdinal(var v: TVarData);
var
i: Int64;
begin
{ only called for types that do no require finalization }
i := VariantToInt64(v);
with v do
if (i < Low(Integer)) or (i > High(Integer)) then begin
vInt64 := not i;
vType := varInt64;
end else begin
vInteger := not Integer(i);
vType := varInteger;
end
end;
procedure DoVarNotWStr(var v: TVarData; const p: Pointer);
var
i: Int64;
e: Word;
b: Boolean;
begin
Val(WideString(p), i, e);
with v do
if e = 0 then begin
DoVarClearIfComplex(v);
if (i < Low(Integer)) or (i > High(Integer)) then begin
vInt64 := not i;
vType := varInt64;
end else begin
vInteger := not Integer(i);
vType := varInteger;
end
end else begin
if not TryStrToBool(WideString(p), b) then
VarInvalidOp(vType, opNot);
DoVarClearIfComplex(v);
vBoolean := not b;
vType := varBoolean;
end;
end;
procedure DoVarNotLStr(var v: TVarData; const p: Pointer);
var
i: Int64;
e: Word;
b: Boolean;
begin
Val(AnsiString(p), i, e);
with v do
if e = 0 then begin
DoVarClearIfComplex(v);
if (i < Low(Integer)) or (i > High(Integer)) then begin
vInt64 := not i;
vType := varInt64;
end else begin
vInteger := not Integer(i);
vType := varInteger;
end
end else begin
if not TryStrToBool(AnsiString(p), b) then
VarInvalidOp(v.vType, opNot);
DoVarClearIfComplex(v);
vBoolean := not b;
vType := varBoolean;
end;
end;
procedure DoVarNotComplex(var v: TVarData);
begin
{ custom variant support ?}
VarInvalidOp(v.vType, opNot);
end;
procedure sysvarnot(var v: Variant);
begin
with TVarData(v) do case vType of
varEmpty: v := -1;
varNull:;
varSmallint: vSmallInt := not vSmallInt;
varInteger: vInteger := not vInteger;
{$ifndef FPUNONE}
varSingle,
varDouble,
varCurrency,
varDate: DoVarNotOrdinal(TVarData(v));
{$endif}
varOleStr: DoVarNotWStr(TVarData(v), Pointer(vOleStr));
varBoolean: vBoolean := not vBoolean;
varShortInt: vShortInt := not vShortInt;
varByte: vByte := not vByte;
varWord: vWord := not vWord;
varLongWord: vLongWord := not vLongWord;
varInt64: vInt64 := not vInt64;
varQWord: vQWord := not vQWord;
varVariant: v := not Variant(PVarData(vPointer)^);
else {with TVarData(v) do case vType of}
case vType of
varString: DoVarNotLStr(TVarData(v), Pointer(vString));
varAny: DoVarNotAny(TVarData(v));
else {case vType of}
if (vType and not varTypeMask) = varByRef then
case vType and varTypeMask of
varSmallInt: begin
vSmallInt := not PSmallInt(vPointer)^;
vType := varSmallInt;
end;
varInteger: begin
vInteger := not PInteger(vPointer)^;
vType := varInteger;
end;
{$ifndef FPUNONE}
varSingle,
varDouble,
varCurrency,
varDate: DoVarNotOrdinal(TVarData(v));
{$endif}
varOleStr: DoVarNotWStr(TVarData(v), PPointer(vPointer)^);
varBoolean: begin
vBoolean := not PWordBool(vPointer)^;
vType := varBoolean;
end;
varShortInt: begin
vShortInt := not PShortInt(vPointer)^;
vType := varShortInt;
end;
varByte: begin
vByte := not PByte(vPointer)^;
vType := varByte;
end;
varWord: begin
vWord := not PWord(vPointer)^;
vType := varWord;
end;
varLongWord: begin
vLongWord := not PLongWord(vPointer)^;
vType := varLongWord;
end;
varInt64: begin
vInt64 := not PInt64(vPointer)^;
vType := varInt64;
end;
varQWord: begin
vQWord := not PQWord(vPointer)^;
vType := varQWord;
end;
varVariant:
v := not Variant(PVarData(vPointer)^);
else {case vType and varTypeMask of}
DoVarNotComplex(TVarData(v));
end {case vType and varTypeMask of}
else {if (vType and not varTypeMask) = varByRef}
DoVarNotComplex(TVarData(v));
end; {case vType of}
end; {with TVarData(v) do case vType of}
end;
{
This procedure is needed to destroy and clear non-standard variant type array elements,
which can not be handled by SafeArrayDestroy.
If array element type is varVariant, then clear each element individually before
calling VariantClear for array. VariantClear just calls SafeArrayDestroy.
}
procedure DoVarClearArray(var VArray: TVarData);
var
arr: pvararray;
i, cnt: cardinal;
data: pvardata;
begin
if VArray.vtype and varTypeMask = varVariant then begin
if WordBool(VArray.vType and varByRef) then
arr:=PVarArray(VArray.vPointer^)
else
arr:=VArray.vArray;
VarResultCheck(SafeArrayAccessData(arr, data));
try
{ Calculation total number of elements in the array }
cnt:=1;
{$push}
{ arr^.bounds[] is an array[0..0] }
{$r-}
for i:=0 to arr^.dimcount - 1 do
cnt:=cnt*cardinal(arr^.Bounds[i].ElementCount);
{$pop}
{ Clearing each element }
for i:=1 to cnt do begin
DoVarClear(data^);
Inc(pointer(data), arr^.ElementSize);
end;
finally
VarResultCheck(SafeArrayUnaccessData(arr));
end;
end;
VariantClear(VArray);
end;
procedure DoVarClearComplex(var v : TVarData);
var
Handler : TCustomVariantType;
begin
with v do
if vType < varInt64 then
VarResultCheck(VariantClear(v))
else if vType = varString then
begin
AnsiString(vString) := '';
vType := varEmpty;
end
else if vType = varUString then
begin
UnicodeString(vString) := '';
vType := varEmpty;
end
else if vType = varAny then
ClearAnyProc(v)
else if vType and varArray <> 0 then
DoVarClearArray(v)
else if FindCustomVariantType(vType, Handler) then
Handler.Clear(v)
else begin
{ ignore errors, if the OS doesn't know how to free it, we don't either }
VariantClear(v);
vType := varEmpty;
end;
end;
type
TVarArrayCopyCallback = procedure(var aDest: TVarData; const aSource: TVarData);
procedure DoVarCopyArray(var aDest: TVarData; const aSource: TVarData; aCallback: TVarArrayCopyCallback);
var
SourceArray : PVarArray;
SourcePtr : Pointer;
DestArray : PVarArray;
DestPtr : Pointer;
Bounds : array[0..63] of TVarArrayBound;
Iterator : TVariantArrayIterator;
Dims : Integer;
HighBound : Integer;
i : Integer;
begin
with aSource do begin
if vType and varArray = 0 then
VarResultCheck(VAR_INVALIDARG);
if (vType and varTypeMask) = varVariant then begin
if (vType and varByRef) <> 0 then
SourceArray := PVarArray(vPointer^)
else
SourceArray := vArray;
Dims := SourceArray^.DimCount;
for i := 0 to Pred(Dims) do
with Bounds[i] do begin
VarResultCheck(SafeArrayGetLBound(SourceArray, Succ(i), LowBound));
VarResultCheck(SafeArrayGetUBound(SourceArray, Succ(i), HighBound));
ElementCount := HighBound - LowBound + 1;
end;
DestArray := SafeArrayCreate(varVariant, Dims, PVarArrayBoundArray(@Bounds)^);
if not Assigned(DestArray) then
VarArrayCreateError;
DoVarClearIfComplex(aDest);
with aDest do begin
vType := varVariant or varArray;
vArray := DestArray;
end;
Iterator.Init(Dims, @Bounds);
try
if not(Iterator.AtEnd) then
repeat
VarResultCheck(SafeArrayPtrOfIndex(SourceArray, Iterator.Coords, SourcePtr));
VarResultCheck(SafeArrayPtrOfIndex(DestArray, Iterator.Coords, DestPtr));
aCallback(PVarData(DestPtr)^, PVarData(SourcePtr)^);
until not Iterator.Next;
finally
Iterator.Done;
end;
end else
VarResultCheck(VariantCopy(aDest, aSource));
end;
end;
procedure DoVarCopyComplex(var Dest: TVarData; const Source: TVarData);
var
Handler: TCustomVariantType;
begin
DoVarClearIfComplex(Dest);
with Source do
if vType < varInt64 then
VarResultCheck(VariantCopy(Dest, Source))
else if vType = varString then begin
Dest.vType := varString;
Dest.vString := nil;
AnsiString(Dest.vString) := AnsiString(vString);
end else if vType = varAny then begin
Dest := Source;
RefAnyProc(Dest);
end else if vType and varArray <> 0 then
DoVarCopyArray(Dest, Source, @DoVarCopy)
else if (vType and varByRef <> 0) and (vType xor varByRef = varString) then
Dest := Source
else if FindCustomVariantType(vType, Handler) then
Handler.Copy(Dest, Source, False)
else
VarResultCheck(VariantCopy(Dest, Source));
end;
procedure DoVarCopy(var Dest : TVarData; const Source : TVarData);
begin
if @Dest <> @Source then
if (Source.vType and varComplexType) = 0 then begin
DoVarClearIfComplex(Dest);
Dest := Source;
end else
DoVarCopyComplex(Dest, Source);
end;
procedure sysvarcopy (var Dest : Variant; const Source : Variant);
begin
DoVarCopy(TVarData(Dest),TVarData(Source));
end;
procedure DoVarAddRef(var v : TVarData); inline;
var
Dummy : TVarData;
begin
Dummy := v;
v.vType := varEmpty;
DoVarCopy(v, Dummy);
end;
procedure sysvaraddref(var v : Variant);
begin
DoVarAddRef(TVarData(v));
end;
procedure DoVarCastWStr(var aDest : TVarData; const aSource : TVarData);
begin
SysVarFromWStr(Variant(aDest), VariantToWideString(aSource));
end;
procedure DoVarCastLStr(var aDest : TVarData; const aSource : TVarData);
begin
SysVarFromLStr(Variant(aDest), VariantToAnsiString(aSource));
end;
procedure DoVarCastDispatch(var aDest : TVarData; const aSource : TVarData);
var
Disp: IDispatch;
begin
SysVarToDisp(Disp, Variant(aSource));
SysVarFromDisp(Variant(aDest), Disp);
end;
procedure DoVarCastInterface(var aDest : TVarData; const aSource : TVarData);
var
Intf: IInterface;
begin
SysVarToIntf(Intf, Variant(aSource));
SysVarFromIntf(Variant(aDest), Intf);
end;
procedure DoVarCastAny(var aDest : TVarData; const aSource : TVarData; aVarType : LongInt);
begin
VarCastError(aSource.vType, aVarType)
end;
procedure DoVarCastFallback(var aDest : TVarData; const aSource : TVarData; aVarType : LongInt);
begin
if aSource.vType and varTypeMask >= varInt64 then begin
DoVarCast(aDest, aSource, varOleStr);
VarResultCheck(VariantChangeTypeEx(aDest, aDest, VAR_LOCALE_USER_DEFAULT,
0, aVarType), aSource.vType, aVarType);
end else if aVarType and varTypeMask < varInt64 then
VarResultCheck(VariantChangeTypeEx(aDest, aSource, VAR_LOCALE_USER_DEFAULT,
0, aVarType), aSource.vType, aVarType)
else
VarCastError(aSource.vType, aVarType);
end;
procedure DoVarCastComplex(var aDest : TVarData; const aSource : TVarData; aVarType : LongInt);
var
Handler: TCustomVariantType;
begin
if aSource.vType = varAny then
DoVarCastAny(aDest, aSource, aVarType)
else if FindCustomVariantType(aSource.vType, Handler) then
Handler.CastTo(aDest, aSource, aVarType)
else if FindCustomVariantType(aVarType, Handler) then
Handler.Cast(aDest, aSource)
else
DoVarCastFallback(aDest, aSource, aVarType);
end;
procedure DoVarCast(var aDest : TVarData; const aSource : TVarData; aVarType : LongInt);
begin
with aSource do
if vType = aVarType then
DoVarCopy(aDest, aSource)
else begin
if (vType = varNull) and NullStrictConvert then
VarCastError(varNull, aVarType);
case aVarType of
varEmpty, varNull: begin
DoVarClearIfComplex(aDest);
aDest.vType := aVarType;
end;
varSmallInt: SysVarFromInt(Variant(aDest), VariantToSmallInt(aSource), -2);
varInteger: SysVarFromInt(Variant(aDest), VariantToLongInt(aSource), -4);
{$ifndef FPUNONE}
varSingle: SysVarFromSingle(Variant(aDest), VariantToSingle(aSource));
varDouble: SysVarFromDouble(Variant(aDest), VariantToDouble(aSource));
varCurrency: SysVarFromCurr(Variant(aDest), VariantToCurrency(aSource));
varDate: SysVarFromTDateTime(Variant(aDest), VariantToDate(aSource));
{$endif}
varOleStr: DoVarCastWStr(aDest, aSource);
varBoolean: SysVarFromBool(Variant(aDest), VariantToBoolean(aSource));
varShortInt: SysVarFromInt(Variant(aDest), VariantToShortInt(aSource), -1);
varByte: SysVarFromInt(Variant(aDest), VariantToByte(aSource), 1);
varWord: SysVarFromInt(Variant(aDest), VariantToLongInt(aSource), 2);
varLongWord: SysVarFromInt(Variant(aDest), Integer(VariantToCardinal(aSource)), 4);
varInt64: SysVarFromInt64(Variant(aDest), VariantToInt64(aSource));
varQWord: SysVarFromWord64(Variant(aDest), VariantToQWord(aSource));
varDispatch: DoVarCastDispatch(aDest, aSource);
varUnknown: DoVarCastInterface(aDest, aSource);
else
case aVarType of
varString: DoVarCastLStr(aDest, aSource);
varAny: VarCastError(vType, varAny);
else
DoVarCastComplex(aDest, aSource, aVarType);
end;
end;
end;
end;
procedure sysvarcast (var aDest : Variant; const aSource : Variant; aVarType : LongInt);
begin
DoVarCast(TVarData(aDest), TVarData(aSource), aVarType);
end;
procedure sysvarfromdynarray(var Dest : Variant; const Source : Pointer; TypeInfo: Pointer);
begin
DynArrayToVariant(Dest,Source,TypeInfo);
if VarIsEmpty(Dest) then
VarCastError;
end;
procedure sysolevarfrompstr(var Dest : olevariant; const Source : ShortString);
begin
sysvarfromwstr(Variant(TVarData(Dest)), Source);
end;
procedure sysolevarfromlstr(var Dest : olevariant; const Source : AnsiString);
begin
sysvarfromwstr(Variant(TVarData(Dest)), Source);
end;
procedure DoOleVarFromAny(var aDest : TVarData; const aSource : TVarData);
begin
VarCastErrorOle(aSource.vType);
end;
procedure DoOleVarFromVar(var aDest : TVarData; const aSource : TVarData);
var
Handler: TCustomVariantType;
begin
with aSource do
if vType = varByRef or varVariant then
DoOleVarFromVar(aDest, PVarData(vPointer)^)
else begin
case vType of
varShortInt, varByte, varWord:
DoVarCast(aDest, aSource, varInteger);
varLongWord:
if vLongWord and $80000000 = 0 then
DoVarCast(aDest, aSource, varInteger)
else
{$ifndef FPUNONE}
if OleVariantInt64AsDouble then
DoVarCast(aDest, aSource, varDouble)
else
{$endif}
DoVarCast(aDest, aSource, varInt64);
varInt64:
if (vInt64 < Low(Integer)) or (vInt64 > High(Integer)) then
{$ifndef FPUNONE}
if OleVariantInt64AsDouble then
DoVarCast(aDest, aSource, varDouble)
else
{$endif}
DoVarCast(aDest, aSource, varInt64)
else
DoVarCast(aDest, aSource, varInteger);
varQWord:
if vQWord > High(Integer) then
{$ifndef FPUNONE}
if OleVariantInt64AsDouble or (vQWord and $8000000000000000 <> 0) then
DoVarCast(aDest, aSource, varDouble)
else
{$endif}
DoVarCast(aDest, aSource, varInt64)
else
DoVarCast(aDest, aSource, varInteger);
varString:
DoVarCast(aDest, aSource, varOleStr);
varAny:
DoOleVarFromAny(aDest, aSource);
else
if (vType and varArray) <> 0 then
DoVarCopyArray(aDest, aSource, @DoOleVarFromVar)
else if (vType and varTypeMask) < CFirstUserType then
DoVarCopy(aDest, aSource)
else if FindCustomVariantType(vType, Handler) then
Handler.CastToOle(aDest, aSource)
else
VarCastErrorOle(vType);
end;
end;
end;
procedure sysolevarfromvar(var aDest : OleVariant; const aSource : Variant);
begin
DoOleVarFromVar(TVarData(aDest), TVarData(aSource));
end;
procedure sysolevarfromint(var Dest : olevariant; const Source : LongInt; const range : ShortInt);
begin
DoVarClearIfComplex(TVarData(Dest));
with TVarData(Dest) do begin
vInteger := Source;
vType := varInteger;
end;
end;
procedure DoVarCastOle(var aDest: TVarData; const aSource: TVarData; aVarType: LongInt);
var
Handler: TCustomVariantType;
begin
with aSource do
if vType = varByRef or varVariant then
DoVarCastOle(aDest, PVarData(VPointer)^, aVarType)
else
if (aVarType = varString) or (aVarType = varAny) then
VarCastError(vType, aVarType)
else if FindCustomVariantType(vType, Handler) then
Handler.CastTo(aDest, aSource, aVarType)
else
DoVarCast(aDest, aSource, aVarType);
end;
procedure sysvarcastole(var Dest : Variant; const Source : Variant; aVarType : LongInt);
begin
DoVarCastOle(TVarData(Dest), TVarData(Source), aVarType);
end;
procedure sysdispinvoke(Dest : PVarData; const Source : TVarData;calldesc : pcalldesc;params : Pointer);cdecl;
var
temp : TVarData;
tempp : ^TVarData;
customvarianttype : TCustomVariantType;
begin
if Source.vType=(varByRef or varVariant) then
sysdispinvoke(Dest,PVarData(Source.vPointer)^,calldesc,params)
else
begin
try
{ get a defined Result }
if not(assigned(Dest)) then
tempp:=nil
else
begin
fillchar(temp,SizeOf(temp),0);
tempp:=@temp;
end;
case Source.vType of
varDispatch,
varAny,
varUnknown,
varDispatch or varByRef,
varAny or varByRef,
varUnknown or varByRef:
VarDispProc(pvariant(tempp),Variant(Source),calldesc,params);
else
begin
if FindCustomVariantType(Source.vType,customvarianttype) then
customvarianttype.DispInvoke(tempp,Source,calldesc,params)
else
VarInvalidOp;
end;
end;
finally
if assigned(tempp) then
begin
DoVarCopy(Dest^,tempp^);
DoVarClear(temp);
end;
end;
end;
end;
procedure sysvararrayredim(var a : Variant;highbound : SizeInt);
var
src : TVarData;
p : pvararray;
newbounds : tvararraybound;
begin
src:=TVarData(a);
{ get final Variant }
while src.vType=varByRef or varVariant do
src:=TVarData(src.vPointer^);
if (src.vType and varArray)<>0 then
begin
{ get Pointer to the array }
if (src.vType and varByRef)<>0 then
p:=pvararray(src.vPointer^)
else
p:=src.vArray;
{$push}
{$r-}
if highbound<p^.Bounds[p^.dimcount-1].LowBound-1 then
VarInvalidArgError;
newbounds.LowBound:=p^.Bounds[p^.dimcount-1].LowBound;
{$pop}
newbounds.ElementCount:=highbound-newbounds.LowBound+1;
VarResultCheck(SafeArrayRedim(p,newbounds));
end
else
VarInvalidArgError(src.vType);
end;
function getfinalvartype(const v : TVarData) : TVarType;{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
var
p: PVarData;
begin
p := @v;
while p^.vType = varByRef or varVariant do
p := PVarData(p^.vPointer);
Result := p^.vType;
end;
function sysvararrayget(const a : Variant;indexcount : SizeInt;indices : plongint) : Variant;cdecl;
var
src : TVarData;
p : pvararray;
arraysrc : pvariant;
arrayelementtype : TVarType;
begin
src:=TVarData(a);
{ get final Variant }
while src.vType=varByRef or varVariant do
src:=TVarData(src.vPointer^);
if (src.vType and varArray)<>0 then
begin
{ get Pointer to the array }
if (src.vType and varByRef)<>0 then
p:=pvararray(src.vPointer^)
else
p:=src.vArray;
{ number of indices ok? }
if p^.DimCount<>indexcount then
VarInvalidArgError;
arrayelementtype:=src.vType and varTypeMask;
if arrayelementtype=varVariant then
begin
VarResultCheck(SafeArrayPtrOfIndex(p,PVarArrayCoorArray(indices),arraysrc));
Result:=arraysrc^;
end
else
begin
TVarData(Result).vType:=arrayelementtype;
VarResultCheck(SafeArrayGetElement(p,PVarArrayCoorArray(indices),@TVarData(Result).vPointer));
end;
end
else
VarInvalidArgError(src.vType);
end;
procedure sysvararrayput(var a : Variant; const value : Variant;indexcount : SizeInt;indices : plongint);cdecl;
var
Dest : TVarData;
p : pvararray;
arraydest : pvariant;
valuevtype,
arrayelementtype : TVarType;
tempvar : Variant;
begin
Dest:=TVarData(a);
{ get final Variant }
while Dest.vType=varByRef or varVariant do
Dest:=TVarData(Dest.vPointer^);
valuevtype:=getfinalvartype(TVarData(value));
if not(VarTypeIsValidElementType(valuevtype)) and
{ varString isn't a valid varArray type but it is converted
later }
(valuevtype<>varString) then
VarCastError(valuevtype,Dest.vType);
if (Dest.vType and varArray)<>0 then
begin
{ get Pointer to the array }
if (Dest.vType and varByRef)<>0 then
p:=pvararray(Dest.vPointer^)
else
p:=Dest.vArray;
{ number of indices ok? }
if p^.DimCount<>indexcount then
VarInvalidArgError;
arrayelementtype:=Dest.vType and varTypeMask;
if arrayelementtype=varVariant then
begin
VarResultCheck(SafeArrayPtrOfIndex(p,PVarArrayCoorArray(indices),arraydest));
{ we can't store ansistrings in Variant arrays so we convert the string to
an olestring }
if valuevtype=varString then
begin
tempvar:=VarToWideStr(value);
arraydest^:=tempvar;
end
else
arraydest^:=value;
end
else
begin
VarCast(tempvar,value,arrayelementtype);
if arrayelementtype in [varOleStr,varDispatch,varUnknown] then
VarResultCheck(SafeArrayPutElement(p,PVarArrayCoorArray(indices),TVarData(tempvar).vPointer))
else
VarResultCheck(SafeArrayPutElement(p,PVarArrayCoorArray(indices),@TVarData(tempvar).vPointer));
end;
end
else
VarInvalidArgError(Dest.vType);
end;
{ import from system unit }
Procedure fpc_Write_Text_AnsiStr (Len : LongInt; Var f : Text; S : RawByteString); external name 'FPC_WRITE_TEXT_ANSISTR';
function syswritevariant(var t : text; const v : Variant;width : LongInt) : Pointer;
var
s : AnsiString;
variantmanager : tvariantmanager;
begin
GetVariantManager(variantmanager);
variantmanager.vartolstr(s,v);
fpc_write_text_ansistr(width,t,s);
Result:=nil; // Pointer to what should be returned?
end;
function syswrite0Variant(var t : text; const v : Variant) : Pointer;
var
s : AnsiString;
variantmanager : tvariantmanager;
begin
getVariantManager(variantmanager);
variantmanager.vartolstr(s,v);
fpc_write_text_ansistr(-1,t,s);
Result:=nil; // Pointer to what should be returned?
end;
Const
SysVariantManager : TVariantManager = (
vartoint : @sysvartoint;
vartoint64 : @sysvartoint64;
vartoword64 : @sysvartoword64;
vartobool : @sysvartobool;
{$ifndef FPUNONE}
vartoreal : @sysvartoreal;
vartotdatetime: @sysvartotdatetime;
{$endif}
vartocurr : @sysvartocurr;
vartopstr : @sysvartopstr;
vartolstr : @sysvartolstr;
vartowstr : @sysvartowstr;
vartointf : @sysvartointf;
vartodisp : @sysvartodisp;
vartodynarray : @sysvartodynarray;
varfrombool : @sysvarfromBool;
varfromint : @sysvarfromint;
varfromint64 : @sysvarfromint64;
varfromword64 : @sysvarfromword64;
{$ifndef FPUNONE}
varfromreal : @sysvarfromreal;
varfromtdatetime: @sysvarfromtdatetime;
{$endif}
varfromcurr : @sysvarfromcurr;
varfrompstr : @sysvarfrompstr;
varfromlstr : @sysvarfromlstr;
varfromwstr : @sysvarfromwstr;
varfromintf : @sysvarfromintf;
varfromdisp : @sysvarfromdisp;
varfromdynarray: @sysvarfromdynarray;
olevarfrompstr: @sysolevarfrompstr;
olevarfromlstr: @sysolevarfromlstr;
olevarfromvar : @sysolevarfromvar;
olevarfromint : @sysolevarfromint;
varop : @SysVarOp;
cmpop : @syscmpop;
varneg : @sysvarneg;
varnot : @sysvarnot;
varinit : @sysvarinit;
varclear : @sysvarclear;
varaddref : @sysvaraddref;
varcopy : @sysvarcopy;
varcast : @sysvarcast;
varcastole : @sysvarcastole;
dispinvoke : @sysdispinvoke;
vararrayredim : @sysvararrayredim;
vararrayget : @sysvararrayget;
vararrayput : @sysvararrayput;
writevariant : @syswritevariant;
write0Variant : @syswrite0variant;
);
Var
PrevVariantManager : TVariantManager;
Procedure SetSysVariantManager;
begin
GetVariantManager(PrevVariantManager);
SetVariantManager(SysVariantManager);
end;
Procedure UnsetSysVariantManager;
begin
SetVariantManager(PrevVariantManager);
end;
{ ---------------------------------------------------------------------
Variant support procedures and functions
---------------------------------------------------------------------}
function VarType(const V: Variant): TVarType;
begin
Result:=TVarData(V).vType;
end;
function VarTypeDeRef(const V: Variant): TVarType;
var
p: PVarData;
begin
p := @TVarData(V);
Result := p^.vType and not varByRef;
while Result = varVariant do begin
p := p^.vPointer;
if not Assigned(p) then
VarBadTypeError;
Result := p^.vType and not varByRef;
end;
end;
function VarTypeDeRef(const V: TVarData): TVarType;
begin
Result := VarTypeDeRef(Variant(v));
end;
function VarAsType(const V: Variant; aVarType: TVarType): Variant;
begin
sysvarcast(Result,V,aVarType);
end;
function VarIsType(const V: Variant; aVarType: TVarType): Boolean; overload;
begin
Result:=((TVarData(V).vType and varTypeMask)=aVarType);
end;
function VarIsType(const V: Variant; const AVarTypes: array of TVarType): Boolean; overload;
Var
I : Integer;
begin
I:=Low(AVarTypes);
Result:=False;
While Not Result and (I<=High(AVarTypes)) do
begin
Result:=((TVarData(V).vType and varTypeMask)=AVarTypes[I]);
inc(i);
end;
end;
function VarIsByRef(const V: Variant): Boolean;
begin
Result:=(TVarData(V).vType and varByRef)<>0;
end;
function VarIsEmpty(const V: Variant): Boolean;
begin
Result:=TVarData(V).vType=varEmpty;
end;
procedure VarCheckEmpty(const V: Variant);
begin
If VarIsEmpty(V) Then
VariantError(SErrVarIsEmpty);
end;
procedure VarClear(var V: Variant);{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
begin
sysvarclear(v);
end;
procedure VarClear(var V: OleVariant);{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
begin
{ strange casting using TVarData to avoid call of helper olevariant->Variant }
sysvarclear(Variant(TVarData(v)));
end;
function VarIsNull(const V: Variant): Boolean;
begin
Result:=TVarData(V).vType=varNull;
end;
function VarIsClear(const V: Variant): Boolean;
Var
VT : TVarType;
CustomType: TCustomVariantType;
begin
VT:=TVarData(V).vType and varTypeMask;
if VT<CFirstUserType then
Result:=(VT=varEmpty) or
(((VT=varDispatch) or (VT=varUnknown))
and (TVarData(V).vDispatch=Nil))
else
Result:=FindCustomVariantType(VT,CustomType) and CustomType.IsClear(TVarData(V));
end;
function VarIsCustom(const V: Variant): Boolean;
begin
Result:=TVarData(V).vType>=CFirstUserType;
end;
function VarIsOrdinal(const V: Variant): Boolean;
begin
Result:=(TVarData(V).vType and varTypeMask) in OrdinalVarTypes;
end;
function VarIsFloat(const V: Variant): Boolean;
begin
Result:=(TVarData(V).vType and varTypeMask) in FloatVarTypes;
end;
function VarIsNumeric(const V: Variant): Boolean;
begin
Result:=(TVarData(V).vType and varTypeMask) in (OrdinalVarTypes + FloatVarTypes);
end;
function VarIsStr(const V: Variant): Boolean;
begin
case (TVarData(V).vType and varTypeMask) of
varOleStr,
varUString,
varString :
Result:=True;
else
Result:=False;
end;
end;
function VarToStr(const V: Variant): string;
begin
Result:=VarToStrDef(V,'');
end;
function VarToStrDef(const V: Variant; const ADefault: string): string;
begin
If TVarData(V).vType<>varNull then
Result:=V
else
Result:=ADefault;
end;
function VarToWideStr(const V: Variant): WideString;
begin
Result:=VarToWideStrDef(V,'');
end;
function VarToWideStrDef(const V: Variant; const ADefault: WideString): WideString;
begin
If TVarData(V).vType<>varNull then
Result:=V
else
Result:=ADefault;
end;
function VarToUnicodeStr(const V: Variant): UnicodeString;
begin
Result:=VarToUnicodeStrDef(V,'');
end;
function VarToUnicodeStrDef(const V: Variant; const ADefault: UnicodeString): UnicodeString;
begin
If TVarData(V).vType<>varNull then
Result:=V
else
Result:=ADefault;
end;
{$ifndef FPUNONE}
function VarToDateTime(const V: Variant): TDateTime;
begin
Result:=VariantToDate(TVarData(V));
end;
function VarFromDateTime(const DateTime: TDateTime): Variant;
begin
SysVarClear(Result);
with TVarData(Result) do
begin
vType:=varDate;
vdate:=DateTime;
end;
end;
{$endif}
function VarInRange(const AValue, AMin, AMax: Variant): Boolean;
begin
Result:=(AValue>=AMin) and (AValue<=AMax);
end;
function VarEnsureRange(const AValue, AMin, AMax: Variant): Variant;
begin
If Result>AMAx then
Result:=AMax
else If Result<AMin Then
Result:=AMin
else
Result:=AValue;
end;
function VarSameValue(const A, B: Variant): Boolean;
var
v1,v2 : TVarData;
begin
v1:=FindVarData(a)^;
v2:=FindVarData(b)^;
if v1.vType in [varEmpty,varNull] then
Result:=v1.vType=v2.vType
else if v2.vType in [varEmpty,varNull] then
Result:=False
else
Result:=A=B;
end;
function VarCompareValue(const A, B: Variant): TVariantRelationship;
var
v1,v2 : TVarData;
begin
Result:=vrNotEqual;
v1:=FindVarData(a)^;
v2:=FindVarData(b)^;
if (v1.vType in [varEmpty,varNull]) and (v1.vType=v2.vType) then
Result:=vrEqual
else if not(v2.vType in [varEmpty,varNull]) and
not(v1.vType in [varEmpty,varNull]) then
begin
if a=b then
Result:=vrEqual
else if a>b then
Result:=vrGreaterThan
else
Result:=vrLessThan;
end;
end;
function VarIsEmptyParam(const V: Variant): Boolean;
begin
Result:=(TVarData(V).vType = varError) and
(TVarData(V).vError=VAR_PARAMNOTFOUND);
end;
procedure SetClearVarToEmptyParam(var V: TVarData);
begin
VariantClear(V);
V.vType := varError;
V.vError := VAR_PARAMNOTFOUND;
end;
function VarIsError(const V: Variant; out aResult: HRESULT): Boolean;
begin
Result := TVarData(V).vType = varError;
if Result then
aResult := TVarData(v).vError;
end;
function VarIsError(const V: Variant): Boolean;
begin
Result := TVarData(V).vType = varError;
end;
function VarAsError(AResult: HRESULT): Variant;
begin
TVarData(Result).vType:=varError;
TVarData(Result).vError:=AResult;
end;
function VarSupports(const V: Variant; const IID: TGUID; out Intf): Boolean;
begin
case TVarData(v).vType of
varUnknown:
Result := Assigned(TVarData(v).vUnknown) and (IInterface(TVarData(v).vUnknown).QueryInterface(IID, Intf) = S_OK);
varUnknown or varByRef:
Result := Assigned(TVarData(v).vPointer) and Assigned(pointer(TVarData(v).vPointer^)) and (IInterface(TVarData(v).vPointer^).QueryInterface(IID, Intf) = S_OK);
varDispatch:
Result := Assigned(TVarData(v).vDispatch) and (IInterface(TVarData(v).vDispatch).QueryInterface(IID, Intf) = S_OK);
varDispatch or varByRef:
Result := Assigned(TVarData(v).vPointer) and Assigned(pointer(TVarData(v).vPointer^)) and (IInterface(TVarData(v).vPointer^).QueryInterface(IID, Intf) = S_OK);
varVariant, varVariant or varByRef:
Result := Assigned(TVarData(v).vPointer) and VarSupports(Variant(PVarData(TVarData(v).vPointer)^), IID, Intf);
else
Result := False;
end;
end;
function VarSupports(const V: Variant; const IID: TGUID): Boolean;
var
Dummy: IInterface;
begin
Result := VarSupports(V, IID, Dummy);
end;
{ Variant copy support }
{$push}
{$warnings off}
procedure VarCopyNoInd(var Dest: Variant; const Source: Variant);
begin
NotSupported('VarCopyNoInd');
end;
{$pop}
{****************************************************************************
Variant array support procedures and functions
****************************************************************************}
{$push}
{$r-}
function VarArrayCreate(const Bounds: array of SizeInt; aVarType: TVarType): Variant;
var
hp : PVarArrayBoundArray;
p : pvararray;
i,lengthb : SizeInt;
begin
if not(VarTypeIsValidArrayType(aVarType)) or odd(length(Bounds)) then
VarArrayCreateError;
lengthb:=length(Bounds) div 2;
try
GetMem(hp,lengthb*SizeOf(TVarArrayBound));
for i:=0 to lengthb-1 do
begin
hp^[i].LowBound:=Bounds[i*2];
hp^[i].ElementCount:=Bounds[i*2+1]-Bounds[i*2]+1;
end;
SysVarClear(Result);
p:=SafeArrayCreate(aVarType,lengthb,hp^);
if not(assigned(p)) then
VarArrayCreateError;
TVarData(Result).vType:=aVarType or varArray;
TVarData(Result).vArray:=p;
finally
FreeMem(hp);
end;
end;
{$pop}
function VarArrayCreate(const Bounds: PVarArrayBoundArray; Dims : SizeInt; aVarType: TVarType): Variant;
var
p : pvararray;
begin
if not(VarTypeIsValidArrayType(aVarType)) then
VarArrayCreateError;
SysVarClear(Result);
p:=SafeArrayCreate(aVarType,Dims,Bounds^);
if not(assigned(p)) then
VarArrayCreateError;
TVarData(Result).vType:=aVarType or varArray;
TVarData(Result).vArray:=p;
end;
function VarArrayOf(const Values: array of Variant): Variant;
var
i : SizeInt;
begin
Result:=VarArrayCreate([0,high(Values)],varVariant);
for i:=0 to high(Values) do
Result[i]:=Values[i];
end;
function VarArrayAsPSafeArray(const A: Variant): PVarArray;
var
v : TVarData;
begin
v:=TVarData(a);
while v.vType=varByRef or varVariant do
v:=TVarData(v.vPointer^);
if (v.vType and varArray)=varArray then
begin
if (v.vType and varByRef)<>0 then
Result:=pvararray(v.vPointer^)
else
Result:=v.vArray;
end
else
VarResultCheck(VAR_INVALIDARG);
end;
function VarArrayDimCount(const A: Variant) : LongInt;
var
hv : TVarData;
begin
hv:=TVarData(a);
{ get final Variant }
while hv.vType=varByRef or varVariant do
hv:=TVarData(hv.vPointer^);
if (hv.vType and varArray)<>0 then
Result:=hv.vArray^.DimCount
else
Result:=0;
end;
function VarArrayLowBound(const A: Variant; Dim: LongInt) : LongInt;
begin
VarResultCheck(SafeArrayGetLBound(VarArrayAsPSafeArray(A),Dim,Result));
end;
function VarArrayHighBound(const A: Variant; Dim: LongInt) : LongInt;
begin
VarResultCheck(SafeArrayGetUBound(VarArrayAsPSafeArray(A),Dim,Result));
end;
function VarArrayLock(const A: Variant): Pointer;
begin
VarResultCheck(SafeArrayAccessData(VarArrayAsPSafeArray(A),Result));
end;
procedure VarArrayUnlock(const A: Variant);
begin
VarResultCheck(SafeArrayUnaccessData(VarArrayAsPSafeArray(A)));
end;
function VarArrayRef(const A: Variant): Variant;
begin
if (TVarData(a).vType and varArray)=0 then
VarInvalidArgError(TVarData(a).vType);
TVarData(Result).vType:=TVarData(a).vType or varByRef;
if (TVarData(a).vType and varByRef)=0 then
TVarData(Result).vPointer:=@TVarData(a).vArray
else
TVarData(Result).vPointer:=@TVarData(a).vPointer;
end;
function VarIsArray(const A: Variant; AResolveByRef: Boolean): Boolean;
var
v : TVarData;
begin
v:=TVarData(a);
if AResolveByRef then
while v.vType=varByRef or varVariant do
v:=TVarData(v.vPointer^);
Result:=(v.vType and varArray)=varArray;
end;
function VarIsArray(const A: Variant): Boolean;
begin
VarIsArray:=VarIsArray(A,true);
end;
function VarTypeIsValidArrayType(const aVarType: TVarType): Boolean;
begin
Result:=aVarType in [varSmallInt,varInteger,
{$ifndef FPUNONE}
varSingle,varDouble,varDate,
{$endif}
varCurrency,varOleStr,varDispatch,varError,varBoolean,
varVariant,varUnknown,varShortInt,varByte,varWord,varLongWord];
end;
function VarTypeIsValidElementType(const aVarType: TVarType): Boolean;
var
customvarianttype : TCustomVariantType;
begin
Result:=((aVarType and not(varByRef) and not(varArray)) in [varEmpty,varNull,varSmallInt,varInteger,
{$ifndef FPUNONE}
varSingle,varDouble,varDate,
{$endif}
varCurrency,varOleStr,varDispatch,varError,varBoolean,
varVariant,varUnknown,varShortInt,varByte,varWord,varLongWord,varInt64]) or
FindCustomVariantType(aVarType,customvarianttype);
end;
{ ---------------------------------------------------------------------
Variant <-> Dynamic arrays support
---------------------------------------------------------------------}
function DynArrayGetVariantInfo(p : Pointer; var Dims : sizeint) : sizeint;
begin
Result:=varNull;
{ skip kind and name }
inc(Pointer(p),ord(pdynarraytypeinfo(p)^.namelen)+2);
p:=AlignToPtr(p);
{ skip elesize }
inc(p,SizeOf(sizeint));
{ search recursive? }
if pdynarraytypeinfo(ppointer(p)^)^.kind=21{tkDynArr} then
Result:=DynArrayGetVariantInfo(ppointer(p)^,Dims)
else
begin
{ skip dynarraytypeinfo }
inc(p,SizeOf(pdynarraytypeinfo));
Result:=plongint(p)^;
end;
inc(Dims);
end;
{$push}
{$r-}
procedure DynArrayToVariant(var V: Variant; const DynArray: Pointer; TypeInfo: Pointer);
var
i,
Dims : sizeint;
vararrtype,
dynarrvartype : LongInt;
vararraybounds : PVarArrayBoundArray;
iter : TVariantArrayIterator;
dynarriter : tdynarrayiter;
p : Pointer;
temp : Variant;
dynarraybounds : tdynarraybounds;
type
TDynArray = array of Pointer;
begin
DoVarClear(TVarData(v));
Dims:=0;
dynarrvartype:=DynArrayGetVariantInfo(TypeInfo,Dims);
vararrtype:=dynarrvartype;
if (Dims>1) and not(DynamicArrayIsRectangular(DynArray,TypeInfo)) then
exit;
{ retrieve Bounds array }
Setlength(dynarraybounds,Dims);
GetMem(vararraybounds,Dims*SizeOf(TVarArrayBound));
try
p:=DynArray;
for i:=0 to Dims-1 do
begin
vararraybounds^[i].LowBound:=0;
vararraybounds^[i].ElementCount:=length(TDynArray(p));
dynarraybounds[i]:=length(TDynArray(p));
if dynarraybounds[i]>0 then
{ we checked that the array is rectangular }
p:=TDynArray(p)[0];
end;
{ .. create Variant array }
V:=VarArrayCreate(vararraybounds,Dims,vararrtype);
VarArrayLock(V);
try
iter.init(Dims,PVarArrayBoundArray(vararraybounds));
dynarriter.init(DynArray,TypeInfo,Dims,dynarraybounds);
if not iter.AtEnd then
repeat
case vararrtype of
varSmallInt:
temp:=PSmallInt(dynarriter.data)^;
varInteger:
temp:=PInteger(dynarriter.data)^;
{$ifndef FPUNONE}
varSingle:
temp:=PSingle(dynarriter.data)^;
varDouble:
temp:=PDouble(dynarriter.data)^;
varDate:
temp:=PDouble(dynarriter.data)^;
{$endif}
varCurrency:
temp:=PCurrency(dynarriter.data)^;
varOleStr:
temp:=PWideString(dynarriter.data)^;
varDispatch:
temp:=PDispatch(dynarriter.data)^;
varError:
temp:=PError(dynarriter.data)^;
varBoolean:
temp:=PBoolean(dynarriter.data)^;
varVariant:
temp:=PVariant(dynarriter.data)^;
varUnknown:
temp:=PUnknown(dynarriter.data)^;
varShortInt:
temp:=PShortInt(dynarriter.data)^;
varByte:
temp:=PByte(dynarriter.data)^;
varWord:
temp:=PWord(dynarriter.data)^;
varLongWord:
temp:=PLongWord(dynarriter.data)^;
varInt64:
temp:=PInt64(dynarriter.data)^;
varQWord:
temp:=PQWord(dynarriter.data)^;
else
VarClear(temp);
end;
dynarriter.next;
VarArrayPut(V,temp,Slice(iter.Coords^,Dims));
until not(iter.next);
finally
iter.done;
dynarriter.done;
VarArrayUnlock(V);
end;
finally
FreeMem(vararraybounds);
end;
end;
procedure DynArrayFromVariant(var DynArray: Pointer; const V: Variant; TypeInfo: Pointer);
var
DynArrayDims,
VarArrayDims : SizeInt;
iter : TVariantArrayIterator;
dynarriter : tdynarrayiter;
temp : Variant;
dynarrvartype : LongInt;
vararraybounds : PVarArrayBoundArray;
dynarraybounds : tdynarraybounds;
i : SizeInt;
type
TDynArray = array of Pointer;
begin
VarArrayDims:=VarArrayDimCount(V);
DynArrayDims:=0;
dynarrvartype:=DynArrayGetVariantInfo(TypeInfo,DynArrayDims);
if (VarArrayDims=0) or (VarArrayDims<>DynArrayDims) then
VarResultCheck(VAR_INVALIDARG);
{ retrieve Bounds array }
Setlength(dynarraybounds,VarArrayDims);
GetMem(vararraybounds,VarArrayDims*SizeOf(TVarArrayBound));
try
for i:=0 to VarArrayDims-1 do
begin
vararraybounds^[i].LowBound:=VarArrayLowBound(V,i+1);
vararraybounds^[i].ElementCount:=VarArrayHighBound(V,i+1)-vararraybounds^[i].LowBound+1;
dynarraybounds[i]:=vararraybounds^[i].ElementCount;
end;
DynArraySetLength(DynArray,TypeInfo,VarArrayDims,PSizeInt(dynarraybounds));
VarArrayLock(V);
try
iter.init(VarArrayDims,PVarArrayBoundArray(vararraybounds));
dynarriter.init(DynArray,TypeInfo,VarArrayDims,dynarraybounds);
if not iter.AtEnd then
repeat
temp:=VarArrayGet(V,Slice(iter.Coords^,VarArrayDims));
case dynarrvartype of
varSmallInt:
PSmallInt(dynarriter.data)^:=temp;
varInteger:
PInteger(dynarriter.data)^:=temp;
{$ifndef FPUNONE}
varSingle:
PSingle(dynarriter.data)^:=temp;
varDouble:
PDouble(dynarriter.data)^:=temp;
varDate:
PDouble(dynarriter.data)^:=temp;
{$endif}
varCurrency:
PCurrency(dynarriter.data)^:=temp;
varOleStr:
PWideString(dynarriter.data)^:=temp;
varDispatch:
PDispatch(dynarriter.data)^:=temp;
varError:
PError(dynarriter.data)^:=temp;
varBoolean:
PBoolean(dynarriter.data)^:=temp;
varVariant:
PVariant(dynarriter.data)^:=temp;
varUnknown:
PUnknown(dynarriter.data)^:=temp;
varShortInt:
PShortInt(dynarriter.data)^:=temp;
varByte:
PByte(dynarriter.data)^:=temp;
varWord:
PWord(dynarriter.data)^:=temp;
varLongWord:
PLongWord(dynarriter.data)^:=temp;
varInt64:
PInt64(dynarriter.data)^:=temp;
varQWord:
PQWord(dynarriter.data)^:=temp;
else
VarCastError;
end;
dynarriter.next;
until not(iter.next);
finally
iter.done;
dynarriter.done;
VarArrayUnlock(V);
end;
finally
FreeMem(vararraybounds);
end;
end;
{$pop}//{$r-} for DynArray[From|To]Variant
function FindCustomVariantType(const aVarType: TVarType; out CustomVariantType: TCustomVariantType): Boolean; overload;
begin
Result:=(aVarType>=CMinVarType);
if Result then
begin
EnterCriticalSection(customvarianttypelock);
try
Result:=(aVarType-CMinVarType)<=high(customvarianttypes);
if Result then
begin
CustomVariantType:=customvarianttypes[aVarType-CMinVarType];
Result:=assigned(CustomVariantType) and
(CustomVariantType<>InvalidCustomVariantType);
end;
finally
LeaveCriticalSection(customvarianttypelock);
end;
end;
end;
function FindCustomVariantType(const TypeName: string; out CustomVariantType: TCustomVariantType): Boolean; overload;
var
i: Integer;
tmp: TCustomVariantType;
ShortTypeName: shortstring;
begin
ShortTypeName:=TypeName; // avoid conversion in the loop
result:=False;
EnterCriticalSection(customvarianttypelock);
try
for i:=low(customvarianttypes) to high(customvarianttypes) do
begin
tmp:=customvarianttypes[i];
result:=Assigned(tmp) and (tmp<>InvalidCustomVariantType) and
tmp.ClassNameIs(ShortTypeName);
if result then
begin
CustomVariantType:=tmp;
Exit;
end;
end;
finally
LeaveCriticalSection(customvarianttypelock);
end;
end;
function Unassigned: Variant; // Unassigned standard constant
begin
SysVarClear(Result);
TVarData(Result).vType := varEmpty;
end;
function Null: Variant; // Null standard constant
begin
SysVarClear(Result);
TVarData(Result).vType := varNull;
end;
procedure VarDispInvokeError;
begin
raise EVariantDispatchError.Create(SDispatchError);
end;
{ ---------------------------------------------------------------------
TCustomVariantType Class.
---------------------------------------------------------------------}
{ All TCustomVariantType descendants are singletons, they ignore automatic refcounting. }
function TCustomVariantType.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
begin
if GetInterface(IID, obj) then
result := S_OK
else
result := E_NOINTERFACE;
end;
function TCustomVariantType._AddRef: Integer; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
begin
result := -1;
end;
function TCustomVariantType._Release: Integer; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
begin
result := -1;
end;
{$warnings off}
procedure TCustomVariantType.SimplisticClear(var V: TVarData);
begin
VarDataInit(V);
end;
procedure TCustomVariantType.SimplisticCopy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean = False);
begin
NotSupported('TCustomVariantType.SimplisticCopy');
end;
procedure TCustomVariantType.RaiseInvalidOp;
begin
VarInvalidOp;
end;
procedure TCustomVariantType.RaiseCastError;
begin
VarCastError;
end;
procedure TCustomVariantType.RaiseDispError;
begin
VarDispInvokeError;
end;
function TCustomVariantType.LeftPromotion(const V: TVarData; const Operation: TVarOp; out RequiredVarType: TVarType): Boolean;
begin
NotSupported('TCustomVariantType.LeftPromotion');
end;
function TCustomVariantType.RightPromotion(const V: TVarData; const Operation: TVarOp; out RequiredVarType: TVarType): Boolean;
begin
NotSupported('TCustomVariantType.RightPromotion');
end;
function TCustomVariantType.OlePromotion(const V: TVarData; out RequiredVarType: TVarType): Boolean;
begin
NotSupported('TCustomVariantType.OlePromotion');
end;
procedure TCustomVariantType.DispInvoke(Dest: PVarData; const Source: TVarData; CallDesc: PCallDesc; Params: Pointer);
begin
RaiseDispError;
end;
procedure TCustomVariantType.VarDataInit(var Dest: TVarData);
begin
FillChar(Dest,SizeOf(Dest),0);
end;
procedure TCustomVariantType.VarDataClear(var Dest: TVarData);
begin
VarClearProc(Dest);
end;
procedure TCustomVariantType.VarDataCopy(var Dest: TVarData; const Source: TVarData);
begin
DoVarCopy(Dest,Source)
end;
procedure TCustomVariantType.VarDataCopyNoInd(var Dest: TVarData; const Source: TVarData);
begin
// This is probably not correct, but there is no DoVarCopyInd
DoVarCopy(Dest,Source);
end;
procedure TCustomVariantType.VarDataCast(var Dest: TVarData; const Source: TVarData);
begin
DoVarCast(Dest, Source, VarType);
end;
procedure TCustomVariantType.VarDataCastTo(var Dest: TVarData; const Source: TVarData; const aVarType: TVarType);
begin
DoVarCast(Dest, Source, AVarType);
end;
procedure TCustomVariantType.VarDataCastTo(var Dest: TVarData; const aVarType: TVarType);
begin
DoVarCast(Dest,Dest,AVarType);
end;
procedure TCustomVariantType.VarDataCastToOleStr(var Dest: TVarData);
begin
VarDataCastTo(Dest, Dest, varOleStr);
end;
procedure TCustomVariantType.VarDataFromStr(var V: TVarData; const Value: string);
begin
sysvarfromlstr(Variant(V),Value);
end;
procedure TCustomVariantType.VarDataFromOleStr(var V: TVarData; const Value: WideString);
begin
sysvarfromwstr(variant(V),Value);
end;
function TCustomVariantType.VarDataToStr(const V: TVarData): string;
begin
sysvartolstr(Result,Variant(V));
end;
function TCustomVariantType.VarDataIsEmptyParam(const V: TVarData): Boolean;
begin
Result:=VarIsEmptyParam(Variant(V));
end;
function TCustomVariantType.VarDataIsByRef(const V: TVarData): Boolean;
begin
Result:=(V.vType and varByRef)=varByRef;
end;
function TCustomVariantType.VarDataIsArray(const V: TVarData): Boolean;
begin
Result:=(V.vType and varArray)=varArray;
end;
function TCustomVariantType.VarDataIsOrdinal(const V: TVarData): Boolean;
begin
Result:=(V.vType and varTypeMask) in OrdinalVarTypes;
end;
function TCustomVariantType.VarDataIsFloat(const V: TVarData): Boolean;
begin
Result:=(V.vType and varTypeMask) in FloatVarTypes;
end;
function TCustomVariantType.VarDataIsNumeric(const V: TVarData): Boolean;
begin
Result:=(V.vType and varTypeMask) in (OrdinalVarTypes + FloatVarTypes);
end;
function TCustomVariantType.VarDataIsStr(const V: TVarData): Boolean;
begin
Result:=
((V.vType and varTypeMask) = varOleStr) or
((V.vType and varTypeMask) = varString);
end;
procedure RegisterCustomVariantType(obj: TCustomVariantType; RequestedVarType: TVarType;
UseFirstAvailable: Boolean);
var
index,L: Integer;
begin
EnterCriticalSection(customvarianttypelock);
try
L:=Length(customvarianttypes);
if UseFirstAvailable then
begin
repeat
inc(customvariantcurrtype);
if customvariantcurrtype>=CMaxVarType then
raise EVariantError.Create(SVarTypeTooManyCustom);
until ((customvariantcurrtype-CMinVarType)>=L) or
(customvarianttypes[customvariantcurrtype-CMinVarType]=nil);
RequestedVarType:=customvariantcurrtype;
end
else if (RequestedVarType<CFirstUserType) or (RequestedVarType>CMaxVarType) then
raise EVariantError.CreateFmt(SVarTypeOutOfRangeWithPrefix, ['$', RequestedVarType]);
index:=RequestedVarType-CMinVarType;
if index>=L then
SetLength(customvarianttypes,L+1);
if Assigned(customvarianttypes[index]) then
begin
if customvarianttypes[index]=InvalidCustomVariantType then
raise EVariantError.CreateFmt(SVarTypeNotUsableWithPrefix, ['$', RequestedVarType])
else
raise EVariantError.CreateFmt(SVarTypeAlreadyUsedWithPrefix,
['$', RequestedVarType, customvarianttypes[index].ClassName]);
end;
customvarianttypes[index]:=obj;
obj.FVarType:=RequestedVarType;
finally
LeaveCriticalSection(customvarianttypelock);
end;
end;
constructor TCustomVariantType.Create;
begin
RegisterCustomVariantType(Self,0,True);
end;
constructor TCustomVariantType.Create(RequestedVarType: TVarType);
begin
RegisterCustomVariantType(Self,RequestedVarType,False);
end;
destructor TCustomVariantType.Destroy;
begin
EnterCriticalSection(customvarianttypelock);
try
if FVarType<>0 then
customvarianttypes[FVarType-CMinVarType]:=InvalidCustomVariantType;
finally
LeaveCriticalSection(customvarianttypelock);
end;
inherited Destroy;
end;
function TCustomVariantType.IsClear(const V: TVarData): Boolean;
begin
result:=False;
end;
procedure TCustomVariantType.Cast(var Dest: TVarData; const Source: TVarData);
begin
DoVarCast(Dest,Source,VarType);
end;
procedure TCustomVariantType.CastTo(var Dest: TVarData; const Source: TVarData; const aVarType: TVarType);
begin
DoVarCast(Dest,Source,AVarType);
end;
procedure TCustomVariantType.CastToOle(var Dest: TVarData; const Source: TVarData);
begin
NotSupported('TCustomVariantType.CastToOle');
end;
procedure TCustomVariantType.BinaryOp(var Left: TVarData; const Right: TVarData; const Operation: TVarOp);
begin
RaiseInvalidOp;
end;
procedure TCustomVariantType.UnaryOp(var Right: TVarData; const Operation: TVarOp);
begin
RaiseInvalidOp;
end;
function TCustomVariantType.CompareOp(const Left, Right: TVarData; const Operation: TVarOp): Boolean;
begin
NotSupported('TCustomVariantType.CompareOp');
end;
procedure TCustomVariantType.Compare(const Left, Right: TVarData; var Relationship: TVarCompareResult);
begin
NotSupported('TCustomVariantType.Compare');
end;
{$warnings on}
{ ---------------------------------------------------------------------
TInvokeableVariantType implementation
---------------------------------------------------------------------}
procedure TInvokeableVariantType.DispInvoke(Dest: PVarData; const Source: TVarData;
CallDesc: PCallDesc; Params: Pointer);
var
method_name: ansistring;
arg_count: byte;
args: TVarDataArray;
arg_idx: byte;
arg_type: byte;
arg_byref, has_result: boolean;
arg_ptr: pointer;
arg_data: PVarData;
dummy_data: TVarData;
const
argtype_mask = $7F;
argref_mask = $80;
begin
arg_count := CallDesc^.ArgCount;
method_name := ansistring(pchar(@CallDesc^.ArgTypes[arg_count]));
setLength(args, arg_count);
if arg_count > 0 then
begin
arg_ptr := Params;
for arg_idx := 0 to arg_count - 1 do
begin
arg_type := CallDesc^.ArgTypes[arg_idx] and argtype_mask;
arg_byref := (CallDesc^.ArgTypes[arg_idx] and argref_mask) <> 0;
arg_data := @args[arg_count - arg_idx - 1];
case arg_type of
varUStrArg: arg_data^.vType := varUString;
varStrArg: arg_data^.vType := varString;
else
arg_data^.vType := arg_type
end;
if arg_byref then
begin
arg_data^.vType := arg_data^.vType or varByRef;
arg_data^.vPointer := PPointer(arg_ptr)^;
Inc(arg_ptr,sizeof(Pointer));
end
else
case arg_type of
varError:
arg_data^.vError:=VAR_PARAMNOTFOUND;
varVariant:
begin
arg_data^ := PVarData(PPointer(arg_ptr)^)^;
Inc(arg_ptr,sizeof(Pointer));
end;
varDouble, varCurrency, varInt64, varQWord:
begin
arg_data^.vQWord := PQWord(arg_ptr)^; // 64bit on all platforms
inc(arg_ptr,sizeof(qword))
end
else
arg_data^.vAny := PPointer(arg_ptr)^; // 32 or 64bit
inc(arg_ptr,sizeof(pointer))
end;
end;
end;
has_result := (Dest <> nil);
if has_result then
variant(Dest^) := Unassigned;
case CallDesc^.CallType of
1: { DISPATCH_METHOD }
if has_result then
begin
if arg_count = 0 then
begin
// no args -- try GetProperty first, then DoFunction
if not (GetProperty(Dest^,Source,method_name) or
DoFunction(Dest^,Source,method_name,args)) then
RaiseDispError
end
else
if not DoFunction(Dest^,Source,method_name,args) then
RaiseDispError;
end
else
begin
// may be procedure?
if not DoProcedure(Source,method_name,args) then
// may be function?
try
variant(dummy_data) := Unassigned;
if not DoFunction(dummy_data,Source,method_name,args) then
RaiseDispError;
finally
VarDataClear(dummy_data)
end;
end;
2: { DISPATCH_PROPERTYGET -- currently never generated by compiler for Variant Dispatch }
if has_result then
begin
// must be property...
if not GetProperty(Dest^,Source,method_name) then
// may be function?
if not DoFunction(Dest^,Source,method_name,args) then
RaiseDispError
end
else
RaiseDispError;
4: { DISPATCH_PROPERTYPUT }
if has_result or (arg_count<>1) or // must be no result and a single arg
(not SetProperty(Source,method_name,args[0])) then
RaiseDispError;
else
RaiseDispError;
end;
end;
function TInvokeableVariantType.DoFunction(var Dest: TVarData; const V: TVarData; const Name: string; const Arguments: TVarDataArray): Boolean;
begin
result := False;
end;
function TInvokeableVariantType.DoProcedure(const V: TVarData; const Name: string; const Arguments: TVarDataArray): Boolean;
begin
result := False
end;
function TInvokeableVariantType.GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean;
begin
result := False;
end;
function TInvokeableVariantType.SetProperty(const V: TVarData; const Name: string; const Value: TVarData): Boolean;
begin
result := False;
end;
{ ---------------------------------------------------------------------
TPublishableVariantType implementation
---------------------------------------------------------------------}
function TPublishableVariantType.GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean;
begin
Result:=true;
Variant(Dest):=GetPropValue(getinstance(v),name);
end;
function TPublishableVariantType.SetProperty(const V: TVarData; const Name: string; const Value: TVarData): Boolean;
begin
Result:=true;
SetPropValue(getinstance(v),name,Variant(value));
end;
procedure VarCastError;
begin
raise EVariantTypeCastError.Create(SInvalidVarCast);
end;
procedure VarCastError(const ASourceType, ADestType: TVarType);
begin
raise EVariantTypeCastError.CreateFmt(SVarTypeCouldNotConvert,
[VarTypeAsText(ASourceType),VarTypeAsText(ADestType)]);
end;
procedure VarCastErrorOle(const ASourceType: TVarType);
begin
raise EVariantTypeCastError.CreateFmt(SVarTypeCouldNotConvert,
[VarTypeAsText(ASourceType),'(OleVariant)']);
end;
procedure VarInvalidOp;
begin
raise EVariantInvalidOpError.Create(SInvalidVarOp);
end;
procedure VarInvalidOp(const aLeft, aRight: TVarType; aOpCode: TVarOp);
begin
raise EVariantInvalidOpError.CreateFmt(SInvalidBinaryVarOp,
[VarTypeAsText(aLeft),VarOpAsText[aOpCode],VarTypeAsText(aRight)]);
end;
procedure VarInvalidOp(const aRight: TVarType; aOpCode: TVarOp);
begin
raise EVariantInvalidOpError.CreateFmt(SInvalidUnaryVarOp,
[VarOpAsText[aOpCode],VarTypeAsText(aRight)]);
end;
procedure VarInvalidNullOp;
begin
raise EVariantInvalidOpError.Create(SInvalidvarNullOp);
end;
procedure VarParamNotFoundError;
begin
raise EVariantParamNotFoundError.Create(SVarParamNotFound);
end;
procedure VarBadTypeError;
begin
raise EVariantBadVarTypeError.Create(SVarBadType);
end;
procedure VarOverflowError;
begin
raise EVariantOverflowError.Create(SVarOverflow);
end;
procedure VarOverflowError(const ASourceType, ADestType: TVarType);
begin
raise EVariantOverflowError.CreateFmt(SVarTypeConvertOverflow,
[VarTypeAsText(ASourceType),VarTypeAsText(ADestType)]);
end;
procedure VarRangeCheckError(const AType: TVarType);
begin
raise EVariantOverflowError.CreateFmt(SVarTypeRangeCheck1,
[VarTypeAsText(AType)])
end;
procedure VarRangeCheckError(const ASourceType, ADestType: TVarType);
begin
if ASourceType<>ADestType then
raise EVariantOverflowError.CreateFmt(SVarTypeRangeCheck2,
[VarTypeAsText(ASourceType),VarTypeAsText(ADestType)])
else
VarRangeCheckError(ASourceType);
end;
procedure VarBadIndexError;
begin
raise EVariantBadIndexError.Create(SVarArrayBounds);
end;
procedure VarArrayLockedError;
begin
raise EVariantArrayLockedError.Create(SVarArrayLocked);
end;
procedure VarNotImplError;
begin
raise EVariantNotImplError.Create(SVarNotImplemented);
end;
procedure VarOutOfMemoryError;
begin
raise EVariantOutOfMemoryError.Create(SOutOfMemory);
end;
procedure VarInvalidArgError;
begin
raise EVariantInvalidArgError.Create(SVarInvalid);
end;
procedure VarInvalidArgError(AType: TVarType);
begin
raise EVariantInvalidArgError.CreateFmt(SVarInvalid1,
[VarTypeAsText(AType)])
end;
procedure VarUnexpectedError;
begin
raise EVariantUnexpectedError.Create(SVarUnexpected);
end;
procedure VarArrayCreateError;
begin
raise EVariantArrayCreateError.Create(SVarArrayCreate);
end;
procedure RaiseVarException(res : HRESULT);
begin
case res of
VAR_PARAMNOTFOUND:
VarParamNotFoundError;
VAR_TYPEMISMATCH:
VarCastError;
VAR_BADVARTYPE:
VarBadTypeError;
VAR_EXCEPTION:
VarInvalidOp;
VAR_OVERFLOW:
VarOverflowError;
VAR_BADINDEX:
VarBadIndexError;
VAR_ARRAYISLOCKED:
VarArrayLockedError;
VAR_NOTIMPL:
VarNotImplError;
VAR_OUTOFMEMORY:
VarOutOfMemoryError;
VAR_INVALIDARG:
VarInvalidArgError;
VAR_UNEXPECTED:
VarUnexpectedError;
else
raise EVariantError.CreateFmt(SInvalidVarOpWithHResultWithPrefix,
['$',res,'']);
end;
end;
procedure VarResultCheck(AResult: HRESULT);{$IFDEF VARIANTINLINE}inline;{$ENDIF VARIANTINLINE}
begin
if AResult<>VAR_OK then
RaiseVarException(AResult);
end;
procedure VarResultCheck(AResult: HRESULT; ASourceType, ADestType: TVarType);
begin
case AResult of
VAR_OK:
;
VAR_OVERFLOW:
VarOverflowError(ASourceType,ADestType);
VAR_TYPEMISMATCH:
VarCastError(ASourceType,ADestType);
else
RaiseVarException(AResult);
end;
end;
procedure HandleConversionException(const ASourceType, ADestType: TVarType);
begin
if exceptobject is econverterror then
VarCastError(asourcetype,adesttype)
else if (exceptobject is eoverflow) or
(exceptobject is erangeerror) then
varoverflowerror(asourcetype,adesttype)
else
raise exception(acquireexceptionobject);
end;
function VarTypeAsText(const AType: TVarType): string;
var
customvarianttype : TCustomVariantType;
const
names : array[varEmpty..varQWord] of string[8] = (
'Empty','Null','Smallint','Integer','Single','Double','Currency','Date','OleStr','Dispatch','Error','Boolean','Variant',
'Unknown','Decimal','???','ShortInt','Byte','Word','DWord','Int64','QWord');
begin
if ((AType and varTypeMask)>=low(names)) and ((AType and varTypeMask)<=high(names)) then
Result:=names[AType and varTypeMask]
else
case AType and varTypeMask of
varString:
Result:='String';
varAny:
Result:='Any';
else
begin
if FindCustomVariantType(AType and varTypeMask,customvarianttype) then
Result:=customvarianttype.classname
else
Result:='$'+IntToHex(AType and varTypeMask,4)
end;
end;
if (AType and vararray)<>0 then
Result:='Array of '+Result;
if (AType and varByRef)<>0 then
Result:='Ref to '+Result;
end;
function FindVarData(const V: Variant): PVarData;
begin
Result:=PVarData(@V);
while Result^.vType=varVariant or varByRef do
Result:=PVarData(Result^.vPointer);
end;
{ ---------------------------------------------------------------------
Variant properties from typinfo
---------------------------------------------------------------------}
function GetVariantProp(Instance : TObject;PropInfo : PPropInfo) : Variant;
type
TGetVariantProc = function:Variant of object;
TGetVariantProcIndex = function(Index: integer): Variant of object;
var
AMethod : TMethod;
begin
Result:=Null;
case PropInfo^.PropProcs and 3 of
ptField:
Result:=PVariant(Pointer(Instance)+PtrUInt(PropInfo^.GetProc))^;
ptStatic,
ptVirtual:
begin
if (PropInfo^.PropProcs and 3)=ptStatic then
AMethod.Code:=PropInfo^.GetProc
else
AMethod.Code:=PPointer(Pointer(Instance.ClassType)+PtrUInt(PropInfo^.GetProc))^;
AMethod.Data:=Instance;
if ((PropInfo^.PropProcs shr 6) and 1)=0 then
Result:=TGetVariantProc(AMethod)()
else
Result:=TGetVariantProcIndex(AMethod)(PropInfo^.Index);
end;
end;
end;
Procedure SetVariantProp(Instance : TObject;PropInfo : PPropInfo; const Value : Variant);
type
TSetVariantProc = procedure(const AValue: Variant) of object;
TSetVariantProcIndex = procedure(Index: integer; AValue: Variant) of object;
Var
AMethod : TMethod;
begin
case (PropInfo^.PropProcs shr 2) and 3 of
ptfield:
PVariant(Pointer(Instance)+PtrUInt(PropInfo^.SetProc))^:=Value;
ptVirtual,ptStatic:
begin
if ((PropInfo^.PropProcs shr 2) and 3)=ptStatic then
AMethod.Code:=PropInfo^.SetProc
else
AMethod.Code:=PPointer(Pointer(Instance.ClassType)+PtrUInt(PropInfo^.SetProc))^;
AMethod.Data:=Instance;
if ((PropInfo^.PropProcs shr 6) and 1)=0 then
TSetVariantProc(AMethod)(Value)
else
TSetVariantProcIndex(AMethod)(PropInfo^.Index,Value);
end;
end;
end;
Function GetVariantProp(Instance: TObject; const PropName: string): Variant;
begin
Result:=GetVariantProp(Instance,FindPropInfo(Instance,PropName));
end;
Procedure SetVariantProp(Instance: TObject; const PropName: string; const Value: Variant);
begin
SetVariantprop(instance,FindpropInfo(Instance,PropName),Value);
end;
{ ---------------------------------------------------------------------
All properties through Variant.
---------------------------------------------------------------------}
Function GetPropValue(Instance: TObject; const PropName: string): Variant;
begin
Result:=GetPropValue(Instance,PropName,True);
end;
Function GetPropValue(Instance: TObject; const PropName: string; PreferStrings: Boolean): Variant;
var
PropInfo: PPropInfo;
begin
// find the property
PropInfo := GetPropInfo(Instance, PropName);
if PropInfo = nil then
raise EPropertyError.CreateFmt(SErrPropertyNotFound, [PropName])
else
begin
Result := Null; //at worst
// call the Right GetxxxProp
case PropInfo^.PropType^.Kind of
tkInteger, tkChar, tkWChar, tkClass, tkBool:
Result := GetOrdProp(Instance, PropInfo);
tkEnumeration:
if PreferStrings then
Result := GetEnumProp(Instance, PropInfo)
else
Result := GetOrdProp(Instance, PropInfo);
tkSet:
if PreferStrings then
Result := GetSetProp(Instance, PropInfo, False)
else
Result := GetOrdProp(Instance, PropInfo);
{$ifndef FPUNONE}
tkFloat:
Result := GetFloatProp(Instance, PropInfo);
{$endif}
tkMethod:
Result := PropInfo^.PropType^.Name;
tkString, tkLString, tkAString:
Result := GetStrProp(Instance, PropInfo);
tkWString:
Result := GetWideStrProp(Instance, PropInfo);
tkUString:
Result := GetUnicodeStrProp(Instance, PropInfo);
tkVariant:
Result := GetVariantProp(Instance, PropInfo);
tkInt64:
Result := GetInt64Prop(Instance, PropInfo);
tkQWord:
Result := QWord(GetInt64Prop(Instance, PropInfo));
else
raise EPropertyConvertError.CreateFmt('Invalid Property Type: %s',[PropInfo^.PropType^.Name]);
end;
end;
end;
Procedure SetPropValue(Instance: TObject; const PropName: string; const Value: Variant);
var
PropInfo: PPropInfo;
TypeData: PTypeData;
O: Integer;
I64: Int64;
Qw: QWord;
S: String;
B: Boolean;
begin
// find the property
PropInfo := GetPropInfo(Instance, PropName);
if PropInfo = nil then
raise EPropertyError.CreateFmt(SErrPropertyNotFound, [PropName])
else
begin
TypeData := GetTypeData(PropInfo^.PropType);
// call Right SetxxxProp
case PropInfo^.PropType^.Kind of
tkBool:
begin
{ to support the strings 'true' and 'false' }
if (VarType(Value)=varOleStr) or
(VarType(Value)=varString) or
(VarType(Value)=varBoolean) then
begin
B:=Value;
SetOrdProp(Instance, PropInfo, ord(B));
end
else
begin
I64:=Value;
if (I64<TypeData^.MinValue) or (I64>TypeData^.MaxValue) then
raise ERangeError.Create(SRangeError);
SetOrdProp(Instance, PropInfo, I64);
end;
end;
tkInteger, tkChar, tkWChar:
begin
I64:=Value;
if (TypeData^.OrdType=otULong) then
if (I64<LongWord(TypeData^.MinValue)) or (I64>LongWord(TypeData^.MaxValue)) then
raise ERangeError.Create(SRangeError)
else
else
if (I64<TypeData^.MinValue) or (I64>TypeData^.MaxValue) then
raise ERangeError.Create(SRangeError);
SetOrdProp(Instance, PropInfo, I64);
end;
tkEnumeration :
begin
if (VarType(Value)=varOleStr) or (VarType(Value)=varString) then
begin
S:=Value;
SetEnumProp(Instance,PropInfo,S);
end
else
begin
I64:=Value;
if (I64<TypeData^.MinValue) or (I64>TypeData^.MaxValue) then
raise ERangeError.Create(SRangeError);
SetOrdProp(Instance, PropInfo, I64);
end;
end;
tkSet :
begin
if (VarType(Value)=varOleStr) or (VarType(Value)=varString) then
begin
S:=Value;
SetSetProp(Instance,PropInfo,S);
end
else
begin
O:=Value;
SetOrdProp(Instance, PropInfo, O);
end;
end;
{$ifndef FPUNONE}
tkFloat:
SetFloatProp(Instance, PropInfo, Value);
{$endif}
tkString, tkLString, tkAString:
SetStrProp(Instance, PropInfo, VarToStr(Value));
tkWString:
SetWideStrProp(Instance, PropInfo, VarToWideStr(Value));
tkUString:
SetUnicodeStrProp(Instance, PropInfo, VarToUnicodeStr(Value));
tkVariant:
SetVariantProp(Instance, PropInfo, Value);
tkInt64:
begin
I64:=Value;
if (I64<TypeData^.MinInt64Value) or (I64>TypeData^.MaxInt64Value) then
raise ERangeError.Create(SRangeError);
SetInt64Prop(Instance, PropInfo, I64);
end;
tkQWord:
begin
Qw:=Value;
if (Qw<TypeData^.MinQWordValue) or (Qw>TypeData^.MaxQWordValue) then
raise ERangeError.Create(SRangeError);
SetInt64Prop(Instance, PropInfo,Qw);
end
else
raise EPropertyConvertError.CreateFmt('SetPropValue: Invalid Property Type %s',
[PropInfo^.PropType^.Name]);
end;
end;
end;
var
i : LongInt;
Initialization
InitCriticalSection(customvarianttypelock);
// start with one-less value, so first increment yields CFirstUserType
customvariantcurrtype:=CFirstUserType-1;
SetSysVariantManager;
SetClearVarToEmptyParam(TVarData(EmptyParam));
VarClearProc:=@DoVarClear;
VarAddRefProc:=@DoVarAddRef;
VarCopyProc:=@DoVarCopy;
// Typinfo Variant support
OnGetVariantProp:=@GetVariantprop;
OnSetVariantProp:=@SetVariantprop;
OnSetPropValue:=@SetPropValue;
OnGetPropValue:=@GetPropValue;
InvalidCustomVariantType:=TCustomVariantType(-1);
SetLength(customvarianttypes,CFirstUserType);
Finalization
EnterCriticalSection(customvarianttypelock);
try
for i:=0 to high(customvarianttypes) do
if customvarianttypes[i]<>InvalidCustomVariantType then
customvarianttypes[i].Free;
finally
LeaveCriticalSection(customvarianttypelock);
end;
UnSetSysVariantManager;
DoneCriticalSection(customvarianttypelock);
end.