CreateProcess 是 Windows API 中的一个核心函数,用于创建一个新的进程和它主线程,这个函数功能非常强大,但也相对复杂,下面我将从基本用法、参数详解、完整示例以及常见问题几个方面进行说明。

(图片来源网络,侵删)
CreateProcess 是什么?
CreateProcess 函数可以从一个可执行文件(.exe)启动一个新的进程,并在这个新进程中运行指定的程序。
关键点:
- 进程:一个正在运行的程序实例,它拥有独立的虚拟地址空间、系统资源(如文件句柄)和安全上下文。
- 线程:在进程内执行的实体,每个进程至少有一个主线程。
CreateProcess会自动为新进程创建一个主线程。 - Windows API:
CreateProcess不是 C 语言标准库的一部分,而是微软 Windows 操作系统提供的应用程序编程接口,你需要在 Windows 环境下编译你的 C 代码才能使用它。
函数原型
你需要包含必要的头文件并链接相应的库。
#include <windows.h> // 包含 Windows API 的头文件 #include <tchar.h> // 包含 TCHAR 相关的定义,用于 Unicode/ANSI 兼容 // 链接 user32.lib 库 // 在 Visual Studio 中通常不需要手动设置,在 GCC/MinGW 中可以使用 -luser32
CreateProcess 的函数原型如下:

