第347章 ジュワッと出てくるウィンドウを作る


何とも変な表題です。要するにウィンドウが出てくるとき、いきなり 出てくるのではなくだんだん濃くなってきて出現するというものです。 終了するときもいきなり、ぱっと消えるのではなくジワーーッと消えていくようにします。 前章で出てきたSetLayeredWindowAttributes関数を使います。



起動すると、少しずつ濃くなって現れてきます。



完全にアルファが100%となった状態です。

終了するときは徐々に消えていきます。



これは、どのようにするかというと

1.メインウィンドウの拡張ウィンドウスタイルをWS_EX_LAYEREDにする
2.WM_CREATEメッセージが来たらウィンドウのアルファを0にする。
  また、SetTimer関数でタイマーを作成します。
3.WM_TIMERメッセージが来たらウィンドウのアルファを少し上げる
4.アルファが255になったらタイマーを殺す
5.終了確認で「はい」と答えたらタイマーを作成する
6.WM_TIMERメッセージが来たらアルファを少しずつ下げる
7.アルファが0になったらDestroyWindow関数でウィンドウを破棄する
こんな感じで実現します。では、プログラムを見てみましょう。
// layer02.cpp

#define _WIN32_WINNT 0x0500
#define ID_MYTIMER 100

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);

char szClassName[] = "layer02";    //ウィンドウクラス
HINSTANCE hInst;
_WIN32_WINNTを0x0500以上にdefineするのを忘れないでください。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;
    
    hInst = hCurInst;
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow)) 
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

//ウィンドウ・クラスの登録

ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;    //プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;//インスタンス
    wc.hIcon = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hCursor = (HCURSOR)LoadImage(NULL,
        MAKEINTRESOURCE(IDC_ARROW),
        IMAGE_CURSOR,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    //メニュー名
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);

    return (RegisterClassEx(&wc));
}

//ウィンドウの生成

BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;

    hWnd = CreateWindowEx(WS_EX_LAYERED,
            szClassName,
            "猫でもわかるlayer", //タイトルバーにこの名前が表示されます
            WS_OVERLAPPEDWINDOW, //ウィンドウの種類
            CW_USEDEFAULT,    //X座標
            CW_USEDEFAULT,    //Y座標
            180,    //幅
            170,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
メインウィンドウの拡張スタイルにWS_EX_LAYEREDを指定します。
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int id;
    HDC hdc, hdc_mem;
    HBRUSH hBrush;
    PAINTSTRUCT ps;
    char szBuf[32] = "猫でもわかるLayer";
    BITMAP bmp_info;
    HBITMAP hBmp;
    int wx, wy;
    static int nAlpha = 0;
    static BOOL bInc = TRUE;

    switch (msg) {
        case WM_CREATE:
            SetTimer(hWnd, ID_MYTIMER, 100, NULL);
            SetLayeredWindowAttributes(hWnd, 0, 0, LWA_ALPHA);
            break;
        case WM_TIMER:
            if (wp != ID_MYTIMER) {
                return DefWindowProc(hWnd, msg, wp, lp);
            }
            
            if (bInc) {
                nAlpha += 20;
                if (nAlpha >= 255) {
                    nAlpha = 255;
                    KillTimer(hWnd, ID_MYTIMER);
                }
            } else {
                nAlpha -= 20;
                if (nAlpha <= 0) {
                    nAlpha = 0;
                    KillTimer(hWnd, ID_MYTIMER);
                    DestroyWindow(hWnd);
                }
            }
            SetLayeredWindowAttributes(hWnd, 0, (BYTE)nAlpha, LWA_ALPHA);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            hBrush = CreateSolidBrush(RGB(255, 0, 0));
            SelectObject(hdc, hBrush);
            ExtFloodFill(hdc, 1, 1, RGB(255, 255, 255), FLOODFILLSURFACE);
            hBmp = (HBITMAP)LoadImage(hInst, "MYBMP",IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
            GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
            wx = bmp_info.bmWidth;
            wy = bmp_info.bmHeight;
            hdc_mem = CreateCompatibleDC(hdc);
            SelectObject(hdc_mem, hBmp);
            BitBlt(hdc, 0, 0, wx, wy, hdc_mem, 0, 0, SRCCOPY);
            DeleteObject(hBmp);
            DeleteDC(hdc_mem);
            SetBkMode(hdc, TRANSPARENT);
            TextOut(hdc, 10, 90, szBuf, (int)strlen(szBuf)); 
            DeleteObject(hBrush);
            EndPaint(hWnd, &ps);
            break;
        case WM_CLOSE:
            id = MessageBox(hWnd,
                "終了してもよろしいですか",
                "確認",
                MB_YESNO | MB_ICONQUESTION);
            if (id == IDYES) {
                SetTimer(hWnd, ID_MYTIMER, 100, NULL);
                bInc = FALSE;
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}
最初に説明したとおりです。

さて、ここでWM_TIMERメッセージが来るたびに

SetLayeredWindowAttributes(hWnd, 0, (BYTE)nAlpha, LWA_ALPHA);

を実行していますが3番目の引数nAlphaはBYTEに型キャストして使っています。 それなら初めからBYTE型で宣言しておけばよいじゃないか、と思われるかもしれません。

すると、次のような不具合が発生します。

この変数に250が格納されているとき、nAlpha += 20;が実行されると 桁あふれを起こしてしまい、if (nAlpha >= 255)の条件に引っかからなくなってしまいます。 実験するとわかりますが、ウィンドウがかなり濃くなったと思ったら、また薄くなる、という 動作を繰り返すことになります。

さて、今回のプログラムと同様の動作はAnimateWindow関数を使って実現することも可能です。 いろいろ研究してみてください。


[SDK第4部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 27/Apr/2003 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。