Mesoscopic Programming

タコさんプログラミング専門

ウィンドウズプログラミング講座第6回:プロファイルとかの対応

概要

今回はプロファイルとかに対応する処理を実装します。
プロファイルというのは前に実行したときのウィンドウの位置とかサイズとかを覚えているやつのことです。


ミューテックス

先にミューテックスの実装からやります。
ミューテックスというのは同じアプリを2個同時に起動しないようにするやつです。

初期化
class App : public Window
{
    static const LPCTSTR mutexName;
    HANDLE               hMutex;
};

const LPCTSTR App :: mutexName = "WindowsProgramming.mut";

BOOL App :: Init( int nCmdShow )
{
    hMutex = CreateMutex( 0, TRUE, mutexName );

    if ( GetLastError() == ERROR_ALREADY_EXISTS )
    {
        CloseHandle( hMutex );

        return FALSE;
    }
}
解説

ミューテックスハンドルを取得すると同じアプリが起動中かどうか分かります。
エラー判定で終了してますがハンドルは正常に取得できてるみたいです。

終了処理
int App :: Run()
{
    ~
    CloseHandle( hMutex );

    return ( int ) msg.wParam;
}
解説

取得したミューテックスハンドルを解放します。
これをしないと二度と起動できなくなる?かどうかは調べていません。


プロファイル

ウィンドウの位置とかの状態を保存するためにプロファイルを使います。

初期化
class App : public Window
{
    static const LPCTSTR iniFileName;
    static const LPCTSTR sectionMain;
    static const LPCTSTR keyLeft;
    static const LPCTSTR keyTop;
    static const LPCTSTR keyWidth;
    static const LPCTSTR keyHeight;
    static const LPCTSTR keyToolBar;
    static const LPCTSTR keyStatBar;

    VOID GetProfile();
    VOID SetProfile();
    INT  GetProfileInt( LPCTSTR section, LPCTSTR key, INT value );
    VOID SetProfileInt( LPCTSTR section, LPCTSTR key, INT value );
};

const LPCTSTR App :: iniFileName = "WindowsProgramming.ini";
const LPCTSTR App :: sectionMain = "Main Window";
const LPCTSTR App :: keyLeft     = "Left";
const LPCTSTR App :: keyTop      = "Top";
const LPCTSTR App :: keyWidth    = "Width";
const LPCTSTR App :: keyHeight   = "Height";
const LPCTSTR App :: keyToolBar  = "ToolBar";
const LPCTSTR App :: keyStatBar  = "StatusBar";

BOOL App :: Init( int nCmdShow )
{
    GetProfile();
}

VOID App :: GetProfile()
{
    left        = GetProfileInt( sectionMain, keyLeft,    CW_USEDEFAULT );
    top         = GetProfileInt( sectionMain, keyTop,     CW_USEDEFAULT );
    width       = GetProfileInt( sectionMain, keyWidth,   CW_USEDEFAULT );
    height      = GetProfileInt( sectionMain, keyHeight,  CW_USEDEFAULT );
    bToolBar    = GetProfileInt( sectionMain, keyToolBar, TRUE );
    bStatBar    = GetProfileInt( sectionMain, keyStatBar, TRUE );

    if ( width == 0 || height == 0 )
    {
        left    = CW_USEDEFAULT;
        top     = CW_USEDEFAULT;
        width   = CW_USEDEFAULT;
        height  = CW_USEDEFAULT;
    }
}

INT App :: GetProfileInt( LPCTSTR section, LPCTSTR key, INT value )
{
    return GetPrivateProfileInt( section, key, value, iniFileName );
}
解説

最初に起動したときはまだプロファイルが保存されていないのでデフォルトの値を使用します。
またバグなんかでウィンドウサイズがゼロになったときのことを考えて値をチェックしたりなんかもしています。

終了処理
LRESULT App :: OnWmDestroy()
{
    SetProfile();

    PostQuitMessage( 0 );

    return 0;
}

VOID App :: SetProfile()
{
    RECT rc;

    GetWindowRect( hwnd, & rc );

    SetProfileInt( sectionMain, keyLeft,    rc.left );
    SetProfileInt( sectionMain, keyTop,     rc.top );
    SetProfileInt( sectionMain, keyWidth,   rc.right  - rc.left );
    SetProfileInt( sectionMain, keyHeight,  rc.bottom - rc.top );
    SetProfileInt( sectionMain, keyToolBar, bToolBar );
    SetProfileInt( sectionMain, keyStatBar, bStatBar );
}

