C语言CreateProcess函数如何正确使用?

99ANYc3cd6
预计阅读时长 22 分钟
位置: 首页 C语言 正文

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

c语言createprocess函数
(图片来源网络,侵删)

函数概述

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       // 进程信息
);

这个函数的参数非常多,看起来很吓人,但实际上很多参数可以简单地设为 NULL0 来使用默认值。

c语言createprocess函数
(图片来源网络,侵删)

参数详解

让我们逐一分解这些参数:

lpApplicationName (应用程序名)

  • 类型: LPCSTR (常量字符串指针)
  • 说明: 指定要执行的程序的可执行文件路径。"C:\\Windows\\System32\\notepad.exe"
  • 使用: 通常可以设为 NULL,并将完整路径放在 lpCommandLine 的开头,这是更常见的用法,因为它可以和命令行参数放在一起。

lpCommandLine (命令行)

  • 类型: LPSTR (字符串指针)
  • 说明: 指定要传递给新进程的完整命令行。lpApplicationNameNULL,则系统会从这个字符串中解析出可执行文件名。
  • 重要: 如果你的路径或参数中包含空格,必须用双引号 把它们括起来"\"C:\\My Programs\\my app.exe\" -option1"
  • 使用: 这是最常用的参数之一,如果不需要特殊命令行,可以设为 NULL

lpProcessAttributeslpThreadAttributes (进程/线程安全属性)

  • 类型: 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 成员设置为结构体大小,其余成员设为 0NULL

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;
}

如何编译和运行:

  1. 将代码保存为 create_process.c
  2. 打开 Visual Studio 的开发者命令提示工具 (x64 Native Tools Command Prompt)。
  3. 使用 cl 命令进行编译:
    cl create_process.c
  4. 运行生成的 create_process.exe,你会看到一个记事本窗口弹出,控制台会打印出记事本的进程ID和线程ID,当你关闭记事本后,控制台程序才会结束。

总结与注意事项

  1. 平台限制: CreateProcess 是 Windows 独有的,如果你需要编写跨平台代码,可以考虑使用标准 C 库的 system() 函数,但它的功能远不如 CreateProcess 强大和灵活。
  2. 资源管理: CreateProcess 返回的 hProcesshThread 是系统内核对象,使用完毕后必须调用 CloseHandle 来关闭它们,否则会导致句柄泄漏,最终耗尽系统资源。
  3. 路径和空格: 处理包含空格的路径时,务必使用双引号。
  4. 等待与同步: 如果父进程在子进程结束前就退出了,子进程可能会变成一个由系统初始化进程(csrss.exe)管理的“孤儿进程”,父进程需要调用 WaitForSingleObject 来等待子进程执行完毕,特别是当父进程需要根据子进程的退出码进行后续操作时。
  5. 安全性: 对于从不可信来源获取的命令行参数,直接拼接后传给 CreateProcess 可能存在命令注入风险,需要谨慎处理或使用更安全的 API。
c语言createprocess函数
(图片来源网络,侵删)
-- 展开阅读全文 --
头像
织梦如何仿制网站的JS和Flash文件?
« 上一篇 12-02
dede channel list与普通channel列表有何区别?
下一篇 » 12-02

相关文章

取消
微信二维码
支付宝二维码

目录[+]