Announcement

Collapse
No announcement yet.

Abstrakte Methoden identifizieren ...

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

  • Abstrakte Methoden identifizieren ...

    Grüße !

    Ich habe gerade das Problem, daß ich zur Laufzeit feststellen muß, ob eine bestimmte Klasse abstrakte Methoden enthält; also etwa so:

    function ContainsAbstractMethods(AClass: TObject): boolean;

    Die Frage ... bin ich bei der VMT auf der richtigen Spur (ist ja leider Compiler-abhängig und irgendwie ist mein Versuchsbeispiel ziemlich schön über den Heinz gesprungen) oder gibts da eine einfachere Lösung ?

  • #2
    Teils, wenn deine Abstracten Methoden statische Methoden sind dann gibts keinen Weg (ausser man ruft sie auf und erhält eine Exception).
    Wenn sie als virtuell/dynamic definiert wurden, was eigentlich die
    sinnvollste Anwendung ist, dann geht das.<br>

    Dazu musst du die VMT bzw. DMT durchiterieren und die Adresse des Solts mit der von _AbstractError vergleichen.<br>

    An die Adresse von System._AbstractError kommst Du mit:

    <pre>

    function AbstractAdress: Pointer;
    asm
    LEA EAX,System.@AbstractError
    end;

    </pre>

    Größtes Problem beim Aufbau der Klassenstruktur ist das die Länge der VMT nicht bekannt ist. D.h. man kann nicht direkt ermitteln wieviele Slots in der VMT gespeichert sind. Dies ist für den Compiler/Linker auch nicht nötig da im Grunde die VMT/DMT statische/Compilerzeit Records sind. Will man dagegen dynamisch zur Laufzeit die VMT/DMT auswerten dann fehlt halt ein Feld mit der Länge der VMT in der Klassen Struktur.<br>
    Ich habe mir so beholfen: Die Klassenstruktur enthält VOR und NACH der VMT noch andere Informationen. Man kann nun die Länge der VMT berechnen indem man die nachfoldende Adresse der anderen Strukturen berechnet. Die Differenz / 4 Bytes ergibt dann die Anzahl der Slots in der VMT.<br>

    Gruß Hage

    Comment


    • #3
      Das war aber ruckzuck ...

      Nop - ich wußte erstmals gar nicht, daß statische Methoden abstract sein können ... wenn schon, sowas kommt nicht vor.

      1) Wie iteriere ich eben beide am besten durch (ich bin von einer Null-Terminierung ausgegangen ...).

      2) *jetzt langsam überhaupt keinen Durchblick mehr bei Pascal-Pointer hab und mir ein nettes C++ wünsch* *g

      Comment


      • #4
        Im Moment hab ichs mal gerade so ...

        function ContainsAbstractMethods(AClass: TObject): boolean;

        function nAbstractErrorAddress: Pointer;
        begin
        asm LEA EAX,System.@AbstractError
        end;

        function nVmtStart(AClass: TObject): Pointer;
        begin

        Result:=Pointer(Cardinal(AClass)-76);

        end;

        function nVmtCount(AClass: TObject): integer;
        begin
        Result:=0;
        end;

        function nDmtStart(AClass: TObject): Pointer;
        begin
        Result:=Pointer(Carinal(AClass)-48);
        end;

        function nDmtCount(AClass: TObject): integer;
        begin

        end;

        var
        lVmt: Pointer;
        lDmt: Pointer;
        lIndex: integer;
        begin

        Result:=false;

        lVmt:=nVmtStart(AClass);

        for lIndex:=0 to nVmtCount(AClass) do
        if Pointer(Integer(lVmt)+(lIndex shl 2))=nAbstractErrorAddress then
        then
        begin
        Result:=true;
        exit;
        end;

        lDmt:=nDmtStart(AClass);

        for lIndex:=0 to nDmtCount(AClass) do
        if Pointer(Integer(lDmt)+(lIndex shl 2))=nAbstractErrorAddress then
        then
        begin
        Result:=true;
        exit;
        end;

        end;

        Allerdings hab ich noch keine Ahnung, wie ich die Anzahl jetzt rausbekommen ... wie legt der Compiler jetzt die Daten eigentlich ab ? Nach der Offset-Reihenfolge

        Comment


        • #5
          Fesch ... jetzt ist die ganze Formatierung im Sepp ! *ggg

          Comment


          • #6
            Ok, lange theoretische Rede -> wenig Durchblick, kurzer Source -> ah Effekt.<br>

            <pre>

            <code><font size=2 face="Courier New"><b>function </b>VMTSlotCount(AClass: TClass): Integer;
            <i>// calculate VMT Slot Count
            </i><b>var
            </b>P,E: PDWord;
            C,M: DWord;
            <b>begin
            </b>C := DWord(AClass);
            PChar(P) := PChar(AClass) + vmtSelfPtr;
            PChar(E) := PChar(AClass) + vmtClassName;
            M := E^;
            <b>if </b>P^ = C <b>then </b><i>// sanity check
            </i><b>while </b>PChar(P) &lt; PChar(E) <b>do
            begin
            </b>Inc(P);
            <b>if </b>(P^ &gt;= C) <b>and </b>(P^ &lt; M) <b>then </b>M := P^;
            <b>end</b>;
            Result := (M - C) <b>div </b>4;
            <b>end</b>;
            <br>
            <b>function </b>IsAbstract(Addr: Pointer): Boolean;
            <i>// TRUE is Addr points to _AbstractError
            </i><b>asm
            </b>CMP EAX,OFFSET System.@AbstractError <i>// Addr == _AbstractError ??
            </i>JE @@1 <i>// yes, jump to @@1
            </i>CMP Byte Ptr [EAX],0E9h <i>// PByte(EAX)^ == $E9 ?? -&gt; $E9 = JMP OpCode
            </i>JNE @@1 <i>// no, jump to @@1
            </i>ADD EAX,[EAX +1] <i>// load absolute address, from relative JMP
            </i>ADD EAX,5
            CMP EAX,OFFSET System.@AbstractError <i>// PPointer(Addr +1)^ + 5 == _AbstractError ??
            </i>@@1: SETE AL <i>// set AL dependend
            </i><b>end</b>;
            <br>
            <b>function </b>HasAbstracts(ClassType: TClass; CheckInherits: Boolean = True): Boolean;
            <b>type
            </b>PPointer = ^Pointer;
            PDMT = ^TDMT;
            TDMT = <b>packed record
            </b>Count: Word;
            Slots: <b>array</b>[0..0] <b>of </b>Word;
            <b>end</b>;
            <br>
            <b>var
            </b>VMT: PPointer;
            DMT: PDMT;
            I: Integer;
            <b>begin
            </b>Result := ClassType &lt;&gt; <b>nil</b>;
            <b>if not </b>Result <b>then </b>Exit;
            <i>// call inherited Class first
            </i><b>if </b>CheckInherits <b>and </b>HasAbstracts(ClassType.ClassParent) <b>then </b>Exit;
            <br>
            <i>// walk VMT, virtual Method Table
            </i>VMT := Pointer(ClassType);
            <b>for </b>I := 0 <b>to </b>VMTSlotCount(ClassType) -1 <b>do
            if </b>IsAbstract(VMT^) <b>then </b>Exit
            <b>else </b>Inc(VMT);
            <br>
            <i>// walk DMT, dynamic Method Table
            </i>DMT := PPointer(PChar(ClassType) + vmtDynamicTable)^;
            <b>if </b>DMT &lt;&gt; <b>nil then
            begin
            </b><i>// skip to Method Table, VMT points right after the DMT Slot Table to the Address Table of DMT
            </i>PChar(VMT) := PChar(DMT) + DMT.Count * SizeOf(Word) + SizeOf(Word);
            <b>for </b>I := 0 <b>to </b>DMT.Count -1 <b>do
            if </b>IsAbstract(VMT^) <b>then </b>Exit
            <b>else </b>Inc(VMT);
            <b>end</b>;
            Result := False;
            <b>end</b>;
            <br>
            <b>type
            </b>TTest = <b>class</b>(TObject)
            <b>procedure </b>XYZ; <b>dynamic</b>; <b>abstract</b>;
            <b>procedure </b>YZX; <b>dynamic</b>; <b>abstract</b>; <i>// change to virtual; dynamic; to see different results on
            </i><b>end</b>; <i>// how VMT's are shared between classes, instead of DMT's
            <br>
            </i>TTest1 = <b>class</b>(TTest)
            <b>procedure </b>Test1; <b>virtual</b>;
            <b>procedure </b>Test2; <b>dynamic</b>;
            <b>end</b>;
            <br>
            <b>procedure </b>TTest1.Test1;
            <b>begin
            end</b>;
            <br>
            <b>procedure </b>TTest1.Test2;
            <b>begin
            end</b>;
            <br>
            <br>
            <b>procedure </b>VMT;
            <b>begin
            <br>
            </b>WriteLn( HasAbstracts(TTest1) );
            WriteLn( HasAbstracts(TTest1, False) );
            <br>
            WriteLn( IsAbstract(@TTest.XYZ) );
            <b>end</b>;
            </font>
            </code></pre>

            Gruß Hage

            Comment


            • #7
              <pre>
              Habs schon selbst hingebuxt ... trotzdem Danke *g*

              uses
              System;

              type
              TDynamicAddressList = array[0..MaxInt div 16] of Pointer;
              PDynamicAddressList = ^TDynamicAddressList;

              function ContainsAbstractMethods(AClass: TClass): boolean;

              function nAbstractErrorAddress: Pointer; assembler;
              asm
              LEA EAX, System.@AbstractError
              end;

              function nGetVirtualMethodCount(AClass: TClass): integer;
              var
              lBeginVmt: longint;
              lEndVmt: longint;
              lTablePointer: longint;
              lIndex: integer;
              begin
              lBeginVmt := longint(AClass);

              lEndVmt := PLongint(longint(AClass) + vmtClassName)^;
              lIndex := vmtSelfPtr + SizeOf(Pointer);
              repeat
              lTablePointer := PLongint(longint(AClass) + lIndex)^;
              if (lTablePointer <> 0) and (lTablePointer >= lBeginVmt) and
              (lTablePointer < lEndVmt) then
              lEndVmt := longint(lTablePointer);
              Inc(lIndex, SizeOf(Pointer));
              until lIndex >= vmtClassName;

              Result := (lEndVmt - lBeginVmt) div SizeOf(Pointer);
              end;

              function nGetVirtualMethod(AClass: TClass; const AIndex: integer): Pointer;
              begin
              Result := PPointer(integer(AClass) + AIndex * SizeOf(Pointer))^;
              end;

              function nGetDynamicMethodCount(AClass: TClass): integer; assembler;
              asm
              MOV EAX, [EAX].vmtDynamicTable
              TEST EAX, EAX
              JE @@Exit
              MOVZX EAX, WORD PTR [EAX]
              @@Exit:
              end;

              function nGetDynamicAddressList(AClass: TClass): PDynamicAddressList; assembler;
              asm
              MOV EAX, [EAX].vmtDynamicTable
              MOVZX EDX, Word ptr [EAX]
              ADD EAX, EDX
              ADD EAX, EDX
              ADD EAX, 2
              end;

              var
              lIndex: integer;
              lCount: integer;
              lDynTbl: PDynamicAddressList;
              begin
              Result := true;
              for lIndex := 0 to nGetVirtualMethodCount(AClass) - 1 do
              if nGetVirtualMethod(AClass, lIndex) = nAbstractErrorAddress then
              exit;

              lCount := nGetDynamicMethodCount(AClass);
              if lCount > 0 then
              begin
              lDynTbl := nGetDynamicAddressList(AClass);
              if lDynTbl <> nil then
              for lIndex := 0 to lCount - 1 do
              if lDynTbl[lIndex] = nAbstractErrorAddress then
              exit;

              end;
              Result := false;
              end;
              </pre&gt

              Comment


              • #8
                Nachtrag: hier noch eine Function die die Klasse ermittelt in der die erste abstracte Methode deklariert wurde. Sie geht Top-Down vor, heist von der aktuellen Klasse aus zu deren Vorfahrklassen.<br>

                <pre>

                <code><font size=2 face="Courier New"><b>function </b>ClassOfIntroduceAbstracts(ClassType: TClass): TClass;
                <b>begin
                </b>Result := ClassType;
                <b>while </b>Result &lt;&gt; <b>nil do
                if </b>HasAbstracts(Result, False) <b>then </b>Exit
                <b>else </b>Result := Result.ClassParent;
                <b>end</b>;
                </font>
                </code></pre>

                Gruß Hage

                Comment


                • #9
                  Wichtig bei nGetVirtualMethodCount() ist das die einzelnen Pointer der Klassen Struktur NICHT in sortierter Reihenfolge vorliegen. Allerdings, so wie ich deinen Source lese, scheinst du schon einen MinMax Algo zu nutzen )<br>

                  Wie lange haste denn gebraucht um den Code zu implementieren ??<br>
                  Gehört schon einiges an Insight dazu um das fertig zu bringen.<br>
                  Meinen obigen Source habe ich gerade in den letzten 20 Minuten geschrieben, allerdings wusste ich auch wie die Klassen arbeiten <br>

                  Übrigens, schau dir mal IsAbstract() an !!<bR>
                  Damit kannst du nämlich auch isAbstract(@TTest.XYZ) benutzen. Der Compiler legt für solche Derefernezierungen eine virtuellen/dynamischen Methode die Abstract deklariert wurden ein Jump Tabelle an !! Warum er dies macht ist mit aber nicht ganz klar, einzigster Grund könnte sein das es ja Klassen-Methoden/Object-Methoden Derefenzierungen sind, und der Self-Pointer in EAX berücksichtigt werden muß. Allerdngs benötigt _AbstractError kein Self in EAX !!<br>

                  Zusätzlich musst du noch eines beachten: Wird eine dynamische abstrakte Methode deklariert, so taucht die NUR in der DMT der deklariernende Klasse auf. D.h. da dein Code nicht rekursiv die Parent Klassen untersucht, könnten dir solche dynamischen abstrakten Methoden durch die Lappen gehen. D.h. z.B. bei TTest1 und TTest mit nur dynamischen abstrakten Methoden, würde TTest1 als abstrakt-los ermittelt.<br>

                  Gruß Hage

                  Comment


                  • #10
                    Ok, alles lässt sich schöner machen, hier meine endgültigen Funktionen<br>

                    Gruß Hagen<br>

                    <pre>

                    <code><font size=2 face="Courier New"><b>function </b>IsAbstract(Addr: Pointer): Boolean;
                    <i>// TRUE is Addr points to _AbstractError
                    </i><b>asm
                    </b>CMP EAX,OFFSET System.@AbstractError <i>// Addr == _AbstractError ??
                    </i>JE @@1 <i>// yes, jump to @@1
                    </i>CMP Byte Ptr [EAX],0E9h <i>// PByte(EAX)^ == $E9 ?? -&gt; $E9 = JMP OpCode
                    </i>JNE @@1 <i>// no, jump to @@1
                    </i>ADD EAX,[EAX +1] <i>// load absolute address, from relative JMP
                    </i>ADD EAX,5
                    CMP EAX,OFFSET System.@AbstractError <i>// PPointer(Addr +1)^ + 5 == _AbstractError ??
                    </i>@@1: SETE AL <i>// set AL dependend
                    </i><b>end</b>;
                    <br>
                    <b>function </b>HasAbstracts(ClassType: TClass; CheckInherits: Boolean = True): Boolean;
                    <br>
                    <b>type
                    </b>PPointer = ^Pointer;
                    <br>
                    <b>function </b>VMTSlotCount(AClass: TClass): Integer;
                    <i>// calculate VMT Slot Count
                    </i><b>var
                    </b>P,E: PDWord;
                    C,M: DWord;
                    <b>begin
                    </b>C := DWord(AClass);
                    PChar(P) := PChar(AClass) + vmtSelfPtr;
                    PChar(E) := PChar(AClass) + vmtClassName;
                    M := E^;
                    <b>if </b>P^ = C <b>then </b><i>// sanity check
                    </i><b>while </b>PChar(P) &lt; PChar(E) <b>do
                    begin
                    </b>Inc(P);
                    <b>if </b>(P^ &gt;= C) <b>and </b>(P^ &lt; M) <b>then </b>M := P^;
                    <b>end</b>;
                    Result := (M - C) <b>div </b>4;
                    <b>end</b>;
                    <br>
                    <b>function </b>VMT(ClassType: TClass): Pointer;
                    <i>// get VMT
                    </i><b>begin
                    </b>Result := ClassType;
                    <b>end</b>;
                    <br>
                    <b>function </b>DMTSlotCount(ClassType: TClass): Integer;
                    <i>// get DMT slot count
                    </i><b>var
                    </b>D: PWord;
                    <b>begin
                    </b>D := PPointer(PChar(ClassType) + vmtDynamicTable)^;
                    <b>if </b>D &lt;&gt; <b>nil then </b>Result := D^ <b>else </b>Result := 0;
                    <b>end</b>;
                    <br>
                    <b>function </b>DMT(ClassType: TClass): Pointer;
                    <i>// get DMT method table
                    </i><b>begin
                    </b>Result := PPointer(PChar(ClassType) + vmtDynamicTable)^;
                    <b>if </b>Result &lt;&gt; <b>nil then
                    </b>Result := PChar(Result) + PWord(Result)^ * SizeOf(Word) + SizeOf(Word);
                    <b>end</b>;
                    <br>
                    <b>function </b>Walk(MT: PPointer; Count: Integer): Boolean;
                    <i>// walk trough Method Table
                    </i><b>begin
                    </b>Result := False;
                    <b>while not </b>Result <b>and </b>(Count &gt; 0) <b>do
                    begin
                    </b>Result := IsAbstract(MT^);
                    Inc(MT);
                    Dec(Count);
                    <b>end</b>;
                    <b>end</b>;
                    <br>
                    <br>
                    <b>begin
                    </b>Result := (ClassType &lt;&gt; <b>nil</b>) <b>and
                    </b>((CheckInherits <b>and </b>HasAbstracts(ClassType.ClassParent)) <b>or
                    </b>Walk(VMT(ClassType), VMTSlotCount(ClassType)) <b>or
                    </b>Walk(DMT(ClassType), DMTSlotCount(ClassType)));
                    <b>end</b>;
                    <br>
                    <b>function </b>ClassOfIntroduceAbstracts(ClassType: TClass): TClass;
                    <b>begin
                    </b>Result := ClassType;
                    <b>while </b>Result &lt;&gt; <b>nil do
                    if </b>HasAbstracts(Result, False) <b>then </b>Exit
                    <b>else </b>Result := Result.ClassParent;
                    <b>end</b>;
                    </font>
                    </code></pre&gt

                    Comment


                    • #11
                      <i>Nop - ich wußte erstmals gar nicht, daß statische Methoden abstract sein können ... wenn schon, sowas kommt nicht vor. </i>

                      Und ich wusste nicht das das in den neueren Delphi Versionen geändert wurden ist. Nunmehr müssen abstrakte Methoden immer dynamisch oder virtuell sein. Früherer Pascal Versionen ließen auch statische zu

                      Gruß Hage

                      Comment


                      • #12
                        Wow - das ist ja eine Menge, was sich da angesammelt hat ... ich hätte nicht sooo schnell Feierabend machen sollen.
                        <br>
                        Erstens mals hab ich mir viel von meinem Code geborgt (also assimiliert) und zwar von der Jedi Code Library (JCL). Ich hoffe, die Jungs und Mädls killen mich nicht dafür ;-).
                        <br>
                        Zweitens die Implementierungszeit: zirka 10 Minuten inkl. Test - aber okay, war ja simple mit Copy & Paste. Hab mich dann noch ein bisserl reingelesen und mir auf den Kopf gehauen, daß ich nicht selber drauf gekommen bin ... sobald man mal die Offsets weiß und die prinzipielle Struktur, schon ist der Rest ziemlich einfach. *g*
                        <br>
                        Drittens war mir das mit der DMT war mir schon klar - naja - wie dem auch sei, dynamische Methoden sind sowieso nur Beipack; also wenn die schon jemand verwendet (sind ja angeblich ein Relikt aus BP7, wo die VMT noch im Datensegment war ... und da war damals jedes Byte wertvoll *g*), Pech gehabt. *eg*
                        <br>
                        Viertens, macht doch nix mit dem statisch - abwarten auf .NET, da gehts dann wieder. *ggg*
                        <br>
                        Fünftens und Danke nochmals für den Code ! Ich schau mir den jetzt durch und grübble ein bisserl drüber ... nebenbei esse ich noch meinen Müller Milchreis Zimt (made in Germany *g*) und hoffe, ich habe jetzt dann absoluten Durchblick in Sachen VMT/DMT. ;-

                        Comment


                        • #13
                          <i>Drittens war mir das mit der DMT war mir schon klar - naja - wie dem auch sei, dynamische Methoden sind sowieso nur Beipack; also wenn die schon jemand verwendet (sind ja angeblich ein Relikt aus BP7, wo die VMT noch im Datensegment war ... und da war damals jedes Byte wertvoll *g*), </i>

                          Oh, diese Annahme ist schlichtweg falsch. Es ist sogar umgekeht, dynamische Methoden sind wichtiger als virtuelle, nur sieht man das als Delphicoder nicht sofort.<br>
                          Es gibt nämlich statische, virtuelle, als dynmisch deklarierte UND autom. durch den Compiler referenzierte dynamische Methoden die als <b>message</b> deklariert wurden. D.h. Methoden die als <B>message</b> deklariert sind sind tatsächlich dynmaische Methoden mit vorgebener Slot ID. Diese ID identifiziert den Solt in der DMT und ist gleichbedeutend mit der Messagekonstante, z.B. wm_Paint.<br>

                          Die wenigsten wissen das man bei einer dynamischen Methode die Slot ID angeben kann, also <br>

                          <pre>

                          type
                          TMyObject = class(TObject)
                          procedure XYZ; dynamic 1234; -> // dynamic wm_Paint; --> message wm_Paint;
                          end;<br>

                          </pre>

                          D.h. so wie TControls mit Messages umgehen kann, kann jede beliebige Klasse mit Messages umgehen, also z.B. auch TFont könnte "Messages" dispatchen.<br>

                          Damit sind dynamische Methoden sehr sehr effizient, da man diese mit allen Klassen benutzen kann, egal ob die spezifische Klasse diese dynamische Methode implementiert oder eben NICHT.<br>

                          Im Gegensatz dazu muß der Designer bei virtuellen Methoden von Anfang an die virtuellen Methoden "durchdenken". Bei solchen dynamsichen Methoden sollte NUR die zukünftige Parameterliste feststehen (z.B. var Message: Tmeasseg); und die ID's.<br>

                          Die ID einer dynamischen Methode kann maximal $FFFF sein, es können in einer Klasse also maximal 65535 dynamsiche Methoden deklariert werden. ABER, würde man das per virtuellen Methoden simmulieren wollen so müssten wir alle 65535 möglichen Methoden im vorhinein deklarieren. Es entstünde eine Monsterklasse die 65535 * 4 Bytes an VMT benötigte. Trotzdem würde nur ein Bruchteil dieser Methoden tatsächlich benutzt, eg. durchimplementiert. Der Rest wäre ein möglicher Überhang. Nun dynamische Methoden speichern NUR die durchimplemenierten Slots. Wird versucht eine nicht durchimplementierte Methode aufzurufen, so wird als erstes in der Vorgängerklasse danach gesucht, bis hinab zu TObject. Wird KEINE Methode mit dem entsprechenen Slot gefunden, so wird TObject.DefaultHandler() aufgerufen. Und nun passierts: wir können Defaulthandler() überschreiben und über diesen unsere Funktionalität NACHTRÄGLICH impelemntieren OHNE das wir eine dynamsiche Methode mit dem entsprechenden Slot gecodet haben.!!<br>
                          Dynmische Methoden sind extrem flexibler, habe aber ihren Preis. Das Aufrufen, Raussuchen der Methode zum Slot, benötigt viel mehr zeit als z.B. das direkte Anspringen bei statischen, bzw. indirekte Aufrufen bei virtuellen Methoden.<br>

                          Ein gutes Anwendungsbeispiel sind meine TNodes. TNode sind so ähnlich wie die beim TTreeView, allerdings effizienter. Sie können also Datenbäume representiern.<br> Nun der Unterschied zu z.B. TTreeView-Nodes ist das bei meinen TNodes diese auch Messages-Dynamic-Methoden haben. Über diese Methoden managen die TNodes nicht nur die enthaltenen Daten sondern auch die Darstellung/Bearbeitung usw. D.h. das anzeigenden Control setzt diese Messages an den TNode Baum ab, und die einzelne TNode stellt sich im Control selber dar, editiert sich selber usw. Dadurch kann in einem solchen Baum verschiedene TNode Klassen eingefügt werden und jede stellt sich abhängig von dessen individueller Implementation anders dar.<br> Das anzeigende, übergeordnete Control muß demzufolge NICHT umprogrammiert werden, sondern die Nodes "konfigurieren" es selber. Es wurde also die Datenverwaltung und Speicherung mit der Datenrepräsentation in einem Object = TNode integriert.<br>

                          Gruß Hage

                          Comment


                          • #14
                            Oki - jetzt hast du mich in den Boden argumentiert, alter Knabe. *g*
                            Und was mich noch mehr ärgert ist die Tatsache, daß alle Message-Handler durch den Rost fallen - obs wichtig ist ? Ich weiß es gerade nicht und das ärgert mich noch mehr. *gerade fürchterlich ärgerlich sei*
                            Aber Wurscht - das Grundprinzip mit den dynamischen kannte ich, nur das mit dem Messaging hab ich wohl überlesen - wieder mal schlauer geworden ;-).
                            TNX ähm - für diesen Megavortrag ! *zwinker

                            Comment


                            • #15
                              <i>Megavortrag</i>, yes ist'ne Schwäche von mir. Ich denke, bzw. hoffe,das noch mehr User die Posting lesen, und man lernt bekanntlioch nie aus )

                              Gruß Hage

                              Comment

                              Working...
                              X