CMFCMenuBarが勝手に複数段になる – プログラミング – Home

CMFCMenuBarが勝手に複数段に...
 
通知
すべてクリア

[解決済] CMFCMenuBarが勝手に複数段になる

固定ページ 1 / 2

NOR
 NOR
(@NOR)
ゲスト
結合: 22年前
投稿: 128
Topic starter  

Visual C++ 2008 MFCです。

2008のCMFCMenuBarで微妙な動作があり、
環境によるものなのかMFCのバグなのかわからないことがあります。

デフォルトの設定のままSDIのプロジェクトをビルドして実行させ、
タスクバーに表示される自分自身の部分をマウスで連打していると、
アプリケーションが最小化と復元を繰り返しますが、
そのうちメニューが勝手に複数段になってしまいます。

タイトルバーなどをドラッグすると、元に戻ります。

自分の環境はXPで、最小化時にアニメーションが入る設定ですが、
このような動作に遭遇されたかたはいらっしゃいませんでしょうか。
また、他の環境でも発生するか試していただけませんでしょうか。


引用未解決
トピックタグ
hirocco
 hirocco
(@hirocco)
ゲスト
結合: 13年前
投稿: 138
 

CMFCMenuBarって具合悪いよね
動いちゃうのが嫌い
そのままじゃ使いずらいねぇ


返信引用
えーと
 えーと
(@えーと)
ゲスト
結合: 18年前
投稿: 54
 

VS2010 + XPでも発生するみたいですね。
#他のOSではテストしていません。

ちなみに、Office2007スタイルだと大丈夫みたいです。

MFCのソースを眺めてみましたが、
以下のようにすれば一応対処はできている感じです。

CMainFrame::OnSize()を処理し、

void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CFrameWndEx::OnSize(nType, cx, cy);

CMFCVisualManager* pManager = CMFCVisualManager::GetInstance();
if( !pManager->IsOwnerDrawCaption() &&
(GetRibbonBar() == NULL ) &&
( nType == SIZE_RESTORED ) &&
( !m_bWasMaximized )){
RecalcLayout();
}
}

MFCのソースを見てみると、最大⇔最小ではならないようになっていました。

原因を特定するために、MFCの中身を追いかけてみると、
CFrameWndEx::OnSize()内でAdjustDockingLayout()が復元時に呼び出されますが、
メニューバーの配置情報がツールバーの高さを含んでしまってます。
#必ずなるとは限らないが、MFC内部ではPostMessage()でメッセージを置いて
#くる処理がたくさんあるので、今回のような連打による状態変更では
#置いてきたメッセージが先に処理されていないので不具合が発生したのかと
#思います。

Office2007のテーマを使用している場合、(WindowsXPかVista/7/2008でDWMをOFF)
その処理の前でm_Impl.OnWindowPosChanging(&wndpos);
が呼び出されており、そのおかげでその後のAdjustDockingLayout()で
領域が正しく計算されているようです。

本当はm_Impl.OnWindowPosChanging()を直接呼び出したいとこなのですが、
protectのメンバですし、この関数内でもリボン+Office2007以外は処理しない
ようなコードになっていました。

なので、AdjustDockingLayout()した後にRecalcLayout()と2度手間になりますが、
これが確実のようです。


返信引用
hirocco
 hirocco
(@hirocco)
ゲスト
結合: 13年前
投稿: 138
 

VS9+XPではなりますねぇ
ちなみに
VS9+Vista
VS9+7
ではかなり連打してみましたけどなりませんねぇ

でも,なぜメニューバーを移動可能なものにしたんでしょうね
VS自体メニューは動かせないのにねぇ
XPだとフォントもおかしいしねぇ
そういうのはあくまでオプション的な位置づけで
デフォルトはいわゆる王道で維持してほしいですね


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

横から失礼します。

> #必ずなるとは限らないが、MFC内部ではPostMessage()でメッセージを置いて
> #くる処理がたくさんあるので、今回のような連打による状態変更では
> #置いてきたメッセージが先に処理されていないので不具合が発生したのかと
> #思います。

これ、確認しました。
タスクバーからPostされるWM_SYSCOMMANDとアプリケーション内からのPostMessageは
どちらが先に来るか、わからないということですか、面白いです。

ということは、一般的に言って、
WM_SYSCOMMANDの終了時には、メッセージキューを空にする必要がありますね。


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 22年前
投稿: 128
Topic starter  

hiroccoさん、えーとさん、ロマさん、試していただきありがとうございます。

えーとさんに提案していただいたCMainFrame::OnSize()の処理を入れてみたところ、
メニューが正しく復元されるようになりました。
MFCの詳しい流れも教えていただき、ありがとうございます。

ちなみに、上記のSDI形式だとクラスビューなどのペインが作られますが、
これらのペインも同じ操作でレイアウトが壊れてしまいます。

