Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Wie private Setter-Methode überschreiben? (https://www.delphipraxis.net/78049-wie-private-setter-methode-ueberschreiben.html)

BlueStarHH 28. Sep 2006 13:39

Re: Wie private Setter-Methode überschreiben?
 
Zitat:

Zitat von negaH
Das funktioniert aber dann nur wenn im Source explizit der Cursor deiner Klasse angesprochen wird. So bald zb. die VCL den Cursor von deinen Vorfahren anspricht bekommst du nichts mehr davon mit. Probiere mal TWinControl(MeineKlassenInstance).Cursor := crXYZ aus.

Stimmt, ich habs ausprobiert, so bekommt meine Klasse nichts mehr vom Cursortausch mit. Also wie von Dir beschrieben die Nachrichten abfangen. Nur was macht man, wenn in anderen Fällen, in denen es nicht um den Cursor geht, keine Nachrichten gibt, die man abfangen kann? Ist "Patchen der RTTI der neuen Klasse" das das einzige? Und wie würde das geht das? Mir ist klar, dass das nicht die feine Art ist. Aber besser so, als gar nicht.

Elvis 28. Sep 2006 14:16

Re: Wie private Setter-Methode überschreiben?
 
Zitat:

Zitat von BlueStarHH
Und wie würde das geht das?

Naja, du suchst dir den Funktionszeiger in der VMT und änderst ihn zu einer anderen Funktion. Aber dafür brauchst du die Adresse der Funktion, die du ersetzen willst. Sonst wirst du wohl nicht den richtigen Slot finden um ihn Umzubiegen...

negaH 28. Sep 2006 15:33

Re: Wie private Setter-Methode überschreiben?
 
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

Delphi-Quellcode:
GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo;
kommt man an diesen Record der ja als RTTI im Codesegment gespreichert wurde ran.

Delphi-Quellcode:
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;

Elvis 28. Sep 2006 15:48

Re: Wie private Setter-Methode überschreiben?
 
Zitat:

Zitat von negaH
Das Patchen einer Setter-Methode erfolgt dabei nicht über die VMT der Klasse, sondern man patcht den RTTI Eintrag zu der Property.

Ich habe den Blödsinn gerade bemerkt, den ich geschrieben habe. Das umbiegen durch Ändern der VMT funktioniert nur bei virtuellen Methoden, wenn deren Sprünge nicht fest in den Code kompiliert wurden. Das würde aber heißen, dass er das Umbiegen nicht gebraucht hätte. *g*
Ich habe das mal gemacht um virtual *class* methods umzubiegen...

negaH 28. Sep 2006 15:57

Re: Wie private Setter-Methode überschreiben?
 
macht nichts, denn das Patchen der RTTI ist ebenfalls nur die halbe Miete. Bei einem Zugriff wie Control.Cursor := crHourGlass benutzt der Compiler ja ebenfalls nicht die RTTI um die Setter Methode zu ermitteln. Der Compiler erzeugt einen direkten Code wie MyControl.SetCursor() weil er ja die privaten statischen/virtuellen Methoden ja selber kennt. Nur wenn man zb. ein Control/Form im OI ändert oder aber aus einer DFM lädt wird die RTL über die RTTI gehen. Deshalb eben nur halbe Miete.

Allerdings kann er mit der RTTI die Addresse im Codesegment ermitteln an der die private Setter Methode steht. Er patcht nun nicht mehr die RTTI der Property sondern überschreibt den Code dr Setter Methode. Er patcht also direkt diese Methode und baut ein JMP zu seiner neuen Setter Methode rein. ALLE Controls die von der Basisklasse abgeleitet sind würden ab diesem Moment seine neue Setter Methode benutzen.

Kein schöne Sache, aber es geht alles ;)

Gruß Hagen

PS: ich werde hier keinen fertigen Source liefern ! selber machen und lernen ist also angesagt.

Lasse2002 29. Sep 2006 10:37

Re: Wie private Setter-Methode überschreiben?
 
Wenn du dir die Function SetCursor etwas genauer anschaust, siehst du, daß dort eine Message verschickt wird. Das bedeutet, daß du nur noch CMCursorChanged überschreiben mußt.

Elvis 30. Sep 2006 20:29

Re: Wie private Setter-Methode überschreiben?
 
Zitat:

Zitat von negaH
PS: ich werde hier keinen fertigen Source liefern ! selber machen und lernen ist also angesagt.

Definitiv! Die Delphi RTL und das Typenstem sind IMO sehr schön gelöst. Und darin Ouchy Banana spielen zu wollen ist ein guter Grund sich damit auseinanderzusetzen.
Und danach eine Entscheidung zu treffen ob es zu ouchy banana ist und eine andere Lösung angebrachter wäre.
Man stelle sich ein Package vor, dass das ungefragt machen würde. :shock:


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:11 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz