EternalWindows
ウインドウ管理 / レイヤードウインドウ

レイヤードウインドウとは、自分の下に位置するウインドウとアルファブレンドを 行うことで、色が混ざって見えるようなウインドウことです。 デスクトップには様々なウインドウが散在し、重なり合うことがありますが、 そのウインドウがレイヤードウインドウであれば、 それが占めている面積に限っては下に位置するウインドウを見えるため、 デスクトップという限られた範囲を有効に利用できます。

ウインドウをレイヤードウインドウにするには、 Windows 2000から登場したWS_EX_LAYEREDという拡張ウインドウスタイルを CreateWindowExの第1引数に指定します。

hwnd = CreateWindowEx(WS_EX_LAYERED, ...);

これで、ウインドウはレイヤードウインドウになります。 しかし、レイヤードウインドウのアルファ値(不透明度)は初期値が0であるため、 完全に透明の状態であり、表示しても何も見えることはありません。 そのため、明示的にアルファ値を設定する必要がでてきます。 これには、SetLayeredWindowAttributesを呼び出します。

BOOL SetLayeredWindowAttributes(
    HWND hwnd,
    COLORREF crKey,
    BYTE bAlpha,
    DWORD dwFlags
);

hwndは、レイヤードウインドウのハンドルを指定します。 crKeyは、カラーキー(透明にする色)を指定します。 bAlphaは、アルファ値を指定します。 BYTE型であるため取りうる範囲は0から255までであり、 0は完全な透明、255は不透明となります。 dwFlagsは、どの引数を使用するかを示す定数を指定します。 LWA_COLORKEYを指定するとcrKeyが有効になり、 LWA_ALPHAを指定するとbAlphaが有効になります。

次に、SetLayeredWindowAttributesの呼び出しの例を示します。

SetLayeredWindowAttributes(hwnd, 0, 128, LWA_ALPHA);

第4引数に、LWA_ALPHAを指定しているため第3引数が有効になり、 ここでは128としています。 この場合、ウインドウは次のように見えることになります。

見て分かるように、ウインドウの色が混ざっています。 先のコードではアルファ値を128としていたため、 ちょうどウインドウが半透明になったといえます。

アルファ値を取得したいときは、GetLayeredWindowAttributesを呼び出します。

BOOL GetLayeredWindowAttributes(
    HWND hwnd,
    COLORREF *pcrKey,
    BYTE *pbAlpha,
    DWORD *pdwFlags
);

hwndは、レイヤードウインドウのハンドルを指定します。 pcrKeyは、カラーキーを受け取る変数のアドレスを指定します。 pbAlphaは、アルファ値を受け取る変数のアドレスを指定します。 pdwFlagsは、レイヤリングフラグを受け取る変数のアドレスを指定します。 レイヤリングフラグとは、LWA_COLORKEYやLWA_ALPHAのことを意味します。 pcrKeyやpbAlpha、pdwFlagsには共にNULLを指定できるため、 必要となる値のみを取得するとよいでしょう。

次に、GetLayeredWindowAttributesの呼び出しの例を示します。

BYTE alpha;

GetLayeredWindowAttributes(hwnd, NULL, &alpha, NULL);

SetLayeredWindowAttributesとは異なり、GetLayeredWindowAttributesは WindowsXPから登場した関数であるため、前者の関数が呼び出せるからといって、 後者の関数が呼び出せるとは限らないことに注意してください。

今回のプログラムはタイマを作成し、一定間隔毎にSetLayeredWindowAttributesを呼ぶように します。これにより、ウインドウが透明から不透明になる動きを確認しやすくなります。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(WS_EX_LAYERED, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static int  nAlpha = 255;
	static BOOL bUp = FALSE;

	switch (uMsg) {

	case WM_CREATE:
		SetTimer(hwnd, 1, 60, NULL);
		SetLayeredWindowAttributes(hwnd, 0, (BYTE)nAlpha, LWA_ALPHA);
		return 0;

	case WM_TIMER:
		if (bUp) {
			nAlpha += 10;
			if (nAlpha > 255) {
				nAlpha = 255;
				bUp = FALSE;
			}
		}
		else {
			nAlpha -= 10;
			if (nAlpha < 0) {
				nAlpha = 0;
				bUp = TRUE;
			}
		}
		SetLayeredWindowAttributes(hwnd, 0, (BYTE)nAlpha, LWA_ALPHA);
		return 0;

	case WM_DESTROY:
		KillTimer(hwnd, 1);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

WindowProcで宣言されているnAlphaは、ウインドウの不透明度を表すアルファ値であり、 SetLayeredWindowAttributesの第3引数に指定されます。 BYTE型ではなくint型で宣言しているのは、アルファ値の増減の際にあふれを起こすのを 防ぐためです。初期値が255としており、またbUpがFALSEとなっていることから、 255が0に近づく、即ち不透明が透明に近づいていきます。 WM_CREATEの処理を確認します。

case WM_CREATE:
	SetTimer(hwnd, 1, 60, NULL);
	SetLayeredWindowAttributes(hwnd, 0, (BYTE)nAlpha, LWA_ALPHA);
	return 0;

SetLayeredWindowAttributesの引数の指定の仕方は既に説明してきた通りです。 拡張ウインドウスタイルにWS_EX_LAYEREDが含まれていなければ SetLayeredWindowAttributesの効果は意味を持たないことに注意してください。 SetTimerの第3引数が60となっていることから、WM_TIMERが約60ミリ秒毎に送られます。

case WM_TIMER:
	if (bUp) {
		nAlpha += 10;
		if (nAlpha > 255) {
			nAlpha = 255;
			bUp = FALSE;
		}
	}
	else {
		nAlpha -= 10;
		if (nAlpha < 0) {
			nAlpha = 0;
			bUp = TRUE;
		}
	}
	SetLayeredWindowAttributes(hwnd, 0, (BYTE)nAlpha, LWA_ALPHA);
	return 0;

nAlphaは増加し続けるべきと減り続けるべきときがあり、それをbUpで制御します。 増減のどちらかに関わらず限界値は設定し、それを超過する場合は bUpの値を変更します。最後に、更新されたnAlphaをSetLayeredWindowAttributesに 指定します。

ウインドウのフェードイン

SetLayeredWindowAttributesとWM_TIMERのような周期的なメカニズムを利用すれば、 ウインドウのアルファ値の変更を自動で行うことが可能となります。 しかし、そのような効果がウインドウを表示するときにのみ必要であれば、 つまり、ウインドウをフェードインしながら表示したいだけの場合は、 AnimateWindowという関数を呼び出すのが効果的でしょう。

AnimateWindow(hwnd, 1000, AW_BLEND | AW_ACTIVATE);
ShowWindow(hwnd, nCmdShow);

このようにした場合、ウインドウは徐々に不透明になりつつ表示されることになります。 つまり、簡単なフェードイン効果(AW_BLEND)をもたらしているといえます。 AW_ACTIVATEはアニメーションが終了した後、そのウインドウをアクティブにする意味を持ち、 第2引数は再生時間をミリ秒単位で指定します。 なお、AnimateWindowはレイヤードウインドウと直接繋がりがあるわけでないため、 CreateWindowExの拡張スタイルにWS_EX_LAYEREDを指定する必要はありません。



戻る