■ MFCがメモリーリーク?

[Visual C++ Q & A 掲示板] [過去ログの一覧]


閃人 2008/02/07(木) 19:13:54 <中級者>
お世話になります。閃人です。

以下の構成でアプリを作っています。
A. Win32 Application
B. Win32 Static Library(MFCをサポート)
AにBをlinkさせて、Bでダイアログを表示させる。

--------------------------------
Bのグローバル変数として、以下を定義しています。
CWinApp theApp;

AのWinMainの引数を使って、Bで以下を実行しています。
if( !AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow) ) {
ASSERT( "MFC failed to initialize!" );
return false;
}

AからBを呼び出し、Bで以下を実行しています。
CSampleDialog pcDialog = new CSampleDialog()
pcDialog->Create();
pcDialog->ShowWindow(SW_SHOW);
--------------------------------
以上の手順で、ダイアログを表示することには成功しました。

ですが、アプリを終了すると、大量メモリリークが表示されます。
Detected memory leaks!
Dumping objects ->
{100478} normal block at 0x0B1E8850, 4 bytes long.
 Data: <8J  > 38 4A EA 01 
{100477} normal block at 0x03C82400, 16 bytes long.
 Data: < @              > 98 40 EA 01 00 00 00 00 00 00 00 00 00 00 00 00 
(一部抜粋)

ご覧の通り、場所が表示されていないため特定できません。
AfxWinInitを呼ばなければメモリリークは表示されません。
(それだと、MFCは使えないのでダイアログの部分はコメントアウト)

ググッて見たのですが、1件だけ同じ問題を記述している人がいましたが、解決策は書かれていませんでした。

何か終了処理的なことを明示的に呼ばなければいけないのか。
コンパイルオプションが足りないのか。
何日も調べているのですが、私の力では解決できませんでした。

この構成で必ず作らなければならないため、
解決策をお教えいただけないでしょうか。

宜しくお願い致します。

Blue 2008/02/07(木) 20:28:33
>CSampleDialog pcDialog = new CSampleDialog()
はどこでdeleteしていますか?
(CSampleDialog::OnNcDestroy?)

閃人 2008/02/07(木) 20:56:00 <中級者>
>>CSampleDialog pcDialog = new CSampleDialog()
>はどこでdeleteしていますか?
>(CSampleDialog::OnNcDestroy?)
CSampleDialogを管理しているクラスがいますので、そのクラスのデストラクタでdeleteしています。

尚、CSampleDialogを生成しないで、AfxWinInitだけ使った状態でもメモリリークが出るので、
CSampleDialogの解放漏れではありません。

他にも必要な情報がありましたら、どんどん聞いてください。

通りすがりのニート 2008/02/08(金) 03:29:27
いまいち質問がわからないで、直感ですが、AfxWinTerm()が必要なのでは?
だめでも、CWinAppクラスのデストラクタを追っていけば、わかるような気がします。

気になったのは、CWinAppのオブジェクト作って(これがまず変)、更にAfxWinInitって呼ぶ必要があるのでしょうか?
通常のMFCならCWinApp継承クラスのオブジェクトを作ることになると思うのですが。

(つまり、MFCのプログラムでAfxWinInitを自分で呼び出すことはあまり考えられないんですけど。。。)

あと、ASSERT(文字列)って使いかた変じゃないですか?TRACE(文字列)じゃない?

閃人 2008/02/08(金) 11:12:46 <中級者>
アドバイスありがとうございます。

> いまいち質問がわからないで、直感ですが、AfxWinTerm()が必要なのでは?
> だめでも、CWinAppクラスのデストラクタを追っていけば、わかるような気がします。
AfxWinTermをWinMainが終了する前に呼んでみましたが、駄目でした。
CWinAppのデストラクタを追ってみたところ、正常に終了していました。


> (つまり、MFCのプログラムでAfxWinInitを自分で呼び出すことはあまり考えられないんですけど。。。)
コンソールアプリで、MFCを使うような感じをイメージして頂けますでしょうか。
Exeは、非MFCなので、MFC対応のLIBを作っているため自分で初期化しております。

