VisualC++のウィザードでMFC(SDI)アプリケーションを選択した後、
自作のCWnd(ClassWizardでMFCのgeneric CWnd派生)クラスを追加しました。
そのあと、ウィザードで作成されたCxxAppクラスのInitInstance関数内の
メインウィンドウ作成部分を以下のようにメインウィンドウを差し替えました。
CMyWndがメインウィンドウにしたい追加クラスです。
CMyWnd* pMyWnd = new CMyWnd;
m_pMainWnd = pMyWnd ;
pMyWnd->Create();
// メイン ウィンドウが初期化されたので、表示と更新を行います。
pMyWnd->ShowWindow(SW_SHOW);
pMyWnd->UpdateWindow();
pMyWnd->Create();は自分で追加した関数で、以下のように
親ウィンドウをNULLにしてトップレベルウィンドウにしています。
BOOL CMyWnd::Create()
{
LPCTSTR lpszClassName;
CRect rect( 0, 0, 300, 200);
HCURSOR hCursor = ::LoadCursor( NULL, IDC_ARROW);
DWORD dwStyle, dwExStyle;
lpszClassName = AfxRegisterWndClass( CS_HREDRAW | CS_VREDRAW, hCursor);
dwExStyle = 0;
dwStyle = WS_POPUP;
return CreateEx( dwExStyle, lpszClassName, _T("TopLevelWindow"), dwStyle, rect, NULL, NULL, NULL);
}
すると、この差し替えたメインウィンドウをWM_CLOSEやWM_DESTROYメッセージを
送るなりして破棄すると、アプリケーションは正常終了しているように
見えたのですが、デバックさせるとメモリーリークが発生してしまいます。
デバックでメモリーリーク発生もとのコードは、CxxAppクラスInitInstance内の
CMyWnd* pMyWnd = new CMyWnd;
の部分となっています。
どうして、メモリーリークが発生してしまうのでしょうか?
メインウィンドウ終了前に、子ウィンドウ全てにWM_DESTROYを送ったりして
色々と試したのですが回避できません。
CMyWnd* pMyWnd = new CMyWnd;
↑
newしたpMyWndをdeleteしてますか?
レスありがとうございます。
ウィザードで作成したコードのままだとなぜ、メモリーリークを
起こさないのでしょうか?
また、deleteすることもちろん試しましたが
どのクラスのデストラクタも、ExitInstance関数も
デバックでブレークポイントをおいたり、TRACEを仕掛けたりしたのですが
どれも呼び出された気配がありません。
OnNcDestroy()関数で自分自身をdeleteしてもメモリーリークが発生します。
どこでdeleteすればようのでしょうか?
>どのクラスのデストラクタも、ExitInstance関数も
>デバックでブレークポイントをおいたり、TRACEを仕掛けたりしたのですが
>どれも呼び出された気配がありません。
空のデストラクタだと最適化によって実際には呼ばれないらしいですよ。
>OnNcDestroy()関数で自分自身をdeleteしてもメモリーリークが発生します。
Delete thisのような処理はPostNcDestroy()に書くようにと
MSDNのOnNcDestroy()の解説に書いてありますよ。
tetuo さんがすでに回答済みですが、興味があってのでちょっとMFC(VC++6.0)のソースを見てみました。
CWnd::PostNcDestroy() は空ですが、CFrameWnd::PostNcDestroy() には
delete this;
の一行が記述されています。
MFC のウィザードで SDI または MDI のアプリを作成した場合、メインウィンドウは常に CFrameWnd の派生クラスになりますので、このあたりがウィザードで作成したときと、独自のウィンドウを使用したときの差になっていると思われます。
PostNcDestroy() で、delete this; をするのは
自動クリナップと呼ばれていますね(MS推奨だったと思う)。
モードレスダイアログで必ず使ってます。
クリナップというかクリーンアップ(cleanup)
ウィンドウの自己消滅(delete this)は、PostNcDestroy()でしたね。
基本を忘れていました。
しかし、こうすると今度は自作メインウィンドウを閉じるとアクセス違反で以上終了してしまいます。
試しに、メインウィンドウにしたいウィンドウを、
ウィザードのMFCのSDKで作成されたメインウィンドウ(CFrameWnd)の
子ウィンドウにしてCFrameWndの×ボタンで閉じると、以上終了しません。
どんどん、深みにはまっていきます。
どうしてでしょうか?
>ウィザードのMFCのSDKで作成されたメインウィンドウ(CFrameWnd)の
>子ウィンドウにしてCFrameWndの×ボタンで閉じると、以上終了しません。
以上終了は異常終了、MFCのSDKはMFCのSDIの誤りでしょうか?
少し落ち着きましょう。
異常終了しません。ということですがメモリリークはどうなんですか?
CMyWndの中身は本当に問題ないですか?
CFrameWnd は PostNcDestroy() だけでなく他にも多くの関数をオーバーライドしています。それらは WINCORE.CPP や WINFRM.CPP、WINFRM2.CPP などに記述されているようです。
わたしでは、このうちのどの関数が今回の問題に関係してくるのか見当もつきmせんが…。
極端な話1つづつ同じ関数をオーバーライドしていけばいつか必ず正解にたどり着くと思います。
別の方法として、素直(?)に CFrameWnd から派生するという方法は取れないのでしょうか?
ないしは、すでに試されているようですが CFrameWnd の子ウィンドウにしてはどうでしょうか?
もともと MFC では CFrameWnd 内に CView 派生クラスを置く、というのが基本の設計のような気がします。
ちなみにわたしであれば、CFrameWnd の中身を理解する苦労より、これらの方法で希望の動作を実現する方法を探ります。
MFC での開発は、素直(?)にやると非常に楽ですが、それを超えたことをする場合にはフレームワークの、つまり MFC のソースの中身を充分に理解しないと難しいのではないかと思います。
その場合、MFC を使わない、という選択肢も考えてみるとよいかもしれません。
一応、メインウィンドウにしたい自作ウィンドウをMFCデフォルトの
メインウィンドウCFramWndの子にして、通常時はCFrameWndを隠し、
自作ウィンドウを閉じる動作をしたら、CFrameWndにWM_CLOSEを
送ることで、メモリーリークも異常終了もしないので妥協します。
いつまでも、ここで立ち止まるわけにはいかないので。
今度、また、じっくり調べようと思います。
みなさん、後返答ありがとうございました。
一応、メインウィンドウにしたい自作ウィンドウをMFCデフォルトの
メインウィンドウCFramWndの子にして、通常時はCFrameWndを隠し、
自作ウィンドウを閉じる動作をしたら、CFrameWndにWM_CLOSEを
送ることで、メモリーリークも異常終了もしないので妥協します。
いつまでも、ここで立ち止まるわけにはいかないので。
今度、また、じっくり調べようと思います。
みなさん、後返答ありがとうございました。
ツイート | ![]() |