GetWindowPlacement()に変な値が入る

[戻る]
亀山 2009/04/20(月) 16:32:19
VC2005 MFC です。
フレームウィンドウのOnCreate()で
作成直後の座標情報をフレームウィンドウ自身が憶えておくために
GetWindowPlacement()を使っているのですが、
いまいち理解できない挙動になってしまっています。

以下のソースは、問題が発生する原因と思われる部分のみを
初期コードに追加してみたもので、起動後の動作などは考慮していません。

1:SDI(ドキュメント/ビューは不使用)のプロジェクトを作成

2:初期コードに入っているOnAppAbout()を以下のように変更
void CTestApp::OnAppAbout()
{
    CMainFrame* pFrame = new CMainFrame;
    pFrame->LoadFrame(IDR_MAINFRAME,
        WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
        NULL);
    pFrame->ShowWindow(SW_SHOW);
}

3:CMainFrame::PreCreateWindow()に以下の1行を追加
    cs.style |= WS_MAXIMIZE;

これによって、「?」ボタンを押すたびに、最大化されたウィンドウが出てきます
(起動直後のウィンドウが最大化されないのは把握しています)。

ところがこの状態から、

4:CMainFrame::OnCreate()の最後に以下の2行を追加
    WINDOWPLACEMENT wp;
    GetWindowPlacement(&wp);

を入れると、変な位置に最大化された状態で表示されます。

WINDOWPLACEMENT::ptMaxPositionに変な値(x=22,y=29)が入っているようなのですが、
なぜこの段階でGetWindowPlacement()しただけで座標がおかしくなるのか、
原因がわからずに困っています。

なにか情報をお持ちのかたはいらっしゃいませんでしょうか。

Kelly 2009/04/20(月) 16:53:03
WINDOWPLACEMENT wp;
wp.length = sizeof(wp); // ←これが必要かと思います。
GetWindowPlacement(&wp);

亀山 2009/04/20(月) 17:04:13
> wp.length = sizeof(wp); // ←これが必要かと思います。

ご意見ありがとうございます。
この処理を入れてみましたが、内容は変わりませんでした。

CWnd::GetWindowPlacement()の中で
    lpwndpl->length = sizeof(WINDOWPLACEMENT);
という処理が入っているようです。

Kelly 2009/04/20(月) 17:29:19
そんなバカな、と思いつつやってみたら再現しました。
なかなか不思議な現象ですね。


> CWnd::GetWindowPlacement()の中で
>    lpwndpl->length = sizeof(WINDOWPLACEMENT);
> という処理が入っているようです。

それは知りませんでした。失礼しましたm(__)m

Kelly 2009/04/20(月) 17:32:58
原因はわかりませんが、とりあえず問題の回避を行うのであれば、

> 3:CMainFrame::PreCreateWindow()に以下の1行を追加
>     cs.style |= WS_MAXIMIZE;

↑この処理を削除して、

>    pFrame->ShowWindow(SW_SHOW);

↑この部分を

pFrame->ShowWindow(SW_SHOWMAXIMIZED);

とすればうまくいきました。

亀山 2009/04/20(月) 18:00:48
>> 3:CMainFrame::PreCreateWindow()に以下の1行を追加
>>     cs.style |= WS_MAXIMIZE;
>
>↑この処理を削除して、
>
>>    pFrame->ShowWindow(SW_SHOW);
>
>↑この部分を
>
>pFrame->ShowWindow(SW_SHOWMAXIMIZED);
>
>とすればうまくいきました。

試していただきありがとうございます。
たしかに、提案していただいた方法だと正しい位置に最大化されるようです。
WINDOWPLACEMENT::ptMaxPositionには(x=-1,y=-1)が入っていました。

ただ、この手順だとOnCreate()の段階では最大化されていないので、
GetWindowPlacement()を呼んだときに最大化状態が入らず、
「自分が最大化状態で作成された」ことがわからなくなってしまいます。

またこの方法だと、最大化を指示するのはウィンドウを作成する側に
なってしまうのですが(この例だとCTestApp)、MFCのフレームウィンドウは、
自分自身を最大化した状態で作成することはできないのでしょうか?

