質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.04%

MFC(VS2015) + boost::Logで終了時に例外が発生

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,703

PineMatsu

score 3379

mfcアプリにboost::Logを組み込もうとしています。ログの保存などロギングに関する動作は問題ないのですが、アプリ終了時に一般保護例外が出てしまいます。(boostのバージョンは1.60.0)
例外が発生する箇所は、boostのsrw_lock.hppの

BOOST_FORCEINLINE VOID_ AcquireSRWLockExclusive(PSRWLOCK_ SRWLock)
{
    ::AcquireSRWLockExclusive(reinterpret_cast< ::_RTL_SRWLOCK* >(SRWLock));
}


で、::AcquireSRWLockExclusiveでSRWLockが0xddddddddと不正なアドレスを示しているために一般保護例外になっているようなのです。Visual Studioの呼出履歴を見ると

LPCExplorer.exe!boost::detail::winapi::AcquireSRWLockExclusive(boost::detail::winapi::_RTL_SRWLOCK * SRWLock) 行 82    C++
LPCExplorer.exe!boost::log::v2s_mt_nt6::aux::light_rw_mutex::lock() 行 67    C++
LPCExplorer.exe!boost::log::v2s_mt_nt6::aux::exclusive_lock_guard<boost::log::v2s_mt_nt6::aux::light_rw_mutex>::exclusive_lock_guard<boost::log::v2s_mt_nt6::aux::light_rw_mutex>(boost::log::v2s_mt_nt6::aux::light_rw_mutex & m) 行 102    C++
LPCExplorer.exe!boost::log::v2s_mt_nt6::core::remove_all_sinks() 行 511    C++


となっていて、終了時のboost.Logの後片付けで発生しているんだろうなぁまでは分かるんですが、それ以上はboost.Logの内部の仕組みをよく理解してないので追いきれてません。

どなたか、わかる方がいれば助かります。
ちなみに、ロガーはクラス化してあって、MFC以外のアプリ(Win32)では問題なく動作しています。

MFC + Boost.Logは無理なんでしょうかね?(MFCを辞めたいのは山々なんですが、このアプリをWin32で作りなおすのは時間とコストを考えると事実上不可能に近い・・・)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+3

Boost.Logドキュメントに Why my application crashes on process termination when file sinks are used? という情報がありました。

MFCアプリケーション終了の直前に、下記コードを呼び出してみたらどうでしょう?

logging::core::get()->remove_all_sinks();

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/02/02 09:01

    remove_all_sinks()はクラスのデストラクタで呼び出していたのですが、呼出履歴にあるようにこのremove_all_sinks()の中で例外が起こっているようでした。
    それで、ロガーのインスタンスをメインクラスのメンバーではなく、グローバルでポインターとして定義し、new,deleteで生成と破棄をメインの最初と最後に置くようにしたところ、例外は出なくなりました。
    しかし、終了時にロガークラス内でsink追加した時に指定した日付指定文字列やベースファイル名などがメモリーリークするようになりました。むむむ・・・。
    なんかもう少しのような気がするのですが・・・。

    キャンセル

  • 2016/02/02 11:08

    今回の問題は、main関数を抜けた後にremove_all_sinks()が呼ばれたことが原因のように_見えます_。グローバルロガーを生ポインタ保持すると別の問題が出そうですし、単にmain関数終了直前にremove_all_sinks()呼び出しで良いかもしれません(実動作させたわけじゃないので自信ナシ)

    参考:http://www.boost.org/doc/libs/1_60_0/libs/log/doc/html/log/rationale/init_term_support.html

    「終了時のメモリリーク」はどのように検出されたのでしょう?
    プロセス終了時の内部メモリリーク(≠外部リソースのリーク)であれば実用上問題はありませんから、無視するという戦略もあります。

    キャンセル

  • 2016/02/02 11:38

    なるほど、ありがとうございます。デストラクタではなくremove_all_sinks()の積極的な呼び出しをやってみます。
    メモリリークは、MFCで通常やっているやり方です。外部リソースではないので仰るとおり実用上は問題なさそうですね。

    キャンセル

  • 2016/02/02 13:24

    生ポインター保持をやめて、ExitInstance()でremove_all_sinks()を呼び出すようにしたら例外もメモリリークも発生しなくなりました。

    ありがとうございました!助かりました。

    キャンセル

+3

こんにちは。ちょっと長くなるので、こちらから。

ソースを追いかけてみたのですが、私もyohhoyさんの見解に賛成です。

::AcquireSRWLockExclusiveでSRWLockが0xddddddddと不正なアドレスを示している

このSRWLockはlog\core\core.hpp内で定義されているcoreクラスの実体(m_impl)のメンバm_mutexです。
超絶ややこしくて読みそこなっているかも知れませんが、m_implはstaticに獲得されるシングルトンでした。
これは、main()関数終了後制御できないタイミングで自動的に解放されます。・・・①

そして、PineMatsuさんのロガークラスをMFCのクラス内で獲得/解放していた場合、MFCのインスタンスが解放されるタイミングで解放されます。
そのMFCのインスタンスがどこで獲得されているのか分かりませんが、もし、グローバル変数的にstaticに獲得されている場合、main()関数終了後の制御できないタイミングで解放されます。②

つまり、①→②の順序で解放された場合、②のタイミングでremove_all_sinks()が呼ばれますが、①にて既にm_implの実体が解放されているため、冒頭の例外が発生するのだと思います。

対策は、yohhoyさんの言う通り、下記が正解のように感じます。(私も実動作させたわけではないので確信まではないですが。)

単にmain関数終了直前にremove_all_sinks()呼び出しで良いかもしれません(実動作させたわけじゃないので自信ナシ)

なお、MFCのインスタンス解放処理内で、ロガークラスがboost::logを操作すると何か問題が起きる可能性を懸念します。デストラクタ内では後始末だけに限定し、ログを取るようなことはしない方がよいかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/02/02 13:32

    解析ありがとうございます。(よくあんなややこしいのを読めますね。尊敬します)
    ロガークラスはCWinAppを継承したクラスで保持しています。
    yohhoyさんのコメントにも書きましたが、ExitInstance()でremove_all_sinks()を呼ぶようにしてやったら、メモリリークも含めてうまくいきました。
    これで、Win32アプリとMFCアプリ両方で共通に使えるロガークラスが出来そうです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる