CreateProcess 是 Windows API 的一部分,而不是标准 C 语言库(如 stdio.h)的函数,你只能在 Windows 操作系统上使用它,并且需要包含相应的头文件和链接相应的库。

(图片来源网络,侵删)
函数概述
CreateProcess 的主要功能是创建一个新的进程(及其主线程),并指定这个新进程要运行的程序。
你可以把它想象成:在你的程序(父进程)中,启动另一个程序(子进程),并且你可以对这个子进程进行精细的控制,
- 指定它的命令行参数。
- 控制它的窗口显示方式(隐藏、正常、最大化等)。
- 重定向它的输入、输出和错误流。
- 让它在创建后立即暂停运行,等待你发出指令。
- 获取新进程的 ID 和主线程的 ID,以便后续管理。
函数原型
要使用 CreateProcess,你需要包含 windows.h 头文件。
#include <windows.h> BOOL CreateProcess( LPCSTR lpApplicationName, // 应用程序名 LPSTR lpCommandLine, // 命令行字符串 LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程安全属性 LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性 BOOL bInheritHandles, // 是否继承句柄 DWORD dwCreationFlags, // 创建标志 LPVOID lpEnvironment, // 环境块 LPCSTR lpCurrentDirectory, // 当前工作目录 LPSTARTUPINFOA lpStartupInfo, // 启动信息 LPPROCESS_INFORMATION lpProcessInfo // 进程信息 );
这个函数的参数非常多,看起来很吓人,但实际上很多参数可以简单地设为 NULL 或 0 来使用默认值。

(图片来源网络,侵删)
参数详解
让我们逐一分解这些参数:
lpApplicationName (应用程序名)
- 类型:
LPCSTR(常量字符串指针) - 说明: 指定要执行的程序的可执行文件路径。
"C:\\Windows\\System32\\notepad.exe"。 - 使用: 通常可以设为
NULL,并将完整路径放在lpCommandLine的开头,这是更常见的用法,因为它可以和命令行参数放在一起。
lpCommandLine (命令行)
- 类型:
LPSTR(字符串指针) - 说明: 指定要传递给新进程的完整命令行。
lpApplicationName为NULL,则系统会从这个字符串中解析出可执行文件名。 - 重要: 如果你的路径或参数中包含空格,必须用双引号 把它们括起来。
"\"C:\\My Programs\\my app.exe\" -option1"。 - 使用: 这是最常用的参数之一,如果不需要特殊命令行,可以设为
NULL。
lpProcessAttributes 和 lpThreadAttributes (进程/线程安全属性)
- 类型:
LPSECURITY_ATTRIBUTES - 说明: 用于设置新进程和主线程的安全描述符,如果你想让子进程也能继承这个句柄,可以在这里设置,对于简单的调用,通常都设为
NULL,表示使用默认的安全描述符。
bInheritHandles (是否继承句柄)
- 类型:
BOOL - 说明: 如果为
TRUE,新进程将继承父进程中所有可继承的句柄,如果你不进行句柄重定向(比如用管道),通常设为FALSE。
dwCreationFlags (创建标志)
- 类型:
DWORD - 说明: 这是一个非常重要的参数,它用位掩码来控制进程的创建方式,常用值包括:
CREATE_NEW_CONSOLE: 为新进程创建一个新的控制台窗口。DETACHED_PROCESS: 新进程不继承父进程的控制台,适用于无 GUI 程序。CREATE_NO_WINDOW: 不为新进程创建控制台窗口,对于命令行程序,它在后台静默运行。CREATE_SUSPENDED: 进程创建后立即挂起,直到你调用ResumeThread才开始执行,这对于调试或初始化非常有用。CREATE_UNICODE_ENVIRONMENT: 指定环境块使用 Unicode 字符串。- 默认值:
0。
lpEnvironment (环境块)
- 类型:
LPVOID - 说明: 指定新进程的环境变量块,如果为
NULL,新进程将继承父进程的环境变量。
lpCurrentDirectory (当前工作目录)
- 类型:
LPCSTR - 说明: 指定新进程的初始工作目录,如果为
NULL,则使用父进程的当前目录。
lpStartupInfo (启动信息)
- 类型:
LPSTARTUPINFOA(或LPSTARTUPINFOW用于宽字符) - 说明: 这是一个结构体,用于指定新进程的主窗口如何显示,以及输入、输出和错误句柄的重定向。
- 结构体成员:
cb: 结构体的大小,必须设置。lpReserved: 保留,必须为NULL。lpDesktop: 桌面名。lpTitle: 控制台窗口的标题。dwX,dwY: 窗口初始位置。dwXSize,dwYSize: 窗口初始大小。dwXCountChars,dwYCountChars: 控制台屏幕缓冲区大小。dwFillAttribute: 控制台文本和背景颜色。dwFlags: 启动标志,STARTF_USESTDHANDLES表示使用下面三个句柄成员。wShowWindow: 如何显示窗口 (SW_SHOW,SW_HIDE)。cbReserved2: 保留。lpReserved2: 保留。hStdInput,hStdOutput,hStdError: 标准输入、输出、错误的句柄,如果你想重定向子进程的 I/O,就需要在这里设置。
- 使用: 对于最简单的调用,你可以创建一个局部变量,将其
cb成员设置为结构体大小,其余成员设为0或NULL。
lpProcessInfo (进程信息)
- 类型:
LPPROCESS_INFORMATION - 说明: 这是一个指向
PROCESS_INFORMATION结构体的指针。CreateProcess会将新进程和主线程的信息填入这个结构体。 - 结构体成员:
hProcess: 新进程的句柄,你可以用这个句柄来操作进程,例如等待其结束 (WaitForSingleObject)。hThread: 新进程主线程的句柄。dwProcessId: 新进程的 ID (PID)。dwThreadId: 新进程主线程的 ID (TID)。
- 使用: 你必须分配一个
PROCESS_INFORMATION结构体变量,并将它的地址传给CreateProcess。
返回值
- 成功: 返回非零值 (
TRUE)。lpProcessInfo结构体中会填充有效信息。 - 失败: 返回零 (
FALSE),你可以调用GetLastError()函数来获取具体的错误码。
完整示例代码
下面是一个最基本、最常用的示例:用 C 语言调用 Windows 自带的记事本程序 (notepad.exe)。
#include <windows.h>
#include <tchar.h> // 为了使用 _tprintf 等函数,增强可移植性
int main() {
// 1. 定义并初始化 STARTUPINFO 结构体
STARTUPINFO si;
PROCESS_INFORMATION pi;
// 清零结构体,这是一个好习惯
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); // 必须设置此成员
ZeroMemory(&pi, sizeof(pi));
// 2. 定义要执行的命令
// 注意:如果路径有空格,必须用双引号括起来
LPCTSTR lpApplicationName = _T("C:\\Windows\\System32\\notepad.exe");
LPCTSTR lpCommandLine = _T("notepad.exe"); // 也可以直接在这里写,并设 lpApplicationName 为 NULL
// 3. 调用 CreateProcess
// 注意:lpCommandLine 包含路径,lpApplicationName 应设为 NULL
// 这里我们使用 lpApplicationName
if (CreateProcess(
lpApplicationName, // 应用程序名
NULL, // 命令行 (这里为NULL,因为路径在lpApplicationName中)
NULL, // 进程安全属性 (默认)
NULL, // 线程安全属性 (默认)
FALSE, // 不继承句柄
0, // 创建标志 (默认)
NULL, // 使用父进程的环境变量
NULL, // 使用父进程的当前目录
&si, // 指向 STARTUPINFO 的指针
&pi)) { // 指向 PROCESS_INFORMATION 的指针
_tprintf(_T("进程创建成功!\n"));
_tprintf(_T("进程 ID: %d\n"), pi.dwProcessId);
_tprintf(_T("线程 ID: %d\n"), pi.dwThreadId);
// 4. (可选) 等待子进程结束
// 如果不等待,父进程会继续执行,然后立即退出,子进程可能变成“孤儿进程”
WaitForSingleObject(pi.hProcess, INFINITE);
// 5. 关闭句柄
// 使用完句柄后一定要关闭,否则会造成资源泄漏
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
// 创建失败
_tprintf(_T("创建进程失败,错误码: %d\n"), GetLastError());
return 1;
}
return 0;
}
如何编译和运行:
- 将代码保存为
create_process.c。 - 打开 Visual Studio 的开发者命令提示工具 (x64 Native Tools Command Prompt)。
- 使用
cl命令进行编译:cl create_process.c
- 运行生成的
create_process.exe,你会看到一个记事本窗口弹出,控制台会打印出记事本的进程ID和线程ID,当你关闭记事本后,控制台程序才会结束。
总结与注意事项
- 平台限制:
CreateProcess是 Windows 独有的,如果你需要编写跨平台代码,可以考虑使用标准 C 库的system()函数,但它的功能远不如CreateProcess强大和灵活。 - 资源管理:
CreateProcess返回的hProcess和hThread是系统内核对象,使用完毕后必须调用CloseHandle来关闭它们,否则会导致句柄泄漏,最终耗尽系统资源。 - 路径和空格: 处理包含空格的路径时,务必使用双引号。
- 等待与同步: 如果父进程在子进程结束前就退出了,子进程可能会变成一个由系统初始化进程(
csrss.exe)管理的“孤儿进程”,父进程需要调用WaitForSingleObject来等待子进程执行完毕,特别是当父进程需要根据子进程的退出码进行后续操作时。 - 安全性: 对于从不可信来源获取的命令行参数,直接拼接后传给
CreateProcess可能存在命令注入风险,需要谨慎处理或使用更安全的 API。

(图片来源网络,侵删)
