17.2.2 进程的创建 .txt

来自「网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节」· 文本 代码 · 共 202 行

TXT
202
字号
17.2.2 进程的创建 . 
. 

为了启动一个进程,可以调用 CreateProcess函数,该函数的原型声明如下所示 : 
BOOL CreateProcess( 
LPCTSTR lpApplicationName , 
LPTSTR lpCommandLine, 

LPSECURITY_ATTRIBUTES lpProcessAttributes , 
LPSECURITY_ATTRIBUTES lpThreadAttributes, 
BOOL blnheritHandles, 
DWORD dwCreationFlags , 
LPVOID lpEnvironrnent , 
LPCTSTR lpCurrentDirectory, 
LPSTARTUPINFO lpStartuplnfo, 

LPPROCESS_INFORMATION lpProcessInformation
下面将详细地介绍 Cre ateProcess函数的各个参数的含义。 
. lpApplicationName 
一个指向 NULL终止的字符串,用来指定可执行程序的名称。该名称可以是该程序的完整路径和文件
名,也可以是部分名称。如果是后者, CreateProcess函数就在当前路径下搜索可执行文件名,但
不会使用搜索路径进行搜索。注意 :一定要加上扩展名,系统将不会自动假设文件名有一个" .exe"
扩展名。 
lpApplicationName参数可以为 NULL,这时,文件名必须是 lpCommandLine指向的字符串中的第一
个空格界定的标记。如果使用了包含空格的长文件名,那么应该使用引号将该名称包含起来,以表
明文件名的结束和参数的开始。否则文件名会产生歧义。例如 " c:\program files\sub 
dii\program name"这个字符串会被解释为多种形式,系统将按照下面的顺序来进行处理 :
c:\ program.exe files\sub dii\prograrn name

c:\ program files\sub.exe dir\prograrn name 

c: program files\sub dii\,program.exe name 

c:\program files\sub dir'\program name.exe 

. lpCommandLine 
一个指向 NULL终止的字符串,用来指定传递给新进程的命令行字符串。系统会在该字符串的最后增
加一个 NULL字符,并且如有必要,它会去掉首尾空格。
我们可以在 lpApplicationName参数中传递可执行文件的名称,在 lpCommandLine参
数中传递命令行的参数。但应注意,如果在 lpCommandLine参数中传递了一个可执行的文
件名,并且没有包含路径,那么这时 CreateProcess函数将按照以下顺序搜索可执行文件:
(1)
应用程序被装载的目录 

(2)
父进程的目录 


( 3) Windows Me/98/95: Windows系统目录 
Windows NT/2000: 32位 Windows系统目录,即c:\W的N1\System32目录(笔者使用的机器上,操作系
统装于 C盘,读者应根据自己机器上系统安装所在目录找到这个目录)。 
(4) Windows NT/2000: 16位 Windows系统目录:即 C:\.WINN1飞System目录 
(5) Windows目录 

(6) PATH环境变量中列出的目录


可以将文件名和命令行参数构造为一个字符串,一并传递给这个参数,当 CreateProcess函数分析 
lpCommandLine参数所指向的字符串时,它将查看该字符串中以空格分隔的第一个标记,并假设该标
记就是将要运行的可执行文件的名字。如果这个可执行文件的文件名没有扩展名,便假设它的扩展
名为(( .exe),当然,如果文件名包含全路径,那么系统将使 
 用全路径来查看可执行文件,并且不再搜索上述目录。 
lpCommandLine参数也可以为空,这时. CreateProcess函数将使用 lpApplicationName参数作为命
令行。这两个参数的区别在于,如果在 lpApplicationName参数中指定可执行文件名,那么系统将
只在当前目录下查找该可执行文件,如果没有找到,就失败返回。另外,系统不会为该文件加上一
个扩展名;在 lpCommandLine参数中指定可执行文件名,如果没有指定目录的话,系统就会按照上面
介绍的顺序查找该文件,若按此顺序在所有路径下都没有找到该文件. CreateProcess函数才失败返
回。另外,如果在 lpCommandLine参数指定文件名时没有加扩展名,那么这个函数会自动添加 .exe
扩展名。通常在调用 CreateProcess函数时,我们将可执行文件名和命令行参数都传递给 
IpCommandLine参数。 
. lpProcessAttributes和 lpThreadAttributes
参数 lpProcessAttributes和 lpThreadAttributes都是指向 SECURITY ATIRIBUTES结构体的指针。
当调用 CreateProcess函数创建新进程时,系统将为新进程创建一个进程内核对象和一个线程内核
对象,后者用于进程的主线程。而 lpProcessAttributes和 lpThreadAttributes这两个参数就是分
别用来设置新进程的进程对象和线程对象的安全性,以及指定父进程将来创建的其他子进程是否可
以继承这两个对象的句柄,在我们的程序中不需要创建其他的子进程,可以为这两个参数传递 NULL.
让系统为这两个对象赋予默认的安全描述符。 
.blnheritHandles
该参数用来指定父进程随后创建的子进程是否能够继承父进程的对象句柄。如果该参数为 TRUE.那
么父进程的每个可继承的打开句柄都能被子进程继承。继承的句柄与原始句柄拥有同样的值和访问
特权。在下面的例子中,我们将会把这个参数设置为 TRUE.让子进程继承父进程创建的管道的读写
句柄。
. dwCreationFIags 
指定控件优先级类和进程创建的附加标记。如果只是为了启动子进程,并不需要设置它创建的标记,
可以直接将此参数设置为 0。这个参数可以取的创建标志如表 17.2所示,这些标志可以利用或操作
符组合,从而同时设定多个标记。
表 17.2进程创建标志

进程创建附加标记 说明 
CREATE_BREAKAWAY_FROM_JoB  如果调用进程与某个作业相关联,那么指定此标志后,该进程的子进
程将不与 该作业相关联。如果调用进程没有与任一作业相关联,那么这个标志将没有作用 
CREATE_DEFAULT_ERROR_MODE  告诉系统,新进程不继承父进程使用的错误模式,而是将获得当前默
认的错误 模式 
CREATE_NEW_CONSOLE  告诉系统,为新进程创建一个新控制台窗口,而不是继承父进程的控件台窗
口。 该标志不能与 DETACHED]ROCESS标志一起使用 
CREATE NEW PROCESS GROUP  如果设定本标志,函数将创建一个新进程组,这个新进程是该新进程
组的根进 程 .进程组包括该根进程的所有子进程 . GenerateConsoleCtrlE vent函数使用进程组向
一组控件台进程发送 Ctrl+Break信号 
CREATE_NO_WINDOW  告诉系统不要为应用程序创建任何控制台窗口。可以使用本标志运行一个没有 
用户界面的控制台应用程序  
CREATE_SEPARATE_WOW_VDM  Windows Me/98/95不支持这个标志,在其他平台上运行 16位 Window s
应用程序时使用这个标志。它告诉系统创建一个单独的 DOS虚拟机 ( VDM ),并且在该 VDM中运行 16
位 Windows应用程序。按照默认设置,所有 16位 Window s应用程序都在单个共享的 V DM中运行。
在单独的 VDM中运行应用程序的优点 是:如果应用程序崩溃,它只会使单个 VDM停止工作,而在别
的 VDM中运行的其他程序仍然可以继续正常运行。另外,在单独的 VDM中运行的 16位 Window s应
用程序有它单独的输入队列 .这意味着如果一个应用程序临时挂起,在各个 VDM中的其他应用程序
仍然可以继续接收输入信息。运行多个 VDM的缺点是 :每个 VDM都要消耗大量的物理存储器  
CREATE SHARED WOW VDM  Windows Me/98/95不支持这个标志,在其他平台上运行 16位 Windows应
用程序时使用这个标志。按照默认设置,除非设定了 CREA:四_SEPARA四_WOW_ VDM标志,否则所有 16
位 Windows应用程序都必须在单个 VDM中运行。如 果注册表中 HKEY_LOCAL_MACHINE\s ystem'飞
CurrentControISet\Control\WOW 下的 DefaultSeparateVDM的值是: "yes",那么这个标志促使 
Create阶ocess函数重写此值,改变该默认行为特性,并且在系统共享的 VDM中运行新进程 
CREATE SUSPENDED  新进程创建后,它的主线程被挂起,而且直到调用了 ResumeThr臼d函数时才能
运行。这使得父进程能够修改子进程的地址空间中的内存,改变子进程的主线 程的优先级,或者在
进程有机会执行任何代码之前将进程添加给一个作业。在父进程修改了子进程后,它可以通过调用 
ResumeThread函数允许子进程开始执行 
CREATE_UNICODE_ENVIRON如ffiNT 告诉系统,子进程的环境块使用 Unicode字符集。按照默认设置,
进裂的环境 块使用的是 ANSI字符集 
DEBUG_PROCESS  如果设置此标志,父进程 (即调用进程〉被看作是一个调试程序,而新进程被 看
作是一个正被调试的进程,系统将被调试进程中发生的一切调试事件都通知给父m:程 

续表
进程创建附加标记 说明 
DEBUG ONLY THIS PROCESS  如果没有设置本标志,并且调用进程正被调试,那么新进程将成为调用
进程的 调试程序调试的另一个进程。如果调用进程不是一个正被调试的进程,则没有与调试相关的
行为发生 
DETACHED PROCESS  用于阻止基于 CUI的进程对它的父进程的控制台窗口的访问,并告诉系统将它
的输出发送到新的控制台窗口。如果基于 CUI的进程是由另一个基于 CUI的进程创建的,那么按照
默认设置,新进程将使用父进程的控制台窗口(当通过命 令外壳程序来运行 C编译器时,新控制台
窗口并不创建,它的输出将被附加在现有控制台窗口的底部〉。通过设定本标志,新进程将把它的
输出发送到一个新控制台窗口  

dwCreationFlags参数也用于控制新进程的优先级别,其取值如表 17.3所示。表 17.3进程优先级别
进程优先类别标志 说明  
IDLE_PRIORITY_CLASS  空闲  
BELOW NORMAL PRIORITY CLASS  低于正常  
NORMAL_PRIORITY_CLASS  正常  
ABOVE NORMAL PRIORITY CLASS  高于正常  
HIGH PRIORITY CLASS  高  
REALTIME PRIORITY CLASS  实时  

. IpEnvironment 
一个指向环境块的指针,如果此参数是 NULL,那么新进程使用调用进程的环境。通常都是给此参数
传递 NULL。 
. IpCurrentDirectory 
一个指向空终止的字符串,用来指定子进程当前的路径,这个字符串必须是一个完整的路径名,包
括驱动器的标识符,如果此参数为 NULL,那么新的子进程将与调用进程,即父进程拥有相同的驱动
器和日录。 
. lpStartuplnfo 
一个指向 STARTUPINFO结构体的指针,用来指定新进程的主窗口将如何显示。该结构体的定义如下
所示= 
typedef struct _STARTUPINFO { // si 
DWORD cb; , 
LPTSTR lpReserved; 
LPTSTR lpDesktop; 
LPTSTR lpTitle; 
DWORD dwX; 
DWORD dwY; 
DWORD dwXSize; 
DWORD dwYSize; 
DWORD dwXCountChars; 
DWORD dwYCountChars; 
DWORD dwFillAttribute; 
DWORD dwFlags; 
WORD wShowwindow; 
WORD cbReserved2; 
LPBYTE lpReserved2; 
HANDLE hStdlnput; 
HANDLE hStdOutput; 
HANDLE hStdError; 

} STARTUPI NFO, *LPSTARTUPINFO; 
可以看到, STARTUPINFO结构体的成员比较多,对这种拥有很多成员的结构体,在使用时井不需要
为其所有成员都赋值,那么如何才能知道哪些成员能满足我们的需要呢?这里为读者介绍一个技巧,
像这种结构体,开始时可以大概浏览一下,找到一些特殊成员,例如上述的 dwFl ags成员,通过其
字面上的意思,我们猜测该字段可能是用来设置一个标记。在一个结构体中的标记往往就是用来设
置在什么情况下应该使用该结构中的哪些成员。对这里的 dwFlags成员来说,如果选择、
STARπ'_USESTDHANDLES标记,那么将使用 STARTUP剧FO结构体中的 hStdlnput、 h StdOutput
和 hStdError成员设置所创建的新进程的标准输入、标准输出和标准错误句柄,也就是说,这时只
需要为 STARTUPINFO结构体中的这三个成员赋值即可。另外,在很多结构体中都有类似于 cb或 
nlens这样的成员,它们都是用来表示该结构体本身的大小,以字节为单位,通常都需要为此类成员
赋值,否则函数调用可能会失败。因此,在使用 STARπJPINFO结构体时,还应设置其 cb成员的值,
即该结构体的大小。 
. lpPrOCessInfOI mation 
这个参数作为返回值使用,是一个指向 PROCESS剧FORMA'咀ON结构体的指针,用来接收关于新进程
的标识信息。该结构体的定义如下所示: 
typedef struct _PROCESS_工NFORMATION { 
HANDLE hProcess; 
HANDLE hThread; 
DWORD dwprocessId; 

DWORD . dwThreadId; 
} PROCESS_INFORMATION; 
PROCESS INFORMATION结构有四个成员。前两个成员: hProcess和 hThread分别是标识新创建的进程
句柄和新创建进程的主线程句柄:后两个成员 : dwProcessId和 dwThreadId分别是全局进程标识符
和全局线程标识符,前者可以用来标识一个进程,后者可以用来标识一个线程。
当启动一个进程时,系统为会此进程分配一个标识符,同时这个进程中的线程也会被分配→个标识
符,在一个进程运行时,该进程的标识符和线程的标识符是惟一的,但应注
意,当该进程停止运行时,该进程的标识符和其线程的标识符可能会被系统分配给另一个进程和另
一个线程使用。如果一个函数调用依赖于进程的标识符或者线程的标识符,那么就要确保该进程当
前是处于运行状态,否则结果无法预料。

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?