1 .典型 C/C++ 程序
2. Windows 编程的第一种方式是传统的 SDK 方式, Windows 操作系统是由 C 语言编写的,故可采用 C 语言直接调用 windows 的 API 函数进行编程。
以下为 Win SDK 应用程序窗口程序框架示例及框架解析。
(1) 建立 Win32 应用程序项目 Win32SDK :
Visual Studio 2005 à 文件 à 新建 à 项目 à Visual C++ à win32 à win32 项目 à win32 应用程序 à 空项目(默认使用 Unicode 字符集)。 如果运行时提示找不到 mfc80d.dll 或 msvcr80d.dll 文件,在“项目属性 à 配置属性 à 清单工具 à 常规”中的“使用 FAT32 解决办法”处选择“是”,再重新生成解决方案。
(2) 添加源文件 Win32SDK.cpp
3. Windows 下的 C/C++ 应用程序运行机制
( 1 ) subsystem 选项
VC6 :
Project Settings à C/C++ à Preprocessor à Preprocessor Definitions : _CONSOLE ( _WINDOWS )
Project Settings à C/C++ à Project Options : /D "_CONSOLE" ( /D "_WINDOWS " )
Project Settings à Link à Project Options : /subsystem:console ( /subsystem:windows )
VC2005 :
项目属性 à C/C++ à 预处理器 à 预处理器定义: _CONSOLE ( _WINDOWS )
项目属性 à C/C++ à 命令行: /D "_CONSOLE" ( /D "_WINDOWS " )
项目属性 à 链接器 à 命令行: /SUBSYSTEM:CONSOLE ( /SUBSYSTEM:WINDOWS )
项目属性 à 链接器 à 系统 à 子系统:控制台 (/SUBSYSTEM:CONSOLE) ( Windows (/SUBSYSTEM:WINDOWS) )
( 2 )可执行文件的 Entry Point
可执行文件都有一个 Entry Point (起始地址), LINK 时可以用 /entry 指定。
缺省情况下,如果 subsystem 是 “console” , Entry Point 是 mainCRTStartup(ANSI) 或 wmainCRTStartuup(UNICODE) ,即:
/subsystem:"console" /entry:"mainCRTStartup" (ANSI)
/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)
mainCRTStartup 或 wmainCRTStartuup 会调用用户编写的 main 或 wmain 。 在进入可执行文件的代码前,系统将会创建一个控制台窗口。
值得一提的是,在进入应用程序的 Entry Point 前, Windows 的装载器已经做过 C 变量的初始化,有初值的全局变量拥有了它们的初值,没有初值的变量被设为 0 。
如果 subsystem 是 “windows” , Entry Point 是 WinMain(ANSI) 或 wWinMain(UINCODE) ,即:
/subsystem:"windows" /entry:"WinMainCRTStartup" (ANSI)
/sbusystem:"windows" /entry:"wWinMainCRTStartup" (UINCODE)
WinMainCRTStartup 或 wWinMainCRTStartup 会调用 用户编写的 WinMain 或 wWinMain 。 窗口由用户调用 CreateWindow(Ex) 创建 。
在 VC2005 中, 项目属性 à 链接器 à 高级处有入口点选项,在编写 Windows Mobile ANSI 控制台程序时需指定入口点为 mainWCRTStartup ( WindowsCE (/SUBSYSTEM:WINDOWSCE) )。
在 VC2005 中,建一个简单的 Console 程序( main 为入口函数),编译后 F10 将进入 crtexe.c ,查看调用堆栈如下:
à int main ( int argc , char * argv [])
à int __tmainCRTStartup ( void )
à int mainCRTStartup ( void )
<1>mainCRTStartup 函数只是简单的调用 __tmainCRTStartup 函数,其代码如下:
#ifdef _WINMAIN_
int WinMainCRTStartup ( void ) // UNICODE 版本: int wWinMainCRTStartup(void)
#else
int mainCRTStartup ( void ) // UNICODE 版本: int wmainCRTStartup(void)
{
__security_init_cookie ();
return __tmainCRTStartup ();
}
<2>__tmainCRTStartup 函数调用 main/WinMain 函数,其代码如下:
int __tmainCRTStartup ( void )
{
// ……
#ifdef _WINMAIN_
GetStartupInfo ( & StartupInfo );
……
mainret = WinMain (…) // UNICODE 版本: int wWinMain(…)
#else /* _WINMAIN_ */
mainret = main ( … ); // UNICODE 版本: int wmain(…)
// ……
/*
* do C++ constructors (initializers) specific to this EXE
*/
if ( __native_startup_state == __initializing )
{
_initterm ( __xc_a , __xc_z );
__native_startup_state = __initialized ;
}
// ……
if ( ! managedapp )
exit ( mainret );
// ……
return mainret ;
}
可以推测上述 _WINMAIN_ 宏是与 subsystem 相关的一个 VC 编译器内部宏。
(3)应用程序的启动
程序是一连串 静态 的指令,而进程是一个容器,它包含了一系列运行在这个程序实例上下文中的线程使用的资源。
当我们用鼠标点击磁盘上的可执行文件 App.exe 后, App.exe 被装载至内存后就形成了进程,因此进程是一个正在运行的程序的实例。一般我们可以同时启动多个 App.exe 的实例,即创建同名的多个不同 ID 的进程。
进程内核对象不是进程本身,仅仅是一个系统用来管理这个进程的一个小的数据结构( PCB , Process Control Block )。进程是不活泼的,程序代码的执行是线程的工作,线程是进程内执行代码的独立实体。一个进程要完成任何的事情,它必须有一个运行在其地址空间的线程。 Windows 操作系统调用 CreateProcess 函数创建一个新的进程和该进程的主线程,返回 LPPROCESS_INFORMATION 信息。 主线程通过执行 C/C++ 运行期启动代码初始化 C/C++ 运行期库, C/C++ 运行期启动代码又会调用 main 函数。主线程在运行期间,可以调用 CreateThread 创建辅助线程,即所谓的多线程。
当 App.exe 进程的主线程入口函数 main/WinMain 返回后,启动函数 mainCRTStartup ( __tmainCRTStartup )调用 C/C++ 运行期退出函数 exit( 参数为 main/WinMain 返回值 ) 。
全局变量(包括内置类型和类类型)存储在全局区。 exit 函数会销毁所有全局的或静态的 C++ 对象, 全局 C++ 对象的构造函数在进入应用程序的 Entry Point 之后,调用用户编写的 main/WinMain 之前调用。编译器调用 atexit 记录全局对象的析构函数( dynamic atexit destructor for 'x' , 多个函数形成多播链),在 exit 退出时( main/WinMain 返回之后)调用。
在 exit 处 F11 进入 crt0dat.c 中的 doexit ,其中析构函数的调用代码片段如下:
/*
* do _onexit/atexit() terminators(if there are any)
* These terminators MUST be executed in reverse order (LIFO)!
*/
_PVFV * onexitbegin = ( _PVFV *) _decode_pointer ( __onexitbegin );
_PVFV * onexitend = ( _PVFV *) _decode_pointer ( __onexitend );
// 依次调用 atexit 注册的函数( oneixtbegin à onexitend )
HelloCPP2.cpp 程序在 VC6 中,没输出 "X deconstructor" ,在 VC2005 中输出 "X deconstructor" 。
最后( /* return to OS or to caller */ )调用 ExitProcess ( doexit à __crtExitProcess )促使操作系统终止应用程序。
参考:
《 如何屏蔽控制台应用程序的窗口? 》
《深入浅出 MFC 》第一章 à Win32 程序基本概念 à 进程与线程 à 一个进程的诞生与死亡
《 Windows 核心编程 第五版》 4.1 编写第一个 Windows 应用程序。