MFCがメモリーリーク?

解決


閃人  2008-02-07 19:13:54  No: 67457  IP: [192.*.*.*]

お世話になります。閃人です。

以下の構成でアプリを作っています。
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  No: 67458  IP: [192.*.*.*]

>CSampleDialog pcDialog = new CSampleDialog()
はどこでdeleteしていますか?
(CSampleDialog::OnNcDestroy?)

編集 削除
閃人  2008-02-07 20:56:00  No: 67459  IP: [192.*.*.*]

>>CSampleDialog pcDialog = new CSampleDialog()
>はどこでdeleteしていますか?
>(CSampleDialog::OnNcDestroy?)
CSampleDialogを管理しているクラスがいますので、そのクラスのデストラクタでdeleteしています。

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

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

編集 削除
通りすがりのニート  2008-02-08 03:29:27  No: 67460  IP: [192.*.*.*]

いまいち質問がわからないで、直感ですが、AfxWinTerm()が必要なのでは?
だめでも、CWinAppクラスのデストラクタを追っていけば、わかるような気がします。

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

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

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

編集 削除
閃人  2008-02-08 11:12:46  No: 67461  IP: [192.*.*.*]

アドバイスありがとうございます。

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


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

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

編集 削除
閃人  2008-02-08 11:19:17  No: 67462  IP: [192.*.*.*]

===================================
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  No: 67463  IP: [192.*.*.*]

> ご覧の通り、場所が表示されていないため特定できません。
ということなので、うまくいくのかわからないけど

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

編集 削除
通りすがりのニート  2008-02-09 00:04:38  No: 67464  IP: [192.*.*.*]

CWinApp theApp;←何やってるかわかっていますか?

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

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

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

編集 削除
yoh2  2008-02-09 00:47:45  No: 67465  IP: [192.*.*.*]

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

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

編集 削除
閃人  2008-02-13 12:48:07  No: 67466  IP: [192.*.*.*]

回答ありがとうございます。

> 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  No: 67467  IP: [192.*.*.*]

NaviMain -> WinMain
間違えました。

編集 削除
閃人  2008-02-13 14:37:43  No: 67468  IP: [192.*.*.*]

int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag(tmpDbgFlag);

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

編集 削除
通りがかり  2008-02-15 04:00:23  No: 67469  IP: [192.*.*.*]

質問が酷すぎる。本当に中級者?

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

編集 削除
名無し  2008-02-15 05:04:42  No: 67470  IP: [192.*.*.*]

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

編集 削除
中級者…ね。ふーん。  2008-02-15 09:58:38  No: 67471  IP: [192.*.*.*]

>DLL側のメモリが解放されていないため、メモリリークが表示されているように感じます。
>ちなみに、DLLには、手が加えられません。

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

編集 削除
wclrp ( 'o')  2008-02-15 23:18:50  No: 67472  IP: [192.*.*.*]

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

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

編集 削除
閃人  2008-02-20 10:08:33  No: 67473  IP: [192.*.*.*]

皆々様、ご回答ありがとうございます。

多くの方が、中級者について触れていますが、
多少難しい方法でも回答をいただければ試すことが可能という意味で中級者にしてあります。
他の方が書かれている通り、全てのことに対して中級者の人はいないですし、皆様にとっては初歩的な質問だったとしても、調べないで聞いているわけではありませんので、その辺はご了承いただけたらと思っております。
本当に困ったときのみ、こちらを利用させていただいております。

wclrp ( 'o') さん
> EXEがmallocやnewで確保したメモリをDLLで解放するとかは正しく動作しないよ。
この仕組みが使われている時点で、今回の問題はどうにもできないのですね。
そうなのでは無いかなとは思っていたのですが、何か勘違いや別の方法があるのかもと思い、投稿させて頂きました。
ありがとうございます。

今回の件は、対処方法は現時点では無いが、問題が無い為そのままにします。
皆様、たくさんのアドバイスありがとうございました。

編集 削除