< September 2005 | October 2005 | December 2005 >

October 21, 2005

非表示のウインドウの最大化を解除する

ウインドウを最大化した後、非表示にする。

SendMessage (hWnd, WM_SYSCOMMAND, SC_MAXIMIZE);
ShowWindow (hWnd, SW_HIDE);

次に表示すると、ウインドウは最大化状態のままだ。

ShowWindow (hWnd, SW_SHOW);

ここで問題です。ウインドウを非表示のままでリストア(元のサイズに戻す、つまり最大化状態を解除)するにはどうしたらいいでしょう。

// 元のサイズに戻るが、同時にウインドウが表示されてしまう
ShowWindow (hWnd, SW_RESTORE);

// これも
SendMessage (hWnd, WM_SYSCOMMAND, SC_RESTORE);

// これも
WINDOWPLACEMENT Placement;
ZeroMemory (&Placement, sizeof (WINDOWPLACEMENT));
Placement.length = sizeof (WINDOWPLACEMENT);
GetWindowPlacement (hWnd, &Placement);
Placement.showCmd = SW_RESTORE;
SetWindowPlacement (hWnd, &Placement);

意外と難しい。最終的に、私が思いついたのはこんな方法。

DWORD dwStyle = GetWindowLong (hWnd, GWL_STYLE);
dwStyle &= ~WS_MAXIMIZE;
SetWindowLong (hWnd, GWL_STYLE, dwStyle);

この場合、最大化時のウインドウ位置とサイズがそのままでリストアされてしまう。非表示のままリストアを厳密にシミュレートするには、あらかじめ GetWindowPlacement で、リストア時のウインドウ矩形を取得しておき、リストア後に MoveWindow してあげないといけない。

October 7, 2005

ダイアログのデフォルトボタンを変更する

ダイアログのデフォルトボタンを動的に変更したい、あるいは、一時的にデフォルトでなくしたい、などを行うには、ダイアログに対して DM_SETDEFID メッセージを送出し、デフォルトにするボタンの ID を渡す。MFC だと、CDialog::SetDefID がそれにあたる。しかし、それだけではボタンの周囲の黒枠が更新されないんである。Enter キーを押下すると、確かにデフォルトボタンが変更されていることが分かるが、表示上は更新されていない。SendMessage で送出したあと Invalidate したり、UpdateWindow したりしてもだめだったので、メッセージ受信と非同期に「デフォルト」という属性を更新しているのかもしれない。

あれこれやってみたところ、以下のようにフォーカスを強制的にセットしてやる以外のうまい方法は見つけられなかった。

// フォーカスのあるコントロールを取得しておく
CWnd *pWndFocus = GetFocus ();

// ボタンをデフォルトに
SetDefID (IDC_BUTTON1);

// 一旦フォーカスをセットする
GotoDlgCtrl (&m_Button1);

// 以前にフォーカスのあったコントロールに戻る
if (pWndFocus)
{
    GotoDlgCtrl (pWndFocus);
}

もともと IDC_BUTTON1 にフォーカスがあったとしても、この方法でうまくいくようだ。ちなみに、単にデフォルト属性をはずす場合は、WPARAM を 0 にして DF_SETDEFID するのでいけるようだ。(SetDefID (0); ってこと)

October 5, 2005

PathAddExtension の挙動

PathAddExtension という関数がある。パスの最後に拡張子を追加するというもの。

BOOL PathAddExtension(
    LPTSTR pszPath,
    LPCTSTR pszExtension
);
pszPath
[in, out] 拡張子を追加する対象となる null 終端文字列を格納したバッファのポインタ。
pszExtension
[in] 拡張子を格納した文字列のポインタ。拡張子はピリオドから始まっている必要がある。

戻り値は、拡張子を追加した場合は TRUE、以外は FALSE が返る。

注意しなきゃなんないのは

Remarks
If there is already a file extension present, no extension will be added. If the pszPath points to a NULL string, the result will be the file extension only. If pszExtension points to a NULL string, an ".exe" extension will be added.

すでに拡張子が付いてる場合は、この関数はさらに拡張子を付けることをしない。つまり、ファイル名にピリオドを含みたい場合には不適だ("MyAppVer1.1.exe"とかさ)。ユーザに入力されたファイル名に拡張子を付けるような場合に使うのはやめた方がいい。