Announcement

Collapse
No announcement yet.

initialization - Reihenfolge

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

  • initialization - Reihenfolge

    Wie kann ich die Reihenfolge, in der die <b>initialization</b> -Abschnitte der units beim Programmstart ausgeführt werden, beeiflussen bzw festlegen??

    Die Reihenfolge der uses-Angaben im Hauptprogramm bzw im Mainform scheint den Compiler überhaupt nicht zu beeindrucken: in der Reihenfolge die er erzeugt sehe ich keine Systematik.

  • #2
    Hallo Uwe,<br>ich weiß ja nicht in welcher unit Du nachschaust, um etwas über die Reihenfolge zu erfahren, aber schau doch mal in die Projekt-Datei. Dort wird die Reihenfolge festgelegt.<br>Jens Schuman

    Comment


    • #3
      Hallo Jens,<br>
      <br>
      probier das mal mit Programmstart durch F7 aus.<br>
      Bis dahin hatte ich das auch angenommen. <br>
      Zuerst sah es so aus, als würden die Units in umgedrehter Reihenfolge, also von unten nach oben abgearbeitet,<br>
      dann habe ich die Reihenfolge geändert, und dachte dann 'alles klar, wird wohl tatsächlich rückwärts abgearbeitet'.<br>
      War nichts. Nachdem ich die ursprüngliche Reihenfolge wiederhergestellt hatte ging es diesmal von Vorne los.<br>
      Die Hilfe zu Initialization-Abschnitt ist da auch nicht gerade erhellend.<br>
      Das Wissen um die Reihenfolge brauchte ich bislang zwar auch nicht, aber die Frage erschien mir interessant.<br>
      Nur mit dem Ergebnis das ich dabei erzielen konnte bin ich nicht so recht zufrieden. <br>
      <br>
      Ciao<br>
      Chri

      Comment


      • #4
        <pre>

        type
        PInitContext = ^TInitContext;
        TInitContext = record
        OuterContext: PInitContext; { saved InitContext }
        ExcFrame: Pointer; { bottom exc handler }
        InitTable: PackageInfo; { unit init info }
        InitCount: Integer; { how far we got }
        Module: PLibModule; { ptr to module desc }
        DLLSaveEBP: Pointer; { saved regs for DLLs }
        DLLSaveEBX: Pointer; { saved regs for DLLs }
        DLLSaveESI: Pointer; { saved regs for DLLs }
        DLLSaveEDI: Pointer; { saved regs for DLLs }
        DLLInitState: Byte;
        ExitProcessTLS: procedure; { Shutdown for TLS }
        end;<br>

        function InitContext: PInitContext;
        asm
        MOV EAX,OFFSET System.@StartEXE
        MOV EAX,[EAX + 2]
        SUB EAX,8
        end;<br>

        type
        TSymbol = packed record
        Start,Stop: Pointer;
        Name: String[32];
        end;<br>

        PSymbols = ^TSymbols;
        TSymbols = packed record
        Count: Integer;
        Entries: array[0..0] of TSymbol;
        end;<br>

        function BuildSymbols: PSymbols;
        var
        Map: TStringList;
        I,J,K,C: Integer;
        S: String;
        P,T: PChar;
        begin
        T := @TextStart;
        Integer(T) := Integer(T) and not $FFF;
        Result := AllocMem(SizeOf(TSymbol) * 1024 + 4);
        Map := TStringList.Create;
        try
        Map.LoadFromFile(ChangeFileExt(ParamStr(0), '.MAP'));
        for I := 0 to Map.Count -1 do
        begin
        S := Map[I];
        J := Pos('C=CODE', S);
        if J > 0 then
        begin
        K := Pos('M=', S);
        if K > J then
        begin
        Inc(K, 2);
        P := @S[K];
        C := 0;
        while P^ <> ' ' do
        begin
        Inc(P);
        Inc(C);
        end;
        Result.Entries[Result.Count].Name := Copy(S, K, C);
        K := Pos(':', S);
        if (K > 0) and (K < J) then
        begin
        C := StrToInt('$' + Copy(S, K+ 1, 8));
        K := StrToInt('$' + Copy(S, K+10, 8));
        Result.Entries[Result.Count].Start := Pointer(T + C);
        Result.Entries[Result.Count].Stop := Pointer(T + C + K);
        Inc(Result.Count);
        end;
        end;
        end;
        end;
        finally
        ReallocMem(Result, Result.Count * SizeOf(TSymbol) + 4);
        Map.Free;
        end;
        end;<br>

        function FindSymbol(Ptr: Pointer; Symbols: PSymbols): String;
        var
        I: Integer;
        begin
        for I := 0 to Symbols.Count -1 do
        if (PChar(Ptr) >= PChar(Symbols.Entries[I].Start)) and
        (PChar(Ptr) <= PChar(Symbols.Entries[I].Stop)) then
        begin
        Result := Symbols.Entries[I].Name;
        Exit;
        end;
        if Ptr = nil then Result := '?nil'
        else Result := '????';
        end;<br>

        </pre&gt

        Comment


        • #5
          <pre>

          procedure ShowUnits;
          var
          I: Integer;
          Symbols: PSymbols;
          S: String;
          begin
          S := '';
          Symbols := BuildSymbols;
          if Symbols <> nil then
          try
          with InitContext^ do
          for I := 0 to InitTable.UnitCount -1 do
          S := S + FindSymbol(@InitTable.UnitInfo[I].Init, Symbols) + ',';
          SetLength(S, Length(S) -1);
          MessageBox(0, PChar(S), nil, 0);
          finally
          FreeMem(Symbols);
          end;
          end;<br>

          </pre&gt

          Comment


          • #6
            So, nun die Erklärung zum obigen Code.
            Dieser ermittelt die verwendeten Units einer Delphi APP und zeigt diese in der Initialization Reihenfolge. Die Finalisierung erfolgt dann genau umgekehrt. Um nun die die Unit Namen zu ermitteln mus eine MAP (Linker Symboldatei) beim compilieren erzeugt werden. Einfach in Menu\Project\Optionen\Linker\MAP-datei\Segmente = TRUE setzen. Dann projekt neu compilieren und obigen Code einfügen. Procedure Test aufrufen. JEDE Unit in delphi hat ihre <b>inialization</b> und <b>finalization</b> Section, egal ob im Source diese benutzt wird oder nicht. Damit nun beim StartUp die EXE bzw. der StartUp Code weiß wo diese "Proceduren" im Code liegen wird durch den Linker/Compiler eine Tabelle erzeugt -> "InitTable". Die Addresse wird dem StartUp Code als parameter übergeben und in der Unit System.pas in der privaten Variable "InitContext" gespeichert. Die obige Funktion "InitContext" gibt nun eine Zeiger auf diese Struktur zurück. Das Field InitContext.initTable ist nun unsere gesuchte tabelle die die vom StartUp Code abzuarbeitenden einzelnen initialization/finalization procedures enthält. Fehlt nun noch diese Adressen zur Laufzeit in den dazugehörigen Unit namen umzuwandeln. Dies macht procedure BuildSymbols auf Grund der extern generierten .MAP datei der Anwendung.<br>

            Nun zum problem der Reihenfolge dieser Units. Angenommen eine Unit X nutzt Unit Y und diese Unit System.pas. Nun MUSS logischer Weise VOR der Unit X die Unit Y initialisiert werden. Da diese aber Unit System benötigt muss also noch vorher diese initialisiert werden. D.h. der Compiler lösst zur compiletime diese Abhängigkeiten auf und erstellt die korrekte Reihenfolge. Ein eng verknüpftes problem sind demzufolge die "überkreuzenden" Unit Bezüge. Sollte nun eine Unit keinerlei Abhängigkeiten besitzen, z.B. Windows.pas, wird diese in der Reihenfolge wie sie in den uses Klauseln vorgefunden wird eingebunden.
            Nur, bei diesen Units hat man sozusagen die Chance die Reihenfolge zu beeinflussen.<br>

            Gruß Hage

            Comment


            • #7
              Alternativ kann für function InitContext: PInitContext folgender Code verwendet werden:

              <pre>

              function InitContext: PInitContext;
              asm
              MOV EAX,OFFSET System.@StartLIB
              MOV EAX,[EAX + 4]
              end;

              </pre>

              Vorteil, funktioniert unter D3/D4/D5 und D6 {$MSWINDOWS}.<br>

              Gruß Hage

              Comment


              • #8
                Hi Hagen,
                vielen Dank für den Code und die ausführliche Beschreibung.
                Mit der Reihenfolge dachte ich auch daß es so läuft wie du beschrieben hast.
                Also hab ich die Unit X, von der ich wollte, daß sie von den von mir erzeugten Units als erste initialisiert wird, im interface-Abschnitt von keiner anderen selbst erzeugten Unit abhängig gemacht (d.h. in der uses-Klausel dort steht keine selbst geschriebene Unit) und diese Unit X auch in den uses-Klauseln aller anderen Units, wo ich diese brauche, als erste angegeben.
                Trotzdem wird der initialization-Abschnitt dieser Unit X erst sehr spät nach etlichen anderen Units abgearbeitet.

                Gruß Uw

                Comment


                • #9
                  Nun, haste mal die *.dpr Datei geöffnet und dort Deine Unit als erste eingetragen ?? Die "ganze" Menge anderer Units sind halt die die als erstes initialisiert werden müssen. Übrigens welche Uses Klausel hast Du geändert ? Beide, die interface und implementation Uses Klausel sind wichtig, wobei in Deinem Falle die am weitesten innenliegenden, also die implementation Uses Klausel die wichtigere ist.

                  Gruß Hage

                  Comment

                  Working...
                  X