CreateProcess函数详细使用说明函数原型BOOLCreateProcess(LPCTSTRlpApplicationName,LPTSTRlpCommandLine,LPSECURITY_ATTRIBUTESlpProcessAttributes。LPSECURITY_ATTRIBUTESlpThreadAttributes,BOOLbInheritHandles,DWORDdwCreationFlags,LPVOIDlpEnvironment,LPCTSTRlpCurrentDirectory,LPSTARTUPINFOlpStartupInfo,LPPROCESS_INFORMATIONlpProcessInformation);CreateProcess函数用于创建进程:1.BOOLCreateProcess(2.PCTSTRpszApplicationName,3.PTSTRpszCommandLine,4.PSECURITY_ATTRIBUTESpsaProcess,5.PSECURITY_ATTRIBUTESpsaThread,6.BOOLbInheritHandles,7.DWORDfdwCreate,8.PVOIDpvEnvironment,9.PCTSTRpszCurDir,10.PSTARTUPINFOpsiStartInfo,11.PPROCESS_INFORMATIONppiProcInfo);线程调用CreateProcess时,系统会创建一个进程内核对象,将其引用计数初始化为1(进程内核对象并不是进程本身,它只是操作系统用来管理进程的数据结构,其中包含了进程的一些统计信息)。然后系统为新进程开辟虚拟地址空间,并将可执行文件的代码和数据以及所需的DLL装载到该地址空间中。接着系统为进程主线程创建线程内核对象,并将其引用计数初始为1(同进程一样,线程内核对象也不是线程本身,而且操作系统用来管理线程的数据结构)。主线程将链接器设置的入口点函数作为C/C++运行时启动函数调用,这些启动函数最终又调用代码中的入口点函数如WinMain、wWinMain、main和wmain。当操作系统成功创建了新的进程和主线程后,CreateProcess返回TRUE。以上是CreateProcess的简要介绍,下面我们来详细讨论它的参数。pszApplicationName和pszCommandLinepszApplicationName和pszCommandLine分别表示进程使用的可执行文件名和向其传递的命令行字符串,我们先来看看pszCommandLine参数。注意pszCommandLine是PTSTR,这意味着你必须为其传递指向非常量字符串的地址。CreateProcess内部会更改向其传递的命令行字符串,但在CreateProcess返回之前,它会将该字符串恢复原样。这一点是非常重要的,因为如果你向CreateProcess传递的命令行字符串位于进程的只读存储区,就会发生AccessViolation错误。比如,下面的代码执行时会触发AccessViolation,因为微软的C/C++编译器会把常量字符串放入只读存储区(注意早期的微软C/C++编译器会将常量字符串放在可读写存储区,因此下面的代码在旧的编译环境下不会出错):1.STARTUPINFOsi={sizeof(si)};2.PROCESS_INFORMATIONpi;3.CreateProcess(NULL,TEXT(NOTEPAD),NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);解决这个问题的方法很简单,将命令行字符串复制到临时缓冲区既可,如下所示:1.STARTUPINFOsi={sizeof(si)};2.PROCESS_INFORMATIONpi;3.TCHARszCommandLine[]=TEXT(NOTEPAD);4.CreateProcess(NULL,szCommandLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);微软在其C++编译器选项中提供了/GF开关,/GF打开时,程序中所有用到的常量字符串将只维护单一副本,且位于只读存储部分。在调用CreateProcess时,开发人员应该打开/GF开关并使用缓冲区。我们希望微软在未来版本的Windows中会改进CreateProcess,使其接受常量字符串作为命令行参数,并在其内部分配/释放临时缓冲区而不是让API调用者来做。另外,假如你使用常量ANSI字符串作为CreateProcess参数,并不会发生AccessViolation错误,我们在前面的章节已经提到过,许多WinAPI函数的ANSI版本会将ANSI参数转换为UNIDOE编码后调用其Unicode版本,CreateProcess会把ANSI字符串转换为Unicode编码后放在临时缓冲区,并调用Unicode版的CreateProcess,因此不会触发AccessViolation。pszCommandLine参数指定了CreateProcess创建新进程所需的完整命令行。当CreateProcess解析该参数时,它会检查命令行参数中的第一个标记,并将其作为进程要执行的可执行文件名,如果该文件名没有指定后缀,函数将把它当作exe文件。CreateProcess会按下面的顺序查找该文件:1.包含当前进程可执行文件的目录2.当前进程的当前目录3.Windows系统目录,既GetSystemDirectory返回的目录4.Windows目录5.PATH环境变量列出的目录当然,如果文件名包含了完整路径,系统将会在该路径中查找文件而不会再做上面的搜索。如果系统找到了可执行文件,它会创建一个新的进程并把可执行文件的代码和数据映射到进程的地址空间,然后调用CRT启动函数(linker选项卡中的入口点函数),接着CRT启动函数检查命令行参数,过滤掉其中的可执行文件部分,并把剩下字符串的地址作为pszCmdLine传给wWinMain/WinMain。以上情形都是在pszApplicationName为NULL时发生的。pszApplicationName指定了进程要执行的可执行文件的名称,假如没有指定文件后缀,系统并不会做任何处理。pszApplicationName不包含完整路径时,CreateProcess只从当前目录中查找可执行文件,查找失败时函数失败并返回FALSE。即使指定了pszApplicationName,CreateProcess仍然会将pszCommandLine参数作为新进程的命令行。比如下面的代码:1.//Makesurethatthepathisinaread/writesectionofmemory.2.TCHARszPath[]=TEXT(WORDPADREADME.TXT);3.//Spawnthenewprocess.4.CreateProcess(TEXT(C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE),szPath,...);执行上面代码时,系统会打开notepad.exe(记事本),但它的命令行却是WORDPADREADME.TXT(WORDPAD是写字板),这看上去非常奇怪,但CreateProcess就是这样工作的。这种pszApplicationName提供的特性被用来支持Windows的POSIX子系统。psaProcess,psaThread和bInheritHandles创建新进程时,系统会创建一个进程内核对象和一个线程内核对象(用于进程的主线程),和其它内核对象一样,创建者(在这儿是父进程)必须指定其安全属性。psaProcess和psaThread分别指定了新进程的进程内核对象和线程内核对象的安全属性。将其设为NULL时,系统为对应的内核对象指定默认的安全属性。你可以创建SECURITY_ATTRIBUTES类型的变量,设置其中各个域的值然后将变量地址传递给psaProcess或psaThread,以应用指定的安全属性。正如在第3章讨论内核对象时谈到的,当你想要控制新的进内核对象的句柄能否被父进程以后创建的子进程继承时,你应该设置SECURITY_ATTRIBUTES变量的bInheritHandle域的值。下面的Inherit.cpp展示了内核对象句柄继承。假设执行该代码的进程为A,它调用CreateProcess创建了进程B,接着又创建了子进程C。注意调用CreateProcess时A使用的psaProcess、psaThread和bInheritHandles参数,代码注释很详细的描述了这些参数的作用:1./************************************************************2.Modulename:Inherit.cpp3.Notices:Copyright(c)2008JeffreyRichter&ChristopheNasarre4.************************************************************/5.#includeWindows.h6.intWINAPI_tWinMain(HINSTANCEhInstanceExe,HINSTANCE,7.PTSTRpszCmdLine,intnCmdShow){8.//PrepareaSTARTUPINFOstructureforspawningprocesses.9.STARTUPINFOsi={sizeof(si)};10.SECURITY_ATTRIBUTESsaProcess,saThread;11.PROCESS_INFORMATIONpiProcessB,piProcessC;12.TCHARszPath[MAX_PATH];13.//PreparetospawnProcessBfromProcessA.14.//Thehandleidentifyingthenewprocess15.//objectshouldbeinheritable.16.saProcess.nLength=sizeof(saProcess);17.saProcess.lpSecurityDescriptor=NULL;18.saProcess.bInheritHandle=TRUE;19.//Thehandleidentifyingthenewthread20.//objectshouldNOTbeinheritable.21.saThread.nLength=sizeof(saThread);22.saThread.lpSecurityDescriptor=NULL;23.saThread.bInheritHandle=FALSE;//SpawnProcessB.24._tcscpy_s(szPath,_countof(szPath),TEXT(ProcessB));25.CreateProcess(NULL,szPath,&saProcess,&saThread,26.FALSE,0,NULL,NULL,&si,&piProcessB);27.//Thepistructurecontainstwohandles28.//relativetoProcessA:29.//hProcess,whichidentifiesProcessB'sprocess30.//objectandisinheritable;andhThread,whichidentifies31.//ProcessB'sprimarythreadobjectandisNOTinheritable.32.//PreparetospawnProcessCfromProcessA.33.//SinceNULLispassedforthepsaProcessandpsa