(图片来源网络,侵删)
BOOL CreateProcess( [in, optional] LPCSTR lpApplicationName, [in, out, optional] LPSTR lpCommandLine, [in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes, [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] BOOL bInheritHandles, [in] DWORD dwCreationFlags, [in, optional] LPVOID lpEnvironment, [in, optional] LPCSTR lpCurrentDirectory, [in] LPSTARTUPINFOA lpStartupInfo, [out] LPPROCESS_INFORMATION lpProcessInformation );
参数详解
CreateProcess 有 10 个参数,理解它们是正确使用该函数的关键。
| 参数 | 类型 | 描述 |
|---|---|---|
lpApplicationName |
LPCSTR |
要执行的程序名,可以是 .exe 文件的完整路径,如果为 NULL,则从 lpCommandLine 中解析。 |
lpCommandLine |
LPSTR |
命令行字符串,包含了要执行的程序名和所有参数。lpApplicationName 为 NULL,这个字符串的第一个 token 必须是程序名。 |
lpProcessAttributes |
LPSECURITY_ATTRIBUTES |
新进程的安全属性,通常设置为 NULL,表示使用默认安全描述符,且句柄不能被继承。 |
lpThreadAttributes |
LPSECURITY_ATTRIBUTES |
新主线程的安全属性,通常也设置为 NULL。 |
bInheritHandles |
BOOL |
是否继承句柄,如果为 TRUE,新进程会继承父进程所有可继承的句柄,通常为 FALSE。 |
dwCreationFlags |
DWORD |
创建标志,用于控制进程的创建方式。CREATE_NEW_CONSOLE(创建新控制台窗口)、DETACHED_PROCESS(不继承父进程控制台)等,可以组合使用(用 )。 |
lpEnvironment |
LPVOID |
新进程的环境块,如果为 NULL,新进程将继承父进程的环境变量。 |
lpCurrentDirectory |
LPCSTR |
新进程的当前工作目录,如果为 NULL,新进程将继承父进程的当前目录。 |
lpStartupInfo |
LPSTARTUPINFO |
指向 STARTUPINFO 结构的指针,用于指定新进程的主窗口如何显示(如标题、位置、大小等),必须初始化此结构。 |
lpProcessInformation |
LPPROCESS_INFORMATION |
指向 PROCESS_INFORMATION 结构的指针,函数成功后会返回新进程和主线程的信息(如句柄和ID)。 |
核心结构体
STARTUPINFO
在调用 CreateProcess 之前,你需要初始化这个结构体。
STARTUPINFO si; ZeroMemory(&si, sizeof(si)); // 将结构体所有成员初始化为0 si.cb = sizeof(si); // 必须设置此成员为结构体的大小 si.dwFlags = STARTF_USESHOWWINDOW; // 告诉系统我们使用了 wShowWindow 成员 si.wShowWindow = SW_SHOW; // 显示窗口 (SW_HIDE 可以隐藏窗口)
PROCESS_INFORMATION
这个结构体由 CreateProcess 填充,用于获取新进程的信息。
PROCESS_INFORMATION pi; // 在调用 CreateProcess 之前,通常也将其清零 ZeroMemory(&pi, sizeof(pi));
完整示例
这个示例演示了如何启动 Windows 自带的记事本程序 (notepad.exe)。

(图片来源网络,侵删)
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int main() {
// 1. 定义和初始化所需的变量和结构体
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
// 设置 STARTUPINFO 结构
si.cb = sizeof(si);
// 2. 定义要执行的程序和命令行
// 这里我们启动记事本
LPCSTR applicationName = "notepad.exe";
// 命令行参数,lpApplicationName 不为 NULL,这个参数可以简单设为 NULL
LPSTR commandLine = NULL;
// 3. 调用 CreateProcess 函数
// CREATE_NEW_CONSOLE 表示为控制台程序创建一个新的控制台窗口
// DETACHED_PROCESS 表示新进程不继承父进程的控制台(适用于非控制台程序)
if (CreateProcess(
applicationName, // 应用程序名
commandLine, // 命令行
NULL, // 进程安全属性
NULL, // 线程安全属性
FALSE, // 不继承句柄
CREATE_NEW_CONSOLE, // 创建标志
NULL, // 使用父进程的环境变量
NULL, // 使用父进程的当前目录
&si, // STARTUPINFO 指针
&pi // PROCESS_INFORMATION 指针
)) {
// 4. 如果函数成功,处理返回的信息
_tprintf(_T("进程创建成功!\n"));
_tprintf(_T("进程 ID: %d\n"), pi.dwProcessId);
_tprintf(_T("线程 ID: %d\n"), pi.dwThreadId);
_tprintf(_T("进程句柄: %p\n"), pi.hProcess);
_tprintf(_T("线程句柄: %p\n"), pi.hThread);
// 5. 等待新进程结束(可选)
// WaitForSingleObject 会阻塞当前线程,直到指定的对象(这里是进程)被 signaled(进程结束)
WaitForSingleObject(pi.hProcess, INFINITE);
// 6. 关闭句柄(非常重要!)
// 进程和线程句柄在使用后必须关闭,否则会造成资源泄露
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
_tprintf(_T("记事本程序已关闭,\n"));
} else {
// 7. 如果函数失败,输出错误信息
// 使用 GetLastError() 获取具体的错误代码
DWORD error = GetLastError();
_tprintf(_T("创建进程失败!错误代码: %d\n"), error);
// 可以使用 FormatMessage 来获取更友好的错误信息
// LPSTR msgBuffer = NULL;
// FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
// NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// (LPSTR)&msgBuffer, 0, NULL);
// printf("错误描述: %s\n", msgBuffer);
// LocalFree(msgBuffer);
}
return 0;
}
如何编译和运行:
- Visual Studio: 创建一个新的 C++ 控制台项目(即使写 C 代码也可以),将代码粘贴进去,直接运行。
- GCC/MinGW (命令行):
gcc -o myprocess myprocess.c -luser32 ./myprocess
重要注意事项和常见问题
-
资源泄露 (Resource Leak)
- 问题:
CreateProcess返回的hProcess和hThread是系统资源,如果在使用后不调用CloseHandle关闭它们,即使进程结束了,这些句柄仍然会存在于系统中,直到你的父进程退出,这会耗尽系统句柄资源。 - 解决方案: 在使用完进程和线程信息后,务必调用
CloseHandle(pi.hProcess)和CloseHandle(pi.hThread)。
- 问题:
-
lpApplicationNamevslpCommandLinelpApplicationName: 更安全、更高效,系统直接使用这个路径来加载程序,避免了复杂的命令行解析。lpCommandLine: 更灵活,可以方便地传递带空格的参数,但如果lpApplicationName为NULL,系统需要自己解析命令行来提取程序名,这可能导致问题(如果程序名或路径中包含空格但没有用引号括起来)。- 最佳实践: 如果可能,尽量使用
lpApplicationName,如果必须使用命令行参数,请确保正确地用引号将路径和参数括起来。
-
等待进程结束
- 父进程可以决定是否等待子进程结束,如果不等待,父进程会继续执行自己的代码,而子进程在后台运行。
WaitForSingleObject(pi.hProcess, INFINITE)是一个简单的方法,它会阻塞父进程,直到子进程完全退出。- 对于更复杂的场景(如需要同时等待多个对象或设置超时),可以使用
WaitForMultipleObjects。
-
错误处理
CreateProcess失败时返回FALSE。永远不要只检查返回值,一定要调用GetLastError()来获取具体的错误码,这能帮助你快速定位问题所在(文件未找到、权限不足、路径无效等)。
-
Unicode 支持
- 上面示例使用了
ANSI版本的CreateProcessA,Windows API 同时提供Unicode版本CreateProcessW。 - 在现代 Windows 开发中,推荐使用
Unicode版本。TCHAR宏可以帮助你编写兼容ANSI和Unicode的代码,如果你的项目字符集设置为 "Unicode",LPCSTR会自动映射到LPCWSTR(宽字符字符串)。
- 上面示例使用了
