GetOrdProp 函数详解
SetOrdProp 函数
GetEnumProp / SetEnumProp 函数
GetSetProp / SetSetProp 函数
GetObjectProp / SetObjectProp 函数
GetOrdProp 是 Delphi RTTI 中使用频繁的函数。GetOrdProp 根据对象句柄和对象属性的 TPropInfo 指针获得对象的属性值。它的返回值是 Longint,需要强制转换成相应的属性类型才能使用。
function GetOrdProp(Instance: TObject; PropInfo: PPropInfo): Longint;
GetOrdProp 调用 TPropInfo.GetProc 函数指针得到属性的返回值。它的工作过程是:
如果该属性的类型是 class 类型,那么返回值是 4 个字节(对象句柄)。
否则通过 TTypeData.OrdType 得到返回值的类型,存储在 BL 中。
{ TOrdType = (otSByte, otUByte, otSWord, otUWord, otSLong, otULong); }
检查 TPropInfo.GetProc 的第一个字节(注意是 GetProc 指针的第一个字节):
如果 GetProc[0] = $FF,说明 GetProc 是 field offset;
如果 GetProc[0] = $FE,说明 GetProc 是 virtual method offset;
如果 GetProc[0] < $FE,说明 GetProc 是 static method;
然后根据不同的 GetProc 类型解析后,调用 GetProc。
根据 BL 中存储的类型符号信息修正返回值(EAX)的符号信息。
根据 BL 中存储的类型的大小裁剪返回值 EAX 为 EAX/AX/AL。
EAX(AX/AL) 即是返回的属性值。
GetOrdProp 的汇编代码及注释如下:
function GetOrdProp(Instance: TObject; PropInfo: PPropInfo): Longint; asm PUSH EBX PUSH EDI MOV EDI,[EDX].TPropInfo.PropType ; EDI <- PPTypeInfo MOV EDI,[EDI] ; EDI <- PTypeInfo MOV BL,otSLong ; BL <- otSLong CMP [EDI].TTypeInfo.Kind,tkClass ; if Prop is Class JE @@isClass ; jmp @@isClass XOR ECX,ECX ; ECX <- 0 MOV CL,[EDI].TTypeInfo.Name.Byte[0] ; CL <- Name StrLength MOV BL,[EDI].TTypeInfo.Name[ECX+1].TTypeData.OrdType ; BL <- Prop OrdType @@isClass: MOV ECX,[EDX].TPropInfo.GetProc ; ECX <- GetProc Addr CMP [EDX].TPropInfo.GetProc.Byte[3],$FE ; cmp HiByte(GetProc), $FE MOV EDX,[EDX].TPropInfo.Index ; EDX <- Prop Index JB @@isStaticMethod ; if below $FE JA @@isField ; if is $FF { the GetProc is a virtual method } ; if is $FE MOVSX ECX,CX { sign extend slot offs } ADD ECX,[EAX] { vmt + slotoffs } CALL dword ptr [ECX] { call vmt[slot] } JMP @@final @@isStaticMethod: CALL ECX ; call GetProc directly JMP @@final @@isField: AND ECX,$00FFFFFF ; clear HiByte(GetProc) ADD ECX,EAX ; ECX <- Field Addr MOV AL,[ECX] ; AL <- Field Addr[0] CMP BL,otSWord ; if OrdType < otSWord JB @@final ; Exit MOV AX,[ECX] ; else AX <- Field[0..1] CMP BL,otSLong ; if OrdType < otSLong JB @@final ; Exit MOV EAX,[ECX] ; else EAX <- Field[0..3] @@final: CMP BL,otSLong ; if OrdType >= otSLong JAE @@exit ; Exit CMP BL,otSWord ; if OrdType >= otSWord JAE @@word ; jmp @@word CMP BL,otSByte ; if OrdType = otSByte MOVSX EAX,AL ; AL <- Sign(EAX) JE @@exit ; Exit AND EAX,$FF ; clear HiWord(EAX) JMP @@exit ; Exit @@word: MOVSX EAX,AX ; AX <= Sign(EAX) JE @@exit ; if OrdType = otSWord then Exit AND EAX,$FFFF ; clear HiWord(EAX) @@exit: POP EDI POP EBX end;
TypInfo.pas 中重载了 GetOrdProp 函数,将 PPropInfo 参数替换为 PropName,方便程序员调用,它其实也是调用了上面介绍的 GetOrdProp 函数。
function GetOrdProp(Instance: TObject; const PropName: string): Longint; begin Result := GetOrdProp(Instance, FindPropInfo(Instance, PropName)); end;
下面是使用 GetOrdProp 的例子:
Self.Width := Self.Width - GetOrdProp(Self, 'Height');
上面的语句相当于:
Self.Width := Self.Width - Self.Height;
* 后文介绍的 Get___Prop 系列函数或者调用本函数,或者它的实现方法与本函数类似。
SetOrdProp 函数是 GetOrdProp 的逆过程,它调用 TPropInfo.SetProc 函数指针设置对象的属性值。SetProc 指针的第一个字节的意义同 GetProc 一样,也是表示该 SetProc 是字段偏移、虚方法偏移和静态方法。
procedure SetOrdProp(Instance: TObject; PropInfo: PPropInfo; Value: Longint);
SetOrdProc 也根据属性名称重载了:
procedure SetOrdProp(Instance: TObject; const PropName: string; Value: Longint);
由于 SetOrdProp 的汇编代码与 GetOrdProp 的几乎一样,在此就不再列出。作为练习,试用一下:
SetOrdProp(Self, 'Height', Self.Height + 10);
该语句的功能相当于:
Self.Height := Self.Height + 10;
* 后文介绍的 Set___Prop 系列函数或者调用本函数,或者它的实现方法与本函数类似。
GetEnumProp 函数获取枚举类型属性的枚举字符串,它调用 GetEnumName 转换 GetOrdProp 的返回值。
function GetEnumProp(Instance: TObject; PropInfo: PPropInfo): string;
function GetEnumProp(Instance: TObject; const PropName: string): string;
SetEnumProp 函数使用枚举字符串设置枚举类型属性值,它调用 GetEnumValue 转换枚举字符串后再调用 SetOrdProp 设置属性值。
procedure SetEnumProp(Instance: TObject; PropInfo: PPropInfo;
const Value: string);
procedure SetEnumProp(Instance: TObject; const PropName: string;
const Value: string);
GetSetProp 函数用于获取集合类型属性的字符串值,它也是调用 GetOrdProp 获得属性值,然后调用 SetToString 函数把数值转换成字符串。
注意:GetOrdProp 函数返回值是 Integer,那么它是如何表示可以存储 256 个元素的集合类型呢?答案是:如果是 published 集合属性,那么该集合最大只能是 4 个字节,也就是最多只能存储 32 个元素。
function GetSetProp(Instance: TObject; PropInfo: PPropInfo;
Brackets: Boolean): string;
function GetSetProp(Instance: TObject; const PropName: string;
Brackets: Boolean = False): string;
SetSetProp 函数用于通过字符串设置集合类型属性的值。它先调用 StringToSet 函数把字符串转换为整数值,然后使用 SetOrdProp 函数设置属性值。
procedure SetSetProp(Instance: TObject; PropInfo: PPropInfo;
const Value: string);
procedure SetSetProp(Instance: TObject; const PropName: string;
const Value: string);
试验: SetSetProp(Self, 'BorderIcons', '[biSystemMenu]');
对象实际上是指针,也就是整数值,所以 GetObjectProp 直接调用 GetOrdProp 就可以了。
MinClass 参数指定得到的 Object 必须属于某个 class ,如果不是则返回 nil 。
function GetObjectProp(Instance: TObject; PropInfo: PPropInfo;
MinClass: TClass = nil): TObject;
function GetObjectProp(Instance: TObject; const PropName: string;
MinClass: TClass = nil): TObject;
SetObjectProp 用于设置属性的对象句柄。ValidateClass 参数表示是否需要检查传入的对象类型与属性信息的类信息是否兼容。
procedure SetObjectProp(Instance: TObject; PropInfo: PPropInfo;
Value: TObject; ValidateClass: Boolean = True);
procedure SetObjectProp(Instance: TObject; const PropName: string;
Value: TObject);
例子:
var
MyFont: TFont;
begin
MyFont := TFont.Create;
MyFont.Height := 20;
SetObjectProp(Self, 'Font', MyFont);
end;
GetStrProp 函数用于获得字符串类型的属性值。
function GetStrProp(Instance: TObject; PropInfo: PPropInfo): string;
function GetStrProp(Instance: TObject; const PropName: string): string;
由于 Delphi 支持三种类型的字符串,GetStrProp 根据字符串的类型,分别调用三个获得字符串属性值的函数:
case PropInfo^.PropType^.Kind of
tkString: GetShortStrPropAsLongStr(Instance, PropInfo, Result);
tkLString: GetLongStrProp(Instance, PropInfo, Result);
tkWString: GetWideStrPropAsLongStr(Instance, PropInfo, Result);
end;
其中 GetShortStrPropAsLongStr 又调用了 GetShortStrProp;GetWideStrPropAsLongStr 又调用了 GetWideStrProp,进行字符串间的类型转换。
SetStrProp 函数用于设置字符串类型的属性值。它的实现方法与 GetStrProp 类似。
procedure SetStrProp(Instance: TObject; PropInfo: PPropInfo;
const Value: string);
procedure SetStrProp(Instance: TObject; const PropName: string;
const Value: string);