ウィンドウの作成
CreateWindow関数
さて、いよいよウィンドウの作成の説明に入ります。
ウィンドウを作るにはCreateWindow
関数を使用します。
-
HWND CreateWindowW(
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
); -
ウィンドウを作成する。
戻り値は作成したウィンドウのハンドル。
作成に失敗した場合はNULLを返す。
当サイトでは基本的にワイド文字版の関数を使用して説明します。
マルチバイト文字版の関数を使用したい場合は関数名の末尾に「A」を付け、文字列はワイド文字からマルチバイト文字に変更してください。
(例:LPWSTR→LPSTR)
関数名の末尾に「W」も「A」も付けずに使用した場合、ワイド文字版が呼び出されます。
(ただし設定によって変わります。三種類の同名関数も参照のこと)
#include <windows.h>
int APIENTRY wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nCmdShow)
{
//ウィンドウ作成
HWND hWnd = CreateWindow(
L"STATIC", L"テストアプリ",
WS_OVERLAPPEDWINDOW,
100, 100,
300, 300,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
//ウィンドウ作成の失敗
return 0;
}
//作成したウィンドウの表示
ShowWindow(hWnd, nCmdShow);
MessageBox(NULL, L"ウィンドウアプリのテスト", L"タイトル", MB_OK);
return 0;
}
このコードを実行すると以下のようなウィンドウが作られます。
同時にメッセージボックスが開き、OKを押下するとプログラムが終了します。
まだウィンドウを「作って表示しただけ」で、操作することはできません。
この関数は引数が多いので、順を追って説明します。
lpClassName
第一引数lpClassName
はウィンドウのクラス名を指定します。
ウィンドウクラスとは、ウィンドウの機能を決めるものです。
これは通常は自分で定義したものを使用します。
しかしまだその手順を説明していないので、ここではSTATIC
という文字列を指定しています。
これはWindowsが最初から用意しているクラス名(システムクラス)のひとつで、「STATIC」は単純な文字列を表示する機能を持つウィンドウです。
ウィンドウ作成の説明のためにとりあえずこのウィンドウクラスを使用します。
lpWindowName
第二引数のlpWindowName
はウィンドウのタイトルバーに表示される文字列です。
第一引数がSTATIC
の時、ウィンドウ内にも同じ文字列が表示されます。
dwStyle
第三引数dwStyle
はウィンドウのスタイルを定数で指定します。
スタイルはウィンドウの見た目のほか、ウィンドウに対して可能な操作に関わります。
WS_OVERLAPPEDWINDOW
は一般的なウィンドウアプリのスタイルです。
ここに指定できる定数は後でまとめて説明します。
X、Y、nWidth、nHeight
第四、第五、第六、第七引数は、デスクトップ画面の左上を基点(0, 0)としたウィンドウの位置とサイズの指定です。
X
は横位置、Y
は縦位置です。
nWidth
は横幅、nHeight
は縦幅です。
ウィンドウが子ウィンドウの場合、親ウィンドウの左上が基点となります。
CW_USEDEFAULT
位置とサイズにはCW_USEDEFAULT
という定数が使用できます。
位置やサイズに数値を指定すると、指定した通りの位置とサイズでウィンドウが起動します。
これでも構わないのですが、例えば同じアプリを複数同時に起動した場合に同じ位置とサイズにウィンドウが表示されることになり、少し使いづらいことがあります。
CW_USEDEFAULTを指定すると、現在開かれている別のウィンドウを基準にしてWindowsが適当な位置とサイズでウィンドウを生成してくれます。
特にこだわりがない場合はこの定数を指定したほうが良いでしょう。
ただしウィンドウスタイルによってはCW_USEDEFAULT
が使用できず、0を指定したものとみなされます。
hWndParent
第八引数hWndParent
は親ウィンドウ、またはオーナーウィンドウのウィンドウハンドルを指定します。
NULL
を指定すると独立したウィンドウを作成します。
hMenu
第九引数hMenu
はウィンドウが持つメニューを指定します。
メニューはあらかじめ作成しておく必要があります。
必要がない場合はNULL
を指定します。
子ウィンドウを作成する場合はここに子ウィンドウID(整数値)を指定します。
このIDは親ウィンドウが子ウィンドウを識別するために使用します。
hInstance
第十引数hInstance
はウィンドウのインスタンスハンドルを指定します。
これはWinMain関数の引数で渡されるhInstance
をそのまま指定します。
インスタンスは「実体」という意味で、ハンドルはその(メモリ上の)位置を意味します。
WinMain関数の引数のhInstanceは、プログラム(実行ファイル)をメモリにロードした位置を表します。
インスタンスやハンドルなどの「中身」のデータが具体的にどうなっているかはプログラマは知る必要はありませんし、直接変更してはいけません。
操作する場合は専用の関数が用意されているので、それを使用します。
lpParam
最後のlpParam
はウィンドウが作成される時に使用できる任意の値を指定します。
今は使用しないのでNULLを指定します。
戻り値
CreateWindow関数の戻り値は作成したウィンドウのハンドルです。
プログラムはこのウィンドウハンドルでウィンドウを識別します。
ウィンドウ作成に失敗した場合はNULL
になります。
ShowWindow関数
CreateWindow関数が成功すればウィンドウが作成されます。
が、そのままではメモリ上に作成されただけであり画面には表示されません。
作成したウィンドウは初期状態では非表示なので、ShowWindow
関数で表示する必要があります。
- BOOL ShowWindow(
HWND hWnd,
int nCmdShow
); - ウィンドウhWndの表示状態をnCmdShowに変更する。
戻り値はウィンドウが表示状態だった場合は0以外を返す。
非表示常態だった場合は0を返す。
hWnd
には先ほど作成したウィンドウのハンドルを指定します。
nCmdShow
は表示状態を表す以下の定数のいずれかを指定します。
ただし、アプリケーションが最初にShowWindow関数を呼び出す場合はWinMain関数の引数nCmdShow
をそのまま渡す必要があります。
定数名 | 説明 |
---|---|
SW_HIDE | ウィンドウを非表示にし、別のウィンドウをアクティブにする |
SW_SHOWNORMAL SW_NORMAL |
ウィンドウをアクティブにし、通常状態で表示 最大化/最小化されている場合はオリジナルサイズに戻す |
SW_SHOWMINIMIZED | ウィンドウをアクティブにし、最小化 |
SW_SHOWMAXIMIZED SW_MAXIMIZE |
ウィンドウをアクティブにし、最大化 |
SW_SHOWNOACTIVATE | ウィンドウを通常状態で表示 アクティブにしない |
SW_SHOW | ウィンドウをアクティブにし、現在の位置とサイズで表示 |
SW_MINIMIZE | 最小化し、次に前面にある別のウィンドウをアクティブにする |
SW_SHOWMINNOACTIVE | 最小化 ウィンドウをアクティブにしない |
SW_SHOWNA | ウィンドウを現在の位置とサイズで表示 アクティブにしない |
SW_RESTORE | ウィンドウをアクティブにし、通常状態で表示 最大化/最小化されている場合はオリジナルサイズに戻す 最小化されたウィンドウを元に戻す場合に使用する |
SW_SHOWDEFAULT | プログラム起動時の初期状態で表示 |
SW_FORCEMINIMIZE | ウィンドウを強制的に最小化 ウィンドウのスレッドが応答していない場合に、別のスレッドから最小化する場合に使用 |
dwStyleの定数
CreateWindow関数の引数dwStyle
は、ウィンドウのスタイルを指定する定数です。
以下はその定数の一覧です。
定数名 | 説明 |
---|---|
WS_OVERLAPPED WS_TILED |
オーバーラップウィンドウ (タイトルバーと枠のあるウィンドウ) |
WS_POPUP | ポップアップウィンドウ このスタイルは WS_CHILD と併用できない |
WS_CHILD WS_CHILDWINDOW |
子ウィンドウ このスタイルは WS_POPUP と併用できない |
WS_MINIMIZE WS_ICONIC |
最小化状態 |
WS_VISIBLE | 表示(可視)状態 |
WS_DISABLED | 無効な状態のウィンドウ (ユーザー入力を受け付けない) |
WS_CLIPSIBLINGS | 子ウィンドウの描画時、兄弟ウィンドウが重なっているときに、重なっている箇所を描画しない(クリッピングする) 子ウィンドウがこのスタイルを持たない場合、重なっている兄弟ウィンドウ内への描画が行われる(上書きする) |
WS_CLIPCHILDREN | 親ウィンドウの描画時に子ウィンドウ領域を描画しない このスタイルは親ウィンドウを作成する時に使用する |
WS_MAXIMIZE | 最大化状態 |
WS_CAPTION | タイトルバーのあるウィンドウWS_BORDER も同時に指定される
|
WS_BORDER | 境界線を持つウィンドウ |
WS_DLGFRAME | ダイアログボックスで通常使われる境界線を持つウィンドウ このスタイルはタイトルバーを持てない |
WS_VSCROLL | 垂直スクロールバーを持つウィンドウ |
WS_HSCROLL | 水平スクロールバーを持つウィンドウ |
WS_SYSMENU | システムメニューを持つウィンドウWS_CAPTION と同時に指定しなければならない(システムメニュー→ウィンドウ左上のボタン) |
WS_THICKFRAME WS_SIZEBOX |
サイズ変更境界線を持つウィンドウ |
WS_GROUP | コントロールグループの最初のグループを指定する 次にWS_GROUPを持つコントロールが現れるまでに定義されたコントロールがひとつのグループとなる このスタイルが指定されたコントロールが WS_TABSTOP スタイルを持つことにより、Tabキーで各グループ間のフォーカスを移動できるクループ内では方向キーでコントロールのフォーカスを移動できる |
WS_TABSTOP | このスタイルが指定されたコントロールはTabキーによりフォーカスを受け取ることができる |
WS_MINIMIZEBOX | 最小化ボタンを持つウィンドウWS_SYSMENU と同時に指定しなければならない拡張ウィンドウスタイルの WS_EX_CONTEXTHELP と同時に指定できない
|
WS_MAXIMIZEBOX | 最大化ボタンを持つウィンドウWS_SYSMENU と同時に指定しなければならない拡張ウィンドウスタイルの WS_EX_CONTEXTHELP と同時に指定できない
|
WS_OVERLAPPEDWINDOW WS_TILEDWINDOW |
WS_OVERLAPPED 、WS_CAPTION 、WS_SYSMENU 、WS_THICKFRAME 、WS_MINIMIZEBOX 、WS_MAXIMIZEBOX の組み合わせ一般的なオーバーラップウィンドウが作成される |
WS_POPUPWINDOW | WS_POPUP 、WS_BORDER 、WS_SYSMENU の組み合わせシステムメニューを使用する場合は WS_CAPTION を同時に指定しなければならない |
ウィンドウスタイル定数は「WS_○○」の形式です。
数も多く、ところどころ不明な用語もあるかと思いますが、今はあまり気にしなくて良いです。
なお、複数の定数を同時に指定する場合はビット演算のOR演算子を使用します。
(ビットとビット演算参照)
//WS_OVERLAPPEDWINDOWと同等の指定をビット演算子を使用して行う
HWND hWnd = CreateWindow(
L"STATIC", L"テストアプリ",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
WS_THICKFRAME | WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
CreateWindowEx関数
ウィンドウの作成にはCreateWindowEx
という関数を使用することもできます。
これは第一引数に拡張ウィンドウスタイルが指定できる以外はCreateWindow関数と同じです。
-
HWND CreateWindowW(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
); -
ウィンドウを作成する。
戻り値は作成したウィンドウのハンドル。
作成に失敗した場合はNULLを返す。
CreateWindow関数の第一引数が拡張スタイルの指定になっています。
以降の引数はCreateWindow関数と同じものです。
拡張スタイルは以下の定数(の組み合わせ)が使用できます。
WS_EX_DLGMODALFRAME | 二重の境界線を持つウィンドウ |
WS_EX_NOPARENTNOTIFY | このスタイルを子ウィンドウに指定すると、ウィンドウの作成や破棄時に親ウィンドウにメッセージを送らない |
WS_EX_TOPMOST | ウィンドウを常に最前面に表示 この状態は SetWindowPos 関数で変更できる |
WS_EX_ACCEPTFILES | ファイルをドラッグ&ドロップ可能なウィンドウ |
WS_EX_TRANSPARENT | 透過ウィンドウ |
WS_EX_MDICHILD | MDI子ウィンドウ MDI=マルチドキュメントインターフェイス ウィンドウの中に小さなウィンドウを入れ子にしたもの |
WS_EX_TOOLWINDOW | フローティングツールウィンドウ タスクバーに表示されない |
WS_EX_WINDOWEDGE | 盛り上がった境界を持つウィンドウ |
WS_EX_CLIENTEDGE | くぼんだ境界を持つウィンドウ |
WS_EX_CONTEXTHELP | タイトルバーに「?」ボタンを表示 これをクリックするとマウスポインタに「?」アイコンが表示され、その状態で子ウィンドウをクリックすると WM_HELP メッセージが子ウィンドウに送られるWM_HELPメッセージは親ウィンドウに送らなければならない 親ウィンドウはHELP_WM_HELPメッセージを用いてWinHelpを呼び出さなければならない このスタイルは WS_MAXIMIZEBOX やWS_MINIMIZEBOX と同時に指定できない
|
WS_EX_RIGHT | 右から左へ読む言語(ヘブライ語やアラビア語など)のために、右揃えで表示する 日本語などの環境ではこのスタイルは無視される |
WS_EX_LEFT | 左揃えで表示 このスタイルは既定(指定しなくても標準で設定されている) |
WS_EX_RTLREADING | 右から左へ読む言語のために、テキストを右から左に表示する 日本語などの環境ではこのスタイルは無視される |
WS_EX_LTRREADING | 左から右へテキストを表示 このスタイルは既定 |
WS_EX_LEFTSCROLLBAR | 右から左へ読む言語のために、垂直スクロールバーを左端に表示する 日本語などの環境ではこのスタイルは無視される |
WS_EX_RIGHTSCROLLBAR | 垂直スクロールバーを右端に表示 このスタイルは既定 |
WS_EX_CONTROLPARENT | このスタイルを指定すると、Tabキー等のフォーカス移動の対象に子ウィンドウが含まれるようになる |
WS_EX_STATICEDGE | ユーザー入力を受け付けない項目のための、3D境界線を持つウィンドウ |
WS_EX_APPWINDOW | ウィンドウが可視状態の時、タスクバーにトップレベルウィンドウを強制的に表示 |
WS_EX_OVERLAPPEDWINDOW | WS_EX_WINDOWEDGE 、WS_EX_CLIENTEDGE の組み合わせ |
WS_EX_PALETTEWINDOW | WS_EX_WINDOWEDGE 、WS_EX_TOOLWINDOW 、WS_EX_TOPMOST の組み合わせ |
WS_EX_LAYERED | レイヤーウィンドウCS_OWNDC やCS_CLASSDC と同時に指定できない |
WS_EX_NOINHERITLAYOUT | 子ウィンドウにウィンドウレイアウトを渡さない |
WS_EX_NOREDIRECTIONBITMAP | リダイレクトサーフィスを描画しない 可視コンテンツを持たない場合や、標準以外の方法で描画する場合に使用 (Windows8以降) |
WS_EX_LAYOUTRTL | 右から左へ読む言語のために、レイアウトの水平方向の起点を右端に設定 座標は右から左へと値が増えていく |
WS_EX_COMPOSITED | 子孫ウィンドウを下から上へ描画するCS_OWNDC やCS_CLASSDC を同時に指定できない(Windows XP以降) |
WS_EX_NOACTIVATE | トップレベルウィンドウはユーザーのクリック等の動作でアクティブにならない アクティブにするには SetActiveWindow 関数やSetForegroundWindow 関数を使用する標準ではタスクバーに表示されない タスクバーに表示するには WS_EX_APPWINDOW を同時に指定する |
拡張ウィンドウスタイルの定数はすべて「WS_EX_○○」という形式です。
ウィンドウひとつ作るだけでかなりの情報量ですが、全てを把握する必要はありません。
とりあえずはサンプルコード通りのウィンドウを作成し、必要に応じていろいろと変更してみましょう。