2014年3月28日金曜日

遠回りで学ぶWin32 APIでOpenGL 002

遠回りで学ぶWin32 APIでOpenGL 001の続き

Win32 APIでのエラー取得

Win32 APIの実行によりエラーが発生した場合,GetLastError関数(MSDNのドキュメント)でエラーコードを取得し,FormatMessage関数(MSDNのドキュメント)でメッセージを文字列に変換する.

// 002_print_last_error.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

//! Win32 APIでのエラーをコンソールに出力する
void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    // cout, cinが使えるようにコンソールを割り当てる
    using_console uc;

    // 敢えて失敗するように未設定のウィンドウクラスの登録を行う
    WNDCLASSEX wcx = { 0 };
    if(RegisterClassEx(&wcx) == 0){
        printLastError();
        cout << "終了します.何か入力してください." << endl;
        cin.get();
        return 0;
    }

    return 0;
}

/*!
 *  GetLastError()によるエラーコードをFormatMessage()により
 *  文字列に変換し,標準出力に出力する.
 */
void printLastError()
{
    LPSTR pBuffer = NULL;
    DWORD error = GetLastError();
    DWORD result = FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        error,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPSTR>(&pBuffer),
        0,
        NULL
    );

    if(result > 0 && pBuffer){
        cout << pBuffer << endl;
        LocalFree(pBuffer);
    }
}

コンパイル

前回実装したコンソール割り当て用クラスのcppを追加し,User32.libをリンクする.

cl /EHsc 002_print_last_error.cpp using_console.cpp /link User32.lib

ウィンドウクラスの登録

エラーを確認するための準備が整ったので,実際にウィンドウを表示する.まずは,ウィンドウクラスのWNDCLASSEX構造体(MSDNのドキュメント)を登録する必要がある.

// 003_register_window_class.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

//! Win32 APIでのエラーをコンソールに出力する
void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    using_console uc;

    // 登録が成功するための最低限の設定
    WNDCLASSEX wcx = { 0 };
    // 構造体のサイズ
    wcx.cbSize = sizeof wcx;
    // ウィンドウクラス名
    wcx.lpszClassName = TEXT("hello");

    if(RegisterClassEx(&wcx) == 0){
        printLastError();
        cout << "終了します.何か入力してください." << endl;
        cin.get();
        return 0;
    }

    return 0;
}

void printLastError()
{
    /* 省略 */
}

コンパイル

cl /EHsc 003_register_window_class.cpp using_console.cpp /link User32.lib

ウィンドウの生成

ウィンドウクラスの登録に成功したら,そのウィンドウクラスを使ってウィンドウを生成する.ウィンドウの生成には,CreateWindow関数(MSDNのドキュメント)を使う.ただし,今の状態のウィンドウクラスでウィンドウを生成しようとすると,強制終了してしまう.

// 004_fail_to_create_window.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

//! Win32 APIでのエラーをコンソールに出力する
void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    using_console uc;

    /* 
       ウィンドウクラスの登録 
       (003_register_window_class.cppと同じ)
     */

    HWND hWnd = CreateWindow(
        wcx.lpszClassName,
        TEXT("hello"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if(!hWnd){
        printLastError();
        cin.get();
        return 0;
    }

    return 0;
}

void printLastError()
{
    /* 省略 */
}

このコードが動くようにするためには,WNDLCASSEX構造体の初期化時にウィンドウプロシージャを登録しておく必要がある.

// 005_create_window.cpp
// 色々省略

WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof wcx;
wcx.lpszClassName = TEXT("hello");
wcx.lpfnWndProc = DefWindowProc;

Win32 APIに用意されているデフォルトのウィンドウプロシージャDefWindowProc(MSDNのドキュメント)を登録しておくことで,無事ウィンドウが生成される.

コンパイル

cl /EHsc 004_fail_to_create_window.cpp using_console.cpp /link User32.lib
cl /EHsc 005_create_window.cpp using_console.cpp /link User32.lib

ウィンドウの表示

ウィンドウの生成に成功したら,ShowWindow関数(MSDNのドキュメント)を呼び出し,ウィンドウを表示する.初回の呼び出し時にはWinMain関数の引数のnCmdShowを与える必要がある.

// 006_show_window.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

//! Win32 APIでのエラーをコンソールに出力する
void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    using_console uc;

    WNDCLASSEX wcx = { 0 };
    wcx.cbSize = sizeof wcx;
    wcx.lpszClassName = TEXT("hello");
    wcx.lpfnWndProc = DefWindowProc;
    RegisterClassEx(&wcx);

    HWND hWnd = CreateWindow(/* 省略 */);
    if(!hWnd){
        printLastError();
        cin.get();
        return 0;
    }

    ShowWindow(hWnd, nCmdShow);

    cout << "終了します.何かキーを入力してください." << endl;
    cin.get();

    return 0;
}

void printLastError()
{
    /* 省略 */
}

コンパイル

cl /EHsc 006_show_window.cpp using_console.cpp /link User32.lib

第二回まとめ

  • ウィンドウを生成するにはまずRegisterClassExでウィンドウクラスを登録
  • CreateWindowでウィンドウの生成
  • ShowWindowでウィンドウの表示

遠回りなだけあってOpenGLが出てくるまでが長い.GLUTやGLEWが色々やってくれているのがよく分かる.

第三回へ

0 件のコメント:

コメントを投稿