Win32 App Managing Application State - yoshimune/LearningDirectX11 GitHub Wiki
This page refers to 【Managing Application State】
ウィンドウプロシージャはメッセージに対して実行される単なる関数です。よって本質的にステートレスです。したがって、アプリケーションの状態を津伊勢駅する方法が必要です。
簡単な方法は、単純に全ての状態をグローバル変数に格納することです。この手法は十分に小さいプログラムであればうまくいきます。またおおくのSDKサンプルはこの方法を使用しています。しかし、大きなプログラムの場合、グローバル変数の拡散を招いてしまいます。また、それぞれ個別のウィンドウプロシージャを持ついくつかのウィンドウを使うかもしれません。どのウインドウにどの変数がアクセスするのかを把握することが前提のプログラムは、混乱しやすくなり、エラーを起こしやすくなります。
CreateWindowEX関数はいくつかのデータ構造体をウィンドウに渡す方法を提供します。この関数が呼ばれると、次の2つのメッセージをウィンドウプロシージャに送信します。
これらのメッセージはリストされた順序で送信されます。(CreateWindowEXの間に送信されるメッセージはこれら2つだけではありません。しかし、その他のメッセージについては今回は無視します。)
WM_NCCREATEとWM_CREATEメッセージはウィンドウが表示される前に送信されます。ということはUIなどの初期化をするには都合が良いということになります。
CreateWindowExで最後の引数は**void* **ポインタです。渡したい値を引数に渡すことができます。ウィンドウプロシージャがWM_NCCREATEかWM_CREATEメッセージをハンドルするとき、メッセージからこの値を抜粋します。
このパラメータを使用してがアプリケーションデータをウィンドウに渡す方法を見てみましょう。最初に、ステート情報を保持するクラスか構造体を定義してください。
struct StateInfo
{
int state = 100;
};
CreateWindowExを呼ぶとき、ポインタをこの構造体の最後の void* 引数に渡してください。
//Create the window.
// #include <new> が必要
StateInfo *pState = new (std::nothrow) StateInfo;
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent Window
NULL, // Menu
hInstance, // Instance handle
pState // Additional application data
);
WM_NCCREATEとWM_CREATEメッセージを受け取ったとき、それぞれのメッセージのlParamパラメータはCREATESTRUCT構造体のポインタです。CREATESTRUCT構造体はCreateWindowExに渡したポインタを含みます。
inline StateInfo* GetAppState(HWND hwnd)
{
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
return pState;
}
WindowProc
StateInfo *pState;
if (uMsg == WM_CREATE)
{
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);
}
else
{
pState = GetAppState(hwnd);
}
データ構造へのポインタを抽出する方法は次のとおりです。最初にCREATESTRUCT構造体をIParam引数をキャストすることにより取得します。
CREATESTRUCT構造体のlpCreateParamsメンバーはCreateWindowExで指定されたオリジナルのvoidポインターです。lpCreateParamsにキャストして自身のでデータ構造体ポインタを取得してください。
次に、SetWindowLongPtr関数を呼び出し、データ構造体へのポインタを渡します。
最後の関数を呼ぶ目的は、ウインドウのインスタンスデータ内にStateInfoポインタを保持するためです。一度これを行うと、常にウィンドウからGetWindowLongPtrを呼ぶことによってポインタにアクセスすることができます。
各々のウィンドウは自身のインスタンスデータを保持しています。よって、複数のウィンドウの作成とそれぞれのウインドウに自身のインスタンスデータ構造体を渡す事ができます。この手法はウィンドウクラスの定義と一つ以上のウインドウクラスの作成に特に有効です。例えば、カスタムコントロールクラスを作成する場合などです。小さなヘルパー関数でGetWindowLongPtr呼び出しをラップすると便利です。(inline StateInfo* GetAppState(HWND hwnd)
)
このアプローチはさらに拡張できます。ウィンドウのステート情報を保持したデータ構造体を既に定義しています。このデータ構造体に、データ操作をするメンバー関数を提供することは理にかなっています。
Next:Win32 App Module 2. Using COM in Your Windows-Based Program