ObjAuto.pas单元补完
大家都知道D2009提供的ObjAuto.pas是一个半成品,只能取方法,不能取参数。但是其实这些功能都是可以实现的我在此做个补完,供Delphi6~Delphi2009使用,同时添加了编译预处理,供各个不同版本的Delphi兼容编译特地做了个D7下的Demo,老版本的Delphi一样可以使用。也可以去CodeGear官方网站下载,链接在此http://cc.codegear.com/item/26122
unit ObjAutoX;
interface
uses TypInfo;
{$IFDEF VER130}
Not Support Delphi 5
{$ENDIF}
{$IFDEF VER120}
Not Support Delphi 4
{$ENDIF}
{$IFDEF VER100}
Not Support Delphi 3
{$ENDIF}
{$IFDEF VER90}
Not Support Delphi 2
{$ENDIF}
{$IFDEF VER80}
Not Support Delphi 1
{$ENDIF}
{$IFDEF VER200}
{$DEFINE DELPHI2009}
{$ENDIF}
const
paEAX = Word(0);
paEDX = Word(1);
paECX = Word(2);
paStack = Word(3);
SHORT_LEN = SizeOf(ShortString) - 1;
type
TCallingConvention = (ccRegister, ccCdecl, ccPascal, ccStdCall, ccSafeCall);
TParamFlags = set of (pfVar, pfConst, pfArray, pfAddress, pfReference, pfOut,
pfResult);
PPointer = ^Pointer;
PWord = ^Word;
PMethodInfoHeader = ^TMethodInfoHeader;
TMethodInfoHeader = packed record
Len: Word;
Addr: Pointer;
Name: ShortString;
end;
PReturnInfo = ^TReturnInfo;
TReturnInfo = packed record
Version: Byte; // Must be 1
CallingConvention: TCallingConvention;
ReturnType: ^PTypeInfo;
ParamSize: Word;
end;
PParamInfo = ^TParamInfo;
TParamInfo = packed record
Flags: TParamFlags;
ParamType: ^PTypeInfo;
Access: Word;
Name: ShortString;
end;
TMethodInfoArray = array of PMethodInfoHeader;
TParamInfoArray = array of PParamInfo;
function ObjectInvoke(Instance: TObject; MethodHeader: PMethodInfoHeader;
const ParamIndexes: array of Integer; const Params: array of Variant): Variant;
function GetMethodInfo(Instance: TObject;
const MethodName: ShortString): PMethodInfoHeader;
type
IMethodHandler = interface
['{4E61C8CD-16CC-4830-B1E4-84F86FBC0D23}']
function Execute(const Args: array of Variant): Variant;
function InstanceToVariant(Instance: TObject): Variant;
end;
PParameters = ^TParameters;
TParameters = packed record
Registers: array[paEDX..paECX] of Cardinal;
Stack: array[0..1023] of Byte;
end;
TDynamicInvokeEvent = procedure (Params: PParameters; StackSize: Integer) of object;
function CreateMethodPointer(const MethodHandler: IMethodHandler; TypeData: PTypeData): TMethod; overload;
function CreateMethodPointer(const ADynamicInvokeEvent: TDynamicInvokeEvent; TypeData: PTypeData): TMethod; overload;
procedure ReleaseMethodPointer(MethodPointer: TMethod);
function GetMethods(ClassType: TClass): TMethodInfoArray;
function GetInvokeInstance(MethodPointer: TMethod): TObject;
function GetParams(aObj: TObject; aMethodName: string): TParamInfoArray;
function GetReturnInfo(aObj: TObject; aMethodName: string): PReturnInfo;
implementation
uses SysUtils, Variants, VarUtils, RTLConsts;
function GetTypeSize(TypeInfo: PTypeInfo): Integer;
var
TypeData: PTypeData;
begin
case TypeInfo^.Kind of
tkInteger, tkEnumeration:
begin
TypeData := GetTypeData(TypeInfo);
if TypeData^.MinValue >= 0 then
if Cardinal(TypeData^.MaxValue) > $FFFF then
Result := 4
else if TypeData^.MaxValue > $FF then
Result := 2
else
Result := 1
else
if (TypeData^.MaxValue > $7FFF) or (TypeData^.MinValue < -$7FFF - 1) then
Result := 4
else if (TypeData^.MaxValue > $7F) or (TypeData^.MinValue < -$7F - 1) then
Result := 2
else
Result := 1;
end;
tkFloat:
begin
TypeData := GetTypeData(TypeInfo);
case TypeData^.FloatType of
ftSingle: Result := 4;
ftComp, ftCurr, ftDouble: Result := 8;
else
Result := -1;
end;
end;
tkString, tkLString,{$IFDEF DELPHI2009}tkUString,{$ENDIF} tkWString, tkInterface, tkClass:
Result := 4;
tkMethod, tkInt64:
Result := 8;
tkVariant:
Result := 16;
else
Assert(False);
Result := -1;
end;
end;
type
TConvertKind = (ckNone, ckConvert, ckError);
function ConvertKindOf(Source, Dest: TVarType): TConvertKind;
const
none = ckNone;
cvt = ckConvert;
err = ckError;
Codes: array[varEmpty..{$IFDEF DELPHI2009}varUInt64{$ELSE}varInt64{$ENDIF}, varEmpty..{$IFDEF DELPHI2009}varUInt64{$ELSE}varInt64{$ENDIF}] of TConvertKind =
({v From} {To >}{vt_empty} {vt_null} {vt_i2} {vt_i4} {vt_r4} {vt_r8} {vt_cy} {vt_date} {vt_bstr} {vt_dispatch} {vt_error} {vt_bool} {vt_variant} {vt_unknown} {vt_decimal} {0x0f } {vt_i1} {vt_ui1} {vt_ui2} {vt_ui4} {vt_i8} {vt_ui8}
{vt_empty} (none, err, err, err, err, err, err, err, err, err, err, err, none, err, err, err, err, err, err, err, err {$IFDEF DELPHI2009},err{$ENDIF}),
{vt_null} (err, none, err, err, err, err, err, err, err, err, err, err, none, err, err, err, err, err, err, err, err {$IFDEF DELPHI2009},err{$ENDIF}),
{vt_i2} (err, err, none, cvt, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_i4} (err, err, none, none, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_r4} (err, err, cvt, cvt, none, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_r8} (err, err, cvt, cvt, cvt, none, none, none, cvt, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_cy} (err, err, cvt, cvt, cvt, none, none, none, cvt, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_date} (err, err, cvt, cvt, cvt, none, none, none, cvt, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_bstr} (err, err, cvt, cvt, cvt, cvt, cvt, cvt, none, err, err, cvt, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_dispatch} (err, err, err, err, err, err, err, err, err, none, err, err, none, none, err, err, err, err, err, err, err {$IFDEF DELPHI2009},err{$ENDIF}),
{vt_error} (err, err, err, err, err, err, err, err, err, err, none, err, none, err, err, err, err, err, err, err, err {$IFDEF DELPHI2009},err{$ENDIF}),
{vt_bool} (err, err, cvt, cvt, err, err, err, err, cvt, err, err, none, none, err, cvt, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_variant} (cvt, cvt, cvt, cvt, cvt, cvt, cvt, cvt, cvt, cvt, cvt, cvt, none, cvt, cvt, cvt, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_unknown} (err, err, err, err, err, err, err, err, err, err, err, err, none, none, err, err, err, err, err, err, err {$IFDEF DELPHI2009},err{$ENDIF}),
{vt_decimal} (err, err, cvt, cvt, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, none, err, cvt, cvt, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{0x0f } (err, err, err, err, err, err, err, err, err, err, err, err, none, err, err, none, err, err, err, err, err {$IFDEF DELPHI2009},err{$ENDIF}),
{vt_i1} (err, err, cvt, cvt, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, none, none, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_ui1} (err, err, cvt, cvt, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, none, none, cvt, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_ui2} (err, err, none, cvt, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, none, none, none, cvt, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_ui4} (err, err, none, none, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, none, none, none, none, cvt {$IFDEF DELPHI2009},cvt{$ENDIF}),
{vt_i8} (err, err, none, none, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, none, none, none, none, none {$IFDEF DELPHI2009},none{$ENDIF})
{$IFDEF DELPHI2009}
{vt_ui8} ,(err, err, none, none, cvt, cvt, cvt, cvt, cvt, err, err, cvt, none, err, cvt, err, none, none, none, none, none, none)
{$ENDIF}
);
begin
if Source = Dest then
Result := none
else
// < Low(Codes) always evaluates to false since it is zero
if {(Source < Low(Codes)) or} (Source > High(Codes)) or
{(Dest < Low(Codes)) or} (Dest > High(Codes)) then
Result := cvt
else
Result := Codes[Source][Dest];
end;
function InterfaceDerivesFrom(TypeData: PTypeData; const GUID: TGUID): Boolean;
begin
Result := True;
while TypeData <> nil do
begin
if IsEqualGUID(TypeData^.Guid, GUID) then
Exit;
if (TypeData^.IntfParent <> nil) and (TypeData^.IntfParent^ <> nil) then
TypeData := GetTypeData(TypeData^.IntfParent^)
else
Break;
end;
Result := False;
end;
function GetVariantType(TypeInfo: PTypeInfo): TVarType;
var
TypeData: PTypeData;
begin
case TypeInfo^.Kind of
tkUnknown: Result := varError;
tkInteger, tkChar, tkEnumeration, tkWChar:
if (TypeInfo = System.TypeInfo(Boolean)) or
(TypeInfo = System.TypeInfo(WordBool)) or
(TypeInfo = System.TypeInfo(LongBool)) then
Result := varBoolean
else
begin
TypeData := GetTypeData(TypeInfo);
if TypeData^.MinValue >= 0 then
if Cardinal(TypeData^.MaxValue) > $FFFF then
Result := varLongWord
else if TypeData^.MaxValue > $FF then
Result := varWord
else
Result := varByte
else
if (TypeData^.MaxValue > $7FFF) or (TypeData^.MinValue < -$7FFF - 1) then
Result := varInteger
else if (TypeData^.MaxValue > $7F) or (TypeData^.MinValue < -$7F - 1) then
Result := varSmallint
else
Result := varShortint;
end;
tkFloat:
begin
TypeData := GetTypeData(TypeInfo);
case TypeData^.FloatType of
ftSingle: Result := varSingle;
ftDouble:
if TypeInfo = System.TypeInfo(TDateTime) then
Result := varDate
else
Result := varDouble;
ftComp, ftCurr: Result := varCurrency;
else
Result := varError;
end;
end;
tkString: Result := varString;
tkLString: Result := varString;
{$IFDEF DELPHI2009}
tkUString: Result := varUString;
{$ENDIF}
tkWString: Result := varOleStr;
tkInterface:
begin
TypeData := GetTypeData(TypeInfo);
if InterfaceDerivesFrom(TypeData, IDispatch) then
Result := varDispatch
else
Result := varUnknown;
end;
tkVariant: Result := varVariant;
tkInt64:
{$IFDEF DELPHI2009}
begin
TypeData := GetTypeData(TypeInfo);
if TypeData^.MinInt64Value >= 0 then
Result := varUInt64
else
Result := varInt64;
end;
{$ELSE}
Result := varInt64;
{$ENDIF}
tkClass: Result := varInteger;
else
Result := varError;
end;
end;
procedure GetFloatReturn(var Ret; FloatType: TFloatType);
asm
CMP EDX, ftSingle
JE @@Single
CMP EDX, ftDouble
JE @@Double
CMP EDX, ftExtended
JE @@Extended
CMP EDX, ftCurr
JE @@Curr
CMP EDX, ftComp
JE @@Curr // Same as Curr
// should never get here
@@Single:
FSTP DWORD PTR [EAX]
WAIT
RET
@@Double:
FSTP QWORD PTR [EAX]
WAIT
RET
@@Extended:
FSTP TBYTE PTR [EAX]
WAIT
RET
@@Curr:
FISTP QWORD PTR [EAX]
WAIT
end;
function GetMethods(ClassType: TClass): TMethodInfoArray;
var
VMT: Pointer;
MethodInfo: Pointer;
Count: Integer;
I: Integer;
begin
Count := 0;
VMT := ClassType;
repeat
MethodInfo := PPointer(Integer(VMT) + vmtMethodTable)^;
if MethodInfo <> nil then
Inc(Count, PWord(MethodInfo)^);
// Find the parent VMT
VMT := PPointer(Integer(VMT) + vmtParent)^;
if VMT = nil then
Break;
VMT := PPointer(VMT)^;
until False;
SetLength(Result, Count);
I := 0;
VMT := ClassType;
repeat
MethodInfo := PPointer(Integer(VMT) + vmtMethodTable)^;
if MethodInfo <> nil then
begin
Count := PWord(MethodInfo)^;
Inc(Integer(MethodInfo), SizeOf(Word));
while Count > 0 do
begin
Result[I] := MethodInfo;
Inc(I);
Inc(Integer(MethodInfo), PMethodInfoHeader(MethodInfo)^.Len);
Dec(Count);
end;
end;
// Find the parent VMT
VMT := PPointer(Integer(VMT) + vmtParent)^;
if VMT = nil then
Exit;
VMT := PPointer(VMT)^;
until False;
end;
function GetMethodInfo(Instance: TObject; const MethodName: ShortString): PMethodInfoHeader;
var
VMT: Pointer;
MethodInfo: Pointer;
Count: Integer;
begin
// Find the method
VMT := PPointer(Instance)^;
repeat
MethodInfo := PPointer(Integer(VMT) + vmtMethodTable)^;
if MethodInfo <> nil then
begin
// Scan method table for the method
Count := PWord(MethodInfo)^;
Inc(Integer(MethodInfo), 2);
while Count > 0 do
begin
Result := MethodInfo;
if {$IFDEF DELPHI2009}SamePropTypeName{$ELSE}SameText{$ENDIF}(Result^.Name, MethodName) then
Exit;
Inc(Integer(MethodInfo), PMethodInfoHeader(MethodInfo)^.Len);
Dec(Count);
end;
end;
// Find the parent VMT
VMT := PPointer(Integer(VMT) + vmtParent)^;
if VMT = nil then
begin
Result := nil;
Exit;
end;
VMT := PPointer(VMT)^;
until False;
end;
resourcestring
sMethodNotFound = 'Method %s of class %s not found';
sTypeMisMatch = 'Type mismatch in parameter %d for method %s';
sInvalidDispID = 'Invalid DispID for parameter %d in method %s';
sParamRequired = 'Parameter %d required for method %s';
sMethodOver = 'Method definition for %s has over %d parameters';
sTooManyParams = 'Too many parameters for method %s';
/// ObjectInvoke - function to dymically invoke a method of an object that
/// has sufficient type information.
/// Instance - the object to invoke the method on
/// MethodHeader - the type information for the method obtained through
/// GetMethodInfo.
/// ParamIndexs - the indexs of the parameters. This assumes that the
/// indexs are 1 offset. The number of indexs do not need
/// to match the number of parameters. The parameters left
/// over are assumed to fill in the holes left by indexs.
/// Param indexs are assumed to be in lexical order, not
/// inverse lexical order like Params.
/// Params - the parameters for the function invocation. The
/// order of the parameters is assumed to be in inverse
/// lexical order, last parameter first.
function ObjectInvoke(Instance: TObject; MethodHeader: PMethodInfoHeader;
const ParamIndexes: array of Integer;
const Params: array of Variant): Variant;
const
MaxParams = 32;
procedure Swap(var A, B: PParamInfo);
var
T: PParamInfo;
begin
T := A;
A := B;
B := T;
end;
var
MethodName: string;
procedure ParameterMismatch(I: Integer);
begin
raise Exception.CreateFmt(sTypeMisMatch, [I, MethodName]);
end;
var
MethodInfo: Pointer;
ReturnInfo: PReturnInfo;
MethodAddr: Pointer;
InfoEnd: Pointer;
Count: Integer;
I, K, P: Integer;
Param: PParamInfo;
Regs: array[paEAX..paECX] of Cardinal;
RetVal: Variant;
ParamType: TVarType;
VarType: TVarType;
ParamVarData: PVarData;
PushData: Pointer;
ParamBytes: Integer;
Size: Integer;
Frame: {$IFDEF DELPHI2009}PByte{$ELSE}PChar{$ENDIF};
ResultParam: Pointer;
ResultPointer: Pointer;
ParamInfos: array[0..MaxParams - 1] of PParamInfo;
ParamData: array[0..MaxParams - 1] of Pointer;
Pointers: array[0..MaxParams - 1] of Pointer;
Temps: array[0..MaxParams - 1] of Variant;
begin
// MethodInfo now points to the method we found.
MethodInfo := MethodHeader;
MethodAddr := MethodHeader^.Addr;
MethodName := {$IFDEF DELPHI2009}UTF8ToString({$ENDIF} PMethodInfoHeader(MethodInfo)^.Name{$IFDEF DELPHI2009}){$ENDIF};
Inc(Integer(MethodInfo), SizeOf(TMethodInfoHeader) - SizeOf(ShortString) + 1 +
Length(MethodName));
ReturnInfo := MethodInfo;
Inc(Integer(MethodInfo), SizeOf(TReturnInfo));
InfoEnd := Pointer(Integer(MethodHeader) + MethodHeader^.Len);
Count := 0;
while Integer(MethodInfo) < Integer(InfoEnd) do
begin
if Count >= MaxParams then
raise Exception.CreateFmt(sMethodOver, [MethodName, MaxParams]);
ParamInfos[Count] := MethodInfo;
Inc(Count);
Inc(Integer(MethodInfo), SizeOf(TParamInfo) - SizeOf(ShortString) + 1 +
Length(PParamInfo(MethodInfo)^.Name));
end;
if High(Params) >= Count then
raise Exception.CreateFmt(sTooManyParams, [MethodName]);
// Fill the ParamData array, converting the type as necessary, taking
// into account any ParamIndexes supplied
P := 0;
FillChar(ParamData, SizeOf(ParamData), 0);
for I := 0 to High(Params) do
begin
// Figure out what parameter index this parameter refers to.
// If it is a named parameter it will have an entry in the ParamIndexs
// array. If not, P points to the current parameter to use for unnamed
// parameters. K is the formal parameter number.
// This calculation assumes Self is first and any result parameters are last
if I <= High(ParamIndexes) then
begin
K := ParamIndexes[I];
if K >= Count then
raise Exception.CreateFmt(sInvalidDispID, [I, MethodName]);
end
else
K := High(Params) - P + 1; // Add one to account for Self
Param := ParamInfos[K];
ParamType := GetVariantType(Param^.ParamType^);
ParamVarData := @Params[I];
VarType := ParamVarData^.VType;
if Param^.Flags * [pfOut, pfVar] <> [] then
begin
// For pfVar, the variant must be a byref and equal to the type.
if (VarType <> ParamType or varByRef) and (ParamType <> varVariant) then
ParameterMismatch(I);
end
else
// Convert the parameter to the right type
case ConvertKindOf(VarType and varTypeMask, ParamType) of
ckConvert:
try
Temps[I] := VarAsType(Params[I], ParamType);
// The data bytes for sizes < 4 are dirty, that is they are not
// guarenteed to have 0's in the high bytes. We need them to be zero'ed
if ParamType <= CMaxArrayVarType then
case CVarTypeToElementInfo[ParamType].Size of
1: TVarData(Temps[I]).VLongWord := TVarData(Temps[I]).VByte;
2: TVarData(Temps[I]).VLongWord := TVarData(Temps[I]).VWord;
end;
ParamVarData := @Temps[I];
except
ParameterMismatch(I);
end;
ckError: ParameterMismatch(I);
end;
if ParamType = varVariant then
begin
Pointers[K] := ParamVarData;
ParamData[K] := @Pointers[K];
end
else if varByRef and VarType <> 0 then
ParamData[K] := @ParamVarData^.VPointer
else
ParamData[K] := @ParamVarData^.VInteger;
// Update P which is the pointer to the current non-named parameter.
// This assumes that unnamed parameter fill in the holes left by
// named parameters.
while (P <= High(Params)) and (ParamData[High(Params) - P + 1] <> nil) do
Inc(P);
end;
// Set up the call frame RET EBP
ParamBytes := ReturnInfo^.ParamSize - (4 + 4);
asm
SUB ESP,ParamBytes
MOV Frame,ESP
end;
Dec(Integer(Frame), 4 + 4); // Access numbers include RET and EBP
// Push the parameters on the stack (or put them into the correct register)
ResultParam := nil;
for I := 0 to Count - 1 do
begin
Param := ParamInfos[I];
PushData := ParamData[I];
if PushData = nil then
if (Param^.ParamType^.Kind = tkClass) and {$IFDEF DELPHI2009}SamePropTypeName{$ELSE}SameText{$ENDIF}(Param^.Name, 'SELF') then
// Self is special. It doesn't appear in the ParamData array since it
// is not represented in the Params array.
PushData := @Instance
else if pfResult in Param^.Flags then
begin
ResultParam := Param;
VarClear(Result);
TVarData(Result).VType := GetVariantType(Param^.ParamType^);
if TVarData(Result).VType = varVariant then
ResultPointer := @Result
else
ResultPointer := @TVarData(Result).VInteger;
PushData := @ResultPointer;
end
else
raise Exception.CreateFmt(sParamRequired, [I, MethodName]);
if Param^.Access < Word(Ord(paStack)) then
Regs[Param^.Access] := PCardinal(PushData)^
else
begin
if [pfVar, pfOut, pfResult] * Param^.Flags <> [] then
PCardinal(@Frame[Param^.Access])^ := PCardinal(PushData)^
else
begin
Size := GetTypeSize(Param^.ParamType^);
case Size of
1, 2, 4:
PCardinal(@Frame[Param^.Access])^ := PCardinal(PushData)^;
8:
begin
PCardinal(@Frame[Param^.Access])^ := PCardinal(PushData)^;
PCardinal(@Frame[Param^.Access + 4])^ :=
PCardinal(Integer(PushData) + 4)^;
end;
else
Move(PushData^, Frame[Param^.Access and not 3], Size);
end;
end;
end;
end;
// Do the call
asm
MOV EAX,DWORD PTR Regs[0]
MOV EDX,DWORD PTR Regs[4]
MOV ECX,DWORD PTR Regs[8]
CALL MethodAddr
MOV DWORD PTR Regs[0],EAX
MOV DWORD PTR Regs[4],EDX
end;
if ReturnInfo^.CallingConvention = ccCdecl then
asm
ADD ESP,ParamBytes
end;
if (ResultParam = nil) and (ReturnInfo^.ReturnType <> nil) then
begin
// The result came back in registers. Otherwise a result pointer was used
// and the return variant is already initialized (or it was a procedure)
TVarData(RetVal).VType := GetVariantType(ReturnInfo^.ReturnType^);
if ReturnInfo^.ReturnType^.Kind = tkFloat then
GetFloatReturn(TVarData(RetVal).VDouble, GetTypeData(ReturnInfo^.ReturnType^)^.FloatType)
else
begin
// For regular Boolean types, we must convert it to a boolean to
// wipe the high order bytes; otherwise the caller may see a false
// as true.
if (TVarData(RetVal).VType = varBoolean) and
(ReturnInfo^.ReturnType^ = System.TypeInfo(Boolean)) then
TVarData(RetVal).VInteger := Integer(Boolean(Regs[paEAX]))
else
TVarData(RetVal).VInteger := Integer(Regs[paEAX]);
PCardinal(Integer(@TVarData(RetVal).VInteger) + 4)^ := Regs[paEDX];
end;
Result := RetVal;
TVarData(RetVal).VType := varEmpty;
end;
end;
function GetReturnInfo(aObj: TObject; aMethodName: string): PReturnInfo;
var
mi: PMethodInfoHeader;
begin
mi := GetMethodInfo(aObj, ShortString(aMethodName));
if mi.Len <= SizeOf(TMethodInfoHeader) + Length(mi.Name) - SHORT_LEN then
begin
Result := nil;
Exit;
end;
Result := PReturnInfo(integer(mi) + SizeOf(TMethodInfoHeader) + Length(mi.Name) - SHORT_LEN);
end;
function GetParams(aObj: TObject; aMethodName: string): TParamInfoArray;
var
mi: PMethodInfoHeader;
miEnd: Pointer;
param: PParamInfo;
Count: integer;
begin
SetLength(Result, 0);
mi := GetMethodInfo(aObj, ShortString(aMethodName));
if mi.Len <= SizeOf(TMethodInfoHeader) + Length(mi.Name) - SHORT_LEN then
Exit;
miEnd := Pointer(integer(mi) + mi.Len);
param := PParamInfo(integer(mi) + SizeOf(TMethodInfoHeader) + Length(mi.Name) - SHORT_LEN + SizeOf(TReturnInfo));
Count := 0;
while integer(param) < integer(miEnd) do
begin
Inc(Count);
SetLength(Result, Count);
Result[Count - 1] := param;
param := PParamInfo(integer(param) + SizeOf(TParamInfo) + Length(param.Name) - SHORT_LEN);
end;
end;
type
PParameterInfos = ^TParameterInfos;
TParameterInfos = array[0..255] of ^PTypeInfo;
TBaseMethodHandlerInstance = class
protected
TypeData: PTypeData;
ParamInfos: PParameterInfos;
ParamOffsets: array of Word;
StackSize: Integer;
procedure InternalHandler(Params: Pointer);
procedure Handler(Params: Pointer); virtual; abstract;
procedure RegisterStub;
public
constructor Create(TypeData: PTypeData);
end;
TMethodHandlerInstance = class(TBaseMethodHandlerInstance)
protected
MethodHandler: IMethodHandler;
procedure Handler(Params: Pointer); override;
public
constructor Create(const MethodHandler: IMethodHandler; TypeData: PTypeData);
end;
TEventHandlerInstance = class(TBaseMethodHandlerInstance)
protected
FDynamicInvokeEvent: TDynamicInvokeEvent;
procedure Handler(Params: Pointer); override;
public
constructor Create(const ADynamicInvokeEvent: TDynamicInvokeEvent; TypeData: PTypeData);
end;
function AdditionalInfoOf(TypeData: PTypeData): Pointer;
var
P: {$IFDEF DELPHI2009}PByte{$ELSE}PChar{$ENDIF};
I: Integer;
begin
P := @TypeData^.ParamList;
// Skip parameter names and types
for I := 1 to TypeData^.ParamCount do
begin
Inc(P, 1 + {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[1]) + 1);
Inc(P, {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[0]) + 1);
end;
if TypeData^.MethodKind = mkFunction then
// Skip return type name and info
Inc(P, {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[0]) + 1 + 4);
Result := P;
end;
function CreateMethodPointer(const MethodHandler: IMethodHandler; TypeData: PTypeData): TMethod;
begin
Result.Data := TMethodHandlerInstance.Create(MethodHandler, TypeData);
Result.Code := @TMethodHandlerInstance.RegisterStub;
end;
function CreateMethodPointer(const ADynamicInvokeEvent: TDynamicInvokeEvent; TypeData: PTypeData): TMethod; overload;
begin
Result.Data := TEventHandlerInstance.Create(ADynamicInvokeEvent, TypeData);
Result.Code := @TMethodHandlerInstance.RegisterStub;
end;
procedure ReleaseMethodPointer(MethodPointer: TMethod);
begin
TObject(MethodPointer.Data).Free;
end;
function GetInvokeInstance(MethodPointer: TMethod): TObject;
begin
if TObject(MethodPointer.Data) is TEventHandlerInstance then
Result := TObject(TMethod(TEventHandlerInstance(MethodPointer.Data).FDynamicInvokeEvent).Data)
else
Result := nil;
end;
{ TBaseMethodHandlerInstance }
constructor TBaseMethodHandlerInstance.Create(TypeData: PTypeData);
var
P: {$IFDEF DELPHI2009}PByte{$ELSE}PChar{$ENDIF};
Offset: Integer;
CurReg: Integer;
I: Integer;
Size: Integer;
begin
Self.TypeData := TypeData;
P := AdditionalInfoOf(TypeData);
ParamInfos := PParameterInfos(Cardinal(P) + 1);
// Calculate stack size
CurReg := paEDX;
P := @TypeData^.ParamList;
StackSize := 0;
for I := 0 to TypeData^.ParamCount - 1 do
begin
if TParamFlags(P[0]) * [pfVar, pfConst, pfAddress, pfReference, pfOut] <> [] then
Size := 4
else
Size := GetTypeSize(ParamInfos^[I]^);
if (Size <= 4) and (CurReg <= paECX) then
Inc(CurReg)
else
Inc(StackSize, Size);
Inc(P, 1 + {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[1]) + 1);
Inc(P, {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[0]) + 1);
end;
// Calculate parameter offsets
SetLength(ParamOffsets, TypeData^.PropCount);
CurReg := paEDX;
P := @TypeData^.ParamList;
Offset := StackSize;
for I := 0 to TypeData^.ParamCount - 1 do
begin
if TParamFlags(P[0]) * [pfVar, pfConst, pfAddress, pfReference, pfOut] <> [] then
Size := 4
else
Size := GetTypeSize(ParamInfos^[I]^);
if (Size <= 4) and (CurReg <= paECX) then
begin
ParamOffsets[I] := CurReg;
Inc(CurReg);
end
else
begin
Dec(Offset, Size);
ParamOffsets[I] := Offset;
end;
Inc(P, 1 + {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[1]) + 1);
Inc(P, {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[0]) + 1);
end;
end;
procedure TBaseMethodHandlerInstance.InternalHandler(Params: Pointer);
asm
MOV ECX,[EAX]
JMP DWORD PTR [ECX] + VMTOFFSET TMethodHandlerInstance.Handler
end;
procedure TBaseMethodHandlerInstance.RegisterStub;
const
PtrSize = SizeOf(Pointer);
asm
PUSH EAX
PUSH ECX
PUSH EDX
MOV EDX,ESP
CALL InternalHandler
// Pop EDX and ECX off the stack while preserving all registers.
MOV [ESP+4],EAX
POP EAX
POP EAX
POP ECX // Self
MOV ECX,[ECX].TMethodHandlerInstance.StackSize
TEST ECX,ECX
JZ @@SimpleRet
// Jump to the actual return instruction since it is most likely not just a RET
//JMP ECX // Data Exec. Prevention: Jumping into a GetMem allocated memory block
// stack address alignment
ADD ECX, PtrSize - 1
AND ECX, NOT (PtrSize - 1)
AND ECX, $FFFF
// clean up the stack
PUSH EAX // we need this register, so save it
MOV EAX,[ESP + 4] // Load the return address
MOV [ESP + ECX + 4], EAX // Just blast it over the first param on the stack
POP EAX
ADD ESP,ECX // This will move the stack back to where the moved
// return address is now located. The next RET
// instruction will do the final stack cleanup
@@SimpleRet:
end;
{ TMethodHandlerInstance }
constructor TMethodHandlerInstance.Create(const MethodHandler: IMethodHandler;
TypeData: PTypeData);
begin
inherited Create(TypeData);
Self.MethodHandler := MethodHandler;
end;
procedure TMethodHandlerInstance.Handler(Params: Pointer);
const
MaxParams = 10;
var
P: {$IFDEF DELPHI2009}PByte{$ELSE}PChar{$ENDIF};
Parameters: PParameters;
ReturnValue: Variant;
ParamValues: array[0..MaxParams - 1] of Variant;
I: Integer;
Regs: array[paEAX..paEDX] of Cardinal;
Offset: Integer;
Data: Pointer;
Temp: Variant;
begin
Parameters := Params;
// Fetch the parameters into ParamValues
P := @TypeData^.ParamList;
for I := 0 to TypeData^.ParamCount - 1 do
begin
Offset := ParamOffsets[I];
if (Offset >= paEDX) and (Offset <= paECX) then
Data := @Parameters^.Registers[Offset]
else
Data := @Parameters^.Stack[Offset];
if ParamInfos^[I]^.Kind = tkClass then
ParamValues[TypeData^.ParamCount - I - 1] := MethodHandler.InstanceToVariant(PPointer(Data)^)
else if TParamFlags(P[0]) * [pfVar, pfOut] <> [] then
with TVarData(ParamValues[TypeData^.ParamCount - I - 1]) do
begin
VType := GetVariantType(ParamInfos[I]^) or varByRef;
VPointer := Pointer(PCardinal(Data)^);
end
else
begin
TVarData(Temp).VType := GetVariantType(ParamInfos[I]^) or varByRef;
if TParamFlags(P[0]) * [pfVar, pfOut] <> [] then
TVarData(Temp).VPointer := Pointer(PCardinal(Data)^)
else
TVarData(Temp).VPointer := Data;
ParamValues[TypeData^.ParamCount - I - 1] := Temp;
end;
Inc(P, 1 + {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[1]) + 1);
Inc(P, {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[0]) + 1);
end;
// P is left pointing to the return type name if there is one.
ReturnValue := MethodHandler.Execute(Slice(ParamValues, TypeData^.ParamCount));
if TypeData^.MethodKind = mkFunction then
begin
Inc(P, {$IFNDEF DELPHI2009}Byte{$ENDIF}(P[0]) + 1);
ReturnValue := VarAsType(ReturnValue, GetVariantType(PPTypeInfo(P)^));
if PPTypeInfo(P)^.Kind = tkFloat then
else
begin
Regs[paEAX] := TVarData(ReturnValue).VLongWord;
Regs[paEDX] := PCardinal(Integer(@TVarData(ReturnValue).VLongWord) + 4)^;
end;
end;
// Let Stub procedures know where the RET instruction is
asm
MOV EAX,DWORD PTR Regs[paEAX*4]
MOV EDX,DWORD PTR Regs[paEDX*4]
end;
end;
{ TEventHandlerInstance }
constructor TEventHandlerInstance.Create(const ADynamicInvokeEvent: TDynamicInvokeEvent; TypeData: PTypeData);
begin
inherited Create(TypeData);
Self.FDynamicInvokeEvent := ADynamicInvokeEvent;
end;
procedure TEventHandlerInstance.Handler(Params: Pointer);
begin
if Assigned(FDynamicInvokeEvent) then
FDynamicInvokeEvent(Params, StackSize);
end;
end.