VCでプログラムをコンパイル・実行するための小さな知識点(VC 6でプログラムをコンパイル・実行するオプション)


VCでプログラムをコンパイル・実行するための小さな知識点


1、Run-Time Library

Run-Time Libraryは、コンパイラが提供する標準ライブラリであり、基本的なライブラリ関数とシステム呼び出しを提供します.
我々が一般的に使用するRun‐Time LibraryはC Run‐Time Librariesである.もちろんStandard C++librariesもあります.
C Run-Time LibrariesはANSI Cの標準ライブラリを実現する.VCインストールディレクトリのCRTディレクトリには、C Run-Timeライブラリのソースコードの大部分があります.
C Run-Time Librariesには静的ライブラリバージョンと動的リンクライブラリバージョンがある.単一スレッドバージョンもあれば、マルチスレッドバージョンもあります.デバッグと非デバッグのバージョンもあります.「プロジェクト」-「settings」-「C/C+」-「コードジェネレーション」でRun-Time Libraryのバージョンを選択できます.
ダイナミックリンクライブラリバージョン:/MD Multithreaded DLLはインポートライブラリMSVCRTを使用する.LIB/MDd Debug Multithreaded DLLはインポートライブラリMSVCRTDを使用する.LIB
スタティックライブラリバージョン:/ML Single-ThreadedスタティックライブラリLIBCを使用する.LIB/MLd Debug Single-Threaded静的ライブラリLIBCDを使用する.LIB/MT Multithreadedは静的ライブラリLIBCMTを使用する.LIB/MTd Debug Multithreaded静的ライブラリLIBCMTDを使用する.LIB
C Run-Time Libraryの標準io部分はオペレーティングシステムと密接に関係しており、WindowsではCRTのio部分コードはパッケージにすぎず、下部はオペレーティングシステムカーネルkernel 32に使用される.dllの関数は、コンパイル時にインポートライブラリkernel 32を使用する.lib.つまり、組み込み環境では、C標準ライブラリを直接使用することはできません.Linux環境にも当然C標準ライブラリがある、例えばld-o output/lib/crt 0.o hello.o-lcパラメータ「-lc」は、C標準ライブラリlibcを参照する.a.「-lm」がどのライブラリファイルを参照しているか当ててみましょう.

2、よくあるコンパイルパラメータ

VCは、プロジェクトを作成するときに「Win 32」を定義します.コンソールプログラムでは「_CONSOLE」が定義され、そうでない場合は「_WINDOWS」が定義されます.Debug版定義「_DEBUG」、Release版定義「NDEBUG」
MFC DLLに関するコンパイル定数は次のとおりです.WINDLLはMFC用のDLLを作ることを示しています.USRDLLはユーザーDLLとして(MFC拡張DLLに対して)AFXDLLは、MFCダイナミックリンクライブラリを使用することを示す_AFXEXTはMFC拡張DLLを作ることを示しているので:Regular,statically linked to MFC_WINDLL,_USRDLL Regular, using the shared MFC DLL _WINDLL,_USRDLL,_AFXDLLExtension DLL _WINDLL,_AFXDLL,_AFXEXT
CL.EXEはすべてのソースファイルをコンパイルし、LINK.EXEリンクEXEとDLL,LIB.EXEは静的ライブラリを生成します.

3、subsystemと実行可能ファイルの起動

LINKの場合は/subsystemを指定する必要があります.このリンクオプションは、Windowsが実行可能ファイルをどのように実行するかを示します.
コンソールプログラムは/subsystem:“console”
他のプログラムは一般的に/subsystem:“windows”
subsystemを「console」に選択すると、Windowsは実行可能ファイルのコードに入る前に(mainCRTStartupなど)、コンソールウィンドウを生成します.「windows」を選択すると、オペレーティングシステムはconsoleウィンドウを生成しません.このタイプのアプリケーションのウィンドウはユーザー自身が作成します.
実行可能ファイルはいずれもEntry Pointがあり、LINKでは/entryで指定できます.デフォルトでは、subsystemが「console」の場合、Entry PointがmainCRTStartup(ANSI)またはwmainCRTStartup(UNICODE)、すなわち、/subsystem:「console」/entry:「mainCRTStartup」(ANSI)/subsystem:「console」/entry:「wmainCRTStartup」(UNICODE)mainCRTStartupまたはwmainCRTStartupがmainまたはwmainCRTStartupを呼び出します.特筆すべきは、アプリケーションのEntry Pointに入る前にWindowsのローダがC変数の初期化を行い、初期値のあるグローバル変数が初期値を持ち、初期値のない変数が0に設定されていることです.
subsystemが「windows」である場合、Entry PointはWinMain(ANSI)またはwWinMain(UINCODE)である.すなわち、/subsystem:「windows」/entry:「WinMainCRTStartup」(ANSI)/sbusystem:「windows」/entry:「wWinMainCRTStartup」(UINCODE)WinMainCRTStartupまたはwnMainCRTStartupはWinMainまたはwWinMainを呼び出す.
これらのエントリポイント関数は、CRTディレクトリにソースコードが表示されます.たとえば、(簡潔にするために、元のコードのいくつかの条件コンパイルを削除しました):
void mainCRTStartup(void)
{
int mainret;

/* Get the full Win32 version */
_osver = GetVersion();
_winminor = (_osver >> 8) & 0x00FF ;
_winmajor = _osver & 0x00FF ;
_winver = (_winmajor << 8) + _winminor;
_osver = (_osver >> 16) & 0x00FFFF ;

#ifdef _MT
if ( !_heap_init(1) ) /* initialize heap */
#else /* _MT */
if ( !_heap_init(0) ) /* initialize heap */
#endif /* _MT */
fast_error_exit(_RT_HEAPINIT); /* write message and die */

#ifdef _MT
if( !_mtinit() ) /* initialize multi-thread */
fast_error_exit(_RT_THREAD); /* write message and die */
#endif /* _MT */

__try {
_ioinit(); /* initialize lowio */
_acmdln = (char *)GetCommandLineA(); /* get cmd line info */
_aenvptr = (char *)__crtGetEnvironmentStringsA(); /* get environ info */
_setargv();
_setenvp();
__initenv = _environ;
mainret = main(__argc, __argv, _environ);
exit(mainret);
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
_exit( GetExceptionCode() ); /* Should never reach here */
} /* end of try - except */
}

MFCフレームワークを使用すると、WinMainもMFCライブラリに埋め込まれます(APPMODUL.CPP):extern"C"int WINAPI_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow){//call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);}ANSIバージョンの場合、「_tWinMain」は「WinMain」です.UINCODEバージョンでは「_tWinMain」が「wWinMain」です.afxを参照.h:
#ifdef _UNICODE#define _tmain wmain#define _tWinMain wWinMain#else#define _tmain main#define _tWinMain WinMain#endif
グローバルC++オブジェクトのコンストラクション関数はどこで呼び出されますか?答えはアプリケーションのEntry Pointに入った後、main関数を呼び出す前の初期化操作です.だからMFCのtheAppの構造関数は_tWinMainの前に呼び出されました.

4、Consoleウィンドウを表示しないConsoleプログラム

デフォルトでは/subsystemと/entryスイッチが一致します.つまり、次のようになります.
「console」は「mainCRTStartup」または「wmainCRTStartup」に対応
「Windows」は「WinMain」または「wWinMain」に対応
手動で修正することで、彼らを一致させないことができます.例:
#include"windows.h"#pragma comment(linker,"/subsystem:/"windows/"/entry:/"mainCRTStartup/")/エントリアドレスvoid main(void){MesageBox(NULL,"hello","Notice",MB_OK);
このコンソールプログラムではコンソールウィンドウは表示されません./MLdを選択すると、このプログラムはLIBCDにリンクするだけです.LIB user32.lib kernel32.lib.
実はConsoleウィンドウを見たくないなら、EXEファイルでPEファイルヘッダのSubsystemを3から2に直接変更する方法もあります.EXEファイルでは、PEヘッダのオフセットアドレスは0 x 3 cであり、SubsystemはWORDであり、PEヘッダでのオフセットは0 x 5 cである.

5.MFCのライブラリファイル

MFCのライブラリは静的リンクでも動的リンクでもよい.静的ライブラリと動的ライブラリには、DebugとRelease、ANSI、Unicodeのバージョンがあります.
静的MFCライブラリは主にANSI Debug NAFXCWDである.LIBANSI Release NAFXCW.LIBUnicode Debug UAFXCWD.LIBUnicode Release UAFXCW.LIBダイナミックリンクライブラリは主にあります.ANSI Debug MFCxxD.LIB (core,MFCxxD.DLL), MFCOxxD.LIB (OLE,MFCOxxD.DLL), MFCDxxD.LIB (database,MFCDxxD.DLL), MFCNxxD.LIB (network,MFCNxxD.DLL), MFCSxxD.LIB (static)
ANSI Release MFCxx.LIB (combined,MFCxx.DLL)MFCSxx.LIB (static)
Unicode Debug MFCxxUD.LIB (core,MFCxxUD.DLL), MFCOxxUD.LIB (OLE,MFCOxxUD.DLL), MFCDxxUD.LIB (database,MFCDxxUD.DLL), MFCNxxUD.LIB (network,MFCNxxUD.DLL), MFCSxxUD.LIB (static)
Unicode Release MFCxxU.DLL (combined,MFCxxU.DLL), MFCSxxU.LIB (static)
上のLIBファイルはMFCSxx(D、U、UD)を除く.LIB以外はインポートライブラリです.MFCダイナミックリンクライブラリのバージョンも、MFCSxx(D、U、UD)に配置されたいくつかのファイルを静的にリンクする必要がある.LIB中.例えば、含む_tWinMainのappmodul.cpp.

6、終わりの言葉


これらの問題を研究する動機は、私たちのプログラムがどのようにロードされ、実行されているかを明らかにしたいということです.しかし、Windowsはオープンソースプラットフォームではないので、PEファイル(Windows上で実行可能なファイルのフォーマット)しか検討できません.entry point、subsystemはPEファイルヘッダの一部です.
WindowsはPEファイルのentry pointに入る前に何をしたのか、見えなくなり、PEファイルとすべての必要なDLLをロードし、C変数を初期化し、ある起点関数から実行するプロセスを作成するべきだと推測するしかありません.異なるsubsystemには、異なる起点があるはずです.この起点関数を呼び出すときはPEファイルのentry pointアドレスを入力する必要があります.