1

winAPI を使用してウィンドウを作成したい:

    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,
        int nShowCmd)
 {
WNDCLASSEX wClass;
HWND hWnd;


wClass.cbClsExtra=NULL;
wClass.cbSize=sizeof(WNDCLASSEX);
wClass.cbWndExtra=NULL;
wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wClass.hIcon=NULL;
wClass.hIconSm=NULL;
wClass.hInstance=hInst;
wClass.lpfnWndProc=(WNDPROC)WinProc;
wClass.lpszClassName=TEXT("Window Class");
wClass.lpszMenuName=NULL;
wClass.style=CS_HREDRAW|CS_VREDRAW|CS_DROPSHADOW ;

if(!RegisterClassEx(&wClass))
{
    int nResult=GetLastError();
    MessageBox(NULL,
        TEXT("Window class creation failed"),
        TEXT("Window Class Failed"),
        MB_ICONERROR);
}

hWnd=CreateWindowEx(NULL,
        TEXT("Window Class"),
        TEXT("My Process Explorer"),
        WS_OVERLAPPEDWINDOW,
        200,
        20,
        800,
        630,
        NULL,
        NULL,
        hInst,
        NULL);
  }

しかし、アクセス違反エラーが発生します。なんで?

4

1 に答える 1

6

Tenfour は既にコメントでこれを指摘していますが、さらに 6 ~ 8 回繰り返す必要があります。ウィンドウ プロシージャ関数へのポインタを にキャストしないでくださいWNDPROC。実際、キャストする必要がある理由が正確にわからない限り、何もキャストしないでください。キャストの理由を尋ねる彼のコメントに対するあなたの答えは次のとおりです。

警告を受けないために!

実際、それがまさに問題です!キャストが行うことは、コンパイラーに「黙れ、自分が何をしているのかわかっている!」と伝えることだけです。「上書き」ボタンを押したため、警告は表示されなくなりました。しかし、これらの警告には理由があります。コードが壊れていることを伝えようとしていたのです。コンパイラはあなたを助けるためにそこにあります。それを無視したり、さらに悪いことに、オーバーライド ビットを反転させてシャットダウンするように指示したりしても、それほど遠くまで到達することはできません。多作な Win32 ブロガー Raymond Chen が述べているように、「関数ポインタ キャストは発生するのを待っているバグです」 。(これは非常によくある間違いであり、彼はここここにも書いています。)

関数ポインターをキャストせざるを得ないと感じる最も一般的な理由は、関数のシグネチャが正しくないことをコンパイラーが警告しようとしているからです。ウィンドウ プロシージャ関数の正しいシグネチャは、MSDN のこちらに記載されており、次のようになります。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

もちろん、関数には任意の名前を付けることができます。ただし、パラメーターの数、その型、および戻り値はすべて、その署名と一致する必要があります。

そうでない場合、コンパイラはエラーを発行します。オーバーライド ビットを反転してエラーをキャストすると、コードは実行時に失敗します。これは、まさに現在発生している症状です。このCreateWindowEx関数は、「おっと、あなたが私を追い越そうとしたウィンドウ プロシージャを認識できません!」と言っています。

有効なウィンドウ プロシージャ スタブを作成し、スプリアス キャストを削除してコードを実行すると、エラーなく正常に動作します。例えば:

LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
                   LPSTR lpCmdLine, int nShowCmd)
{
    WNDCLASSEX wClass;
    HWND hWnd;

    wClass.cbClsExtra=NULL;
    wClass.cbSize=sizeof(WNDCLASSEX);
    wClass.cbWndExtra=NULL;
    wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
    wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wClass.hIcon=NULL;
    wClass.hIconSm=NULL;
    wClass.hInstance=hInst;
    wClass.lpfnWndProc=WinProc;
    wClass.lpszClassName=TEXT("Window Class");
    wClass.lpszMenuName=NULL;
    wClass.style=CS_HREDRAW|CS_VREDRAW|CS_DROPSHADOW ;

    if(!RegisterClassEx(&wClass))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            TEXT("Window class creation failed"),
            TEXT("Window Class Failed"),
            MB_ICONERROR);
    }

    hWnd=CreateWindowEx(NULL,
            TEXT("Window Class"),
            TEXT("My Process Explorer"),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            200,
            20,
            800,
            630,
            NULL,
            NULL,
            hInst,
            NULL);
}

ただし、他にも注意すべき点がいくつかあります。

  • ANSI エントリ ポイント を使用していますがWinMain、これは 2012 年以降、正しくありません。Unicode 用にコンパイルする必要があります。TEXT()が定義されているときに文字列リテラルがワイド文字列であることを確認するためにマクロを既に使用していUNICODEますが、エントリ ポイント関数で同じことを行う必要があります。定義を次のように変更します。

     int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                          LPTSTR lpCmdLine, int nCmdShow);
    
  • 関数の戻り値を正しくチェックしています。失敗した場合は、デバッグ支援としてRegisterClassEx呼び出します。GetLastError関数で同じことを行う必要がありCreateWindowExます。その関数のドキュメントは、最後のエラーを設定することを示しています。

    関数が失敗した場合、戻り値はNULLです。拡張エラー情報を取得するには、 を呼び出しますGetLastError

    したがって、コードを次のように変更できます。

    hWnd=CreateWindowEx(NULL,
            TEXT("Window Class"),
            TEXT("My Process Explorer"),
            WS_OVERLAPPEDWINDOW,
            200,
            20,
            800,
            630,
            NULL,
            NULL,
            hInst,
            NULL);
    if (!hWnd)
    {
       int nResult = GetLastError();
       MessageBox(NULL,
            TEXT("Window creation failed"),
            TEXT("Window Failed"),
            MB_ICONERROR);
    }
    
于 2012-06-03T11:35:18.843 に答える