! 2009/04/21(火) 11:20:06
CMainFrame::PreCreateWindow()で
AfxGetApp()->m_nCmdShow = SW_SHOWMAXIMIZED;

! 2009/04/21(火) 11:29:37
CMainFrame::LoadFrameをオーバーライド
第二引数で指定されたスタイル値にWS_MAXIMIZEを無条件に追加すれば
自己中で(ウィンドウ作成側の指定を無視して)最大化ができるかな?

亀山 2009/04/21(火) 20:43:47
> CMainFrame::LoadFrameをオーバーライド
> 第二引数で指定されたスタイル値にWS_MAXIMIZEを無条件に追加すれば
> 自己中で(ウィンドウ作成側の指定を無視して)最大化ができるかな?

ご意見ありがとうございます
オーバーライドを想定して、とりあえずCMainFrame::PreCreateWindow()の
    cs.style |= WS_MAXIMIZE;
を削除し、CTestApp::OnAppAbout()内でLoadFrame()しているところに
WS_MAXIMIZEも加えてみたのですが、やはり変な位置に最大化されてしまいました。

この場合も、CMainFrame::OnCreate()内の
GetWindowPlacement()を削除すると、正しい位置に最大化されます。
結果としては、CMainFrame::PreCreateWindow()で
WS_MAXIMIZEを追加するのと同じ挙動になるようです。

CWnd::GetWindowPlacement()の説明をMSDNで調べてみたところ、
「この関数を使って取得した WINDOWPLACEMENT 構造体の flags メンバは、
常に 0 になります」と書かれているのですが、
最初のコードでGetWindowPlacement()したときのWINDOWPLACEMENT::flagsには
「2」という値が入っているようです。

Kellyさんに提案していただいた方法で、
PreCreateWindow()でWS_MAXIMIZEをセットしないようにすると、
ちゃんと「0」が入っています。

この値が入っていること自体がかなり怪しい挙動なのですが、
最大化状態で作成するとGetWindowPlacement()が変な動きになるのには
なにか原因と思われるものはありませんでしょうか?
OnCreate()の中でやってはいけないというような表記も見あたらないのですが。

PATIO 2009/04/22(水) 17:19:01
OnCreateというハンドラ自体はCreate関数から返る前に呼び出されるものですから
OnCreateが呼び出された時に果たして期待した値を取得できる状態になって
いるかは疑問だと思います。

以前にOnSizeをオーバーライドして処理を追加しようとした時に
OnSizeが最初に呼ばれる時のウインドウのサイズは予測していた物とは
違ったような気がしています。
OnCreateは生成されたウインドウがまだ不可視の状態のうちに呼び出されると
ありますから、OnCreateの後にまだ処理があったとしても不思議では無いと
考えています。その辺からするとOnCreateの中でウインドウの情報を収集しても
あてになる数値が得られない可能性はあると思います。

PATIO 2009/04/22(水) 17:20:39
要は、OnCreateの呼出し後にウインドウの位置調整やサイズ調整を
行っている可能性もあるのではないかと言う話です。

亀山 2009/04/22(水) 18:14:11
[[解決]]
CMainFrame::OnShowWindow()を用意し、
その中で「作成直後の1回目の表示なら」という条件のもとで
GetWindowPlacement()を呼んでみたところ、
WS_MAXIMIZEを付けた状態で作成しても変な位置に表示されること無く、
自分自身の状態を取得することができました。

最大化状態だと相変わらずflagsには「2」が入っているようですが、
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200211/02110063.txt
↑のかたも同じ値のようなので、ヘルプのほうの記載ミスなのかもしれません。

やはり、PATIOさんの言われるように、
OnCreate()内でGetWindowPlacement()を呼ぶことがルール違反のようですね。

みなさんありがとうございます。

なお、OnCreate()の後に呼ばれる場所としてOnShowWindow()を選びましたが、
ここよりもっと的確な場所があれば指摘していただけると嬉しいです。

古いスレッドには追加発言できません。