CreateProcess 函数原型:
BOOL CreateProcess( PCTSTR pszApplicationName, PTSTR pszCommandLine, PSECURITY_ATTRIBUTES psaProcess, PSECURITY_ATTRIBUTES psaThread, BOOL bInheritHandles, DWORD fdwCreate, PVOID pvEnvironment, PCTSTR pszCurDir, PSTARTINFO psiStartInfo, PROCESS_INFORMATION ppiProcInfo);
当一个线程调用 CreateProcess 函数时,系统会创建一个进程内核对象,并将这个内核对象的使用计数初始化为 1,系统然后为这个新进程创建一个虚拟地址空间并将可执行文件的代码和数据还有所有需要的 DLL 加载到这个地址空间中,接着,系统为新进程的主线程创建了一个线程内核对象,这个线程开始执行由链接器设置的 C/C++ 运行时启动代码,最后才是你提供的 main 函数开始执行。如果系统成功地创建了新进程和主线程,CreateProcess 返回 TRUE。注意:CreateProcess 函数在进程被完成初始化完成之前返回,这意味着,如果一个所需的 DLL 没有找到或没有正确初始化,进程会终止,但父进程不会知道任何有关初始化的问题。
pszApplicationName 和 pszCommandLine 参数
pszCommandLine 是一个指向 TCHAR 类型的指针,CreateProcess 函数期望你传递一个非常量字符串,这个函数内部会对这个参数进行修改,如果将这个参数设置为常量字符串,会发生访问冲突错误。
pszCommandLine 参数指定了 CreateProcess 函数用来创建新进程的一个完整的命令行,如果 CreateProcess 函数接收到一个 pszCommandLine 参数,函数会假设这个字符串表示一个可执行文件,并假设这个文件有一个 .exe 后缀,CreateProcess 函数会以下面的顺序来搜索这个可执行文件:
1. 包含调用进程可执行文件的目录
2. 调用进程的当前目录
3. Windows\System32 目录,这个目录可通过 GetSystemDircetory 获得
4. Windows 目录
5. 列在 PATH 环境变量中的目录
如果给定的文件名包含了全路径名,系统会只在指定路径中查找可执行文件而不会再搜索以上的目录。
大多数情况下 pszApplicationName 这个参数设置为 NULL,可是如果设置了这个参数,系统会执行这个参数表示的可执行文件,系统不会假设这个参数指定的文件具有 .exe 后缀,所以你必须提供一个完整的名称,系统也不会在以上指定的目录中查找相应的可执行文件,它只会搜索当前目录,如果当前目录中没有可执行文件,CreateProcess 会失败。CreateProcess 有这个参数的原因是为了支持 POSIX 子系统。
psaProcess、psaThread 和 bInheritHandles 参数
psaProcess 和 psaThread 参数表示创建的进程对象和线程对象期望的安全设置,可以将这两个参数都设置为 NULL,系统会给这些对象设置默认的安全描述符。用户还可通过设置这两个参数的 SECURITY_ATTRIBUTES 结构的 bInheritHandle 成员为 TRUE,父进程之后创建的子进程可以继承这些进程和线程的句柄。例如,进程 A 创建了进程 B,将 psaProcess 参数的 SECURITY_ATTRIBUTES 结构的 bInheritHandle 成员设置为 TRUE,将 psaThread 参数的 SECURITY_ATTRIBUTES 结构的 bInheritHandle 成员设置为 FALSE,CreateProcess 返回后,ppiProcInfo 参数结构分别返回了进程 B 的进程和线程句柄,这两个句柄在进程 A 的句柄表中返回的进程句柄标志为可继承,而线程句柄会被打上不可继承标志。之后的某个时刻,进程 A 又创建了进程 C,并向 CreateProcess 函数传递了一个值为 TRUE 的 bInheritHandle 参数,进程 C 创建成功后,进程 A 中的所有可继承句柄都会复制到进程 C 的句柄表中,这样,进程 B 的句柄在进程 C 中可以正确地访问到,而 B 进程的线程句柄不会被进程 C 看到。
bInheritHandle 参数,如上所述,这个参数设置为 TRUE 表示可将父进程的句柄表中可继承句柄继承到新创建的子进程中去。继承的句柄与父进程有相同的句柄值和相同的访问权限。
fdwCreate 参数
这个参数标志指出新进程怎样被创建,还允许你指定一个优先级类型。
pvEnvironment 参数
这个参数是一个向新进程传递的环境变量块字符串的地址,父进程可以使用 GetEnvironmentStrings 函数来取得这个地址并传递给 CreateProcess 函数,大多数情况下传递一个 NULL 给这个函数,这样函数也会在内部调用 GetEnvironmentStrings 函数以便将父进程的环境变量块继承给子进程。
pszCurDir 参数
这个参数允许父进程设置子进程的当前驱动器和当前目录,如果这个参数为 NULL,新进程的当前驱动器和当前目录与父进程的相同,如果这个参数不为 NULL,必须在路径中指定一个驱动器号。
psiStartInfo 参数
当前创建一个新的进程时,由 Windows 使用这个参数的数据结构的成员。大多数应用程序都希望使用这个参数的默认值创建新的进程,所以一般只是简单地将这个数据结构的所有成员置 0,如下所示:
STARTUPINFO si = { sizeof(STARTUPINFO) };
如果你没有将这个结构的成员置 0,它的成员会包含一些垃圾数据,那么 CreateProcess 可能会失败或者新进程会有一些不可预料的行为。
ppiProcInfo 参数
这是一个指向 PROCESS_INFOMATION 结构的指针,你必须为这个结构分配空间,CreateProcess 会在返回之前初始化这个结构。
在子进程运行之前,系统创建了一个子进程的内核对象和子进程主线程的内核对象,并将这两个对象的初始计数设置为 1,CreateProcess 返回之前,会创建与父进程相关的两个句柄并分别初始化 PROCESS_INFORMATION 结构的 hProcess 和 pThread 两个成员,这样,子进程内核对象和子进程主线程内核对象的使用计数又分别增加了 1。如果父进程不使用这个两个句柄,应该在函数返回之后立即关闭这两个句柄,以免造成资源泄漏。
当前一个进程的内核对象被创建后,系统会赋于这个内核对象一个唯一的标识符,同样,也会有一个主线程对象的唯一标识符。这个标识符系统保证它的唯一性,但是,当一个内核对象被释放后,这个标识符可能会被立即用于另一个内核对象。如果你想要使用这个标识符完成一些操作,必须记住不能将这个标识符对应的内核对象释放,所以当这个标识符使用完成后才调用 CloseHandle 才是最安全的方法。使用 GetCurrentProcessId 来获取当前进程的标识符,使用 GetProcessId 来获取指定内核对象句柄的标识符,使用 GetProcessIdOfThread 来获取线程所在进程的标识符。