exeファイルとCRTEternalWindowsexeファイルとCRTここでは、exeファイルのサイズを軽くする方法を項目別に説明します。本質的な部分を理解するためにも、様々なコードと概念を交えて説明することにします。非常に実用的な話題なので、是非とも理解してください。プログラムの開発を手助けするための標準関数を用意しています。printfやstrlen、fopenなどの一連の関数の実装を指しています。10.0/VC/crt/srcのパスから確認することができます。exeファイルのサイズを軽くするためには、CRTにダイナミックリンクするところから始まります。exeファイルのサイズが増えることはありません。CRTにダイナミックリンクするためには、exeファイルを起動するときに必ずCRTのDLLが存在していなければなりません。このCRTがexeファイルと同じフォルダやsystem32フォルダに存在しない場合は、exeファイルを起動することができなくなります。他のPCにもmsvcr100.dllが存在するだろうと考えることはできません。いってCRTにダイナミックリンクすると、exeファイルの起動がCRTの存在に依存してしまうから困るというものです。exeファイルがCRTにリンクする必要はないはずですから、CRTに依存するという問題は解決することができます。CRTの関数を呼び出さないようにするのが大原則になります。CRTが存在しなければ特定の機能を使用できなくなるようなことはありません。CRTを使えなくては困るという状況はあまりないはずです。CRTとマルチスレッドDLLそれでは、具体的な作業に入っていきましょう。CRTをスタティックリンクではなく、ダイナミックリンクするように開発環境を設定します。プロパティを選択して表示することができます。(/MD)」を指定するようにします。これにより、CRTのコードがexeファイルに格納されることはなくなります。ダイナミックリンクは発生しないはずです。lParam);}見て分かるように、このコードではfopenなどのCRT関数は一切呼び出していません。Table)を調べてみましょう。これを調べることでCRTが本当にリンクされていないかどうかを確認することができます。IATを調べるアプリケーションとしては、うさみみハリケーンというソフトを使用させていただいています。次の図は、目的のexeファイルを起動して、そのプロセスのインポート関数を調べた結果です。当初の予想とは裏腹に、exeファイルには何故かmsvcr100.dllがリンクされるようになっており、msvcr100.dllに依存するという問題を解決できていません。このことから分かるように、どうやらCRTをダイナミックリンクしないためには、CRTの関数を呼び出さないだけでは不十分であるようです。この場合はCRTがスタティックリンクされるため、exeファイルにCRTのコードが格納されてしまいます。これではexeファイルのサイズが重くなってしまいますから、当初の目的を達成しているとはいえません。CRTと__tmainCRTStartup上図で確認したIATには_commodeなどの関数が記述されていましたが、このような関数が記述される理由はただ1つです。それは、exeファイルがmsvcr100.dllに実装されている_commodeを呼び出したからです。先に示したソースコードにはそのような関数の呼び出しはなかったはずです。このような呼び出していない関数がIATに格納されているのでしょうか。CRTの__tmainCRTStartup(crtexe.cに記述)になっているからです。__tmainCRTStartupの主な作業はCRTの初期化及び、WinMain関数の呼び出しです。そのときに指定するヒープハンドルは、__tmainCRTStartupが呼び出している関数内で初期化されることになっています。SEHを張ることで例外の発生をキャッチするようにもしています。stdlib.hに定義されている_winmajorのようなグローバル変数も初期化します。エントリポイントを明示的にWinMainに設定しても問題はありません。APIのみで構成されることになります。ポイント」という項目にWinMainを指定します。この関数におけるCRT関連の処理が行われることはなくなります。1つ考慮すべき点が残っています。「いいえ」に設定しなければなりません。仮想関数による関数のオーバーライドでも発生することが知られています。C++特有の一部の機能は制限されていると考えてよいでしょう。それを間接的に生じさせるコードもカットしなければならないことを意味しているのです。それでは改めて、CRTをリンクしないアプリケーションの例を示します。必要となるWinMainの引数はコード上で明示的に初期化しなければならないという2点です。ウインドウクラスの登録にhinstが、ウインドウを表示するためにnCmdShowが必要となります。これらはそれぞれ、GetModuleHandleとGetStartupInfoで初期化することができます。そのようなときはGetCommandLineを呼び出すとよいでしょう。その文字列にexeファイルの名前が含まれることを忘れてはいけません。終了コードをExitProcessに指定するようにします。最後に、作成されたexeファイルのIATを確認してみましょう。見て分かるように、msvcr100.dllの関数を一切記述されておらず、exeファイルがCRTに依存していないことが分かります。IATに記述されている関数が、コード上で呼び出している関数だけで構成されていることも分かります。APIで実現することになります。メモリの開放ならばmallocではなく、HeapAllocを呼び出すという感じです。これらのAPIには少し例外があります。それは、次のヘッダーファイルを見るとよく分かります。StudioでZeroMemoryを検索してみると、winbase.hの次の部分が表示されます。これはつまり、ZeroMemoryの呼び出しが実際にはRtlZeroMemoryの呼び出しになっているということです。それでは次に、RtlZeroMemoryで検索してみます。見て分かるように、RtlZeroMemoryの呼び出しがmemsetの呼び出しになっています。結局CRTに依存してしまうことになります。した問題を回避する策として考えられるのは、処理をアプリケーションが明示的に行うことです。メモリを0に初期化するコードは次のように記述できます。その限りではありません。IATにmemsetの呼び出しが含まれていることを確認しました。コンパイル時に、コードが0クリアの処理と解釈され、それがmemsetの呼び出しに置き換わったように思えます。今のところよい案は見つかっていない状態です。複雑な方法ですが、kernel32.dll(及びntdll.dll)がエクスポートしているRtlZeroMemoryを呼び出すという方法があります。この方法ならば、CRTの呼び出しが入る余地はないはずです。n);FreeLibrary(hmod);}まず、LoadLibraryでモジュールのハンドルを取得します。kernel32.dll(及びntdll.dll)はアドレス空間に必ずロードされているので、GetModuleHandleで取得しても構いません。この場合、ハンドルの開放は不要になります。GetProcAddressに"RtlZeroMemory"を指定してアドレスを取得し、lpfnRtlZeroMemoryを使用して呼び出します。これで、メモリは0に初期化されたことになります。CopyMemoryやMoveMemoryを呼び出す場合は、RtlMoveMemoryにリンクするようにします。