2010-04-13 11 views
6

Ich muss meinen Code nach der Fertigstellung der SysUtils-Einheit ausführen.Einheit Finalisierungsreihenfolge für Anwendung, kompiliert mit Laufzeitpaketen?

Ich habe meinen Code in separate Einheit platziert und enthalten zunächst in uses-Klausel des DPR-Datei, wie folgt aus:

project Project1; 

uses 
    MyUnit, // <- my separate unit 
    SysUtils, 
    Classes, 
    SomeOtherUnits; 

procedure Test; 
begin 
    // 
end; 

begin 
    SetProc(Test); 
end. 

MyUnit sieht wie folgt aus:

unit MyUnit; 

interface 

procedure SetProc(AProc: TProcedure); 

implementation 

var 
    Test: TProcedure; 

procedure SetProc(AProc: TProcedure); 
begin 
    Test := AProc; 
end; 

initialization 

finalization 
    Test; 
end. 

Beachten Sie, dass MyUnit hat keine Verwendung.

Dies ist übliche Windows-EXE, keine Konsole, ohne Formulare und mit Standard-Laufzeit-Paketen kompiliert. MyUnit ist nicht Teil eines Pakets (aber ich habe versucht, es auch aus dem Paket zu verwenden).

Ich erwarte, dass Finalisierung Abschnitt von MyUnit nach Finalisierung Abschnitt von SysUtils ausgeführt wird. Das ist es, was Delphi mir erzählt.

Dies ist jedoch nicht immer der Fall.

Ich habe 2 Test-Apps, die ein wenig nach Code in Test-Routine/dpr-Datei und Einheiten unterscheidet, in Anwendungen aufgeführt. MyUnit wird jedoch in allen Fällen zuerst aufgeführt.

Eine Anwendung ausgeführt wird, wie erwartet: Halt0 -> FinalizeUnits -> ... andere Einheiten ... -> SysUtils der Finalisierung -> MyUnit der Finalisierung -> ... andere Einheiten ...

Aber das zweite ist nicht. MyUnit Finalisierung wird vor SysUtils Finalisierung aufgerufen. Die eigentliche Aufrufkette sieht so aus: Halt0 -> FinalizeUnits -> ... andere Einheiten ... -> SysUtils Finalisierung (übersprungen) -> MyUnit Finalisierung -> ... andere Einheiten ... -> SysUtils Finalisierung (ausgeführt)

Beide Projekte haben sehr ähnliche Einstellungen. Ich habe viel versucht, ihre Unterschiede zu beseitigen/zu minimieren, aber ich sehe immer noch keinen Grund für dieses Verhalten.

Ich habe versucht, dies zu debuggen und herausgefunden, dass: es scheint, dass jede Einheit eine Art von Referenz zählen. Und es scheint, dass InitTable mehrfache Referenzen auf dieselbe Einheit enthält. Wenn der Finalisierungsabschnitt von SysUtils zum ersten Mal aufgerufen wird, ändert er den Referenzzähler und tut nichts. Dann wird die Finalisierung von MyUnit ausgeführt. Und dann wird SysUtils wieder genannt, aber diesmal Referenzzählung Null erreicht und Finalisierung Abschnitt ausgeführt wird:

Finalization: // SysUtils' finalization 
5003B3F0 55    push ebp   // here and below is some form of stub 
5003B3F1 8BEC    mov ebp,esp 
5003B3F3 33C0    xor eax,eax 
5003B3F5 55    push ebp 
5003B3F6 688EB50350  push $5003b58e 
5003B3FB 64FF30   push dword ptr fs:[eax] 
5003B3FE 648920   mov fs:[eax],esp 
5003B401 FF05DCAD1150  inc dword ptr [$5011addc] // here: some sort of reference counter 
5003B407 0F8573010000  jnz $5003b580  // <- this jump skips execution of finalization for first call 
5003B40D B8CC4D0350  mov eax,$50034dcc // here and below is actual SysUtils' finalization section 
... 

Kann jemand kann Licht zerkleinern zu diesem Thema? Fehle ich etwas?

Antwort

1

Ich war in der Lage, einen Grund zu finden, und ich fühle mich ein bisschen dumm jetzt :)

Meine zweite Testanwendung einen statischen Verweis auf DLL hat, die mit RTL.bpl kompiliert wurden (es ist leer, mit Ausnahme von Referenzen zu SysUtils und mit einer einfachen Routine). Da DLL also statisch verknüpft ist, wird sie initialisiert, bevor ein Code von exe ausgeführt werden kann.

Das ist es:

DLL -System -> DLL SysUtils -> exe -System (übersprungen) ->MyUnit -> exe SysUtils (übersprungen) -> etc

Die Finalisierung erfolgt in umgekehrter Reihenfolge, was zur Ausführung von MyUnit vor SysUtils führt.

Lösung: erfordern MyUnit zunächst in allen Projekten einzubeziehen.

(oh, wie ich wünsche, eine Zeitmaschine haben, in der Zeit zurück zu reisen und jemand zu zwingen OnBeforeMMShutdown Ereignis hinzuzufügen: D)

0

Sie verpassen nichts. Genau das passiert.

