如何使用创建进程CreateProcess函数。
如何使用创建进程CreateProcess函数。
当一个线程调用CreateProcess时,系统就会创建一个进程内核对象,其初始使用计数是1。
该进程内核对象不是进程本身,而是操作系统管理进程时使用的一个较小的数据结构。可以将进程内核对象视为由进程的统计信息组成的一个较小的数据结构。然后,系统为新进程创建一个虚拟地址空间,并将可执行文件或任何必要的D L L文件的代码和数据加载到该进程的地址空间中。
然后,系统为新进程的主线程创建一个线程内核对象(其使用计数为1)。与进程内核对象一样,线程内核对象也是操作系统用来管理线程的小型数据结构。通过执行C / C + +运行期启动代码,该主线程便开始运行,它最终调用WinMain、wWinMain、main或wmain函数。如果系统成功地创建了新进程和主线程,CreateProcess便返回TRUE。
1 pszApplicationName和p s z C o m m a n d L i n e
pszApplicationName和pszCommandLine参数分别用于设定新进程将要使用的可执行文件的名字和传递给新进程的命令行字符串
pszCommandLine参数的原型是PTSTR。这意味着CreateProcess期望你将传递一个非常量字符串的地址。从内部来讲,CreateProcess实际上并不修改你传递给它的命令行字符串。不过,在CreateProcess返回之前,它将该字符串恢复为它的原始形式。
如果命令行字符串不包含在文件映象的只读部分中,就会出现违规访问的问题。解决这个问题的最好办法是在调用CreateProcess之前像下面这样将常量字符串拷贝到临时缓存中。当CreateProcess分析pszCommandLine字符串时,它将查看字符串中的第一个标记,并假设该标记是想运行的可执行文件的名字。如果可执行文件的文件名没有扩展名,便假设它的扩展名为.exe。CreateProcess也按下面的顺序搜索该可执行文件:
1) 包含调用进程的. e x e文件的目录。
2) 调用进程的当前目录。
3) Wi n d o w s的系统目录。
4) Wi n d o w s目录。
5) PAT H环境变量中列出的目录。
如果文件名包含全路径,系统将使用全路径来查看可执行文件,并且不再搜索这些
目录。如果系统找到了可执行文件,那么它就创建一个新进程,并将可执行文件的代码和数据
映射到新进程的地址空间中。然后系统将调用C / C + +运行期启动例程。
这一切都是在pszApplicationName参数是NULL(99%以上的时候都应该属于这种情况)时
发生的。如果不传递NULL,可以将地址传递给pszApplicationName参数中包含想运行的可执行
文件的名字的字符串。请注意,必须设定文件的扩展名,系统将不会自动假设文件名有一
个.exe扩展名。CreateProcess假设该文件位于当前目录中,除非文件名前面有一个路径。如果在当前目录中找不到该文件,CreateProcess将不会在任何其他目录中查找该文件,它运行失败了。
2 psaProcess、psaThread和binheritHandles
可以使用psaProcess和psaThread参数分别设定进程对象和线程对象需要的安全性。可以为这些参数传递NULL,在这种情况下,系统为这些对象赋予默认安全性描述符。也可以指定两
个SECURITY_ATTRIBUTES结构,并对它们进行初始化,以便创建自己的安全性权限,并将它们赋予进程对象和线程对象。
将SECURITY_ATTRIBUTES结构用于psaProcess和psaThread参数的另一个原因是,父进
程将来生成的任何子进程都可以继承这两个对象句柄中的任何一个。
3 fdwCreate
fdwCreate参数用于标识标志,以便用于规定如何来创建新进程。
4 pvEnvironment
pvEnvironment参数用于指向包含新进程将要使用的环境字符串的内存块。在大多数情况
下,为该参数传递NULL,使子进程能够继承它的父进程正在使用的一组环境字符串。也可以
使用GetEnvironmentStrings函数。
该函数用于获得调用进程正在使用的环境字符串数据块的地址。可以使用该函数返回的地
址,作为CreateProcess的pvEnvironment参数。如果为pvEnvironment参数传递NULL,那么这正
是CreateProcess函数所做的操作。当不再需要该内存块时,应该调用FreeEnvironmentString s函数将内存块释放。
5 pszCurDir
pszCurDir参数允许父进程设置子进程的当前驱动器和目录。如果本参数是NULL,则新进程的工作目录将与生成新进程的应用程序的目录相同。如果本参数不是NULL,那么pszCurDi r
必须指向包含需要的工作驱动器和工作目录的以0结尾的字符串。注意,必须设定路径中的驱
动器名。
6 psiStartInfo
psiStartInfo参数用于指向一个STARTUPINFO结构。
当Windows创建新进程时,它将使用该结构的有关成员。大多数应用程序将要求生成的应
用程序仅仅使用默认值。至少应该将该结构中的所有成员初始化为零,然后将c b成员设置为该
结构的大校
如果未能将该结构的内容初始化为零,那么该结构的成员将包含调用线程的堆栈上的任何
无用信息。将该无用信息传递给CreateProcess,将意味着有时会创建新进程,有时则不能创建
新进程,完全取决于该无用信息。有一点很重要,那就是将该结构的未用成员设置为零,这样,
CreateProcess就能连贯一致地运行。不这样做是开发人员最常见的错误。
7 ppiProcInfo
ppiProcInfo参数用于指向你必须指定的PROCESS_INFORMATION结构。CreateProcess在
返回之前要对该结构的成员进行初始化。
在创建进程的时候,系统为每个对象赋予一个初始使用计数值1。然后,在createProcess返回之前,该函数打开进程对象和线程对象,并将每个对象的与进程相关的句柄放入PROCESS_IN FORMATION结构的hProcess和hThread成员中。当CreateProcess在内部打开这些对象时,每个对象的使用计数就变为2。
在系统能够释放进程对象前,该进程必须终止运行(将使用计数递减为1),并且
父进程必须调用CloseHandle(再将使用计数递减1,使之变为0)。同样,若要释放线程对象,
该线程必须终止运行,父进程必须关闭线程对象的句柄。
注意:必须关闭子进程和它的主线程的句柄,以避免在应用程序运行时泄漏资源。当然,当进程终止运行时,系统会自动消除这些泄漏现象,但是,当进程不再需要访问子进程和它的线程时,编写得较好的软件能够显式关闭这些句柄(通过调用CloseHandle函数来关闭)。不能关闭这些句柄是开发人员最常犯的错误之一。
由于某些原因,许多开发人员认为,关闭进程或线程的句柄,会促使系统撤消该进程或线程。实际情况并非如此。关闭句柄只是告诉系统,你对进程或线程的统计数据不感兴趣。进程或线程将继续运行,直到它自己终止运行。
当进程内核对象创建后,系统赋予该对象一个独一无二的标识号,系统中的其他任何进程
内核对象都不能使用这个相同的I D号。线程内核对象的情况也一样。当一个线程内核对象创建
时,该对象被赋予一个独一无二的、系统范围的I D号。进程I D和线程I D共享相同的号码池。
这意味着进程和线程不可能拥有相同的I D 。另外,对象决不会被赋予0 作为其I D。在
CreateProcess返回之前,它要用这些ID填入PROCESS_INFORMATION结构的dwProcessId和dwThre adId成员中。
应用程序使用I D来跟踪进程和线程,必须懂得系统会立即复用进程I D和线程I D
系统无法记住每个进程的父进程的I D,但是,由于I D是被立即重复使用的,因此,等到获
得父进程的I D时,该I D可能标识了系统中一个完全不同的进程。父进程可能已经终止运行。如果应用程序想要与它的“创建者”进行通信,最好不要使用I D。应该定义一个持久性更
好的机制,比如内核对象和窗口句柄等。
若要确保进程I D或线程I D不被重复使用,唯一的方法是保证进程或线程的内核对象不会
被撤消。