Announcement

Collapse
No announcement yet.

FindClass ohne Registrierung ?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • FindClass ohne Registrierung ?

    Ich hab ein gefinkeltes Problem:

    Es gibt da ein Projekt mit zirka 1 Mio. Codezeilen - von denen gehe ich aus.

    So, jetzt möchte ich nur noch einen Zeiger auf die VMT eines jeden darin vorhanden Forms bekommen ... nur sind eben die Forms nicht registriert.

    Über das MAP-File kann ich die Namen aller Klassen rausfiltern - das ist simple über die Methoden. Wie bekomme ich aus diesem String nun eine Refrerenz ? FindClass scheitert ... schließlich sind ja viele Klassen einfach nicht registriert, die meisten Formulare ebenfalls nicht ...

    Tjo - vielleicht irgendjemand ein Hintertürchen bekannt oder vielleicht gar eine offizielle Lösung für D7 Enter ?

  • #2
    <pre>

    type
    TEnumTypeInfoCallback = function(UserData: Pointer; Info: PTypeInfo): Boolean; register; <br>

    function GetBaseOfCode(Module: hModule; var CodeStart, CodeEnd: PChar): Boolean;
    asm // get Codesegment pointers, check if module is a valid PE
    PUSH EDI
    PUSH ESI
    AND EAX,not 3
    JZ @@2
    CMP Word Ptr [EAX],'ZM';
    JNE @@1
    MOV ESI,[EAX + 03Ch]
    CMP Word Ptr [ESI + EAX],'EP'
    JNE @@1
    MOV EDI,[EAX + ESI + 014h + 008h]
    ADD EAX,[EAX + ESI + 014h + 018h]
    ADD EDI,EAX
    MOV [EDX],EAX
    MOV [ECX],EDI
    XOR EAX,EAX
    @@1: SETE AL
    @@2: POP ESI
    POP EDI
    end; <br>

    function EnumTypeInfo(Module: hModule; Callback: TEnumTypeInfoCallback; UserData: Pointer): PTypeInfo;
    var
    P,E,K,N: PChar;
    L: Integer;
    begin
    Result := nil;
    if Assigned(Callback) then
    try
    if GetBaseOfCode(Module, P, E) then
    while P < E do
    begin
    DWord(P) := DWord(P) and not 3;
    K := P + 4;
    if (PDWord(P)^ = DWord(K)) and (PByte(K)^ > 0) and (PByte(K)^ < 18) then
    // Info.Kind in ValidRange.D6
    begin
    L := PByte(K + 1)^; // length Info.Name
    N := K + 2; // @Info.Name[1]
    if (L > 0) and (N^ in ['_', 'a'..'z', 'A'..'Z']) then // valid ident ??
    begin
    repeat
    Inc(N);
    Dec(L);
    until (L = 0) or not (N^ in ['_', 'a'..'z', 'A'..'Z', '0'..'9']);
    if L = 0 then // length and ident valid
    if Callback(UserData, Pointer(K)) then
    // tell it and if needed abort iteration
    begin
    Result := Pointer(K);
    Exit;
    end else K := N;
    end;
    end;
    P := K;
    end;
    except
    end;
    end; <br>


    procedure Test; <br>

    function MyEnum(Data: Pointer; Info: PTypeInfo): Boolean; register;
    // wir suchen nur Klassen abgeleitet von TCustomForm
    begin
    Result := False;
    if (Info.Kind = tkClass) and GetTypeData(Info).ClassType.InheritsFrom(TCustomFo rm) then
    WriteLn( Info.Name );
    end; <br>

    begin
    EnumTypeInfo(MainInstance, @MyEnum, nil);
    // or with packages
    // EnumTypeInfo(GetModuleHandle('vcl50.bpl'), @MyEnum, nil);
    end; <br>

    </pre>

    Obiger Code nutz die "inoffiziellen" TypeInfo Strukturen des delphi Compilers. Diese werden im implementierenden Modul gespeichert. Mann kann mit obiger Struktur über alle Datentypen iterieren, also auch die Deklarationen von Sets, Enums, ordinale Typen und eben auch Klassen und Interfaces.<br>
    Der Trick bei der Geschichte ist das der Compiler ab dem Codestart des Codesegementes, eg. meistens $40000 +$200 +x diese Typeinfos speichert. Normalerweise sind die Typinfo Records aber NICHT untereinander verlinkt. Um denoch ausgehende von einem solchen Record den nächsten Record zu finden wird getrickst, s.o.<br>
    Der Code ist getestet mit D3,D4,D5,D6 und sollte auch unter D7 laufen.

    ABER VORSICHT ! Wird ohne Packages compiliert und Units/Klassen eingebunden die NICHT benutzt werden so optimiert der Linker diese weg. D.h. in der fertigen Anwendung existieren diese Klassen überhaupt nicht mehr.<br>
    Wird mit Packages compiliert so muß mit SysUtils.EnumModules() über alle installierten Packages iteriert werden. In dieser Callback muß dann EnumTypeInfo() für jedes Package aufgerufen werden.<br>

    Gruß Hage

    Comment


    • #3
      Ach nochwas. Man kann obige Funktion "austricksen", d.h. einen Record im Codesegement erzeugen der für die Funktion wie ein TypeInfo-Record aussieht. Dadurch liefert die Funktion natürlich falsche Resultate ( Allerdings ist mir sowas noch nie untergekommen, d.h. es muß schon explizit gecodet werden.<br>
      Der Vorteil gegenüber MAP-Files liegt wohl auf der Hand. Keine externen Daten nötig, kein Debugbuild nötig und im Gegensatz zu MAP Dateien wird live das was tatsächlich in der Anwendung eincompiliert wurde ausgewertet. MAP Files sind dagegen weniger rubust, wird neu compiliert und die MAP datei aber nicht ersetzt reagiert das System absolut falsch.<br>

      Gruß Hage

      Comment


      • #4
        Falls man mit Callbacks arbeiten möchte die auf Klassen basieren kann man so vorgehen:

        <pre>

        // 1. Beispiel Klasse<br>

        type
        TMyEnumClass = class(TObject)
        class function Callback(Info: PTypeInfo): Boolean;
        end; <br>

        function TMyEnum.Callback(Info: PTypeInfo): Boolean;
        begin
        Result := False;
        WriteLn( Info.Name );
        end;<br>

        begin
        EnumTypeInfo(MainInstance, @TMyEnum.Callback, @TMyEnum);
        end;<br>

        // 2. Beispiel Objectinstance<br>

        type
        TMyEnum = class(TObject)
        private
        FList: TStringList;
        function Callback(Info: PTypeInfo): Boolean;
        public
        constructor Create;
        destructor Destroy; override;
        procedure Enum(Module: hInstance);
        property List: TStringList read FList;
        end;<br>

        function TMyEnum.Callback(Info: PTypeInfo): Boolean;
        begin
        Result := False;
        FList.AddObject(Info.Name, Info);
        end;<br>

        constructor TMyEnum.Create;
        begin
        inherited Create;
        FList := TStringList.Create;
        end;<br>

        destructor TMyEnum.Destroy;
        begin
        FreeAndNil(FList);
        inherited Destroy;
        end;<br>

        procedure TMyEnum.Enum(Module: hInstance);
        begin
        EnumTypeInfo(Module, @TMyEnum.Callback, Self);
        end; <br>

        begin
        with TMyEnum.Create do
        try
        Enum(MainInstance);
        finally
        Free;
        end;
        end;<br>

        </pre>

        usw. usw.

        Gruß Hagen

        PS: ich arbeite aber in diesem Falle lieber prozedural :

        Comment


        • #5
          Erstens WOW.
          Zweitens WOW.
          Drittens TNX *strahl*.

          Ähm ... tschuldigung, aber von wo in der Hölle hast du diese ganzen Infos ? Ich sehe hier nur inoffiziell, inoffiziell *g* wie in den schönen alten Zeiten *lol*.

          Trotzdem - vielen Dank - ich habe das Problem mittlerweile über die MAP/DFM-Files gelöst, da ich sonst natürlich nirgends sooo was gefunden habe; aber wer weiß, vielleicht kanns ja ich oder irgendjemand anders mal brauchen. *zwinker

          Comment


          • #6
            <b>Ähm ... tschuldigung, aber von wo in der Hölle hast du diese ganzen Infos ? </b>

            Um es genau zu sagen: da alle RTL-Sourcen im Quelltext vorliegen kann man sich diese durchlesen. Somit sind solche Funktionen NICHT inoffiziell oder undokumentiert. Ich meinte damit das diese Funktionalität keine von Borland so "garantierte" und "in der Dokumentation dokumentierten" Funktionen sind. Damit besteht die Möglichkeit das es mit Delphi 8 nicht mehr arbeitet. <br>
            Da aber hier auf grundlegende Compiler/Linker Spezifikas eingegangen wurde, glaube ich nicht das sich daran was ändern wird.<br> Dies würde bedeuten den Compiler, das Klassen Design und die RTTI zu ändern.<br>

            Diese Informationen sammeln sich mit der Zeit. Natürlich sollte man Assembler können, um die Borlnd Sourcen lesen zu können. Aber grundsätzlich ist all dies kein Geheimnis und jeder kann dies wissen. Studier doch einfach die Sourcen im Ordner Source\RTL\.<br>
            Entscheidend ist es den Überblick nicht zu verlieren und das Designkonzept von Borland PASCAL/OOP/Delphi/RTTI verstanden zu haben. Ok, ich gebs zu, extrem vorteilhaft ist es wenn man das schon seit Borland Pascal 4.0 (sprich vor 10-15 Jahren) gekonnt hat.

            Gruß Hage

            Comment


            • #7
              Ach - das ist natürlich auch eine Lösung - und die Zeitangabe erklärt einiges. *g*

              Da ich vor 10-15 Jahren gerade mal BASIC/Assembler auf dem C64 gecodet habe und danach eigentlich nur C, hab ich natürlich nicht ganz die Erfahrung (BP4 hab ich übrigens damals blindlinks liegen gelassen, weil ich auf dem Amiga mit dem AmiC++ was wesentlich besseres hatte ;-).

              Gruß zurück, Max

              Comment


              • #8
                Hey, zur selben Zeit (ca. >15 Jahre) habe ich auch Assembler und BASIC gecodet. Damals noch auf'n ATARI 130XE und Commodore P4
                Allerdings beim Einstieg auf dem PC stand ich vor der Alternative, entweder MS-DOS BASIC oder gleich was richtiges. Also wählte ich BP.
                In BP4.0 waren viel mehr Beispielsourcen die demonstrierten wie Objecte intern arbeiten, sprich VMT, später DMT usw. usw. In Delphi hat sich vom Konzept her nichts daran geändert, nur die TypInfo's, Interfaces, Klassentypen usw. sind hinzugekommen.<br>

                Gruß Hage

                Comment


                • #9
                  >15 ?

                  Ups - stimmt ja ... ich bin ja mittlerweile auch älter geworden. *g*

                  Vor 19-16 Jahren: C64 - Traumding zu der Zeit. *g* BASIC letztes Jahr Assembler.

                  Vor 15-10 Jahren: Amiga 500 (später mit fetzigem Turboboard+68020er aufgemotzt) - AmigaBASIC (saulangsam gewesen, deshalb schnell), Ansi-C und C++ (AmiC++) wie auch Assembler (Devpac sei dank *g*).

                  Dann PC ... grindige Prozessorarchitektur, Grafik war ein Witz - was kein on-board Soundchip mit vier Samplekanälen ? *g* Naja ... erster Rechner war ein P100 mit CD-ROM (hab ich extra wegen dem damals heißen WC3 nachgekauft; Kilrathi-Rösten mit Stil ;-).

                  Und jetzt seit einem Monat Delphi. *g* Ist aber ganz nett, die Sprache ... auch wenn ich wunde Finger von den vielen begin-end habe. ;-

                  Comment


                  • #10
                    Es gibt in der IDE die Möglichkeit Code-Templates zu erstellen, such mal in der Hilfe. Hat man sich erstmal, mit viel Disziplin, daran gewöhnt, soll alles viel vile schneller gehen.

                    Gruß Hagen

                    PS: übrigens ich gebe seit ca. 4-5 Jahren an das ich vor ca. 15 Jahren angefangen habe Es macht jünger !!!
                    Aber wenn ich nachrechne, heute 2003 damals 1984, sind ca. 19 Jahre, sogesehen haste recht. Scheiße, wie die Zeit vergeht

                    Comment


                    • #11
                      Jo - die <Ctrl>+<J>s *g*.
                      <BR>
                      Kenne ich schon, aber du hast recht - zuviel Disziplin. Das artet ja schon fast wieder in Denken aus, da bleibe ich lieber bei ein bisserl mehr tippseln; kann ich schon wieder an den Algorithmus für die nächste Methode denken ;-).
                      <BR>
                      Gruß Maxi
                      <BR>
                      PS: Jo Mensch. Sind wir alt. *gg* Ich kann mich noch an eine Zeit erinnern, da hab ich in den BBS gelästert, ein Programmierer, der 23 Jahre überlebt, ist ein schlechter Programmierer. *lol

                      Comment


                      • #12
                        naja, wenn er mit 5 anfängt zu coden dann ist er mit 28 Jahren einer der erfahrensten Programmierer die ich je gesehen hätte <br>
                        Aber genug damit: solche Rechnereien frustrieren mich nur.

                        Gruß Hagen

                        PS: ist schon blöd "15 Jahre meines Lebens hat mir M$ gestohlen&quot

                        Comment


                        • #13
                          Die 5 haut nicht ganz hin - bin leider schon 26. *g*<BR><BR>

                          Ach ja - und bei mir sinds ja nicht ganz soviele von MS gestohlene Jahre; dafür haben auch meine Exfreundinnen Anteil. ;-)<BR><BR>

                          Gruß und TNX nochmals, Maxi<BR><BR>

                          PS: Und jetzt programmier ich mal schnell in VB. *lol

                          Comment


                          • #14
                            Hallo,
                            ich hätte eine Frage zu diesem Problem.
                            Ich möchte etwas ähnliches machen, nur dass ich die Klassen von einer kompilerten exe Datei mithilfe von einem Programm auslesen und anschliessend die Klassenhirarhie ermitteln.
                            Nun habe ich in diesem Beispiel gesehen, dass MainInstance der function EnumTypeInfo übergeben wird:
                            EnumTypeInfo(MainInstance, @MyEnum, nil);
                            Ich dachte mir nun dass ich dieser Funktion ein Handel von einer exe Datei übergeben könnte z.B.
                            handle := LoadLibraryEx('C:\myTest.exe', 0, LOAD_LIBRARY_AS_DATAFILE);
                            Leider geht das nicht.
                            Meine überlegung war dass das Kodesegment der exe in den Speicher geladen wird und ich dieses gleich begandeln kann wie MainInstance. Leider geht das nicht.
                            Leider besitze ich nicht genügend Hintergrundwissen wie ich das am Besten lösen könnte. Bin also für jeden Hinweis Dankbar.
                            Grüße
                            Pau

                            Comment

                            Working...
                            X