トップ回答者
VC++ exeファイルのサイズについて

質問
-
お世話になります。VisualStudio2017にて作成されるexeファイルのサイズについてご教示ください。
●質問
VC++6.0で製造したソースを、VC++2017へ移行しました。
作成されたexeファイルを確認したところ、6.0では600KB程度でしたが、2017(.Net)では4800KBに膨れ上がりました。
6.0と同サイズとまではいかなくても、せめて2000KB以内には収めたいと思っているのですが、何か方法はないでしょうか?
なお、exeファイルは64bit環境で動作するように、ソリューションプラットフォームを"x64"に設定しています。
●試したこと
プロジェクトのプロパティから最適化を行うように設定してみたのですが、ファイルサイズは変わりませんでした。
●環境
Windows10、VisualStudio2017
回答
-
ライブラリを静的リンクしてませんでしょうか。
コンパイルオプションの「ランタイムライブラリ」が
「/MT」(マルチスレッド)になっていたら
「/MD」(マルチスレッドDLL)にします。
その他では、1.インライン化されないように、なるべく
クラスメンバ関数の実装は、*.cppに移動する。
2.文字列リテラル等はリソースにする。
(自動的に圧縮してくれるオプションもありますけど、念のため)
3.デバッグ情報は無しにする(追記)上記3項については、佐祐理さんから「効果はない」とのご指摘をうけています。併せて検討してください。
などは、試してみる価値があるかもしれません。
すべての返信
-
ライブラリを静的リンクしてませんでしょうか。
コンパイルオプションの「ランタイムライブラリ」が
「/MT」(マルチスレッド)になっていたら
「/MD」(マルチスレッドDLL)にします。
その他では、1.インライン化されないように、なるべく
クラスメンバ関数の実装は、*.cppに移動する。
2.文字列リテラル等はリソースにする。
(自動的に圧縮してくれるオプションもありますけど、念のため)
3.デバッグ情報は無しにする(追記)上記3項については、佐祐理さんから「効果はない」とのご指摘をうけています。併せて検討してください。
などは、試してみる価値があるかもしれません。 -
過去にも実行ファイルのサイズがVS2017で大きくなるというスレッドがあったので参考用リンク。
-
回答ありがとうございます。
「ランタイムライブラリ」が「/MT」(マルチスレッド)になっていましたので「/MD」(マルチスレッドDLL)に変更してリビルドしてみたところ、以下のようなエラーが発生しました。
「#error: Building MFC application with /MD[d] (DRT dll version) requires MFC Shared dll version.
Please #define _AFXDLL or do not use /MD[d]」
以下のURLを参考に、ランタイムライブラリの設定は「/MD」のままで「プロパティ > 構成プロパティ > 全般 > MFCの使用」を「共有DLLでMFCを使う」に変更してリビルドしてみたところ、exeファイルは387KBになりました。
参考URL:http://gomi-box.hatenablog.com/entry/20101006/1286355611
VB6時代のexeよりさらに小さくなるという結果に、驚きと「このexeファイル大丈夫だろうか」という不安が・・・。 (^-^;
今回変更したプロパティの内容を確認しつつ、exeファイルも検証してみたいと思います。
ありがとうございました。
-
補足です。
「共有DLLでMFCを使う」に変更されたということですが、こちらの設定で作成された exe は実行に別途 MFC の DLL が必要となります。Visual Studio 等が入っていない環境で動かす場合は、下記の Visual C++ ランタイムライブラリをインストールする必要があります。
https://support.microsoft.com/ja-jp/help/2977003/the-latest-supported-visual-c-downloads
-
1.インライン化されないように、なるべく
クラスメンバ関数の実装は、*.cppに移動する。
2.文字列リテラル等はリソースにする。
(自動的に圧縮してくれるオプションもありますけど、念のため)
3.デバッグ情報は無しにするどれも見当違いであまりEXEファイルサイズの削減には貢献しないと思います。
1.について、Visual C++では/LTCG (Link-time Code Generation)によりコンパイル時の定義場所に関わらず、実行ファイル全体でインライン展開を試みます。
2.について、/GF (Eliminate Duplicate Strings)により文字列リテラルは自動的に共有・削減されます。
3.について、/Ziもしくは/ZIを使っていればデバッグ情報はpdbファイルに書かれ、EXEファイルにはpdbファイルパスが書かれるだけ、数十バイトの増分のはずです。今はあまり使われていない/Z7を使うとEXEファイルが肥大化します。 -
オプションの設定によって、exeファイルのサイズに影響が出ますね・・・。
参考にさせて頂きたいのですが、どなたか以下の環境・設定で動いている実績がある方はいらっしゃいませんか?
●環境
Windows10(64bit)
●VisualStudioのバージョン
VisualStudio2017(ソリューションプラットフォームは"x64")
●VisualStudioのプロジェクトの設定
①「構成プロパティ > C/C++ > コード生成 > ランタイム ライブラリ」を「マルチスレッドDLL(/MD)」に設定。
②「構成プロパティ > 全般 > MFCの使用」を「共有DLLでMFCを使う」に設定。
(VC++のランタイムは別途インストール済)
-
-
手元のVS2017にはVC++2015とVC++2017が入れてあるので比較してみました。MFCダイアログアプリケーションで VC++2015: 450KB、VC++2017: 2500KB。
いろいろ睨めっこしたんですが、MFCのソースコードdlgcore.cppのCWnd::CreateDlgIndirect()がAfxRegisterMFCCtrlClasses()を呼び出していることが原因でした。
適当なcppファイルに
void AfxRegisterMFCCtrlClasses() {}
を書き加えたところ、不要なライブラリをリンクしなくなり、VC++2017: 434KB に削減できました。
# static link(/MT)が前提の話題で、dynamic link(/MD)には関係ありません。
-
とっちゃんさん、佐祐理さん、レスを頂きありがとうございます。
また、返信が遅くなり大変申し訳ございません。
●とっちゃんさん、「その設定で特に問題なく動いてます。」とのことで 安心しました。
ただ、やはり とっちゃんさんが仰っているとおりVC6と.Netのソースの互換性は100%ではないので、しっかりテストした方がよいですね…。
ありがとうございます。
●佐祐理さん、色々と調べていただきありがとうございます。
試してみた結果、最終的に2つ上で書いたVisualStudioのプロジェクトの設定に戻りました・・・。
(確認した手順)
①こちらの環境でも、dlgcore.cppがAfxRegisterMFCCtrlClasses()を呼び出していることを確認しました。
②cppに以下を書き加えました。
void AfxRegisterMFCCtrlClasses() {}
③プロジェクトの設定を、以下の状態に戻してリビルド。
・ランタイムライブラリは「/MT」(マルチスレッド)
・MFCの使用は「スタティックライブラリでMFCを使用する」
→リビルドした結果、以下のエラーが発生しました。
エラー LNK2005 "void __cdecl AfxRegisterMFCCtrlClasses(void)" (?AfxRegisterMFCCtrlClasses@@YAXXZ) は既に MainFrm.obj で定義されています。
エラー LNK1169 1 つ以上の複数回定義されているシンボルが見つかりました。
④MFCの使用を「共有DLLでMFCを使う」に変更してリビルド
→リビルドした結果、以下のエラーが発生しました。
エラー C1189 #error: Please use the /MD switch for _AFXDLL builds
⑤ランタイムライブラリを「/MD」(マルチスレッドDLL)に変更してリビルド。
→エラーはなく、リビルドは正常に終了しました。
なお、exeファイルのサイズは387KBでした。(上の回答で報告していたサイズと同じですね。)
(参考にしたURL)
https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/c83113d2-ff99-42e2-b4e9-4e7ae1719472/mfc124341247312479124861245112483124631252212531124631237712427?forum=vcgeneralja
③のエラーが発生した時に上記のURLを見つけたのですが、その中で話題にあがっていた
_AFX_NO_MFC_CONTROLS_IN_DIALOGS
は、VC6、.Netのどちらのソースでも見つかりませんでした。
-
> エラー LNK2005
MainFrm.cpp にも AfxRegisterMFCCtrlClasses() を書いていませんか?
VC6でもそうですが、同じ関数を複数定義すると内容が一致していても、重複定義でリンクエラーになります。
より正確(将来的にDLLを使う用意変更した場合を含む)を期すなら
#ifndef _AFXDLL void AfxRegisterMFCCtrlClasses() {} #endif
としておけば、DLLを使うように変更した場合にはいらない関数として定義されなくなります。とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
-
おかしいですね。
LNK2005 (とそれに付随するLNK1169) は、プロジェクト内のソースに同じ関数が複数ある場合に表示されるエラーなんですが?
AfxRegisterMFCCtrlClasses() は、どこかのソースファイル(アプリケーションクラスのソースあたりが一番無難)に1つだけ定義されるようにしておく必要があります。そのあたりをもう一度細かく確認してみてください。
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
-
今回作成したソリューションの中には見当たらなかったのですが、検索の方法が悪いのでしょうか?
なお、外部依存関係の afxctrlcontainer.h に宣言がありました。
・"AfxRegisterMFCCtrlClasses"をキーとして、検索する場所をソリューション全体として検索。
・ソリューションのソースファイルを、"AfxRegisterMFCCtrlClasses"をキーとしてテキストエディタでGrep検索。
VC++の知識が乏しく変なことを言っていたら申し訳ないのですが、
外部依存関係のヘッダーもリビルド時に読み込まれて
下記の宣言と 今回追加した宣言が重複しているのでしょうか・・・?
void AfxRegisterMFCCtrlClasses();
また、もう1点確認なのですが、
ご教示頂いた下記の内容をMainFrm.cppに書いたのですが、記載する場所が間違っているのでしょうか?
#ifndef _AFXDLL void AfxRegisterMFCCtrlClasses() {} #endif
お時間をとらせて申し訳ありません。教えて頂けますと幸いです。 -
https://blogs.msdn.microsoft.com/vcblog/2012/02/06/reducing-the-size-of-statically-linked-mfc-applications-in-vc11/
こちらのページを参考に stdafx.hに以下を追加してみたのですが、エラーは変わらずでした・・・。
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS
なお、以下の状態でリビルドしてみたところ、正常に終了してexeファイルが作成されました。
exeファイルのサイズは1867KBでした。
(ソースコード)
・上記の#defineの記述は残す。
・「void AfxRegisterMFCCtrlClasses() {}」はコメントアウト。
(プロジェクトの設定)
・ランタイムライブラリは「/MT」(マルチスレッド)
・MFCの使用は「スタティックライブラリでMFCを使用する」 -
いまいち理解されていないようなで、条件をまとめておきます。
- コンパイルオプション/MT(もしくは/MTd)を選択する
- 「スタティックライブラリでMFCを使用する」を選択する
- MFC関連のヘッダーファイル(afx.hやafxXXX.h)を#includeするよりも前に #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS を記述する
が基本の使い方です。その上で
- Visual Studio 2017 v15.8に限り、CMFCApplication2App theApp;などtheAppオブジェクトを定義しているcppファイル内にvoid AfxRegisterMFCCtrlClasses() {}を記述する
となります。15.7以前や今後リリースされる15.9以降ではこの記述は不要です。また記述するcppファイルは依存関係があるため、なるべく重要なコードが含まれているcppに記述する必要があります。これで動かない場合はそろそろリリースされるであろう15.9を待つのも一つの手かと。