ウィンドウのクライアント領域を Glass にする方法

Aero Glassy Window

Aero Glass な環境でウィンドウを表示しても、そのままではクライアント領域が透けたりしない。もったいないのでクライアント領域まで飴細工みたいにしようよ。

もちろんコンパイルには Vista の プラットフォーム SDK がいる。あ、それと気づかないうちに Visual Studio 2005 にしかない機能使ってるかも。

ヘッダーファイル

Desktop Window Manager ってやつの API を使うのでヘッダーとライブラリを組み込む。ライブラリはプロジェクトのプロパティでも可。

#include <windows.h>
#include <tchar.h>

#pragma comment(lib, "dwmapi.lib")
#include <dwmapi.h>

SetWindowGlassy 関数

いきなりだけどキモ。デスクトップコンポジション (Aero Glass のことか) が有効だったら、クライアント領域を飴細工にする。無効なら無難なデザインに。DwmIsCompositionEnabled() で判別して、DwmExtendFrameIntoClientArea() で細工。クライアント領域とウィンドウの縁とのスキマを広げるカンジみたい。MARGINS 構造体のメンバーのどれか1つが負数ならいい。

void SetWindowGlassy(HWND hWnd)
{
    BOOL bEnabled;
    LONG lColor;
    MARGINS margins = { };

    DwmIsCompositionEnabled(&bEnabled);

    if (bEnabled)
    {
        lColor = COLOR_BACKGROUND + 1;
        margins.cxLeftWidth = -1;
    }
    else
    {
        lColor = COLOR_WINDOW + 1;
        margins.cxLeftWidth = 0;
    }

    SetClassLongPtr(hWnd, GCL_HBRBACKGROUND, lColor);
    DwmExtendFrameIntoClientArea(hWnd, &margins);
    InvalidateRect(hWnd, NULL, TRUE);
}

ウィンドウプロシージャ

ウィンドウが作られたときに、表示する前に SetWindowGlassy() でいじってる。デスクトップ コンポジションの状態が変化したときに流れてくる WM_DWMCOMPOSITIONCHANGED メッセージに反応して切り替えるようにもしてある。

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        SetWindowGlassy(hWnd);
        ShowWindow(hWnd, SW_SHOWDEFAULT);
        UpdateWindow(hWnd);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_DWMCOMPOSITIONCHANGED:
        SetWindowGlassy(hWnd);
        break;

    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    return FALSE;
}

エントリーポイント

何の変哲もないただのWinMain。CS_HREDRAW と CS_VREDRAW がないと最大化とかしたときに変になることがあるかも。

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
    WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
    MSG msg;

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.lpszClassName = _T("Aero Glass Window");
    if (!RegisterClassEx(&wcex))
        return GetLastError();

    if (!CreateWindowEx(NULL, wcex.lpszClassName, wcex.lpszClassName, WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, hInstance, NULL))
        return GetLastError();

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}