◆レイヤードウィンドウ - VC++ 2005 MFC (スケスケ、アニメーションなウィンドウ) その2

[ ホーム ] [ 上へ ]

・UpdateLayeredWindowでのフェードアウト処理

UpdateLayeredWindowの定義は

BOOL    UpdateLayeredWindow(CDC           *pDCDst,
                            POINT         *pptDst,
                            SIZE          *psize,
                            CDC           *pDCSrc,
                            POINT         *pptSrc,
                            COLORREF      crKey,
                            BLENDFUNCTION *pblend,
                            DWORD         dwFlags);

です。引数は

pDCDst ・・・画面のデバイスコンテキストへのポインタ。GetDesktopWindow()->GetDC()で取得可能。
pptDst ・・・レイヤードウィンドウの新しい位置。
psize  ・・・レイヤードウィンドウの新しいサイズ。
pDCSrc・・・レイヤードウィンドウを定義するサーフェスのDCへのポインタ
pptSrc ・・・デバイスコンテキストにおけるレイヤの位置。
             (pDCSrcで選択された透過情報を含んだビットマップ位置)
crKey  ・・・dwFlagsにULW_COLORKEYを指定した時に透過する色。
pblend ・・・dwFlagsにULW_ALPHAを指定した時に
             全体の透明度を定義するBLENDFUNCTION構造体へのポインタ
dwFlags・・・透明度設定、透過色設定を指定するフラグ

この関数はpDCSrcで選択されているサーフェスを元に、dwFlagsにULW_COLORKEYを 指定した場合はcrKeyで設定した色を透過色として描画の更新をします。
今回は主に透明度について書いているので、dwFlagsにULW_ALPHAを指定して、pblendで全体の 透明度を設定した上で、pDCSrcで選択されているサーフェスを更新・描画することになります。

これら引数のうちでキーポイントになるのは青色で書いているのでわかると思いますが pDCSrcです。ここをクリアすることがUpdateLayeredWindowを使う上での最大の難所でしょう。 この「レイヤードウィンドウを定義するサーフェス」ってなんことかよくわからなかったんですが、 いろいろ調べてみて、レイヤードウィンドウが表示される、透過情報を保持した各ピクセルの色情報の 集合体、つまり、透過情報をもったビットマップなんでしょうね。

ということは、pDCSrcを設定するには透明にしようとしているウィンドウの ビットマップを作って、それをpDCSrcで選択しなければならないということです。しかも、 そのビットマップは透過情報を保持してないといけないです。実装の前にその辺のところを 書いてみます。

<DDBビットマップ>

単にウィンドウ部分のビットマップを取得するのであれば、スクリーンのメモリデバイス コンテキストと、互換性のあるビットマップを作り、CDC::BitBltで転送すれば作れます。

CDC         *pDC = CDC::FromHandle( ::GetDC(NULL) );   //スクリーンのデバイスコンテキスト
CDC         memDC;                                     //メモリデバイスコンテキスト
CBitmap     bitmap;

memDC.CreateCompatibleDC(pDC);
bitmap.CreateCompatibleBitmap(pDC, 20, 10); //20,10はウィンドウの幅,高さ-GetWindowRectで取得

CBitmap *pOldBitmap = memDC.SelectObject(&bitmap);
memDC.BitBlt(0, 0, 20, 10, pDC, 2, 1, SRCCOPY);    //2,1はスクリーン上のウィンドウの位置
・
・

これでbitmapにウィンドウ部分のビットマップが作れます。しかし、これでは ビットマップの各ピクセルに透過情報が設定されません。このビットマップはDDBと呼ばれ、 GDI関数で操作は楽にできますが透過情報を持っていません。そこで登場するのが DIBSectionビットマップです。

<DIBSectionビットマップ>

このタイプのビットマップでは、作成する際に透過情報の 属性を設定することもでき、作成すると各ピクセルの色情報がメモリブロックとして返却されます。 あとはこのメモリに直接アクセスしてα値(透過情報)を含め、RGBの値を設定すればいいです。 このDIBSectionビットマップを作成するAPIが

HBITMAP  CreateDIBSection(HDC                hdc,
                          CONST BITMAPINFO   *pbmi,      
                          UINT               iUsage,
                          VOID               **ppvBits,
                          HANDLE             hSection,
                          DWORD              dwOffset);

です。

hdc ・・・・・デバイスコンテキストのハンドル
pbmi・・・・・作成するDIBSectionビットマップの様々な属性を設定
iUsage・・・・pbmiのbmiColorsメンバ指し示すデータの種類
ppvBits ・・・DIBSectionビットマップが作成された時、各ピクセルの色情報を操作できる
              メモリブロックが確保されて、そのポインタがここに返却される
hSection・・・ファイルマッピングオブジェクトのハンドル
dwOffset・・・ファイルマッピングオブジェクト内で色情報が格納されている位置までのオフセット

成功すると、ビットマップのハンドルが返されて、*ppvBitsにDIBSectionビットマップの 色情報が格納できるメモリブロックが返却されます。

この関数で重要なのがpbmiです。ここに1ピクセルあたりの ビット数、各ピクセルにおけるR,G,Gの色要素の位置(マスク値)、透過情報の位置(α値のマスク値)などを 指定してCreateDIBSectionを実行します。BITMAPINFO構造体は

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} 
BITMAPINFO, *PBITMAPINFO;

です。上記に書いたビットマップの属性は、実際にはBITMAPINFOHEADER構造体で 設定します。ですが、MSDNでご覧になって分かるかと思いますが、BITMAPINFOHADERでα値を 設定するメンバが見当たりません。ここでウルトラCがありました。 MSDNでBITMAPINFOHEADERを見てみると、
Applications can use the BITMAPV5HEADER structure for added functionality
と、こんなことが書かれています。つまり、BITMAPINFOHEADERとBITMAPV5HEADERを差し替えることが できるみたいです。そのBITMAPV5HEADERはどうなっているかというと

typedef struct {
    DWORD        bV5Size;        //この構造体のサイズ
    LONG         bV5Width;       //作成するビットマップの幅(単位はピクセル)
    LONG         bV5Height;      //作成するビットマップの高さ(単位はピクセル)
    WORD         bV5Planes;      //1をセット
    WORD         bV5BitCount;    //1ピクセルのビット数
    DWORD        bV5Compression; //ビットマップが圧縮されているか
    DWORD        bV5SizeImage; 
    LONG         bV5XPelsPerMeter;
    LONG         bV5YPelsPerMeter;
    DWORD        bV5ClrUsed;
    DWORD        bV5ClrImportant;
    DWORD        bV5RedMask; //赤色のマスク値(bV5BitCountで指定したビットの内Rを示す位置)
    DWORD        bV5GreenMask;  //緑色のマスク値
    DWORD        bV5BlueMask;   //青色のマスク値
    DWORD        bV5AlphaMask;  //透明度のマスク値
    DWORD        bV5CSType; 
    CIEXYZTRIPLE bV5Endpoints;
    DWORD        bV5GammaRed;
    DWORD        bV5GammaGreen;
    DWORD        bV5GammaBlue;
    DWORD        bV5Intent;
    DWORD        bV5ProfileData;
    DWORD        bV5ProfileSize;
    DWORD        bV5Reserved;
} BITMAPV5HEADER, *PBITMAPV5HEADER;

で今回使うのが青、赤で示されたメンバです。これを使うと 透過情報をもったビットマップが作成できます。これだけでは使い方が よく分からないので例を挙げてみます。

HBITMAP            hBitmap   = NULL;
DWORD              *pdwPixel = NULL;
BITMAPV5HEADER     bi;

bi.bV5Size         = sizeof(BITMAPV5HEADER);
bi.bV5Width        = 20;
bi.bV5Height       = 10;
bi.bV5Planes       = 1;
bi.bV5BitCount     = 32;
bi.bV5Compression  = BI_BITFIELDS;
bi.bV5RedMask      = 0x00FF0000;
bi.bV5GreenMask    = 0x0000FF00;
bi.bV5BlueMask     = 0x000000FF;
bi.bV5AlphaMask    = 0xFF000000;

hBitmap = ::CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS 
                                            (void **)&pdwPixel, NULL, 0);

とすると、幅が20ピクセル、高さが10ピクセル、1ピクセルに32ビット(DWORD)で色を 表現するDIBSectionビットマップを作成してそのハンドルをhBitmapに返します。bV5Compressionで BI_BITFIELDSの指定があるので1ピクセル32ビットのうち17〜24ビットが赤色の要素、9〜16ビットが 緑色の要素、1〜8ビットが青色の要素、25〜32ビットがそのピクセルの透明度を表現しています。
そして、20ピクセル X 10ピクセル X 32ビット(4バイト) = 800(バイト)のビットマップの メモリブロックが確保され、そのポインタがpdwPixelに返却されます。

あとは各ピクセル(pdwPixel)に色情報と透明度情報を書き込んでいけば 透過情報をもったビットマップ(hBitmap)ができあがります。

上図で注意しなければいけないのは返却されたpdwPixelの 先頭はビットマップの左下端のピクセルの色要素を指し示しているということと、pdwPixelを 勝手にdelete(もしかしてfree)してはいけないということです。削除するときはhBitmapを DeleteObject()しなければなりません。

さて、これでスクリーンのウィンドウ部分の色(RGB)を埋めていかなければなりません。 すぐに思いつく方法は ウィンドウ部分のサーフェスのDDBビットマップを既に作成している のでCDC::GetPixelで取得した色情報をDIBSectionにセットすることですが、GetPixelが非常に 遅いということらしく、これを全ピクセルに行うというは現実的じゃないみたいです。

<DDBビットマップからDIBSectionにコピー - GetDIBits>

DDBビットマップを DIBSectionにコピーするAPIがありました。

int GetDIBits(HDC hdc,             // デバイスコンテキストのハンドル
                HBITMAP hbmp,       // ビットマップのハンドル
                UINT uStartScan,    // 取得対象の最初の走査行
                UINT cScanLines,    // 取得対象の走査行の数
                LPVOID lpvBits,     // ビットマップのビットからなる配列
                LPBITMAPINFO lpbi,  // ビットマップデータのバッファ
                UINT uUsage);       // RGBとパレットインデックスのどちらか

hbmpにDDBビットマップのハンドル、lpbiにコピーされる側のDIBSectionビットマップの属性、 コピー範囲をuStartScan、cScanLinesに指定してGetDIBitsを実行するとCreateDIBSectionで確保された DIBSectionのメモリブロックにDDBがコピーされます。上記の例を使うと

GetDIBits(hdc, bitmap, 0, 10, (LPVOID)pdwPixel, (LPBITMAPINFO)&bi, DIB_RGB_COLORS);

これ一発でpdwPixelのDWORD値の配列の各要素にはbitmap(DDB)の各RGB値がコピー されています。あとは透過情報(α値)ですが、DDBにはもともとないので、GetDIBitsを実行しても pdwPixelの各DWORD値(各ピクセル)の上位8ビットはCreateDIBSection実行時の初期値0のままです。

<透過情報 - α値>

α値が0ということはそのピクセルは透明で、それが全ピクセルなので何も表示されない ことになってしまいます。(実際はUpdateLayeredWindow の第七引数のBLENDFUNCTIONのSourceConstantAlphaメンバの値にも影響されるので何も描画されないとも 限りません)
そこで、とりあえず、全ピクセルの各α値を不透明である255にセットして、全体の透過をBLENDFUNCTION のSourceConstantAlphaに任すことにします。

例では、α値は上図のようにpdwPixelの 各DWORD値(ピクセル)の上位8ビットなので

for(DWORD i = 0; i < (20 * 10); i++)
    *(pdwPixel + i) |= 0xFF000000;

とすれば、全ピクセルが不透明になります。

DIBSectionの各ピクセルのα値は分かりましたので、もう1つのBLENDFUNCTIONについて 触れておきます。UpdateLayeredWindow の第七引数にそのポインタを指定します。定義が

typedef struct _BLENDFUNCTION {
    BYTE     BlendOp;            //混合操作を指定
    BYTE     BlendFlags;
    BYTE     SourceConstantAlpha;//透明度を指定
    BYTE     AlphaFormat;        //ソースがα値を持っているときはAC_SRC_ALPHAを指定
}BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;

となっています。今回の例のように UpdateLayeredWindowの第三引数のDCのビットマップの 各ピクセルにα値があるならAC_SRC_ALPHAを指定します。するとこのα値をつかって事前に透明度に応じて RGBの色を変更(blend)するようです。(各ピクセルにα値を持たせないやり方もあり、その場合は AC_SRC_ALPHAは設定しません。)
さらに、SourceConstantAlphaで設定された全体の透明度でRGBの色をブレンドして、画面を更新します。

ここまで読めばもうわかると思いますが、 UpdateLayeredWindowを使う場合、 その透明度は第四引数のデバイスコンテキストで選択されたビットマップの各ピクセルに 保存されているα値(BITMAPV5HEADERのbV5AlphaMask)と第七引数のBLENDFUNCTIONの SourceConstantAlphaの2つによって決定されます

この2つの値がどのような過程で透明度を決めているかはMSDNのBLENDFUNCTION構造体の remarksをご覧ください。(なんかややこしい計算なので書きません)

これでようやく、透過情報を持ったビットマップ、つまりレイヤードウィンドウを 定義するサーフェスができたのでデバイスコンテキストで選択します。MSDNには このデバイスコンテキストはCreateCompatibleDCで取得できるとあるのでスクリーン互換のDCをつくって DIBSectionを結び付けます。

CDC        dc;
CBitmap    dibsec;
CBitmap    *pOldBitmap = NULL; 

dc.CreateCompatibleDC(NULL);
dibsec.Attach(hBitmap);
pOldBitmap = dc.SelectObject(&dibsec);
・
・

このdcをUpdateLayeredWindowの第四引数に指定します。

<コーディング>

UpdateLayeredWindow であと書いていないのはpptDst、psize、pptSrcですが、これは問題ないでしょう。 レイヤードウィンドウを更新した後、それを描画する位置がpptDst、描画するサイズがpsize、 第四引数のpDCSrcのビットマップの位置がpptSrcです。
ここまでの例では更新した画面を描画する位置は変わらないのでpptDst、psizeはGetWindowRectで 取得した位置サイズを指定すればいいです。DIBSectionにはウィンドウ部分の描画しかしていないので (余分なスクリーンを描画しても意味ないですし)pptSrcは(0,0)の位置になります。

コーディングの前に、SetLayeredWindowAttributesで加筆した部分を元に戻しておきます。
(SetLayeredWindowAttributesを実行したあと UpdateLayeredWindowを実行すると必ず失敗します。)

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    // ・
    /*cs.dwExStyle |= WS_EX_LAYERED;  !!コメントアウト!! */
    // ・
    return TRUE;   
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // ・
    /*SetLayeredWindowAttributes(0, 255, LWA_ALPHA); !!コメントアウト!! */

    return 0;
}

void CMainFrame::OnClose()
{
    //if (! SetTimer(1, 200, NULL) )      !!コメントアウト!!
    //    CFrameWnd::OnClose();

    //AnimateWindow(1000, AW_HIDE | AW_BLEND); !AnimateWindowの実装はコメントアウト!
    //CFrameWnd::OnClose();
}

では、UpdateLayeredWindowを使ってAnimateWindowやSetLayeredWindowAttributesと 同様の機能を実装します。全体のロジックは以下のようです。

  1. WM_CLOSEメッセージが届いたら(OnClose)、現在のウィンドウの描画面のビットマップから 透過情報を持ったDIBSectionを作成する。
  2. ウィンドウをレイヤードにする(拡張ウィンドウスタイルにWS_EX_LAYEREDを加える)
  3. スクリーン互換のデバイスコンテキストでDIBSectionを選択して UpdateLayeredWindowを実行
  4. SetTimerでタイマ起動し、CMainFrame::OnTimerでさらに UpdateLayeredWindowを実行してフェードアウトさせる。

