CMDIFrameWndExの仕様なのでしょうか?

[戻る]
hirocco 2010/11/25(木) 18:23:32
VisualStudio2008にてMDIアプリケーションを作成して、ビューを開き最大化にした状態
でビューを閉じるとMDI枠のヘッコミ?クライアントエッジ的な表示がなくなるんです。
最大化させないで閉じるとなくならない。
これはCMDIFrameWndExを使うときは仕方ないのでしょうか?
CMDIFrameWndExを使わないとこの症状は起きません
何か回避策があれば、お手数ですが教えていただけませんか?
ちなみに2010でも同じ症状がでるんです…

仲澤@失業者 2010/11/25(木) 18:57:49
自分しては「バグ」の判定ですね。
Spy++で見ればすぐにわかりますが、その操作をするとMDIClient
の WS_EX_CLIENTEDGE スタイルが外れてしまいます(笑)。
以後、復活することはありません。はっきりいってまぬけです。
自分でコードすれば WS_EX_CLIENTEDGE スタイルを付け直すことも
できますが、面倒なのでやってません。

直接は関係ありませんが、メニューバーのメニューのショートカットキー
の表示(ex. ファイル(F))の(F)の部分のアンダースコアも表示されませんよね
(しくしく)。

仲澤@失業者 2010/11/25(木) 19:03:22
もとい。アンダーラインですねorz。

hirocco 2010/11/25(木) 19:04:34
バグですか…
CMDIFrameWndExのm_hWndMDIClientをサブクラス化できないのも、やっぱりバグなんでし
ょうかね、ちょっとがっかりですね
それともやり方が悪いんでしょうかね

hirocco 2010/11/25(木) 19:12:19
[[解決]]
CMFCCaptionBarの画像を割り当てないと(SetBitmap)しないとXボタンがなくなっちゃう
のもバグっぽいですね
RecalcLayoutをオーバーライドしてコチョコチョしないと具合悪いですね
Visual2010で改善されなかったぽいですし, 改善されないということは仕様ということ
ですかね

あ、解決ということで、ありがとうございます

えーと 2010/11/26(金) 13:00:37
RecalcLayout()を操作する必要はありません。
バグって言えばバグですよね。

原因は
CMDIChildWnd::UpdateClientEdge()です。
子MDIが閉じる時にこの関数が呼ばれますが、この関数は子MDI
が最大化する場合に、WS_EX_CLIENTEDGEを外し、それ以外では
付けます。

ただし、閉じる時には対応できていないので外れた状態のままになります。

このことから、最大化を外してあげれば解決します。

# 子MDIが複数ある場合+最大化の場合はその必要はありません。

子MDI(CMDIChildWnd(Ex))のWM_CLOSEを処理し、以下のようにすれば解決します。
必要であれば、WS_EX_CLIENTEDGEが付いていない時の条件も加えても良いと思います。

void CHogeChildFrame::OnClose()
{
    CMDIFrameWnd* pFrameWnd = dynamic_cast<CMDIFrameWnd *>( AfxGetMainWnd() );
    ASSERT_VALID(pFrameWnd);
    if( (GetStyle() & WS_MAXIMIZE) && 
       ( ::GetWindow( ::GetWindow( pFrameWnd->m_hWndMDIClient, GW_CHILD ), 
GW_HWNDNEXT ) == NULL )){
        ModifyStyle( WS_MAXIMIZE | WS_VISIBLE, 0, 0 );
    }
    __super::OnClose();   
}

参考まで

えーと 2010/11/26(金) 13:15:11
追記。

> 仲澤@失業者さん

>自分でコードすれば WS_EX_CLIENTEDGE スタイルを付け直すことも
>できますが、面倒なのでやってません。
この処理を閉じる時にModifyStyleEx()で付けてあげても、
結局CMDIChildWnd::UpdateClientEdge()が呼ばれて外されてしまいます。(T.T)

仲澤@失業者 2010/11/26(金) 14:38:47
本件について、あまり真剣に考えるつもりはないのですが、
まぁ、暇つぶしにやってみると。

