none
CWinAppのコンストラクタ内でASSERT() RRS feed

  • 質問

  • VS2008 の90日試用版が使えるようになったということで、VS6のプロジェクトを試しに移行してみました。

    スコープの調整や一部の型の64ビット化など、コンパイルを通すべき障害はそこそこに乗り越えていざDEBUG版で実行してみたところ、MFCを含む DLL/EXEにインスタンスとして加えているCWinApp(またはCWinAppの派生クラス)のコンストラクタ内でASSERT()が発生してしまいました。

    場所は appcode.cpp の CWinApp::CWinApp() 内で、
      ...
      // initialize CWinThread state
      AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
      ENSURE(pModuleState);
      AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
      ENSURE(pThreadState);
      ASSERT(AfxGetThread() == NULL);     ← ここと.
      pThreadState->m_pCurrentWinThread = this;
      ...
      // initialize CWinApp state         ↓ ここです.
      ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
      pModuleState->m_pCurrentWinApp = this;
      ...

    コールスタックの状況です。グローバルに置いているCWinAppなのでStartupから呼び出しされています。
    > mfc90ud.dll!CWinApp::CWinApp(const wchar_t * lpszAppName=0x00000000)  行 387 + 0x1e バイト    C++
    > Foo.exe!CwFooApp::CwFooApp()  行 32 + 0xf バイト    C++
    > Foo.exe!`dynamic initializer for 'theApp''()  行 39 + 0xd バイト    C++
    > msvcr90d.dll!_initterm(void (void)* * pfbegin=0x0040a314, void (void)* * pfend=0x0040a41c)  行 903    C
    > Foo.exe!__tmainCRTStartup()  行 497 + 0xf バイト    C
    > Foo.exe!wWinMainCRTStartup()  行 399    C

    見たところ、pModuleState->m_pCurrentWinApp が本来は(NULL)のはずなのに、先にロードされているMFCを含むDLLのCWinAppを引き継げてしまっているのが、問題が無い場合との違いなようです。
    また問題のソリューションでは20プロジェクトほどDLL(全てC++/共有MFC/UNICODE/DEBUG)として含むのですが、このASSERT()が出る場合と出ない場合があり更に混乱しています。
    検索をしてみたところ VS2003 や VS2005 でも同じような現象は見られるようですが、これと言った解決策は見つけられていません。
    対処方法やチェックポイントなどございましたらご教授願います。

    以上、よろしくお願いいたします。

    2008年1月10日 2:54

回答

  •  arpus さんからの引用
    とくにダイアログなどのリソースを含むDLLで、リソースIDの重複などを回避して正しくリソーステーブル参照するための切り替えで、
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    を記述しているのですが、これには各DLL毎にひとつだけCWinAppが必要だと今までは考えていました。


    確かに仰るとおりです。というよりも、レギュラーDLL では CWinApp がある方が普通ですね。
    拡張 DLL に偏った理解で混乱させてしまいました。申し訳ありません。

    少し調べてみたのですが、
    _AFX_CMDTARGET_GETSTATE の定義を見ると _AFXDLL の有無で動作が異なっています。
    もし、このマクロが正しく動作しなかったとしたら(_AFXDLL の定義が何らかの原因で狂ってしまったとしたら)
    以降の処理では正しくない pModuleState に対する書き込みが行われてしまいます。

    全て共有MFCとのことですので、まさかそんなことは無いと思いますが、
    念のため
    _AFX_CMDTARGET_GETSTATE の挙動を確認してみてはいかがでしょうか。

    2008年1月10日 16:33

すべての返信

  • 私は「CWinApp はメインスレッドを意味していて、アプリケーションに1つだけ存在するもの」という風に理解しています。

    ですから、

     arpus さんからの引用
    見たところ、pModuleState->m_pCurrentWinApp が本来は(NULL)のはずなのに、先にロードされているMFCを含むDLLのCWinAppを引き継げてしまっているのが、問題が無い場合との違いなようです。


    この部分に違和感を覚えます。

    先にロードされている DLL が CWinApp を持っていること自体に問題があるのではないでしょうか?
    DLL 側にある CWinApp を片っ端から取り除いてみてはいかがでしょう。

    # VS6 で動いているとのことですから、見当違いでしたらすみません。

    2008年1月10日 4:18
  •  zakio さんからの引用

    先にロードされている DLL が CWinApp を持っていること自体に問題があるのではないでしょうか?
    DLL 側にある CWinApp を片っ端から取り除いてみてはいかがでしょう。


    ご意見をありがとうございます。
    私はCWinApp(またはその派生クラス)はアプリケーションというよりモジュール(EXEやDLL)のインスタンス単位に必要と理解しておりました。
    とくにダイアログなどのリソースを含むDLLで、リソースIDの重複などを回避して正しくリソーステーブル参照するための切り替えで、
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    を記述しているのですが、これには各DLL毎にひとつだけCWinAppが必要だと今までは考えていました。

    CWinAppで便宜上置いているような部分は取り除き可能(実際やってみてASSERT()自体は回避できています)なのですが、派生させてInitInstance()とか使っている部分もあるので全部をきちんと取り除くのはちょっと大変です...

    2008年1月10日 9:30
  •  arpus さんからの引用
    とくにダイアログなどのリソースを含むDLLで、リソースIDの重複などを回避して正しくリソーステーブル参照するための切り替えで、
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    を記述しているのですが、これには各DLL毎にひとつだけCWinAppが必要だと今までは考えていました。


    確かに仰るとおりです。というよりも、レギュラーDLL では CWinApp がある方が普通ですね。
    拡張 DLL に偏った理解で混乱させてしまいました。申し訳ありません。

    少し調べてみたのですが、
    _AFX_CMDTARGET_GETSTATE の定義を見ると _AFXDLL の有無で動作が異なっています。
    もし、このマクロが正しく動作しなかったとしたら(_AFXDLL の定義が何らかの原因で狂ってしまったとしたら)
    以降の処理では正しくない pModuleState に対する書き込みが行われてしまいます。

    全て共有MFCとのことですので、まさかそんなことは無いと思いますが、
    念のため
    _AFX_CMDTARGET_GETSTATE の挙動を確認してみてはいかがでしょうか。

    2008年1月10日 16:33
  • こんにちは、フォーラム オペレータ大久保です。

     

    zakio さん、アドバイスありがとうございます!

     

    arpus さん、その後いかがでしょうか?

    アドバイスをくださった zakio さんの回答に回答済みチェックをつけさせていただきましたが、もし不適切でしたら解除もできますのでご確認ください。

    追加の質問等あれば、遠慮なく投稿してくださいね!よろしくお願いします。

    2008年1月15日 7:25
  • zakioさん、返答が遅くなり申し訳ありません。

    ご指摘の箇所を含めトレースしたりオプションを変えたり、いろいろとやってみたのですが芳しい結果は得られませんでした...
    今後はVS6とVS2008の間を埋めるVS2005を使っても検証してみたいと思います。
    何か進展がありましたらまた投稿したいと考えております。
    では。
    2008年1月16日 1:10
  • ご無沙汰しております。
    あれから業務の間を縫ってVS6,VS2005,VS2008を行き来まして解決に至りました。
    分かってしまえば単なる私のツール操作の把握不足でした。
    各プロジェクトオプションのプリプロセッサに_USRDLL定義が部分的に落ちているモノがあり、
    VS6ではそれでも意図したとおりには動いていました(_AFXDLL定義のおかげ?)が、
    VS2005/2008では許容されないようです。
    また、VS2005でプリプロセッサを設定するときに複数プロジェクトをまとめて選択して
    設定するような操作をしていたのですが、共通設定と異なる設定との表示や反映の挙動を
    勘違いしており、意図した設定がうまく反映できていなかったのも敗因です。
    結局の所、丁寧にひとつひとつのプロジェクトを定義してあげていればすぐ気がついたのに...
    まさしく orz... な気分です。

    お騒がせしました。

    2008年2月6日 2:08