ちょっと長くなるので、1.を実行する関数、2、3を 実行する関数を作成します。
MainFrm.hに以下を加えてください。

// MainFrm.h
class CMainFrame : public CFrameWnd
{
//・
//・
protected:
    BYTE m_alpha; //Layered Windowのα値(透明度〜)

protected:
//レイヤードウィンドウを定義する現在のサーフェスのビットマップ(α値を持ったDIBSection)取得
    BOOL GetAlphaBlendBitmap(CBitmap *pBitmap);
//レイヤードウィンドウ実行
    BOOL ExecLayered(BYTE alpha);

public:
    afx_msg void OnClose();                  //Window破壊シグナル(ここでレイヤード実行)
    afx_msg void OnTimer(UINT_PTR nIDEvent); //フェードアウト用
    afx_msg void OnCommandmenuCommand1();
};

MainFrm.cppにGetAlphaBlendBitmap、ExecLayeredの実装と、OnClose、OnTimerの メッセージハンドラの実装を以下のようにしてください。

// MainFrm.cpp

//Window破壊シグナル
void CMainFrame::OnClose()
{
    if ( !ExecLayered(m_alpha) || !SetTimer(2, 200, NULL) )
        CFrameWnd::OnClose();

    //if (! SetTimer(1, 200, NULL) )           !!コメントアウト!!
    //  CFrameWnd::OnClose();

    //AnimateWindow(1000, AW_HIDE | AW_BLEND); !!AnimateWindowの実装はコメントアウト!!
    //CFrameWnd::OnClose();
}

//タイマ
void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent == 1)
    {
        if (m_alpha < 25)
        {
            KillTimer(1);
            DestroyWindow();
        } else
        {
            SetLayeredWindowAttributes(0, m_alpha, LWA_ALPHA);
            m_alpha -= 25;
        }
    }

    if (nIDEvent == 2)
    {
        if (m_alpha < 25)
        {
            KillTimer(2);
            DestroyWindow();
        } else
        {
            BLENDFUNCTION bf;

            bf.BlendOp             = AC_SRC_OVER;
            bf.BlendFlags          = 0;
            bf.AlphaFormat         = AC_SRC_ALPHA;
            bf.SourceConstantAlpha = m_alpha;

            UpdateLayeredWindow(NULL, NULL, NULL, NULL, NULL, NULL, &bf, ULW_ALPHA);
            m_alpha -= 25;
        }
    }
    //CFrameWnd::OnTimer(nIDEvent);
}

//レイヤードウィンドウ実行
//引数alpha 全体の透明度(0〜255) 0が透明
BOOL CMainFrame::ExecLayered(BYTE alpha) 
{
    CRect rect; 
    GetWindowRect(&rect);

    BOOL          bRet   = FALSE; 
    CDC           *pScDC = CDC::FromHandle( ::GetDC(NULL) );    //スクリーンのDC
    POINT         ptDst  = {rect.left, rect.top};    //レイヤードの表示位置
    POINT         ptSrc  = {0, 0};         //デバイスコンテキスト(memDC)のレイヤの位置
    SIZE          szDst  = {rect.Width(), rect.Height()};    //レイヤードの表示サイズ
    CDC           memDC;    //レイヤードを定義したビットマップ(dibsecBitmap)と関連付けるDC
    CBitmap       *pOldBitmap = NULL; 
    CBitmap       dibsecBitmap;   //レイヤードを定義したサーフェスのビットマップ
    BLENDFUNCTION bf; 

    m_alpha = alpha; 

    bf.BlendOp             = AC_SRC_OVER; 
    bf.BlendFlags          = 0; 
    bf.AlphaFormat         = AC_SRC_ALPHA;
    bf.SourceConstantAlpha = m_alpha; 

    do
    {
        //レイヤードを定義した現在のWindowのサーフェスのビットマップ取得
        bRet = GetAlphaBlendBitmap(&dibsecBitmap); 
        if (! bRet)             break;

        //ウィンドウスタイルをレイヤードウィンドウに対応
        ModifyStyleEx(0, WS_EX_LAYERED); 

        //スクリーンのメモリデバイスコンテキスト作成
        memDC.CreateCompatibleDC(pScDC);

        //スクリーンのメモリデバイスコンテキストにビットマップを関連付ける
        pOldBitmap = memDC.SelectObject(&dibsecBitmap); 

        //レイヤードウィンドウ実行
        bRet = UpdateLayeredWindow(pScDC, &ptDst, &szDst, &memDC, &ptSrc,
                                                          0, &bf, ULW_ALPHA); 
        //ビットマップを切り離す
        memDC.SelectObject(pOldBitmap);

    } while(0); 

    if (dibsecBitmap.GetSafeHandle()) 
        dibsecBitmap.DeleteObject();

    ReleaseDC(pScDC);

    return bRet; 
}

//レイヤードウィンドウを定義する現在のサーフェスのビットマップ(α値を持ったDIBSection)取得
//引数 pBitmap ビットマップのGDIオブジェクトが結び付けられていないCBitmapへのポインタ
//戻値 成功時はpBitpmapにレイヤードを定義したサーフェスのビットマップが関連付けられ、
//     TRUEが返却
//失敗時はFALSEを返却(pBitmapは何もされない)
BOOL CMainFrame::GetAlphaBlendBitmap(CBitmap *pBitmap)
{
    CDC            *pScDC      = CDC::FromHandle( ::GetDC(NULL) );
    CDC            memDC;            //スクリーン互換のメモリデバイスコンテキスト
    CBitmap        ddbBitmap;          //このWindowのサーフェスのDDBビットマップ
    CBitmap        *pOldBitmap = NULL;
    HBITMAP        hBitmap   = NULL; //pBitmapに関連付けるDIBSectionビットマップハンドル
    BITMAPV5HEADER binfo;              //DIBSectionビットマップ情報
    DWORD          *pdwPixel   = NULL; //hBitmapが示すビットマップのピクセル列へのポインタ
    CRect          rect;               //このWindowのサイズ
    BOOL           bRet        = FALSE;
    BOOL          bIsLayered;

    //pBitmapにGDIオブジェクトが結びつけられていないこと
    if ( ! pBitmap || pBitmap->GetSafeHandle() ) return FALSE;

    //現在のWindow位置・サイズ取得
    GetWindowRect(&rect);

    //レイヤードの設定確認
    bIsLayered = GetExStyle() & WS_EX_LAYERED;

    //Windowのサーフェスのビットマップを取得するためにWS_EX_LAYEREDを削除
    //(WS_EX_LAYEREDが設定されているとWindowのカラーピクセルが取得できないため)
    if (bIsLayered)     ModifyStyleEx(WS_EX_LAYERED, 0);

    do
    {
        //スクリーンのサーフェスから該当Window部分のビットマップ(DDB)をddbBitmapにコピー
        bRet = memDC.CreateCompatibleDC(pScDC);
        if (! bRet) break;

        bRet = ddbBitmap.CreateCompatibleBitmap(pScDC, rect.Width(), rect.Height());
        if (! bRet)     break;

        pOldBitmap = memDC.SelectObject(&ddbBitmap);
        bRet = memDC.BitBlt(0, 0, rect.Width(), rect.Height(), pScDC,
                                                    rect.left, rect.top, SRCCOPY);

        memDC.SelectObject(pOldBitmap);
        if (! bRet)     break;

        //レイヤード用DIBSectionビットマップ作成 - BITMAPV5HEADERで定義
        memset(&binfo, 0, sizeof binfo);

        binfo.bV5Size        = sizeof(BITMAPV5HEADER);
        binfo.bV5Width       = rect.Width();
        binfo.bV5Height      = rect.Height();
        binfo.bV5Planes      = 1;
        binfo.bV5BitCount    = 32;
        binfo.bV5Compression = BI_BITFIELDS;
        binfo.bV5RedMask     = 0x00FF0000;
        binfo.bV5GreenMask   = 0x0000FF00;
        binfo.bV5BlueMask    = 0x000000FF;
        binfo.bV5AlphaMask   = 0xFF000000;

        hBitmap = ::CreateDIBSection(pScDC->m_hDC, (BITMAPINFO *)&binfo, DIB_RGB_COLORS,
                                  (void **)&pdwPixel, NULL, 0);
        if (! hBitmap)      break;

        //ddbBitmapのビットマップを作成したDIBSectionのpdwPixelにピクセル列にコピー
        bRet = ::GetDIBits(pScDC->m_hDC, ddbBitmap, 0, rect.Height(),
                                (LPVOID)pdwPixel, (LPBITMAPINFO)&binfo, DIB_RGB_COLORS);
        if (! bRet)         break;

        //pdwPixelの各ピクセルのα値を0xFF(255)にセット
        for (int i=0; i<rect.Width() * rect.Height(); i++)
        {
            static DWORD dwPixelAlpha = 0xFF;

            *(pdwPixel + i) |= (dwPixelAlpha << 24);  //(A)
        }

        //引数のCBitmapにレイヤードを定義したDIBSectionビットマップを関連付ける
        bRet = pBitmap->Attach(hBitmap);

    } while(0);

    if (bIsLayered) //レイヤードの設定を元に戻す
        ModifyStyleEx(0, WS_EX_LAYERED);

    if (hBitmap && !bRet) //失敗時はDIBSectionを削除
        ::DeleteObject(hBitmap);

    if (ddbBitmap.GetSafeHandle())
        ddbBitmap.DeleteObject();

    ReleaseDC(pScDC);

    return bRet;
}