まず、適当なタイミングのメッセージがわからなかったので、
メインフレームのWM_PAINTのコードに下記のコード

 BOOL res    =::EnumChildWindows( m_hWndMDIClient, EnumFunc, ( LPARAM)0);
 if( res == FALSE){
    LONG_PTR Style    =::GetWindowLongPtr( m_hWndMDIClient, GWL_EXSTYLE);
    if( 0 ==( Style & WS_EX_CLIENTEDGE)){
      CRect  rc;
      GetClientRect( &rc);
      Style |= WS_EX_CLIENTEDGE;
      ::SetWindowLongPtr( m_hWndMDIClient, GWL_EXSTYLE, Style);
      ::SetWindowPos( m_hWndMDIClient, NULL, 0, 0, rc.Width(), rc.Height(),
            SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
      ::RedrawWindow( m_hWndMDIClient, NULL, NULL,
                  RDW_ERASE | RDW_FRAME | RDW_UPDATENOW);
    }
  }
をつっこんでやると、なんとかなりますよね(vv;)。
まぁ、正確なタイミングがわかったら、自分も対応したいとおもいます。

仲澤@失業者 2010/11/26(金) 14:45:02
ちなみにEnumのコールバックは
BOOL CALLBACK EnumFunc(
  HWND hwnd, LPARAM lParam)
{return FALSE;}
でよしっと。

hirocco 2010/12/01(水) 09:57:10
色々考えてくださってありがとうございます
えっと,少々強引ですが CMainFrame(:CMDIFrameWndEx)の中で
GetWindowLongにて現在のExスタイルを取得してWS_EX_CLIENTEDGEとのアンドが0
ならばModifyStyleExで足すという処理をOnTimer内でポーリング的に実行させたのです
がダメでした
m_wndClientArea(:CMDIClientAreaWnd)という子がCMDIFrameWnd内のm_hWndMDIClientを
サブクラス(SubclassWindow)していて,この子が悪さをしているっぽいです
m_wndClientAreaをサブクラスかすれば何とかなりそうですけど,m_hWndMDIClientをサブ
クラス化しているm_wndClientAreaをサブクラス化させる方法は知りません,というかそ
んな方法はないのでは?
ダミーでドッカブルペインをクリエイトさせて追加させて,そして常に非表示とすると
OKなんですが…

hirocco 2010/12/01(水) 10:07:10
仲澤@失業者さんの方法だと確かにできますね
TimerでできないのはWM_PAINT時に毎回WS_EX_CLIENTEDGEをはずしにきているんですか
ね?
やな感じですね
xxxEx系の新しいクラスはおかしなとこいっぱいですね

hirocco 2010/12/01(水) 10:40:38
ごめんなさいウソでしたTimerでもできます
スタイル変更後,SetWindowPos忘れてました...

aetos 2010/12/01(水) 10:47:30
バグや改善提案についてはぜひこちらにフィードバックしてください。

https://connect.microsoft.com/VisualStudioJapan/Feedback

> メニューバーのメニューのショートカットキーの表示(ex. ファイル(F))の(F)の部分
> のアンダースコアも表示されませんよね(しくしく)。

それは最近の Windows の共通仕様では? OS は何でしょうか。

えーと 2010/12/01(水) 11:22:50
aetosさん

>> メニューバーのメニューのショートカットキーの表示(ex. ファイル(F))の(F)の部分
>> のアンダースコアも表示されませんよね(しくしく)。

>それは最近の Windows の共通仕様では? OS は何でしょうか。

http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+201007/10070007.txt
これのことです。

aetos 2010/12/01(水) 11:55:50
> これのことです。

MFC のソースまでは追いかけていませんが、Windows Server 2008 R2 x64 + VS2010 
Ultimate で試したところ、メニューを縦にしても下線は表示されませんでした。

また、同環境にて、本題である

> MDI枠のヘッコミ?クライアントエッジ的な表示がなくなるんです。

の方も再現できません。

仲澤@失業者 2010/12/01(水) 12:45:59
>の方も再現できません。

検証ありがとうございます。
VS2010だと直っているようですね。
対策を放棄してて正解だったかもしれません(vv;)。

tenteku 2010/12/01(水) 14:17:09
直接関係無い話題ですみませんが
こういうのってVS2008にもSPxとかでサポートされるんですかね?
配布用ファイルとか…

えーと 2010/12/01(水) 14:23:45
aetosさん

報告ありがとうございます。
こちらでもVisual Studio 2010 pro + WinXP SP3でテストした所、
下線が縦・横ともに表示されているのを確認しました。

MFCのソースを確認したところ、
afxGlobalData.m_bUnderlineKeyboardShortcutsメンバの
状態により、下線のON/OFFをしており、この変数は
SPI_GETMENUUNDERLINESキーによる、システムの設定値を
保持していました。

システム設定を利用する方式に変更してくれたのはありがたいです。

なお、ファイル名に&を付けたファイルに対し、
FWS_PREFIXTITLEを外してみてもファイル名が下線に変換されていたので
まだ直ってないみたいです。

エッジ線のほうですが、直っていないみたいですが...
OSによって、違いがでるのかな?

hirocco 2010/12/01(水) 15:07:48
.netFrameWork的にも2.0で間に合ってますし,納める先もXP希望というトコロがほとんど
で,VS10に切り替える必要性もない感じで,しかも余裕もないですので,希望はVS9のSP2が
出ると嬉しいですね
本題とは関係ないですけど,メニューバーもドッキングで固定にできないのは,なんか感
じ悪いです →CMFCMenuBarをサブクラス化しちゃいましたけど…
それと,クラス名にMFCって入れるのってかっこいいんですかねぇ

aetos 2010/12/01(水) 16:55:16
> なお、ファイル名に&を付けたファイルに対し、
> FWS_PREFIXTITLEを外してみてもファイル名が下線に変換されていたので
> まだ直ってないみたいです。

> エッジ線のほうですが、直っていないみたいですが...

リボンを使うアプリケーションだといずれも 2010 で再現しました。

aetos 2010/12/01(水) 17:29:37
というわけでフィードバックしました。投票をお願いいたします。
VS2010 をお持ちの方は再現試験をして頂き、右側の「再現できます」のところもクリッ
クして頂けると助かります。

https://connect.microsoft.com/VisualStudioJapan/feedback/details/626013/vc-
2010-mfc
https://connect.microsoft.com/VisualStudioJapan/feedback/details/626016/vc-
2010-mfc-mdi-ws-ex-clientedge

えーと 2010/12/01(水) 18:23:27
aetosさん

ファイル名の件ですが、
リボンで再現するのは、外観が[Office2007]だからです。
リボンなしでも概観を同じにすると発生します。
以前付けたリンクに説明を付けていたのでしたが、再度補足するべきでした。
2010では概観に[Windows7]が追加されていますが、そちらでも再現を確認できました。

エッジ線のほうですが、リボンなしでも概観も関係なく
WinXP SP3上で発生しますが...


ファイル名についての解決策というか、Microsoftに修正してもらいたいのは、
関数
CMFCVisualManagerOffice2007::DrawNcText()
CMFCVisualManagerWidows7::DrawNcText()
の
afxvisualmanageroffice2007.cpp(1942行)
afxvisualmanagerwindos7.cpp(360行)
#Visual Studio2010のMFCソース

DWORD dwTextStyle = DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | (bIsRTL ? 
DT_RTLREADING : 0);
を
DWORD dwTextStyle = DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | (bIsRTL ? 
DT_RTLREADING : 0) | ((!bPrefix) ? DT_NOPREFIX : 0) ;

としてくれるだけで良いのですが。
CMFCVisualManagerWidows7::DrawNcText()についてはvirtualが付いているので
派生して対処できるのですが、CMFCVisualManagerOffice2007()には付いて
いないのです。

この件については、コメント追記しておきました。

aetos 2010/12/01(水) 19:43:14
んー…

VS2010 で、タブを OFF にした以外は全部デフォルトの状態、つまり、

・アプリケーションの種類:MDI(非タブ)
・プロジェクト形式:Visual Studio
・Visual スタイルと色:Visual Studio 2008
・コマンドバー:メニューバーとツールバーを使用する

でプロジェクトを作って、名前に & を含むファイルを開いても、子ウィンドウを最大化
したまま閉じても、現象が再現しません。

デフォルトのスタイルが VS2008 なので、

・ファイルを開く&子ウィンドウ最大化してからスタイルを Office2007 に変更
・スタイルを Office2007 に変更してからファイルを開く&子ウィンドウ最大化

のいずれでも正常に動きます。

また、プロジェクト作成時のデフォルトのスタイルを Office2007 にしても同様です。

これら再現しないケースでは、
> afxvisualmanageroffice2007.cpp(1942行)
を通っていないようです。

ちなみに、こちらの OS は Win2008R2 です。

OS 依存の問題かどうか切り分けたいので、よろしければ、XP で現象が発生するプロジ
ェクト一式を Connect のページにアップロードして頂けませんでしょうか。

えーと 2010/12/02(木) 01:21:55
aetosさん

アップロードする必要もなく、提示されたのと同じ設定で、
ウィザード作成したプロジェクトです。
追加で
CMainFrame::PreCreateWindow()
に
cs.style &= ~FWS_PREFIXTITLE;
を追加しているだけです。

自宅ではVista+Visual Studio2008しかないのですが、

1.エッジ線はリボンの有無関係なく消えます
2.ファイル名については、正常に&表示し、リボンの時だけ下線
ファイル名に関しては、2008/2010関係なく同じ症状のようです。

この場合も私が提示した部分のソースは通りませんでした。
#リボン+Office2007の場合のみ通る

Vista上ではタイトルバーが独自描画されないので、もしやとは思いましたが
OS依存の可能性が高そうですね。
リボンの場合は、独自のタイトルバーを形成しているので影響がでるのかもしれませ
ん。

# 2010でテストしたいところですが、バージョンアップしていないので...

aetos 2010/12/02(木) 12:47:00
一度、問題について整理しましょう。

以下の2点があります。
1:名前に & を含むファイルを開くと、タイトルバーに & ではなく下線が表示される
2:非タブ MDI の子ウィンドウを最大化したまま閉じると、MDI クライアントエリアの
 WS_EX_CLIENTEDGE スタイルが復活しない。

1 は、Windows XP、Windows 2008 R2 ともに、VS2008 でも VS2010 でも、
リボンの場合のみ発生します。

2 は、VS2008 on Windows XP の環境ではリボンでなくても発生します。
VS2010 on Windows 2008 R2 の環境では、リボンでしか再現しません。

ということで間違いないですか?

問題 2 について、VS2008 on Windows 2008 R2 の環境で再現することを確認しました。
したがってこの問題は(リボン不使用の場合については)、OS 依存ではなく、VS2010 
で修正された問題ということですね。

問題 1 も、どちらの環境でも発生していることから OS 依存ではなさそうです。

ところで…
アプリケーション ウィザードの最初のページで、Office2007 / Windows7 以外のテーマ
を選んでいても、後のページでリボンを使うようにすると、実行時には Office2007 / 
Windows7 しか選べなくなりますね。
が、初回実行時には最初に選んだスタイルが使われているわけで、その場合でも下線の
問題が発生しました。
というわけで、Office2007 / Windows7 スタイルだけの問題ではない気がします。
ただ、ウィザードのバグと言えないこともないですが。

aetos 2010/12/02(木) 12:58:45
しかし、VisualManager は気持ち悪い継承階層ですね。

CMFCBaseVisualManager
└CMFCVisualManager
 ├CMFCVisualManagerOfficeXP
 |└CMFCVisualManagerOffice2003
 | ├CMFCVisualManagerOffice2007
 | └CMFCVisualManagerVS2005
 |  └CMFCVisualManagerVS2008
 └CMFCVisualManagerWindows
  └CMFCVisualManagerWindows7

もうちょっと抽象的にできなかったのでしょうか。

aetos 2010/12/02(木) 13:00:18
# あ、こうだった。
# どうでもいいけどw

CMFCBaseVisualManager
└CMFCVisualManager
 └CMFCVisualManagerOfficeXP
  ├CMFCVisualManagerOffice2003
  |├CMFCVisualManagerOffice2007
  |└CMFCVisualManagerVS2005
  | └CMFCVisualManagerVS2008
  └CMFCVisualManagerWindows
   └CMFCVisualManagerWindows7

えーと 2010/12/02(木) 14:13:12
aetosさん

> 一度、問題について整理しましょう。
整理ありがとうございます。
こちらでテストした内容を加えると

1 は、Windows Vista、Windows 2008 R2 ともに、VS2008 でも VS2010 でも、
  リボンの場合のみ発生します。
  ※Vista+VS2010については、今夜確認します。(exeのみのチェックになりますが
    がたぶん同じことだと思います。)
  Windows XPではリボンなしだと概観を[Office 2007]を指定すると発生し、
  リボン時には発生。([Windows7]では発生しませんでした。)

2 は、VS2008 on Windows XP/Vista の環境ではリボンでなくても発生します。
  VS2010 on Windows 2008 R2 の環境では、リボンでしか再現しません。
  VS2010 on Windows XP SP3 の環境では、リボンでなくても発生します。

>アプリケーション ウィザードの最初のページで、Office2007 / Windows7 以外のテー>を選んでいても、後のページでリボンを使うようにすると、実行時には Office2007 / 
>Windows7 しか選べなくなりますね。
>が、初回実行時には最初に選んだスタイルが使われているわけで、その場合でも下線の
>問題が発生しました。
>になります。
たしかにリボンを使用するときは選択できるテーマがこの2種しかないので、気がつき
ませんでした。
そこで各CVisualManager****クラスのソースを確認してみると、
リボンのキャプションの描画ですが、全てDT_NOPREFIXフラグが指定されていませんでし
た。

リボンのキャプションはCVisualManager****::OnDrawRibbonCaption()で描画します。
#****は選択したテーマ名

テーマが[Office2007]/[Windows7]に関しては、そこからDrawNcText()が呼ばれます。

WindowsXP+テーマがOffice2007+リボンなしで発生する原因ですが、

CVisualManager****::IsOwnerDrawCaption()が有効のとき、タイトルの独自描画を
行うようになっていますが、テーマが[Office2007]以外では無効でした。
さらに、CMFCVisualManagerOffice2007クラスのソースを追っていくと
DWMが実装されていないOS+リボンを使用していない場合は、
独自描画を行う条件になっていました。

つまり、Windows 2008ではDWMが実装されているのでデフォルトのタイトル描画と
なるので、発生しないことに納得がいきます。
Aeroを無効にすれば発生するかもしれませんので、試してもらえないでしょうか?

えーと 2010/12/02(木) 14:14:57
> リボン時には発生。([Windows7]では発生しませんでした。)

ではなく、
リボン時には発生。(リボンなし+[Windows7]では発生しませんでした。)
です。

aetos 2010/12/02(木) 17:06:23
> VS2010 on Windows XP SP3 の環境では、リボンでなくても発生します。

ええ、ってことはやっぱり OS 依存の部分があるってことですか…

> Aeroを無効にすれば発生するかもしれませんので、試してもらえないでしょうか?

Windows 2008 R2 Aero 無効の非リボン アプリケーションで再現することを確認しまし
た。

詳細な検証、ありがとうございます。
是非 Connect にフィードバックして頂きたいと思います。
お願いできますでしょうか。

えーと 2010/12/03(金) 00:54:56
aetosさん
>Windows 2008 R2 Aero 無効の非リボン アプリケーションで再現することを確認しまし
た。
報告ありがとうございます。
Vistaでも確認したら同じ結果でした。

Vista+VS2010のテストも出来たのでお知らせします。
結果をまとめると、

1 は、Windows Vista、Windows 2008 R2 ともに、VS2008 でも VS2010 でも、
  リボンの場合のみ発生します。
  Windows XPではリボンなしだとテーマが[Office 2007]で発生、
  リボン時には常に発生。
  Windows Vista/2008についてはAeroをOFFにすると、WindowsXPと同じ現象になる


2 は、VS2008 on Windows XP/Vista の環境ではリボンでなくても発生します。
  VS2010 on Windows 2008 R2/Vista の環境では、リボンでしか再現しません。
  VS2010 on Windows XP SP3 の環境では、リボンでなくても発生します。
  VS2010 on Windows Vista/2008についてはAeroをOFFにするとWindowsXPと同じ現象に
なる


さらに、リボンのファイル名&文字についてですが、
最近使用したファイル名のリスト(メニュー上)でも下線になっていました。
この部分についてはソースを追う時間がなかったのですが、DT_NOPREFIXが無い
ためだと思います。

エッジ線の件ですが、Vistaでの挙動をみると最後の子MDIが最大化から閉じるとき
いったん、通常の大きさに戻ってから消えています。
ま、私のPCが遅い(Vistaが重い?)から動きが見えたのですが、これが現象が
発生しない原因のようです。
AeroをOFFにすると発生するので、AeroがONの時だけ最大化を戻す処理がどこかに
組み込まれているかもしれません。
時間があれば確かめてみます。
もし、正しければ本来の質問の対応は閉じるときに最大化を戻すで良いのかもしれませ
んね。

> 是非 Connect にフィードバックして頂きたいと思います。
> お願いできますでしょうか。
了解しました。
週末は忙しく直ぐにはフィードバックできませんが、遅れてでもしたいと思います。

今日はもう眠くて...

えーと 2010/12/09(木) 15:30:44
本題に関係ありませんが、タイトルの&表示に関しては

Connectで

>ご報告ありがとうございます。 こちらの問題は、Visual Studio の次期メジャー リリ
ース版の MFC で修正されました

と報告がありました。
ということは、VS2010(というかFeature Pack?)のSPで修正される
可能性が無いみたいですね。
# エッジの件については、まだ解決できていないようですが、
# 同様にVS2010で修正されない可能性が高そうですね。

エッジの件ですがVista上での挙動から、最大化を戻して閉じているようなのですが、
少し追ってみましたが、現時点では場所を特定できていません。
また、自宅ではVS2008なので追うこともできず、職場でも仕事が
忙しくて時間がありません。

もし、時間が取れて解決できる点が見つかったらConnectに報告したいと思います。

aetos 2010/12/09(木) 17:41:34
[[解決]]
えーとさん、多大なご協力、本っっっ当にありがとうございました!

このスレ、解決済みになってから本格的に火がついた感じですが、Connect の方も 
Close になりましたので、こちらも改めて解決済みとさせて頂きます。

# 個人的には MFC のソースを書き換えてどうにかするのは好みません。

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