Zitat:
Ist "Patchen der
RTTI der neuen Klasse" das das einzige? Und wie würde das geht das?
Der richtige Weg ist es "der Programmierer der Komponente DENKT bei seiner Arbeit nach und deklariert solche Setter Methoden als protected und dynamic". Ein System/Komponenten-Entwickler zeichnet sich dadurch aus das er in der Lage ist über seinen eigenen Horizont hinaus zu entwickeln. Leider sind immer weniger gute Entwickler bei Borland beschäftigt ! Dein Problem ist also nicht das Einzigste das ich in der
VCL als Fehlkonstruktion bezeichnen würde.
Das Patchen einer Setter-Methode erfolgt dabei nicht über die
VMT der Klasse, sondern man patcht den
RTTI Eintrag zu der Property. Deshalb muß die Property in irgendeiner Vorfahrklasse schon mal als published deklariert worden sein, ansonsten gibt es nämlich keine
RTTI zu dieser Property. Ist das der Fall so findet man in der
RTTI zu dieser Property folgende Struktur:
Delphi-Quellcode:
PPropInfo = ^TPropInfo;
TPropInfo = packed record
PropType: PPTypeInfo;
GetProc: Pointer;
SetProc: Pointer;
StoredProc: Pointer;
Index: Integer;
Default: Longint;
NameIndex: SmallInt;
Name: ShortString;
end;
in
Unit TypInfo.pas.
Mit
GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo;
kommt man an diesen Record der ja als
RTTI im Codesegment gespreichert wurde ran.
GetPropInfo(TMyWinControl.ClassInfo, 'Cursor)');
In der PPropInfo.SetProc steht dann der Zeiger der Setter Methode. Aber VORSICHT! dieser Zeiger kann unterschiedliche Bedeutungen haben da ja unsere Setter Methode entweder
1.) statisch ist -> SetProc ist dann ein Zeiger direkt auf die Methode
2.) virtual ist -> SetProc ist dann ein kombinierter Wert aus $01 XXYYZZ und XXYYZZ ist der
VMT Slot Index in die
VMT wo dann erst unsere virtuelle Methode drinnen steht
3.) dynamic ist -> SetProc ist dannn ein kombinierter Wert aus $02 XXYYZZ und XXYYZZ ist der DMT Identify in die DMT wo dann die Addresse der dynamischen Methode drinnen steht.
4.) wenn in SetProc aber das oberste Byte $FE ist so stellt der Rest ein Offest beginnend bei der Addresse der Objekt Instance zu einem privaten Feld dar. Das heist es gibt defakto garkeine SetProc sondern nur ein direkter Zugriff in den Speicher des Objectes. SetProc stellt dann also nur den Offset zu diesem Feld innerhalb des Objectes dar.
Gut, in deinem Falle kannst du davon ausgehen das SetProc ein Zeiger auf eine statische Methode darstellt, einfach indem du bei TWinControl nachschaust wie die Setter Methode deklariert wurde.
Das Patchen ist nun einfach. Du ermittelst wie oben gezeigt die PPropInfo für Property "Cursor" zu deiner Klasse. Gepatcht wird nun @PPropInfo^.SetProc in dem du dort einen Zeiger auf deine Setter Methode rein patcht.
Dies gilt dann für deine Klasse, also für ALLE Instancen = Objecte die von deinem Klassentyp sind !! Man patcht also nur einmalig, zb. in der Initialisation Sektion deiner
Unit, und danach benutzen alle Instancen deiner Klasse die neue Setter Methode.
Schön ist das aber nicht, das möchte ich hier nochmals betonen.
Gruß Hagen
PS: nachzulesen hier für Delphi 5
Delphi-Quellcode:
procedure SetOrdProp(Instance: TObject; PropInfo: PPropInfo;
Value: Longint);
assembler;
asm
{ -> EAX Pointer to instance }
{ EDX Pointer to property info }
{ ECX Value }
PUSH EBX
PUSH ESI
PUSH EDI
MOV EDI,EDX
MOV ESI,[EDI].TPropInfo.PropType
MOV ESI,[ESI]
MOV BL,otSLong
CMP [ESI].TTypeInfo.Kind,tkClass
JE @@isClass
XOR EBX,EBX
MOV BL,[ESI].TTypeInfo.
Name.Byte[0]
MOV BL,[ESI].TTypeInfo.
Name[EBX+1].TTypeData.OrdType
@@isClass:
MOV EDX,[EDI].TPropInfo.
Index { pass Index in DX }
CMP EDX,$80000000
JNE @@hasIndex
MOV EDX,ECX
{ pass value in EDX }
@@hasIndex:
MOV ESI,[EDI].TPropInfo.SetProc
CMP [EDI].TPropInfo.SetProc.Byte[3],$FE
JA @@isField
JB @@isStaticMethod
{ SetProc turned out to be a virtual method. call it }
MOVSX ESI,SI
{ sign extend slot offset }
ADD ESI,[EAX]
{ vmt + slot offset }
CALL dword ptr [ESI]
JMP @@exit
@@isStaticMethod:
CALL ESI
JMP @@exit
@@isField:
AND ESI,$00FFFFFF
ADD EAX,ESI
MOV [EAX],CL
CMP BL,otSWord
JB @@exit
MOV [EAX],CX
CMP BL,otSLong
JB @@exit
MOV [EAX],ECX
@@exit:
POP EDI
POP ESI
POP EBX
end;