none
MFCを使うDLLのプロジェクトの _tmain ついて RRS feed

  • 質問

  • MFCを使うDLLのプロジェクトで、Visual Studioが作成する int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) 関数はDLLのビルドに必要でしょうか?_tmain 関数をコメントアウトしてもDLLのビルドに成功します。
    DLLのプロジェクトから削除しても問題ないでしょうか?
    _tmain 関数の前の行に CWinApp theApp; があるので、必要な処理はCWinAppがやっているのかな?

    プロジェクトの作成方法
    Win32コンソールアプリケーション
    アプリケーションの種類をDLLにする
    共通ヘッダーファイルを追加のMFCをチェックする

    よろしくお願いします。

    2015年2月26日 1:12

回答

  • ではその辺りの仕組みを説明します。

    まずプロジェクトの種類に応じて必須となる関数名が定まります。/ENTRY (エントリ ポイント シンボル)に説明がありますが、DLLプロジェクトを選択するとリンクオプション /DLL が指定されるため _DllMainCRTStartup が必須となります。参考までにDLL以外の場合は/SUBSYSTEMの値でmainCRTStartup or wmainCRTStartup、 WinMainCRTStartup or wWinMainCRTStartupになります。

    ここに挙げた関数はCRTソースコード(C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\srcディレクトリ等)に含まれています。DLL以外はcrt0.cにあり、この中で _tmain()や_tWinMain()を呼び出しています。対してDLLであればdllcrt0.cに書かれていて、この関数内で DllMain() 関数を呼び出しています。ですので開発者は_tmain()、_tWinMain()、DllMain()のいずれか適切な関数を記述する必要があります。
    # この時点でDLLプロジェクトに於いて_tmain()が不要となる理由がはっきりするわけです。

    次にMFCですが、MFCとリンクする際はMFC と動的にリンクされるレギュラー DLLであってもMFC と静的にリンクされるレギュラー DLLであっても_USRDLLマクロを定義します。これによってafx.h内で

    // force inclusion of DLLMODUL.OBJ for _USRDLL
    #ifdef _USRDLL
    #pragma comment(linker, "/include:__afxForceUSRDLL")
    #endif
    が有効になり、MFCソースコード(C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\atlmfc\src\mfcディレクトリ等)に含まれているdllmodul.cppとリンクされます。dllmodul.cppには__afxForceUSRDLLが定義されると共にDllMain()関数も含まれています。このため、MFC DLLを使用する場合に開発者はDllMain()を記述してはいけない、という仕組みになっています。

    2015年2月26日 7:58
  • まぁ、これを仕様だと言われてしまえば仕方ないのですが、
    まず、「新しいプロジェクト」で、「Win32」の「Win32コンソールアプリケーション」を選択したのですから、
    この段階で、DLLは関係ないし、ウインドウも作らない事を選択したわけです。

    にもかかわらず、

    1.次に表示される「Win32アプリケーションウィザード」の「アプリケーションの種類」で、
     「Windowsアプリケーション」や「DLL」が選択できてしまうという、おかしな動きになっている。

    わけですね。

    2.従って、本ケースの様に、上のDLGで、当初の「Win32コンソールアプリケーション」用プロジェクトを作成する
     という意志を「覆して」DLLを選択してみる実験。

    のような、MSさんが想定していない操作を行うと、

    A.当初の「Win32コンソールアプリケーション」の意志が尊重され、
     ウィザードはそれに必要な_tmain()のコードを実装した。
     当初あなたはそう指定しておりました(DLG上での設定は無視しました)。

    という動作になっていると推測できます。ところが一方で、

    B.コンパイルオプション等は、もちろん「DLL」+「MFCを使用する」用に設定しときました。
     (DLG上でそう指定しましたよね)

    となり、両者が乖離してしまったのではないかと推測するわけです。
    DLLプロジェクトに、_tmain()が必要ない理由は佐祐理さんの詳細な説明にある通りです。

    2015年2月26日 8:59
  • 佐祐理さん
    仲澤@失業者さん
    詳しい解説をしていただきありがとうございます。

    最初の質問の結論は、「_tmain 関数は削除しても良い」とします。
    根拠は返信を読めばわかると思います。

    また、プロジェクト作成時のテンプレートを Visual C++ → MFC → MFC DLL
    にすると適切なソースが作成されることも挙げておきます。
    • 回答としてマーク RYO_SAN 2015年2月27日 3:12
    2015年2月27日 3:12

