Tips of VC++ > ウィンドウ > 前回のウィンドウ位置を復元する方法
前へ戻る次へ進む


前回のウィンドウ位置を復元する方法

レジストリを使ってウィンドウの表示位置やサイズをプログラムの終了時に保存して、 次の起動時に読みこんで復元するやり方を説明します。
レジストリは簡単にアクセスするために こちらで作ったクラスを使うことにします。

/*
 *  -------------
 *    place.cpp
 *  -------------
 */


#include "stdafx.h"
#include "registry.h"


const char szClassName[]="Place";

HMODULE hAppModule=NULL;

CRegistry appreg(szClassName);

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

void LoadWindowPlacement(LPWINDOWPLACEMENT);
void SaveWindowPlacement(LPWINDOWPLACEMENT);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
    hAppModule=(HMODULE)hInstance;

    WNDCLASSEX wc;
    wc.cbClsExtra    = 0;
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.cbWndExtra    = 0;
    wc.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
    wc.hCursor       = ::LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW));
    wc.hIcon         = NULL;
    wc.hIconSm       = NULL;
    wc.hInstance     = hInstance;
    wc.lpfnWndProc   = (WNDPROC)WndProc;
    wc.lpszClassName = szClassName;
    wc.lpszMenuName  = NULL;
    wc.style         = CS_HREDRAW|CS_VREDRAW;
    ::RegisterClassEx(&wc);

    HWND hWnd=::CreateWindow(szClassName,szClassName,
        WS_OVERLAPPEDWINDOW,100,100,200,50,
        NULL,NULL,hInstance,0);
    if ( hWnd==NULL )
        return 0;

    WINDOWPLACEMENT wp;
    ::memset(&wp,0,sizeof(WINDOWPLACEMENT));
    wp.length  = sizeof(WINDOWPLACEMENT);
    wp.showCmd = nCmdShow;
    ::SetRect(&wp.rcNormalPosition,
        50,50,200,200);

    LoadWindowPlacement(&wp);
    ::SetWindowPlacement(hWnd,&wp);

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

    return 0;
}

LRESULT CALLBACK WndProc(HWND in_hWnd,
                         UINT in_Message,
                         WPARAM in_wParam,
                         LPARAM in_lParam)
{
    WINDOWPLACEMENT wp;

    switch ( in_Message )
    {
    case WM_CLOSE:
        ::memset(&wp,0,sizeof(WINDOWPLACEMENT));
        wp.length=sizeof(WINDOWPLACEMENT);

        ::GetWindowPlacement(in_hWnd,&wp);
        if ( ::IsZoomed(in_hWnd) )
            wp.flags|=WPF_RESTORETOMAXIMIZED;
        SaveWindowPlacement(&wp);

        ::DestroyWindow(in_hWnd);
        break;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        break;
    default:
        return ::DefWindowProc(in_hWnd,
            in_Message,in_wParam,in_lParam);
    }
    return 0;
}

void LoadWindowPlacement(LPWINDOWPLACEMENT in_WndPlace)
{
    appreg.LoadBinaryValue("WindowPlacement",
        (LPBYTE)in_WndPlace,sizeof(WINDOWPLACEMENT));
}

void SaveWindowPlacement(LPWINDOWPLACEMENT in_WndPlace)
{
    appreg.SetBinaryValue("WindowPlacement",
        (LPBYTE)in_WndPlace,sizeof(WINDOWPLACEMENT));
}

あまり長くないので全ソースを載せてみました。
まず、ポイントとなるAPIと構造体があります。

typedef struct _WINDOWPLACEMENT { 
    UINT  length; 
    UINT  flags; 
    UINT  showCmd; 
    POINT ptMinPosition; 
    POINT ptMaxPosition; 
    RECT  rcNormalPosition; 
} WINDOWPLACEMENT; 

BOOL GetWindowPlacement(
  HWND hWnd,                // ウィンドウのハンドル
  WINDOWPLACEMENT *lpwndpl  // 位置データ
);

BOOL SetWindowPlacement(
  HWND hWnd,                      // ウィンドウのハンドル
  CONST WINDOWPLACEMENT *lpwndpl  // 位置データ
);

WINDOWPLACEMENT構造体の中身を詳しく知る必要はありません。 ここには表示位置やサイズなどのレジストリに書き込むデータが詰まっているので これをまとめて書きこめば楽です。
GetWindowPlacementでWINDOWPLACEMENT構造体を取得できます。 対するSetWindowPlacementはShowWindowみたいなものです。 ウィンドウを指定するWINDOWPLACEMENT構造体のデータによって 表示したり移動したりします。

CreateWindowの直後でWINDOWPLACEMENTを取得して、 ShowWindowを呼び出すところでSetWindowPlacementを使って ウィンドウを表示しています。 このときレジストリに値がない場合もあるので、 そのときのためにあらかじめ構造体には初期値を入れておきます。 WINDOWPLACEMENT::showCmdにはWinMain関数の第四引数を入れておいてください。
次にWM_CLOSEでレジストリに現在のウィンドウの情報を保存します。 GetWindowPlacementで構造体を取得し、レジストリに書きこみます。 このときにウィンドウが最大化されているようであれば、 WINDOWPLACEMENT::flagsにWPF_RESTORETOMAXIMIZEDフラグを追加しておきます。