Tips: APIのエラー

APIはどのようなエラーを返すのか

Win32 APIは中でエラーが発生すると、以下のような方法で その結果を呼び出し元に通知します。

  1. エラー番号を特定の領域に格納する。
  2. エラー番号を戻り値として返す。
  3. 構造化例外を発生する。

エラー番号を特定の領域に格納する

多くのAPIは、エラーが発生するとスレッド毎に特定の領域に エラー番号を格納して呼び出し元に復帰します。呼び出し元は GetLastErrorAPIを用いて この番号を取得することができます。

API名 説明
GetLastError もっとも最近のエラー番号を格納する領域に格納されている エラー番号を取得する。

ほとんどのAPIは正常終了したときにはエラー番号を設定しませんので、 APIが正常終了していた場合には、 GetLastErrorAPI呼び出しの結果は不定です。 そこで、API呼び出し元は一般に以下のような処理を行わなければなりません。

    SomeAPI();
    if  (異常終了) {
        DWORD errorno = GetLastError();
        エラー処理;
    }

さて、APIが異常終了したか否かはどのようにして判定すれば よいのでしょうか。それはたいていの場合APIの戻り値で判定できます。 たとえば、CreateProcessAPIは BOOL値でAPIの結果を返します。 したがって、コードは以下のようになります。

    BOOL result = CreateProcess(...);
    if  (result == FALSE) {
        DWORD errorno = GetLastError();
        エラー処理;
    }

もうひとつの例を出しましょう。CreateFileAPIは HANDLEの値を返しますが、失敗したときには INVALID_HANDLE_VALUEを返すことになっています (APIの成功と失敗の判定方法は各APIのオンラインヘルプに掲載されています)。 したがって、エラー処理は以下のようにします。

    HANDLE handle = CreateFile(...);
    if  (handle == INVALID_HANDLE_VALUE) {
        DWORD errorno = GetLastError();
        エラー処理;
    }

以上のような規則に当てはまらないAPIもあります。代表的なものは CreateMutexAPIや CreateSemaphoreAPIです。 これらのAPIが失敗したときは上に挙げた他のAPIと同様の処理を 行うことができます。一方でこれらのAPIは成功したときにも エラー番号を設定します。具体的にはAPIが成功しているときに GetLastErrorAPIを呼び出すと以下のような値が戻ります。

説明
ERROR_SUCCESS ミューテクスまたはセマフォを作成した。
ERROR_ALREADY_EXISTS 既に存在するミューテクスまたはセマフォをオープンした。

これらのAPIを利用する際にはたとえば次に示すように 成功しているにもかかわらずGetLastErrorAPIを 呼び出すコードを書く場合があることになります。

    HANDLE handle = CreateMutex(...);
    if  (handle == NULL) {
        DWORD errorno = GetLastError();
        エラー処理;
    } else {
        DWORD errorno = GetLastError();
        if  (errorno == ERROR_ALREADY_EXISTS)
            既にミューテクスが存在していたときの処理;
        else
            はじめてミューテクスを作成したときの処理;
    }

エラー番号を戻り値として返す

あるグループのAPIはエラー番号をAPI自体の戻り値として返します。 レジストリをアクセスするAPIはみなこのグループに含まれます。 たとえばRegCreateKeyExAPIのエラー処理は 以下のように行うといいでしょう。

    DWORD errorno = RegCreateKeyEx(...);
    if  (errorno != ERROR_SUCCESS) エラー処理;

構造化例外を発生する

まれにAPIが構造化例外を発生することがあります。 APIが構造化例外を発生するというマニュアルの記述はないため、 一般にはAPIにバグがあるときであると推測されます。 たとえばlstrlenの引数として NULLを渡すとWindows NT 3.51では 例外0xC0000005が送出されました。 この現象はWindows 4.0では発生せず、 APIは0を返します。

他の例として、USER32系APIの中のごく一部のものは、 HWNDの値が既に無効になっている ウインドウハンドルである場合に例外を送出することがあります。 たとえば、GetWindowTextLengthAPIは 無効なウインドウハンドルを渡されると例外0xC0000005を 送出することがあります。アプリケーションはウインドウハンドルが 有効である期間を制限することができないため、 この問題を回避するうまい方法はありません。したがって、 このAPIを利用するアプリケーションは必ず例外を捕獲する しくみを持たねばなりません。すなわち、たとえば下のようなコードを 書く必要があります。

    HANDLE hwnd = あるウインドウのハンドル;
    __try {
        DWORD length = GetWindowTextLength(hwnd);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        エラー処理;
    }

YUTANI Miki <yuta@kt.rim.or.jp>
Copyright © 1998 by YUTANI Miki.
All rights reserved.
$LastUpdated: Sun Mar 29 01:29:18 1998 $