OrangeMaker_logo
memo アイコン SDK-MFC 備忘録

Windows SDKやMFCに関するメモです。
(記載内容について正しいことを保証するものではありません。MSDN等で確認してください)

エアロフレーム拡張を考える

2009/10/11 作成

 通常ウィンドウの外周矩形はGetWindowRect()関数で取得しますが、エアログラス機能で描画されたウィンドウはこの矩形よりすこし大きいサイズで描画されます。このためXPやVISTA-Basicでウインドウ位置をデスクトップの端にピタリとくっ付けるプログラムやウィンドウ同士を並べるプログラムをVISTAのエアログラス機能が有効な状態で実行すると少しずれた位置に表示されます。

  このフレーム拡張とよばれているこの少し大きい目のウインドウの外周矩形を取得するにはVISTA以降で導入されたDWM(デスクトップウィンドウマネージャ)系の関数で取得できます。

1)フレーム拡張されたウィンドウ表示矩形を取得する

 エアログラス機能で描画されたウィンドウの表示矩形を取得するには、

  RECT r;
 HRESULT hr = DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, 
		&r, sizeof(RECT));
 if(SUCCEEDED(hr)){
 }
		

でRECT rに外周の矩形が取得できます。

2)動作環境を考える

 基本的には上記の矩形とGetWindowRect()で取得できる矩形との差をみればフレーム拡張の差分サイズがわかります。ただ、ウィンドウマネージャで扱う(プログラムで扱う)矩形は常にGetWidowRect()で取得する矩形となります。さらにプログラムをXPや、VISTA-Basic等でエアロ機能のON/OFF状態をチェックしながら処理は面倒なので、常に表示上の外周とウィンドウマネージャで管理する矩形を取得するサブルーチンを考えてみました。

 このサブルーチン(関数)はOSバージョンを確認してとりあえずDwmGetWindowAtrribute()を呼び出してみます。エアロ描画がOFFの場合はこの関数はエラーをかえしますのでその場合は改めてGetWindowRect()の矩形を外周とします。

//
//AEAROのフレーム拡張を考慮したWindow外周矩形の取得
//
VOID GetWindowShapeRect(HWND hWnd, LPRECT rect)
{
    //OS Versionの取得
    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx((OSVERSIONINFO*)&osvi);
    if(osvi.dwMajorVersion >= 6){//VISTA以降
        HRESULT hr = DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, 
				rect, sizeof(RECT));
        if(SUCCEEDED(hr)){
        }
        else{
            //エラー
            ::GetWindowRect(hWnd, rect);
        }
    }
    else{
        ::GetWindowRect(hWnd, rect);
    }
	
}
//
//AEAROを考慮したWindow外周矩形と
// orect:表示上の矩形
// inrect:ウィンドウ管理上の矩形
//
VOID CWindowUtl::GetWindowTowiceRect(HWND hWnd, LPRECT orect, LPRECT inrect)
{
    CWindowUtl::GetWindowRect(hWnd, orect);
    ::GetWindowRect(hWnd, inrect);
}
		

(注:DwmGetWindowAttribute(hwnd, DWMWA_NCRENDERING_ENABLED, ・・・)等で描画状態を確認してからDwmGetWidowAtrribute(hwnd,DWMWA_EXTENDED_FRAME_BOUNDS,・・)を呼び出す、またはエラー詳細を確認するなどは適宜アレンジしてください。)

3)SampleCode(デスクトップの端にくっつけてみる)

上記の関数でデスクトップの辺にウィンドウをくっつけるサンプルを考えてみます。

たとえばデスクトップの左辺にくっつける場合は、フレーム拡張がない場合はleft=0の設定でOKですが、フレーム拡張がある場合は拡張分だけ内側にウィンドウを指定してやる必要があります。

//
// hwndで指定したウィンドウをデスクトップの左端に移動させます。
//
VOID MoveDesktopLeft(HWND hwnd)
{
    CRect orect,inrect;
    GetWindowTowiceRect(hwnd, &orect, &inrect);
    //左辺の拡張を算出する
    int deltaLeft = inrect.left - orect.left;
    //フレーム拡張がない場合はdeltaLeftは0になります
    //Desktopの座標を取得する
    HMONITOR hMonitor = MonitorFromRect(&inrect, MONITOR_DEFAULTTONULL);
    if(hMonitor == NULL){//交差するディスクトップがなかった
        //write your error code
        return;
    }
    else{
        MONITORINFO mi;
        mi.cbSize = sizeof(mi);
        ::GetMonitorInfo(hMonitor, &mi);
        //ウィンドウをディスクトップの左辺に移動
        CRect nr;
        nr.left = mi.rcWork.left + deltaLeft;//フレーム拡張分を補正
        nr.right = nr.left + inrect.Width();
        nr.top = inrect.top;
        nr.bottom = nr.top + inrect.Height();
        ::MoveWindow(hWnd, nr.left, nr.top, nr.Width(), nr.Height(), TRUE);
    }
}
		

(注1:デスクトップの矩形は常にプライマリでいいいなら::SystemParametersInfo(SPI_GETWORKAREA,・・・)等で取得しても良いと思います。またもっと簡易にGetDesktopWidnow()でウィンドウハンドルを取得してGetWindowRect()で取得する方法もあります)
(注2:DWM系の関数を使用する場合のインクルードファイルは、dwmapi.hでインポートライブラリはdwmapi.libです。VISTA以前のOSで動作される可能性がある場合は、遅延リンク等を指定を忘れずに行ってください。)
(注3:ここで示したプログラムは実際に動かしていません。考え方の参考として下しさい。実際の使用しているコードを示すと論点以外のコードも多数含まれてしまいますので割愛しています。)