すべての返信

  • _tmain()の中にメッセージ出力などを埋め込めば、呼び出されていないことが確認できると思います。
    • 回答の候補に設定 佐祐理 2015年2月27日 3:17
    • 回答としてマーク AzuleanMVP, Moderator 2015年2月27日 14:37
    • 回答としてマークされていない RYO_SAN 2015年3月3日 0:57
    • 回答の候補の設定解除 RYO_SAN 2015年3月3日 0:58
    2015年2月26日 1:26
  • プロジェクトウィザードで、なぜこのような選択が可能なのか、
    いまいち謎なのですが(VS2003の頃はできなかったはず)、
    さて、まずは、次のことを明確にすべきだと考えられます。

    1.コンソールアプリケーションのEXEが作りたい。
     テンプレート --> 「Win32」の「Win32コンソールアプリケーション」
      アプリケーションの種類 --> 「コンソールアプリケーション」

    2.MFCを使用する、DLLが作りたい。
     テンプレート --> 「MFC」の「MFC DLL」

    のどちらを希望してますでしようか(質問)。


    2015年2月26日 2:33
  • 回答ありがとうございます。

    説明が足りなかったので補足します。

    1.やりたいことはDLLを作成したい。

    2.DLLが提供する関数の引数や戻り値にはMFCのオブジェクトは使わない。(int やchar*などを使う)

    3.DLLが提供する関数の実装にはMFCを使う。

    これで意図は伝わるでしょうか?

    2015年2月26日 2:46
  • デバッグで、ブレークポイントを置いて_tmainが呼び出されていないことは確認しました。

    この事から 質問したDLLのプロジェクトでは、自動的に生成される _tmain 関数はいらないのでは?と疑問に思ったのですが、呼び出されない理由(またはVisualStudioがこのコードをデフォルトで出力する理由)まではわからなかったので質問させていただきました。

    2015年2月26日 5:44
  • >1.やりたいことはDLLを作成したい。

    了解しました。

    >2.DLLが提供する関数の引数や戻り値にはMFCのオブジェクトは使わない。(int やchar*などを使う)

    これは、運用上その様にコードすることで良いのではないでしょうか。

    >3.DLLが提供する関数の実装にはMFCを使う。

    先の発言でもふれましたが、この場合は、テンプレートで「MFC」を選択し、
    「MFC DLL」を選択してプロジェクトを生成してください。
    これを行うことに、何か他の問題を抱えてますでしょうか
    (抱えている問題によっては他の選択肢もありえます)。

    2015年2月26日 6:59
  • ではその辺りの仕組みを説明します。

    まずプロジェクトの種類に応じて必須となる関数名が定まります。/ENTRY (エントリ ポイント シンボル)に説明がありますが、DLLプロジェクトを選択するとリンクオプション /DLL が指定されるため _DllMainCRTStartup が必須となります。参考までにDLL以外の場合は/SUBSYSTEMの値でmainCRTStartup or wmainCRTStartup、 WinMainCRTStartup or wWinMainCRTStartupになります。

    ここに挙げた関数はCRTソースコード(C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\srcディレクトリ等)に含まれています。DLL以外はcrt0.cにあり、この中で _tmain()や_tWinMain()を呼び出しています。対してDLLであればdllcrt0.cに書かれていて、この関数内で DllMain() 関数を呼び出しています。ですので開発者は_tmain()、_tWinMain()、DllMain()のいずれか適切な関数を記述する必要があります。
    # この時点でDLLプロジェクトに於いて_tmain()が不要となる理由がはっきりするわけです。

    次にMFCですが、MFCとリンクする際はMFC と動的にリンクされるレギュラー DLLであってもMFC と静的にリンクされるレギュラー DLLであっても_USRDLLマクロを定義します。これによってafx.h内で

    // force inclusion of DLLMODUL.OBJ for _USRDLL
    #ifdef _USRDLL
    #pragma comment(linker, "/include:__afxForceUSRDLL")
    #endif
    が有効になり、MFCソースコード(C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\atlmfc\src\mfcディレクトリ等)に含まれているdllmodul.cppとリンクされます。dllmodul.cppには__afxForceUSRDLLが定義されると共にDllMain()関数も含まれています。このため、MFC DLLを使用する場合に開発者はDllMain()を記述してはいけない、という仕組みになっています。

    2015年2月26日 7:58
  • Visual C++ → MFC → MFCDLL を選びDLLの種類を「共有MFC DLLを使用する通常のDLL」で
    プロジェクトを作成したところ、このMFC DLLで作成したプロジェクトには_tmain関数はありませんでした。

    そこで、最初の質問した方法のプロジェクトと C/C++オプション、リンカーオプションを比較してみたら、(いくつか異なるオプションがありましたが)この2つのプロジェクトは同じであるとわかりました。(自分の判断ですが。。。)

    Visual Studioのウィザードが出力する最初のソースコードに、MFC を使うDLL場合、不要になる _tmain が残っているだけと考えれば、削除しても問題ないという結論に達します。

    あってますでしょうか?

    2015年2月26日 8:00
  • まぁ、これを仕様だと言われてしまえば仕方ないのですが、
    まず、「新しいプロジェクト」で、「Win32」の「Win32コンソールアプリケーション」を選択したのですから、
    この段階で、DLLは関係ないし、ウインドウも作らない事を選択したわけです。

    にもかかわらず、

    1.次に表示される「Win32アプリケーションウィザード」の「アプリケーションの種類」で、
     「Windowsアプリケーション」や「DLL」が選択できてしまうという、おかしな動きになっている。

    わけですね。

    2.従って、本ケースの様に、上のDLGで、当初の「Win32コンソールアプリケーション」用プロジェクトを作成する
     という意志を「覆して」DLLを選択してみる実験。

    のような、MSさんが想定していない操作を行うと、

    A.当初の「Win32コンソールアプリケーション」の意志が尊重され、
     ウィザードはそれに必要な_tmain()のコードを実装した。
     当初あなたはそう指定しておりました(DLG上での設定は無視しました)。

    という動作になっていると推測できます。ところが一方で、

    B.コンパイルオプション等は、もちろん「DLL」+「MFCを使用する」用に設定しときました。
     (DLG上でそう指定しましたよね)

    となり、両者が乖離してしまったのではないかと推測するわけです。
    DLLプロジェクトに、_tmain()が必要ない理由は佐祐理さんの詳細な説明にある通りです。

    2015年2月26日 8:59
  • 佐祐理さん
    仲澤@失業者さん
    詳しい解説をしていただきありがとうございます。

    最初の質問の結論は、「_tmain 関数は削除しても良い」とします。
    根拠は返信を読めばわかると思います。

    また、プロジェクト作成時のテンプレートを Visual C++ → MFC → MFC DLL
    にすると適切なソースが作成されることも挙げておきます。
    • 回答としてマーク RYO_SAN 2015年2月27日 3:12
    2015年2月27日 3:12
  • 回答としてマークされた投稿は、ログインしていない場合などに、質問の直下に表示されるようになります。
    質問に対する直接的な結論もそうですが、その根拠も併せて表示されるべきだと思いましたので、回答としてマークを増やさせていただきました。

    ご自身のまとめをマークされることを止めるつもりはありませんが、参考になったもの、根拠となるものが含まれる投稿にはぜひ回答としてマークをつけることをご検討ください。

    2015年2月27日 14:39
    モデレータ