元となったBCGSoftのライブラリのサンプルでは、
メニューは壊れませんでしたが、各ペインは同じように壊れました。

自分でもソースを追ってみているのですが、場所を見つけられない状況です。
こちらのほうもCMainFrame自身で対応できそうなものでしょうか?

>> hiroccoさん
> でも,なぜメニューバーを移動可能なものにしたんでしょうね
> VS自体メニューは動かせないのにねぇ

これに関しては、自分の昔のソースを見てみたところ、
メニューバーのEnableDocking()を呼ばずに、
ドッキングの順番を以下のようにしていました。

//m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndToolBar);

> XPだとフォントもおかしいしねぇ

これはCMFCMenuBar::SetMenuFont()で直しています。


返信引用
hirocco
 hirocco
(@hirocco)
ゲスト
結合: 13年前
投稿: 138
 

>> でも,なぜメニューバーを移動可能なものにしたんでしょうね
>> VS自体メニューは動かせないのにねぇ
>
>これに関しては、自分の昔のソースを見てみたところ、
>メニューバーのEnableDocking()を呼ばずに、
>ドッキングの順番を以下のようにしていました。
>
>//m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
>m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
>DockPane(&m_wndMenuBar);
>EnableDocking(CBRS_ALIGN_ANY);
>DockPane(&m_wndToolBar);
忘れたけど,グリップ残りませんでしたっけ?
→うるおぼえ/うろおぼえ

>> XPだとフォントもおかしいしねぇ
>
>これはCMFCMenuBar::SetMenuFont()で直しています。
メイリオが邪魔くさいのよねぇ


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

> ということは、一般的に言って、
> WM_SYSCOMMANDの終了時には、メッセージキューを空にする必要がありますね。

これ、間違いでした。
SC_RESTOREとSC_MINIMIZEがPostされた場合のみでいいのかなぁ、
よくわからなくなりました。


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