> あと、ASSERT(文字列)って使いかた変じゃないですか?TRACE(文字列)じゃない?
MSDNのサンプルがそうなっていたので(汗

閃人 2008/02/08(金) 11:19:17 <中級者>
class SampleApp {
public:
 SampleApp();
 ~SampleApp();
 bool
 Initialize(HINSTANCE &hInstance, HINSTANCE &hPrevInstance, LPTSTR &lpCmdLine, int &nCmdShow);
 void
 DoMain();
};
SampleApp::SampleApp() {
};
SampleApp::~SampleApp() {
 AfxWinTerm();
}
bool
SampleApp::Initialize(HINSTANCE &hInstance, HINSTANCE &hPrevInstance, LPTSTR &lpCmdLine, int &nCmdShow) {
 if( !AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow) ) {
  ASSERT( "MFC failed to initialize!" );
  return false;
 }
}
SampleApp::DoMain() {
 .....
};
CWinApp theApp;
int WINAPI 
WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) {
 SampleApp cApp
 cApp.Initialize(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
 cApp.DoMain();
}
こんな感じです。

ku 2008/02/08(金) 13:41:57
> ご覧の通り、場所が表示されていないため特定できません。
ということなので、うまくいくのかわからないけど

ブレークポイントをはずして、
_crtBreakAlloc = 100478;
とか
_crtBreakAlloc = 100477;
などと書いて、デバッグ状態で起動したら分かりませんかね

通りすがりのニート  2008/02/09(土) 00:04:38
CWinApp theApp;←何やってるかわかっていますか?

ソースの感じから、MFC以前にwindowsプログラム自体を理解していないように思います。

WinMain()から作成してるなら、MFCを使用しないことをお勧めします。

あと、ASSERTの使い方が変ですよ。

yoh2 [E-Mail] 2008/02/09(土) 00:47:45
> あと、ASSERTの使い方が変ですよ。

書くとしたらこうですね。
ASSERT( !"MFC failed to initialize!" );
//      ↑偽と評価するために文字列頭に ! を付ける。

閃人 2008/02/13(水) 12:48:07 <中級者>
回答ありがとうございます。

> CWinApp theApp;←何やってるかわかっていますか?
分かっていたつもりなのですが、再度勉強しなおしました。

> WinMain()から作成してるなら、MFCを使用しないことをお勧めします。
私もできればやりたくないのですが、今回は仕方が無いのです。

> ASSERT( !"MFC failed to initialize!" );
> //      ↑偽と評価するために文字列頭に ! を付ける。
確かにそうですね。参考にさせていただきます。


解析を続けていたところ、メモリリークの原因が分かってきました。

実際は、メモリリークしていないようです。
NaviMainがあるExe以外に、当方で作っているDLLがあるのですが、
NaviMainが終了した後に、DLLが解放される為、
NaviMain終了時にメモリリークチェックをすると、DLL側のメモリが解放されていないため、メモリリークが表示されているように感じます。

DLLは静的リンクしているのですが、
NaviMainの終了より前に解放ってできないのでしょうか?
それても、定石のような方法があるとか?
ググッて見たものの、ベストな方法が見つかりませんでした。

ちなみに、DLLには、手が加えられません。
以上
宜しくお願い致します。

閃人 2008/02/13(水) 12:54:47 <中級者>
NaviMain -> WinMain
間違えました。

閃人 2008/02/13(水) 14:37:43 <中級者>
int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag(tmpDbgFlag);

これを試すと、やっぱり出るんですよねー。。。
これって、ちゃんとDLLも解放した後に呼んでるんですかね?

通りがかり 2008/02/15(金) 04:00:23
質問が酷すぎる。本当に中級者?

DLLの動的リンクもわからないなら、VBで作れば?
DLL内でメモリリークが出てるなら、
静的リンクだろうが、動的リンクだろうが解決にはならないだろうけど。

名無し 2008/02/15(金) 05:04:42
前から思ってけど、何を持って中級者と判断するかはむずかしーねー。
Windowsプログラムは何しろ量が多すぎるから、全分野において上級者になるには10年や20年は掛かりかねないし、そこから逆算してどこまでを中級者をするかも難しい。
一部は詳しくても他は全然知らないという人間も多いだろし、すると
>CWinApp theApp;←何やってるかわかっていますか?
>ソースの感じから、MFC以前にwindowsプログラム自体を理解していないように思います。
みたいな話になってくる。中級者ってなんだろうね。

中級者…ね。ふーん。 2008/02/15(金) 09:58:38
>DLL側のメモリが解放されていないため、メモリリークが表示されているように感じます。
>ちなみに、DLLには、手が加えられません。

…原因が他の所にある(上記発言より)のなら、直し様が無い。

wclrp ( 'o') 2008/02/15(金) 23:18:50
長時間プログラムを実行していると
徐々にメモリリークが増えていくなら問題だけど
実際はメモリリークしていなくてDLLで解放できているはずって
いうなら気にしなくていいんじゃない。
いいことじゃないけど
プログラム終了すればほっといても解放されるし。

EXEとDLLで異なるDLLのランタイムライブラリとリンクしているか
スタティックリンクのランタイムライブラリとリンクしているなら
EXEがmallocやnewで確保したメモリをDLLで解放するとかは正しく動作しないよ。

毎週金曜日はポイント最大3倍!さらに4倍のチャンスも!

Programming Library