6

SysUtilsユニットのファイナライズ後にコードを実行する必要があります。

コードを別のユニットに配置し、最初に次のようにdpr-fileのuses句に含めました。

project Project1;

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

procedure Test;
begin
  //
end;

begin
  SetProc(Test);
end.

MyUnitは次のようになります。

unit MyUnit;

interface

procedure SetProc(AProc: TProcedure);

implementation

var
  Test: TProcedure;

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

initialization

finalization
  Test;
end.

MyUnitには用途がないことに注意してください。

これは通常のWindowsexeであり、コンソールはなく、フォームがなく、デフォルトのランタイムパッケージでコンパイルされています。MyUnitはパッケージの一部ではありません(ただし、パッケージからも使用しようとしました)。

MyUnitのファイナライズセクションは、SysUtilsのファイナライズセクションの後に実行されることを期待しています。これはDelphiの助けが私に教えてくれることです。

ただし、これが常に当てはまるとは限りません。

私は2つのテストアプリを持っていますが、テストルーチン/dprファイルとユニットのコードによって少し異なります。ただし、すべての場合でMyUnitが最初にリストされます。

1つのアプリケーションが期待どおりに実行されます:Halt0-> FinalizeUnits-> ... other units...->SysUtilsのファイナライズ->MyUnitのファイナライズ->...other units .. ..

しかし、2番目はそうではありません。MyUnitのファイナライズは、 SysUtilsのファイナライズの前に呼び出されます。実際のコールチェーンは次のようになります。Halt0->FinalizeUnits->...その他のユニット...->SysUtilsのファイナライズ(スキップ)->MyUnitのファイナライズ->...その他のユニット...->SysUtilsのファイナライズ(実行済み)

どちらのプロジェクトも非常によく似た設定になっています。私はそれらの違いを取り除く/最小化するために多くのことを試みましたが、それでもこの振る舞いの理由はわかりません。

私はこれをデバッグしようとしましたが、次のことがわかりました。すべてのユニットに何らかの参照カウントがあるようです。また、InitTableには同じユニットへの複数の参照が含まれているようです。SysUtilsのファイナライズセクションが最初に呼び出されると、参照カウンターが変更され、何も実行されません。次に、MyUnitのファイナライズが実行されます。次に、SysUtilsが再度呼び出されますが、今回はref-countがゼロに達し、ファイナライズセクションが実行されます。

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
...

誰でもこの問題に光を当てることができますか?私は何かが足りないのですか?

4

4 に答える 4

10

ユニットは初期化の逆の順序でファイナライズされます。初期化の順序は、(プログラムまたはライブラリ内の)主なuses句から始まる、非周期的(つまり、すでにアクセスされたユニットに降りることはない)によって決定されます。SysInitは通常、初期化される最初のユニットであり、その後にSystemが続きます。

メインのEXEまたはDLLは、メインイメージで使用されるユニットの初期化の順序を指定するため、パッケージの動的ロードは事態を複雑にします。したがって、パッケージが動的にロードされると、初期化順序であると考えられるものが実行されますが、すでに初期化されているユニットはスキップされます。パッケージが動的にアンロードされると、これは逆に発生します。

一般的なルール:

  • 低レベルのものは、高レベルのものの前に初期化する必要があります
  • ファイナライズは初期化の逆の順序で行う必要があります

これらのルールはほとんどの場合意味があります。上位レベルのユニットの初期化は、多くの場合、下位レベルのユニットによって提供されるサービスに依存しています。たとえば、SysUtilsがない場合、Delphiでは例外サポートはありません。逆順のファイナライズは同じ理由で意味があります。高レベルのファイナライズは、下位レベルのユニットによって提供されるサービスに依存しているため、下位レベルのユニットのファイナライズの前に実行する必要があります。

とはいえ、あなたの問題に関しては、あなたの言うことが本当なら、コンパイラまたはRTLのどこかにバグがあるように思われます:メインEXEがMyUnit最初にMyUnit使用し、そのインターフェイスまたは実装で他のユニットを使用しない、そして、動的にロードされたパッケージで起こっている面白いビジネスはありません。私が提案できるのは、最小限の再現サンプルが得られるまで、奇妙な振る舞いでプロジェクトを削減し続けることです。その時点で、何が問題を引き起こしているのかを正確に明らかにする必要があります。

于 2010-04-13T12:26:39.270 に答える
1

私は理由を見つけることができました、そして私は今自分自身が少し愚かだと感じています:)

私の2番目のテストアプリケーションには、RTL.bplでコンパイルされたDLLへの静的参照があります(SysUtilsへの参照と1つの単純なルーチンがある場合を除いて空です)。したがって、DLLは静的にリンクされているため、exeからのコードが実行される前に初期化されます。

それでおしまい:

DLLのシステム->DLLのSysUtils- >exeのシステム(スキップ)-> MyUnit- > exeのSysUtils(スキップ)->など

ファイナライズは逆の順序で行われ、SysUtilsの前にMyUnitが実行されます。

解決策:すべてのプロジェクトで最初にMyUnitを含める必要があります。

(ああ、タイムマシンを使って時間を遡り、誰かにOnBeforeMMShutdownイベントを追加するように強制したいのですが:D)

于 2010-04-20T13:12:33.677 に答える
0

あなたは何も見逃していません。それがまさに起こっていることです。

プログラムがパッケージをロードすると、そのパッケージで使用されているすべてのユニットが初期化されます。パッケージをアンロードするときに、すべてのユニットをファイナライズする必要があります。しかし、ファイナライズには多くの場合、変数の解放が含まれます。ダブルフリーは、特に特定の例外処理機能がアンロードされた可能性があるファイナライズ中に発生し、デバッグが非常に困難になる場合は、悪いことであることに注意してください。そのため、ユニットの初期化に参照カウンターを配置して、それらを使用していたすべての処理が完了するまで、ユニットの初期化が完了しないようにします。

MyUnitがSysUtilsの後にファイナライズする必要がある特別な理由はありますか?

于 2010-04-13T11:30:01.687 に答える
0

よくわかりませんが、Turbo / BorlandPascalで有名な古き良きExitProcグローバル変数はまだありませんか?はいの場合、これで問題が解決する可能性があります。

于 2010-04-13T19:57:45.310 に答える