連投すまんです。
暫定案(ただしAPI)
フレームウィンドウで
case WM_SYSCOMMAND: // WM_SYSCOMMANDが来た時に
if(GetQueueStatus(QS_POSTMESSAGE))// もしほかにPostされたメッセージがあれば
return 0; // 無視する
else
return CallWindowProc( // そうでなければ基底のハンドラを呼ぶ


返信引用
えーと
 えーと
(@えーと)
ゲスト
結合: 18年前
投稿: 54
 

ロマさんの案でやってみたんですが、駄目でした。
良い案だと思ったんですが。。。

ほかにPostしたものがなくても通知が来ているようです。

そのときのウィンドウの状態をみると、連打していくうちに
SC_MINIMIZEが来たときに、ウィンドウは既にアイコン状態だったり
SC_RESTOREが来たときに、ウィンドウはアイコン状態でなかったりと
アニメーションしている間で処理が実行されるのが問題なような...

>元となったBCGSoftのライブラリのサンプルでは、
>メニューは壊れませんでしたが、各ペインは同じように壊れました。
確かにペインの幅が小さくなってますね。

で、あれこれMFCのソースを追っていったのですが、
良く思えば、復元するときに配置を再計算する必要があるの?
ということに気がつきました。

なので、
OnSize()の処理を先程提示した処理ではなく、

if( nType == SIZE_RESTORED ){
CDockingManager::m_bDisableRecalcLayout = TRUE;
CFrameWndEx::OnSize(nType, cx, cy);
CDockingManager::m_bDisableRecalcLayout = FALSE;
} else {
CFrameWndEx::OnSize(nType, cx, cy);
}
としてみてください。
かなり連打してみましたが、大丈夫なようです。

やっているうちに、そもそもこのように連打するユーザがいるの?
と思ってしまいました。(笑)


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 22年前
投稿: 128
Topic starter  

ロマさん、えーとさん、
提案していただきありがとうございます。

> OnSize()の処理を先程提示した処理ではなく、
>
> if( nType == SIZE_RESTORED ){
> CDockingManager::m_bDisableRecalcLayout = TRUE;
> CFrameWndEx::OnSize(nType, cx, cy);
> CDockingManager::m_bDisableRecalcLayout = FALSE;
> } else {
> CFrameWndEx::OnSize(nType, cx, cy);
> }
> としてみてください。
> かなり連打してみましたが、大丈夫なようです。

上記の方法で、通常状態時の最小化&復元処理では壊れることはなくなりました。

ただ、最大化状態で当初の連打処理をやると、
やはりペインのサイズがおかしくなってしまいました。

また、最大化から元のサイズに復元した際に、
各ペインの配置が再計算されないようです。

> やっているうちに、そもそもこのように連打するユーザがいるの?
> と思ってしまいました。(笑)

連打自体はまず無いでしょうけど(笑)、テスト環境で、

タスクバー内で自アプリに切り替えようとしてクリック

実はすでにアクティブな状態だったため、最小化の動作が始まった

慌てて元に戻すために、最小化アニメーション中に再クリック

という動作を行った際にこの問題が発生した、という経緯でした。
実際、連打しなくても、最小化アニメーション中に再クリックすると、
結構な確率で一発で発生するようです。

この程度の操作は通常でも起こりえるかなと思ったので、
対応できるならしておきたいと思いました。

Feature Packのクラスはこの手の問題を多く抱えていますね…。


返信引用
ロマ
 ロマ
(@ロマ)
ゲスト
結合: 18年前
投稿: 170
 

えーとさん
試していただきありがとうございます。
確かにアニメーション時の連打はダメですね。
(私はアニメーション効果が嫌いなので、テストしていませんでした)

アニメーション時には何がおきているのでしょうか。
Spy++では見づらいし、よくわかりませんでした。


返信引用
えーと
 えーと
(@えーと)
ゲスト
結合: 18年前
投稿: 54
 

>ただ、最大化状態で当初の連打処理をやると、
>やはりペインのサイズがおかしくなってしまいました。

>また、最大化から元のサイズに復元した際に、
>各ペインの配置が再計算されないようです
もう少しテストしたら良かったです。ToT

一応、改良してみたのですが
CMainFrame::OnSize()を処理し、
if(( nType == SIZE_RESTORED && !m_bWasMaximized ) ||
( nType == SIZE_MAXIMIZED && m_bIsMinimized )){
CDockingManager::m_bDisableRecalcLayout = TRUE;
CFrameWndEx::OnSize(nType, cx, cy);
CDockingManager::m_bDisableRecalcLayout = FALSE;
} else {
CFrameWndEx::OnSize(nType, cx, cy);
}
でいけませんか?
こちらでテストしてみた限りでは、これらの問題も発生していないようですが、
そちらでも試してみてください。

>連打自体はまず無いでしょうけど(笑)、テスト環境で、
:
>結構な確率で一発で発生するようです。
なるほど。
こちらで10回程試したら全然起きなくて、10秒ぐらい連打すると発生した
ものですから...
マシンによって起きやすいなら対処したくなりますね。

>アニメーション時には何がおきているのでしょうか。
>Spy++では見づらいし、よくわかりませんでした
こちらでもいろいろ見ているのですが、私もわからないです。
これを抑制したほうが良いのですが...

> Feature Packのクラスはこの手の問題を多く抱えていますね…。
BCGSoftの方達もXP上でのテストはおざなりになっているのでしょう。
今回の件でも他のOSでは発生していないようですし...
これまでもXP上(もしくはDWMがOFF)で発生するようなバグが多かった
ように思えます。


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 22年前
投稿: 128
Topic starter  

> 一応、改良してみたのですが
> CMainFrame::OnSize()を処理し、
> if(( nType == SIZE_RESTORED && !m_bWasMaximized ) ||
> ( nType == SIZE_MAXIMIZED && m_bIsMinimized )){
> CDockingManager::m_bDisableRecalcLayout = TRUE;
> CFrameWndEx::OnSize(nType, cx, cy);
> CDockingManager::m_bDisableRecalcLayout = FALSE;
> } else {
> CFrameWndEx::OnSize(nType, cx, cy);
> }
> でいけませんか?

いろんなパターンで相当テストしてみましたが、
メニューやペインが崩れることは無さそうです。

こちらでもm_bWasMaximizedやm_bIsMinimizedを参照して
CMainFrame::OnSize()内でいろいろ試していたのですが、
この組み合わせでよかったのですね。

本当にありがとうございます。


返信引用
NOR
 NOR
(@NOR)
ゲスト
結合: 22年前
投稿: 128
Topic starter  

何度も申し訳ありません。
えーとさんに提案していただいた方法を正式に組み込んで
いろいろテストしていたのですが、一つ動かないケースがでてしまいました。

これは連打問題とは関係無い流れに対しての副作用なのですが、
最大化していない状態から最小化状態とし、
タスクバーを右クリックして「最大化」をいきなり選ぶと、
状態がおかしくなってしまいました(内部領域が広がらない)。

原因自体は自分でもよくわかっているつもりですが、
そのときのOnSize()を見ると、nType=2(SIZE_MAXIMIZED)、
m_bIsMinimized=TRUE、m_bWasMaximized=FALSEとなっていて、
あらかじめ最大化されている状態で最小化→最大化をやったときと
同じ状態になっていました。

非最大化状態→最小化→最大化という流れのときには、
CDockingManager::m_bDisableRecalcLayoutをいじらずに、else内の
CFrameWndEx::OnSize(nType, cx, cy);
のほうだけを呼ぶようにすればよいのだろうと思われるのですが、
これはCFrameWndExの状態などから区別できますでしょうか?


返信引用
固定ページ 1 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました