Einzelnen Beitrag anzeigen

Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#3

Re: Ändern der Klassenhierarchie

  Alt 26. Sep 2003, 15:02
Die Technik hinter der Lösung

Um die Klassenhierarchie zu beeinflussen, müssen wir ein wenig in die Klassenverwaltung von Delphi eindringen. Das ist gleichzeitig auch der Beweis für die von Euch, die wirklich glauben, daß Delphi für jedes Objekt auch alle Methoden einzeln vorbehält. Dem ist nämlich nicht so.

Damit wir die Klassenhierarchie der Klassen zur Laufzeit abändern können, müssen wir "lediglich" die Virtual Method Table (VMT) ein wenig manipulieren. In der Unit system.pas sind für uns die wichtigsten Konstanten deklariert, um auf die VMT zuzugreifen. Möge Borland auch in Zukunft hier keine Fehler einbauen

Delphi-Quellcode:
const
  vmtSelfPtr = -76; // von Interesse
  vmtIntfTable = -72;
  ...
  vmtInstanceSize = -40;
  vmtParent = -36; // von Interesse
Eigentlich muß nicht viel getan werden. Anhand dieser Konstanten ermitteln wir den Eintrag (Pointer) auf die Vorgängerklasse und überschreiben diese mit der aktuellen Adresse der neuen Klasse. Diese Änderung gilt von diesem Moment and für alle Klassen, welche von der veränderten Klasse abgeleitet wurden. Desweiteren gilt es auch mit sofortiger Wirkung für alle Objekte, welche dieser Klasse oder deren nachfolgenden Klassen abgeleitet wurden. Das lässt sich dadurch erklären, das Delphi immer in der VMT nachsieht, wenn vererbte Methoden aufgerufen werden. Somit können wir unseren Code einfach in die Struktur injizieren

Die folgende Prozedur erledigt genau dieses.

Delphi-Quellcode:
procedure ReplaceParentClass(clSuccessor, clNewPredecessor: TClass);
var
  OriginalProtection: Cardinal;
  pclPredecessorAddress, pclPredecessor: PPointer;
begin
  // check parameters
  if Assigned(clNewPredecessor) and Assigned(clSuccessor) then
  begin
    if clSuccessor.ClassParent = nil then
      raise Exception.Create('Successor Class has no parent to be replaced.');

    // get the class pointer to the address where the class predecessor is saved
    pclPredecessorAddress := Pointer(Integer(Pointer(clSuccessor)) + vmtParent);

    // get the class pointer of the new predecessor
    pclPredecessor := Pointer(Integer(Pointer(clNewPredecessor)) + vmtSelfPtr);

    // lock and protect the class tables against access from any other threads
    VirtualProtect(pclPredecessorAddress, SizeOf(Pointer), PAGE_READWRITE,
        @OriginalProtection);
    try
      // overwrite the predecessor information
      pclPredecessorAddress^ := pclPredecessor;
    finally
      // unlock the class tables
      VirtualProtect(pclPredecessorAddress, SizeOf(Pointer), OriginalProtection,
          nil);
    end;
  end;
end;
Was passiert hier

Als erstes sichern wir uns ab, das wir nicht den Vorgänger von TObject ändern sollen - das wäre tödlich und kann ungeahnte Auswirkungen haben. Als nächstes ermitteln wir die Speicheradresse, an welcher unsere Klasse, welche einen neuen Vorgänger erhalten soll, die Adresse von deren Vorgänger speichert. Anschließend ermitteln wir die Adresse der neuen Vorgängerklasse. Nun müssen wir noch den entsprechenden Speicherbereich manipulieren und schon sind wir fertig.

Ein kleines Demo-Projekt ist im Anhang

......
Angehängte Dateien
Dateityp: zip demo1_174.zip (2,9 KB, 23x aufgerufen)
Daniel W.
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat