Windows程序必须拥有一个在应用程序启动运行时调用的进入点函数:
int WINAPI WinMain(
HINSTANCE hinstExe,
HINSTANCE,
PSTR pszCmdLine,
int nCmdShow);
int WINAPI wWinMain(
HINSTANCE hinstExe,
HINSTANCE,
PWSTR pszCmdLine,
int nCmdShow);
int __cdecl main(
int argc,
char *argv[],
char *envp[]);
int __cdecl wmain(
int argc,
wchar_t *argv[],
wchar_t *envp[]);
实际上,操作系统不调用编写的进入点函数,而是调用c/c++运行期启动函数.该函数负责对c/c++运行期库进行初始化,这样,就可以调用malloc和free之类函数.它还能够确保已经声明的任何全局对象和静态c++对象能够在代码执行以前正确的创建.
------------------------------------------------------------------------
应用程序类型 进入点 嵌入可执行文件的启动函数
需要ANSI字符和字符串的GUI程序 WinMain WinMainCRTStartup
需要Unicode的GUI程序 wWinMain wWinMainCRTStartup
需要ANSI的CUI程序 main mainCRTStartup
需要Unicode的CUI程序 wmain wmainCRTStartup
启动函数的功能归纳:
#检索指向新进程的完整命令行的指针
#检索指向新进程的环境变量的指针
#对c/c++运行期的全局变量进行初始化.如果包含stdlib.h,代码就能访问这些变量
#对c运行期内存单元分配函数和其他低层输入/输出例程使用的内存栈进行初始化
#为所有全局和静态c++类对象调用构造函数.
当所有以上初始化操作完成后,c/c++启动函数就调用应用程序的进入点函数.
当进入点函数返回时,启动函数便调用C运行期的exit函数,将返回值(nMainRetVal)传递给它.
Exit函数负责下面的操作:
#调用由_onexit函数的调用而注册的任何函数
#为所有全局的和静态的C++类对象调用析构函数
#调用操作系统的ExitProcess函数,将nMainRetVal传递给它.这使得该操作系统能够撤销进程并设置它的exit代码
1.进程的实例句柄:
加载到进程地址空间的每个可执行文件或DLL都被赋予了独一无二的实例句柄.作为WinMain的第一个参数hinstExe来传递.
对于加载资源的函数调用来说,通常都需要这个句柄的值.
加载图标资源:
HICON LoadIcon(HINSTANCE hinst,PCTSTR pszIcon);
**许多程序在全局变量中保存hinstExe参数,这样很容易被所有可执行代码访问.
**HMODULE与HINSTANCE是完全相同的对象.
hinstExe的实际值是系统将可执行文件的映像加载到进程的地址空间时使用的基本地址空间.
获取可执行文件或DLL文件加载到进程的地址空间时所用的句柄/基地址:
HMODULE GetModuleHandle(PCTSTR pszModule);
当调用该函数时,传递一个以0位结尾的字符串,用于设定可执行文件或DLL文件的名字,如果系统找到了指定的可执行文件或DLL文件名,它返回该可执行文件或DLL文件映像加载到的基地址.如果没有找到返回NULL.
当参数pszModule为NULL时,返回可执行文件的基地址.这正是C运行期启动代码调用WinMain函数时该代码执行的操作.
该函数值查看调用进程的地址空间,如果调用进程不使用常用的对话框函数,用对话框名称调用时会返回NULL,即便这些对话框库已经加载到其他进程的地址空间.
DLL中调用NULL参数的该函数返回的是可执行文件的基地址,而不是DLL的基地址.
2.进程的命令行
新进程创建时,他传递一个命令行.该命令行几乎永远不会是空.可执行文件的名字是命令行的第一个标记.
当C运行期的启动代码开始运行时,他要检索进程的命令行,跳过可执行文件的名字,并将指向命令行其余部分的指针传递给WinMain的pszCmdLine参数.
应用程序可以按照自己选择的方法分析和转换命令行字符串.实际上可以写入pszCmdLine参数指向的内存缓存,但是在任何情况下都不应该写到缓存外面去.修改命令行,可以先拷贝到应用程序的本地缓存中,然后再修改本地缓存.
获得一个指向进程的完整命令行的指针:
PTSTR GetCommandLine(); GetCommandLineW()
该函数返回一个指向包含完整命令行的缓存的指针,该命令包括执行文件的完整路径名.
将Unicode字符串分割成为它的各个标记:
PWSTR CommandLineToArgvW(
PWSTR pszCmdLine,
int* pNumArgs);
CommandLineToArgvW负责在内部分配内存,大部分应用程序不释放该内存,他们在进程运行终止时依靠操作系统来释放内存.这是完全可行的,如果手动释放,应该使用下面的方法调用HeapFree()函数:
int nNumArgs;
PWSTR *ppArgv = CommandLineToArgvW(GetCommandLineW(),&nNumArgs);
//使用参数
if( *ppArgv[1] == L'x'){
...
}
//释放内存区域
HeapFree(GetProcessHeap(),0,ppArgv);
3.进程的环境变量
每个进程都有一个相关的环境块,环境块是进程的地址空间中分配的一个内存块.每个环境块都包含一组字符串:
VarName1=VarValue1
VarName2=VarValue2
...
等号前面是变量名字,等号后面的是赋予变量的值.环境块中的所有字符串都必须按环境变量名的字母顺序进行排序.
子进程可以继承父进程的环境变量,父进程能够控制子进程继承什么环境变量.
应用程序通常使用环境变量来使用户调整它的行为特性.
判断环境变量是否存在和获取它的值:
DWORD GetEnvironmentVariable(
PCTSTR pszName,
PTSTR pszValue,
DWORD cchValue);
许多字符串里包含了可取代的字符串,例如%USERPROFLIE%My Documents :
百分数符号之间的部分为可取代的字符串,环境变量的值USERPROFILE应该被放入该字符串中,Windows提供了替换函数:
DWORD ExpandEnvironmentStrings(
PCSTR pszSrc,
PSTR pszDst,
DWORD nSize);
可以使用SetEnvironmentVariable函数来添加变量、删除变量或修改变量的值:
BOOL SetEnvironmentVariable(
PCTSTR pszName,
PCTSTR pszValue);
该函数用于将pszName参数标识的变量设置为pszValue参数标识的值。如果这个变量已经存在,SetEnvironmentVariable就修改该值,如果不能存在就添加该变量。如果pszValue为NULL,变从环境变量中删除该变量
4.进程的亲缘性
一般来说,进程中的线程可以在主计算机中的任何一个CPU上执行。但是一个进程的线程可能被强制在可用的CPU的子集上运行。这成为进程的亲缘性。
5.进程的错误模式
进程可以告诉系统如何处理每一种错误:
UNIT SetErrorMode(UNIT fuErrorMode);
fuError Mode参数的标识:
SEM_FAILCRITICALERRORS 系统不显示关键错误句柄消息框,并将错误返回给调用进程
SEM_NOGOFAULTERRORBOX 系统不显示一般保护故障消息框。本标志只应该由采用异常情况处理程序来处理一般保护(GP)故障的调试应用程序来设定
SEM_NOOPENFILEERRORBOX 当系统找不到文件时,它不显示消息框。
SEM_NOALIGNMENTFAULTEXCEPT 系统自动排除内存没有对齐的故障,并使应用程序看不到这些故障。本标志对x86处理器不起作用
默认情况下,这些标志会被子进程继承。可以在CreateProcess时设定CREATE_DEFAULT_ERROR_MODE标志来防止继承。
6.进程的当前驱动器和目录
当不提供全路径名时,Windows各个函数会在当前驱动器的当前目录中查找文件和目录。
由于该信息时按每个进程来维护的,因此改变当前驱动器或目录的进程中的线程,就可以为该进程中所有线程改变这些信息。
线程获得和设置它进程的当前驱动器和目录:
DWORD GetCurrentDirectory(
DWORD cchCurDir,
PTSTR pszCurDir);
BOOL SetCurrentDirectory(PCTSTR pszCurDir);
可以使用C运行期函数_chdir而不是使用Windows的SetCurrentDirectory函数来变更当前目录。 _chdir函数从内部调用SetCurrentDirectory,但是_chdir也能够添加或修改该环境变量,这样,不同驱动器的当前目录就可以保留。
http://www.office-cn.net/t/api/api_content.htm