1.典型C/C++程序
/*HelloC.c*/ #include
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
/*以下为典型Windows窗口应用程序框架*/ // Win32 大多数API,包含在windows.h头文件中 #include
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)促使操作系统终止应用程序。
参考:
《在VC中编译、运行程序的小知识点》
《如何屏蔽控制台应用程序的窗口?》
《深入浅出MFC》第一章àWin32程序基本概念à进程与线程à一个进程的诞生与死亡
《Windows核心编程 第五版》4.1编写第一个Windows应用程序。