これで出来上がったのでビルドしてウィンドウの表示 & Closeをして フェードアウトを確認してください。

今回の例でしたら同じことをするのに UpdateLayeredWindowを使う意味はほとんど ないように思われます。AnimateWindowやSetLayeredAttrbutesに比べて極端に難解になります。しかし、 UpdateLayeredWindowは細かい設定は可能で、前者のAPIでは不可能なことが可能になります。 その一例を挙げてみます。

<おまけ>

AnimateWindowはウィンドウの表示・非表示時のアニメーション機能、 SetLayeredWindowAttributesはそのウィンドウ全体のレイヤード機能しか制御できません。 しかし、UpdateLayeredWindowではDIBSectionも使ってピクセルごとの透過情報を制御できます。 前項の例ではレイヤードウィンドウを定義するサーフェス(DIBSection)のすべてのピクセルのα値を 不透明(255)に設定しました。これをピクセルごとに変えるだけでもビジュアル的に変化して、それは UpdateLayeredWindowでしか実現できません。 それをご確認ください。

前項のCMainFram::GetAlphaBlendBitmapの実装部の(A)で 全ピクセルのα値を255(不透明)に設定していますが、以下のようにして、 各ピクセルのα値を変更してみてください。

//pdwPixelの各ピクセルのα値を0xFF(255)にセット
for (int i=0; i<rect.Width() * rect.Height(); i++)
{
    static DWORD dwPixelAlpha = 0xFF;

    *(pdwPixel + i) |= (dwPixelAlpha << 24);  //(A) 

    if ( ! --dwPixelAlpha)      //この2行を追加してください
        dwPixelAlpha = 0xFF;
}

そして、ここで 作成したメニューコマンドを実装します。

void CMainFrame::OnCommandmenuCommand1()
{
    ExecLayered(230);
    return ;
}

上記を記述してビルド後、メニューからComman1を実行してください。

レイヤードウィンドウを定義するサーフェスの各ピクセルのα値をちょっと かえるだけでこんな感じのグラデーションができあがります。視覚的こだわりたい方は UpdateLayeredWindowでいろいろ試してみてください。

参考文献 :

Bitmaps                      http://msdn.microsoft.com/ja-jp/library/dd183377.aspx
マルチメディアプログラミング http://www13.plala.or.jp/kymats/study/multimedia.html