2011-12-29 14 views
5

mingw g ++ 4.6.1と-O0、WinXP SP2を使用しています。なぜGetLastError()は呼び出されるかによって0または2を返しますか?

Minimal working example is here.

gが++ --with-DWARF2 --disable-sjlj-例外を除いて構成されています。

GetLastError()戻り0または例外がスローされている方法の2 depeding:

throw runtime_error(error_message()); 

偽 "エラーコード:0" に印刷され、そして

const string msg = error_message(); 

throw runtime_error(msg); 

プリント "エラーコード:2" として期待される。

最初に、私はGetLastError()が2回呼び出されたと思っていましたが、デバッグでは、期待通りに1回だけ呼び出されることを示しています。

何が起こっていますか?

+0

'error_message(GetLastError())'を呼び出すようにコードを変更するとどうなりますか? –

+0

「GetLastError()」、C++のtry/catch例外、Win32「Structured Exception Handling」(SEH)はすべて3つの異なる(関連していますが異なる)ものです。あなたは一般的に1つまたは別のものを使用しますが、お互いに一緒に使用するべきではありません。 – paulsm4

+0

@GregHewgill GetLastError()を非表示にしたいので、代わりにerror_message()を使用したいと思います。 – Ali

答えて

9

それは0。このために、最後の-エラー値をリセットthrowはどこか、それ自体の内部でのWin32 API関数を呼び出す設定したコードは、error_message()から前に、あなたのコールが起こっている可能性あります。

GetLastError()を呼び出すを指定しないと、はLast-Errorの値を自動的に0にリセットするため、2回の呼び出しが安全です。

コンパイラ/ランタイムがWin32 API関数を呼び出すコードを生成するかどうかは、特定の実行時に依存します。まだ、あなたのコードの将来の読者のために、error_message()GetLastError()を呼び出すことが有用であろうより良い

const string msg = error_message(); 
throw runtime_error(msg); 

const string msg = error_message(GetLastError()); 
throw runtime_error(msg); 
、これに依存し、安全でないことが2文のバージョンを使用するために、

このようにして、読者は対応するWin32 API呼び出しの直後にあるGetLastError()呼び出しを表示します。

8

生成されたアセンブリコードを見ると、何が起きているのかが明確になります。次のC++コード:

hDevice = CreateFileA(path, // drive to open 
    // etc... 
    ); 

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive 
{                
    throw runtime_error(error_message()); 
} 

は(少なくともデフォルトの最適化を使用して)アセンブリコードのストレッチを生成します:

call [email protected] # 
LEHE4: 
    sub esp, 28 #, 
    mov DWORD PTR [ebp-12], eax # hDevice, D.51673 
    cmp DWORD PTR [ebp-12], -1 # hDevice, 
    jne L5 #, 
    mov DWORD PTR [esp], 8 #, 

    call ___cxa_allocate_exception # // <--- this call is made between the 
              # // CreateFile() call and the 
              # // error_message() call 

    mov ebx, eax  # D.50764, 
    lea eax, [ebp-16] # tmp66, 
    mov DWORD PTR [esp], eax  #, tmp66 
LEHB5: 
    call __Z13error_messagev # 

あなたがスローされた例外のためのいくつかのメモリブロックを割り当てるために___cxa_allocate_exceptionに行われた呼び出しを参照してください。その関数呼び出しはGetLastError()状態を変更しています。

C++コードがどのように見える時:

hDevice = CreateFileA(path, // drive to open 
    // etc... 
    ); 

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive 
{                
    const string msg = error_message(); 

    throw runtime_error(msg); 
} 

その後、あなたは、次の生成されたアセンブリを得る:

error_message()に失敗した CreateFile()コールとコールの間に外部関数を呼び出すことはありません
call [email protected] # 
    sub esp, 28 #, 
    mov DWORD PTR [ebp-12], eax # hDevice, D.51674 
    cmp DWORD PTR [ebp-12], -1 # hDevice, 
    jne L5 #, 
    lea eax, [ebp-16] # tmp66, 
    mov DWORD PTR [esp], eax  #, tmp66 
    call __Z13error_messagev # 
LEHE4: 
    sub esp, 4 #, 
    mov DWORD PTR [esp], 8 #, 

    call ___cxa_allocate_exception # // <--- now this happens *after* 
             //  error_message() has been called 

この種の問題は、GetLastError()またはerrnoのようなグローバルな状態を使用したエラー処理の主な問題の1つです。

+0

+1あなたの努力に感謝します。この問題を解決するためのきれいな解決策を提案できますか?それとも、グレッグ・ヒュージールの答えは私たちができる最善のものですか? – Ali

+0

Greg Hewgillの答えはこれに近づく正しい方法です(この答えは実際にはGregのポイントを具体的にしようとしていました)。コンパイラが 'throw '(またはその点に関して他の文)を実装するためにコンパイラが何を制御するのかを実際には制御していないので、' GetLastError() 'を得るために' throw'文を投げることはできません。状態。あなたは 'throw'の前に' GetLastError() 'を厳密にしなければなりません。 –

+0

+1分析をお寄せいただきありがとうございます。 –

関連する問題