Windows の使用
このセクションの例では、次のタスクを実行する方法について説明します。
- メイン ウィンドウ を作成する
- 子ウィンドウの作成、列挙、およびサイズ変更
- ウィンドウを壊す
- レイヤードウィンドウの使用
メイン ウィンドウの作成
通常、アプリケーションが作成する最初のウィンドウはメイン ウィンドウです。 メイン ウィンドウを作成するには、CreateWindowEx 関数を使用して、ウィンドウ クラス、ウィンドウ名、ウィンドウ スタイル、サイズ、位置、メニュー ハンドル、インスタンス ハンドル、作成データを指定します。 メイン ウィンドウはアプリケーション定義のウィンドウ クラスに属しているため、メイン ウィンドウを作成する前に、ウィンドウ クラスを登録し、クラスのウィンドウ プロシージャを指定する必要があります。
ほとんどのアプリケーションでは、通常、メイン ウィンドウを作成するために WS_OVERLAPPEDWINDOW スタイルを使用します。 このスタイルにより、ウィンドウにタイトル バー、ウィンドウ メニュー、サイズ設定の境界線、最小化ボタンと最大化ボタンが表示されます。 CreateWindowEx 関数は、ウィンドウを一意に識別するハンドルを返します。
次の例では、アプリケーション定義のウィンドウ クラスに属するメイン ウィンドウを作成します。 メイン ウィンドウ ウィンドウ名がウィンドウのタイトル バーに表示されます。 WS_VSCROLL スタイルと WS_HSCROLL スタイルを WS_OVERLAPPEDWINDOW スタイルと組み合わせることにより、アプリケーションは、WS_OVERLAPPEDWINDOW スタイルによって提供されるコンポーネントに加えて、水平および垂直スクロール バーを含むメイン ウィンドウを作成します。 CW_USEDEFAULT 定数が 4 回出現すると、ウィンドウの初期サイズと位置がシステム定義の既定値に設定されます。 メニュー ハンドルの代わりに null 指定すると、ウィンドウクラスに対してメニューが定義されます。
HINSTANCE hinst;
HWND hwndMain;
// Create the main window.
hwndMain = CreateWindowEx(
0, // no extended styles
"MainWClass", // class name
"Main Window", // window name
WS_OVERLAPPEDWINDOW | // overlapped window
WS_HSCROLL | // horizontal scroll bar
WS_VSCROLL, // vertical scroll bar
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no parent or owner window
(HMENU) NULL, // class menu used
hinst, // instance handle
NULL); // no window creation data
if (!hwndMain)
return FALSE;
// Show the window using the flag specified by the program
// that started the application, and send the application
// a WM_PAINT message.
ShowWindow(hwndMain, SW_SHOWDEFAULT);
UpdateWindow(hwndMain);
前の例では、メイン ウィンドウを作成した後、ShowWindow 関数を呼び出していることに注意してください。 これは、作成後にメイン ウィンドウが自動的に表示されないためです。 SW_SHOWDEFAULT フラグを ShowWindow 渡すことで、アプリケーションを起動したプログラムがメイン ウィンドウの初期表示状態を設定できるようになります。 UpdateWindow 関数は、ウィンドウの最初の WM_PAINT メッセージを送信します。
子ウィンドウの作成、列挙、サイズ変更
子ウィンドウを使用して、ウィンドウのクライアント領域を異なる機能領域に分割できます。 子ウィンドウの作成は、メイン ウィンドウの作成に似ています。CreateWindowEx関数使用します。 アプリケーション定義ウィンドウ クラスのウィンドウを作成するには、子ウィンドウを作成する前に、ウィンドウ クラスを登録し、ウィンドウ プロシージャを指定する必要があります。 子ウィンドウに WS_CHILD スタイルを指定し、子ウィンドウの作成時に親ウィンドウを指定する必要があります。
次の例では、同じサイズの 3 つの子ウィンドウを作成して、アプリケーションのメイン ウィンドウのクライアント領域を 3 つの機能領域に分割します。 各子ウィンドウはメイン ウィンドウのクライアント領域と同じ高さですが、それぞれが幅の 3 分の 1 です。 メイン ウィンドウは、WM_CREATE メッセージに応答して子ウィンドウを作成します。このメッセージは、メイン ウィンドウが独自のウィンドウ作成プロセス中に受け取ります。 各子ウィンドウには WS_BORDER スタイルがあるため、それぞれに細い線の境界線があります。 また、WS_VISIBLE スタイルが指定されていないため、各子ウィンドウは最初は非表示になります。 また、各子ウィンドウには子ウィンドウ識別子が割り当てられていることにも注意してください。
メイン ウィンドウは、サイズが変更されたときにメイン ウィンドウが受け取る WM_SIZE メッセージに応答して、子ウィンドウのサイズと位置を設定します。 WM_SIZEに応答して、メイン ウィンドウは、GetClientRect 関数を使用してクライアント領域のディメンションを取得し、そのディメンションを EnumChildWindows 関数に渡します。 EnumChildWindows は、各子ウィンドウにハンドルを渡し、アプリケーション定義の EnumChildProc コールバック関数に渡します。 この関数は、MoveWindow 関数を呼び出すことによって、各子ウィンドウのサイズを変更し、配置します。サイズと位置は、メイン ウィンドウのクライアント領域のサイズと子ウィンドウの識別子に基づきます。 その後、EnumChildProc 、ShowWindow 関数を呼び出してウィンドウを表示します。
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rcClient;
int i;
switch(uMsg)
{
case WM_CREATE: // creating main window
// Create three invisible child windows.
for (i = 0; i < 3; i++)
{
CreateWindowEx(0,
"ChildWClass",
(LPCTSTR) NULL,
WS_CHILD | WS_BORDER,
0,0,0,0,
hwnd,
(HMENU) (int) (ID_FIRSTCHILD + i),
hinst,
NULL);
}
return 0;
case WM_SIZE: // main window changed size
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
GetClientRect(hwnd, &rcClient);
EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient);
return 0;
// Process other messages.
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
LPRECT rcParent;
int i, idChild;
// Retrieve the child-window identifier. Use it to set the
// position of the child window.
idChild = GetWindowLong(hwndChild, GWL_ID);
if (idChild == ID_FIRSTCHILD)
i = 0;
else if (idChild == ID_SECONDCHILD)
i = 1;
else
i = 2;
// Size and position the child window.
rcParent = (LPRECT) lParam;
MoveWindow(hwndChild,
(rcParent->right / 3) * i,
0,
rcParent->right / 3,
rcParent->bottom,
TRUE);
// Make sure the child window is visible.
ShowWindow(hwndChild, SW_SHOW);
return TRUE;
}
ウィンドウを破壊する
DestroyWindow 関数を使用してウィンドウを破棄できます。 通常、アプリケーションはウィンドウを破棄する前に WM_CLOSE メッセージを送信し、ウィンドウが破棄される前にユーザーに確認を求める機会をウィンドウに与えます。 ウィンドウ メニューを含むウィンドウは、ユーザーがウィンドウ メニューから [閉じる] をクリックすると、自動的に WM_CLOSE メッセージを受け取ります。 ユーザーがウィンドウを破棄する必要があることを確認した場合、アプリケーションは DestroyWindow 呼び出します。 システムは、WM_DESTROY メッセージを画面から削除した後、ウィンドウに送信します。 WM_DESTROYに応答して、ウィンドウはデータを保存し、割り当てられたリソースを解放します。 メイン ウィンドウは、PostQuitMessage 関数を呼び出してアプリケーションを終了することで、WM_DESTROY の処理を終了します。
次の例は、ウィンドウを破棄する前にユーザーの確認を求めるメッセージを表示する方法を示しています。
WM_CLOSEに応答して、[はい ]、[いいえ ]、[キャンセル ] ボタンを含むダイアログ ボックスが表示されます。 ユーザーが [はい] クリックすると、DestroyWindow呼び出されます。それ以外の場合、ウィンドウは破棄されません。 アプリケーションが WM_CLOSE メッセージを処理している場合、0
はすべてのケースで返されます。 破棄されるウィンドウはメイン ウィンドウであるため、この例では、WM_DESTROYに応答 PostQuitMessage を呼び出します。
case WM_CLOSE:
// Create the message box. If the user clicks
// the Yes button, destroy the main window.
if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES)
DestroyWindow(hwndMain);
return 0;
case WM_DESTROY:
// Post the WM_QUIT message to
// quit the application terminate.
PostQuitMessage(0);
return 0;
レイヤード ウィンドウの使用
ダイアログ ボックスを半透明のウィンドウとして表示するには、最初にダイアログを通常どおりに作成します。 次に、WM_INITDIALOGで、ウィンドウの拡張スタイルの階層ビットを設定し、必要なアルファ値 setLayeredWindowAttributes を呼び出します。 コードは次のようになります。
// Set WS_EX_LAYERED on this window
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);
SetLayeredWindowAttributes の 3 番目のパラメーターは 0 から 255 の範囲の値であり、0 はウィンドウを完全に透明にし、255 は完全に不透明にします。 このパラメーターは、より汎用性の高い BLENDFUNCTION を持つ AlphaBlend 関数を模倣します。
このウィンドウをもう一度完全に不透明にするには、SetWindowLong呼び出して WS_EX_LAYERED ビットを削除し、ウィンドウに再描画を依頼します。 ビットの削除は、階層化とリダイレクトに関連付けられているメモリを解放できることをシステムに知らせるために必要です。 コードは次のようになります。
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
RedrawWindow(hwnd,
NULL,
NULL,
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
階層化された子ウィンドウを使用するには、アプリケーションがマニフェストで Windows 8 対応を宣言する必要があります。
Windows 10/11 の場合、この互換性スニペットを app.manifest
に含めて、Windows 10 に対応させることができます。
...
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 GUID -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
...
アプリ マニフェストの変更の詳細については、こちらを参照してください。アプリケーション マニフェスト