解決策が思いつかないので質問させていただきます。
よろしくお願いします。
テキストエディタを作成しているのですが、
VisualStudioのように、表示中のファイルが他のエディタで編集された場合に、
ウィンドウアクティブ時にダイアログで表示を更新するか
問い合わせる機能を実装しています。
ファイルが変更されたことを認知する機能は実装できたのですが、
CWnd::OnActivate(...)関数にてファイル更新確認を行い、
ダイアログを表示すると、基本的には問題ないのですが、
たまにウィンドウがおかしな挙動をするようになりました。
既知の挙動は以下の二つです。
■ドッキングツールバーをクリックしてウィンドウをアクティブにすると
ダイアログを閉じた後ツールバー移動枠がおかしな挙動をし、
その後もしばらくウィンドウをクリックしていると強制終了
■サイズ変更枠をクリックしてアクティブにすると、
ダイアログを閉じた後もウィンドウ変更動作が持続
この症状を回避する良い策はありませんでしょうか?
そもそもCWnd::OnActivate(...)でダイアログを表示するのは
認められていないのでしょうか?
そうすると、VisualStudioはどのようにウィンドウアクティブ時に
処理をしている(ように見せている)のでしょうか?
以下の環境で開発しています。
VC++.NET2005
MFC使用
よろしくお願いします。
解決策として思いついたのは、アクティブ時にタイマーセットしておいて、
一寸待ってから判定を行うという方法ですが、
仕様上、あまりきれいでないような気もします。
他に何か有効な手段をご存知の方がいらっしゃいましたら、
ご教授お願いします。
想像ですが、アクティブ状態の変化通知のさなかに
アクティブ状態を変更するのは、良くないのではないかと思います。
タイマーではなくて、独自メッセージをPostすればよいのでは?
たぶん、WM_ACTIVATE よりも先にツールバーなりサイズ変更枠に
WM_LBUTTONDOWN が来ていて、当該ウィンドウがマウスをキャプチャー
している状態でダイアログを出しているからおかしくなるんだと思います。
おそらく、タイマーだろうと独自メッセージのポストだろうと関係なく、
自スレッドがマウスキャプチャー中にダイアログを出したらだめだと思う。
解決策のひとつとしては、
タイマー処理で自スレッドがマウスキャプチャーしてないことを確認してから
メッセージボックスを出す。
もしキャプチャー中だったら、次回のタイマー処理まで保留。
というのが考えられます。
dairygoodsさん、aさん、ありがとうございます。
> タイマーではなくて、独自メッセージをPostすればよいのでは?
WM_COMMANDとコマンドIDをPostするようにしました。
この方法でとりあえずは解消することができました。
しかし、aさんの指摘を参考にすると、これでは不十分な気もしますね。
ただ、PostしたWM_COMMANDはWM_LBUTTONDOWNなど、
ウィンドウアクティブ処理において発生するウィンドウメッセージよりも
あとにウィンドウに渡されるように思うのですが、間違っていますでしょうか?
もしこの考えが合っているなら、aさんの指摘に対しては
特に気にする必要もないかと考えます。
では、引き続きご意見よろしくお願いします。
WM_LBUTTONDOWN でキャプチャーを開始したウィンドウは
マウスボタンが離されたときにポストされるWM_LBUTTONUPを待ち続けます。
が、記憶によると、この間にダイアログを出したりすると、その後に
ボタンを離しても当該ウィンドウにはWM_LBUTTONUPが来ないです。
それで、ダイアログを消した後でもマウスキャプチャーが生きていて、
マウスを押し下げてもいないのにドラッグし続けているかのような状態に
なったと思います。
>WM_COMMANDとコマンドIDをPostするようにしました。
>この方法でとりあえずは解消することができました。
しばらくボタンを離さずにいたら、WM_LBUTTONUPは発生しないので、
WM_LBUTTONUP よりも先にそのメッセージが届きそうな気がしますが?
WM_LBUTTONDOWN -> ツールバーがキャプチャーを開始
WM_ACTIVATE -> 固有のメッセージをポスト
ポストされたメッセージ -> ダイアログ表示
ボタンを離す -> ツールバーはまだ WM_LBUTTONUP を待っている
になりませんかね?
void CMainFrame::OnActivate(UINT nState, ...)
{
CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
// TODO : ここにメッセージ ハンドラ コードを追加します。
if (nState == WA_CLICKACTIVE)
PostMessage(WM_COMMAND, ID_APP_ABOUT);
}
で試してみました。(WinXP, VC++.net2003)
ウィンドウを非アクティブにしておいてから
ツールバーの移動用つまみ部分をクリックするとプログラムが無応答に。
その後、他の画面をアクティブにしてから、再度その画面をアクティブにすると
バージョン情報ダイアログが表示される。
ダイアログを閉じるとプログラム不正終了。
まぁ、ろくなことにはならない、ってことです。
ちなみに、僕の回避策というのは下記のような感じのものです。
(実際に以前やったのはMFCぢゃなくてSDKでしたが)
もっとうまいやりかたをご存知の方がいらしたら、教えてほしいっす。
void CMainFrame::OnActivate(UINT nState, .....)
{
CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
if (nState == WA_CLICKACTIVE)
SetTimer(1, 0, NULL);
}
void CMainFrame::OnTimer(UINT nIDEvent)
{
KillTimer(nIDEvent);
if (nIDEvent == 1)
{
if (GetCapture() == NULL)
AfxMessageBox(_T(通知です。)); // ダイアログ(メッセージ)表示
else
SetTimer(1, 1000, NULL); // ドラッグ操作が終わるのを待つ
}
CFrameWnd::OnTimer(nIDEvent);
}
Visual Studio 2003 の IDE は非アクティブ状態からいきなりツールバーボタン
や移動つまみを押してもマウスキャプチャを開始しないようです。
(アクティブにしてからもう一度押しなおさないと動作しない)
VS 2003 のエディタ部分で範囲選択しておいて、
他のエディタで同ファイルを保存後、
いきなりVS 2003 エディタの範囲選択部分をドラッグ開始すると、
変更通知のダイアログが表示されますが、
カーソルが砂時計になってマウス操作が効かなくなっています。
大笑いです。
>変更通知のダイアログが表示されますが、
>カーソルが砂時計になってマウス操作が効かなくなっています。
お、VS2005 だとドロップ完了してから(キャプチャが解けてから)
ダイアログ表示してますね。
aさん、色々調べていただいて、ありがとうございます。
私もaさんと同様に、タイマを仕掛けてGetCapture()がNULLを返すまで待つ、
という仕様に変更しました。
仕様書にきっちり書いておかないと忘れてしまいそうですね。
> Visual Studio 2003 の IDE は非アクティブ状態からいきなりツールバーボタン
> や移動つまみを押してもマウスキャプチャを開始しないようです。
> (アクティブにしてからもう一度押しなおさないと動作しない)
確かに、アクティブ時にすぐダイアログが開かれませんし、
IDE はアクティブ時に特別な処理を行っているようですね。
> VS 2003 のエディタ部分で範囲選択しておいて、
> 他のエディタで同ファイルを保存後、
> いきなりVS 2003 エディタの範囲選択部分をドラッグ開始すると、
> 変更通知のダイアログが表示されますが、
> カーソルが砂時計になってマウス操作が効かなくなっています。
> 大笑いです。
厳しいチェックですね(^^;)
開発中のエディタに範囲選択機能を追加した際は注意します。
aさん、dairygoodsさん、お世話になりました。
解決とさせていただきます。