Wenn Ihr Programm ein Paket lädt, wird es alle Einheiten in diesem Paket verwendet initialisieren. Wenn das Paket entladen wird, müssen alle Einheiten abgeschlossen werden. Aber Finalisierung beinhaltet oft die Befreiung von Variablen. Denken Sie daran, dass Double-Frees eine schlechte Sache sind, insbesondere wenn sie während der Finalisierung auftreten, wenn bestimmte Funktionen zur Ausnahmebehandlung möglicherweise entladen wurden, was das Debugging sehr schwierig macht. Also setzt es einen Referenzzähler auf die Initialisierungen der Einheit, so dass sie nicht finalisiert werden, bis alles, das sie verwendet wurde, mit ihnen fertig ist.

Gibt es einen besonderen Grund, warum Ihr MyUnit nach SysUtils abschließen muss?

+0

> Gibt es einen besonderen Grund, warum Ihr MyUnit nach SysUtils abschließen muss? Ja. Es ist Mem-Leaking-Checks. – Alex

+0

@Alexander: Macht es etwas Besonderes, das Sie mit FastMM im FullDebugMode nicht erreichen können? –

+0

> Macht es etwas Besonderes, das Sie mit FastMM im FullDebugMode nicht erreichen können? Das ist nicht die Frage, die ich stelle, also lass es beiseite. FastMM hat in meiner zweiten App genau aus dem gleichen Grund nicht funktioniert: es heißt viel zu früh. – Alex

10

Einheiten werden in umgekehrter Reihenfolge der Initialisierung abgeschlossen. Die Reihenfolge der Initialisierung wird durch eine nichtcyclische bestimmt (d.h. steigt nie in eine bereits besuchten Einheit) Post-Order-Traversal der Einheit Graph verwendet, beginnend mit dem Hauptsatz (im Programm oder Bibliothek) verwendet. SysInit ist normalerweise die erste Einheit, die initialisiert wird, gefolgt von System.

Dynamisches Laden von Paketen verkompliziert die Dinge, weil die Haupt EXE oder DLL, die Reihenfolge der Initialisierung der Geräte durch das Hauptbild verwendet angeben wird. Wenn also ein Paket dynamisch geladen wird, wird es ausgeführt, was es für die Initialisierungsreihenfolge halten sollte, aber bereits initialisierte Einheiten werden übersprungen; Wenn das Paket dynamisch entladen wird, geschieht dies umgekehrt.

Die allgemeinen Regeln:

  • untergeordneten Dinge sollten
  • Finalisierung vor höherer Ebene Dinge initialisiert werden soll

Diese Regeln Sinn machen fast immer in umgekehrter Reihenfolge der Initialisierung sein. Initialisierungen auf höheren Ebenen beruhen häufig auf Diensten, die von Einheiten auf niedrigerer Ebene bereitgestellt werden. Ohne SysUtils gibt es beispielsweise in Delphi keine Ausnahmeunterstützung. Umgekehrter Reihenfolge Abschluss macht Sinn aus dem gleichen Grund: High-Level-finalizations auf von untergeordneten Einheiten erbrachten Leistungen angewiesen sind, so müssen sie vor den untergeordneten Einheiten finalizations laufen.

Alles, was gesagt, in Bezug auf Ihr Problem, es klingt wie es in den Compiler oder RTL, einen Fehler irgendwo sein kann, wenn das, was Sie sagen, ist wahr: dass das Haupt-EXE MyUnit zuerst verwendet, und MyUnit verwendet keine andere Einheiten in seiner Schnittstelle oder Implementierung, und es gibt kein lustiges Geschäft mit dynamisch geladenen Paketen. Alles, was ich vorschlagen kann, ist, das Projekt mit dem seltsamen Verhalten zu reduzieren, bis Sie eine minimale Reproduktionsprobe haben; Zu diesem Zeitpunkt sollte klar sein, was genau das Problem verursacht.

+0

Alle Pakete sind statisch verknüpft und es gibt keine dynamische Belastung - das ist sicher. Danke für die Antwort, aber ich hatte Angst davor :(Ich hoffte, dass jemand einige Hinweise geben kann, um nach bestimmten Dingen zu suchen, weil ich nicht weiß, wo ich sonst suchen sollte. Ich werde noch ein paar Mal versuchen. .. – Alex

0

Ich bin mir nicht sicher, aber ist da noch die gute alte ExitProc globale Variable von Turbo/BorlandPascal? Wenn ja, könnte dies Ihr Problem lösen.

+0

Ja, ist es. Leider heißt diese Routine ** vor ** Aufruf einer Finalisierung Abschnitt. Also, dieser ist zu früh. Es gibt auch ExitProcess-Ereignis, aber es heißt kurz vor Prozessbeendigung - ein Weg zu spät (Ich schreibe nicht Speicher-Manager - installieren Sie nur einen Filter; so ist es mir wichtig, meinen Code vor der Fertigstellung des Systems ausführen, wenn der Standard-Speicher-Manager den gesamten Speicher freigeben kann) Heck, ich denke über die Installation Haken bei System Finalisierung: (Ich mache nur Spaß. – Alex

+0

@Alexander: Vielleicht ein Haken auf * SysUtils * Finalisierung ist nicht so eine schlechte Idee, wenn Sie es sonst nicht gelöst bekommen können. – dummzeuch

Verwandte Themen