6

次の疑似コードを含む DLL ライブラリがあるとします。

var
  LastError: DWORD;

procedure DoSomethingWrong; stdcall;
var
  FileStream: TFileStream;
begin
  try
    FileStream := TFileStream.Create('?', fmCreate);
  except
    on EFCreateError do
      LastError := GetLastError; // <- why does GetLastError return 0 here ?
  end;
end;

GetLastError上記のように DLL ライブラリで関数を使用すると、関数が 0 を返すのはなぜですか? この場合の最後のエラー コードを取得する方法はありますか?

4

2 に答える 2

10

リターンの後に呼び出される他の A​​PI があり、例外コードが実行されるため、GetLastErrorリターンへの呼び出し。0CreateFile

スレッド ローカル変数によって返されるエラー コードGetLastErrorであり、スレッドで実行されるすべてのコード間で共有されます。GetLastErrorしたがって、エラー コードをキャプチャするには、失敗した関数が返された直後に呼び出す必要があります。

ドキュメントでは、次のように説明されています。

呼び出しスレッドによって実行される関数は、 SetLastError関数を呼び出してこの値を設定し ます。関数の戻り値が、そのような呼び出しが有用なデータを返すことを示している場合は、すぐにGetLastError関数を呼び出す必要があります。これは、一部の関数が 成功時にゼロを指定してSetLastErrorを呼び出し、最後に失敗した関数によって設定されたエラー コードを消去するためです。

使用している場合、フレームワークは適切なタイミングでTFileStream.Create呼び出す機会を与えません。GetLastError本当にその情報を取得したい場合は、自分自身を呼び出して、の代わりにCreateFilea を使用する必要があります。THandleStreamTFileStream

THandleStreamのコンストラクタに渡すファイル ハンドルの合成を担当するという考えがありますTHandleStream。これにより、障害が発生した場合にエラー コードをキャプチャする機会が得られます。

于 2013-03-15T17:35:29.667 に答える
3

より高いレベルでは、このコードの実際の問題は、モデルが混在していることです。あるシステム (VCL TStream システム) でファイルを作成または開こうとしていますが、別のシステム (Win32 API) によって生成されたエラーをテストしています。

Win32 GetLastError の結果を信頼できる唯一の方法は、Win32 関数を自分で呼び出す場合です。なんで?これは、Win32 関数呼び出しと GetLastError への呼び出しの間に、Win32 関数への他の呼び出しがないことを確認する唯一の方法だからです。すべての Win32 API 呼び出しは、GetLastError を (再) セットする可能性があります。

VCL は Win32 の上にありますが、エラーが発生してから例外がハンドラーに到達するまでの間に、他の Win32 API 呼び出しが発生する可能性は十分にあります。今日は問題なく動作していたとしても、VCL 実装の将来の変更によって、現在の状況という幸運な偶然が簡単に崩れてしまう可能性があります。

必要なデータが上書きされやすいこの「ハング タイム」を回避する最善の方法は、GetLastError 値を可能な限り障害点に近いところで取得し、VCL 例外オブジェクトのプロパティに組み込むことです。これにより、例外ハンドラーと障害点との間の無実の侵入者が GetLastError グローバル状態を消去するリスクがほぼなくなります。

于 2013-03-16T07:16:58.623 に答える