VOID App :: SetProfileInt( LPCTSTR section, LPCTSTR key, INT value )
{
    TCHAR text[ 80 ];

    sprintf( text, "%d", value );

    WritePrivateProfileString( section, key, text, iniFileName );
}
解説

アプリが終了する直前に現在の値を保存します。
PostQuitMessage() をやったあとではウィンドウサイズがゼロになってるもんで、もう遅いんです。


表示メニュー

プロファイルに対応したもんで、ついでにツールバーとかも表示/非表示を切り替えられるようにしました。

初期化
class App : public Window
{
    enum CommandID
    {
        idViewToolBar,
        idViewStatBar,
    };

    enum MenuID
    {
        menuView,
    };

    enum ViewMenuID
    {
        menuViewToolBar,
        menuViewStatBar,
    };

    HWND        hToolBar;
    HWND        hStatBar;
    DocView     docView;
    BOOL        bToolBar;
    BOOL        bStatBar;

    VOID        InitMenu();
    VOID        InitAccel();
    VOID        UpdateSize();
    LRESULT     OnWmSize();
    LRESULT     OnWmCommand();
    LRESULT     OnViewToolBar();
    LRESULT     OnViewStatBar();
};

BOOL App :: Init( int nCmdShow )
{
    InitMenu();
    InitAccel();
}

VOID App :: InitMenu()
{
    hMenu     = CreateMenu();
    hViewMenu = CreateMenu();

    AppendMenu( hViewMenu, MF_STRING,    idViewToolBar,          _T( "ツールバー(&T)\tAlt+T" ) );
    AppendMenu( hViewMenu, MF_SEPARATOR, 0,                      NULL );
    AppendMenu( hViewMenu, MF_STRING,    idViewStatBar,          _T( "ステータスバー(&S)\tAlt+S" ) );
    AppendMenu( hMenu,     MF_POPUP,     ( UINT_PTR ) hViewMenu, _T( "表示(&V)" ) );
}

VOID App :: InitAccel()
{
    ACCEL accels[] =
    {
        { FVIRTKEY | FALT, 'T', idViewToolBar },
        { FVIRTKEY | FALT, 'S', idViewStatBar },
    };

    hAccel = CreateAcceleratorTable( accels, sizeof accels / sizeof ( ACCEL ) );
}
解説

メニューの追加とアクセラレータの追加のみです。

処理
LRESULT App :: OnWmSize()
{
    UpdateSize();
}

LRESULT App :: OnWmCommand()
{
    switch ( id )
    {
    case idViewToolBar : return OnViewToolBar();
    case idViewStatBar : return OnViewStatBar();
    }
}

VOID App :: UpdateSize()
{
    RECT rc;
    RECT rc2;

    GetClientRect( hwnd, & rc );

    if ( bToolBar )
    {
        CheckMenuItem( hViewMenu, idViewToolBar, MF_BYCOMMAND | MF_CHECKED );
        ShowWindow( hToolBar, SW_SHOW );
        SendMessage( hToolBar, WM_SIZE, wparam, lparam );
        GetWindowRect( hToolBar, & rc2 );

        rc.top += ( rc2.bottom - rc2.top );
    }
    else
    {
        CheckMenuItem( hViewMenu, idViewToolBar, MF_BYCOMMAND | MF_UNCHECKED );
        ShowWindow( hToolBar, SW_HIDE );
    }

    if ( bStatBar )
    {
        CheckMenuItem( hViewMenu, idViewStatBar, MF_BYCOMMAND | MF_CHECKED );
        ShowWindow( hStatBar, SW_SHOW );
        SendMessage( hStatBar, WM_SIZE, wparam, lparam );
        GetWindowRect( hStatBar, & rc2 );

        rc.bottom -= ( rc2.bottom - rc2.top );
    }
    else
    {
        CheckMenuItem( hViewMenu, idViewStatBar, MF_BYCOMMAND | MF_UNCHECKED );
        ShowWindow( hStatBar, SW_HIDE );
    }

    MoveWindow( docView.hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE );
}

LRESULT App :: OnViewToolBar()
{
    bToolBar = ! bToolBar;

    UpdateSize();
}

LRESULT App :: OnViewStatBar()
{
    bStatBar = ! bStatBar;

    UpdateSize();
}
解説

ツールバーとステータスバーがあったり無かったりするようになったので、ドキュメントウィンドウをそれに応じてサイズ変更しています。


実行画面

ソースファイル
  1. Window.h
  2. Window.cpp
  3. main.h
  4. main.cpp
実行ファイル
  1. Windows.zip

以上です。