您现在的位置: 365建站网 > 365文章 > CreateProcess 失败怎么定位?错误码 2/5/740/193 含义与参数陷阱详解

CreateProcess 失败怎么定位?错误码 2/5/740/193 含义与参数陷阱详解

文章来源:365jz.com 点击数:11 更新时间:2026-09-01 23:45

写脚本、做外挂、调工业软件时,几乎每个人都见过这句报错:CreateProcess 失败。它不是一条具体错误,而是“Win32 创建进程这一步没成功”的统称,真正的原因藏在 GetLastError() 返回的错误码里。本文按“如何拿到错误码 → 常见错误码逐个击破 → 参数写法陷阱”三层展开。

Windows CreateProcess 失败错误码排查

一、先把错误码打出来

MSDN 对 CreateProcessW 的定义写得很明确:函数返回 FALSE 只说明“没建起来”,必须立刻调用 GetLastError() 才能拿到根因(https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw )。

if (!CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
    DWORD e = GetLastError();
    LPSTR buf = NULL;
    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                   NULL, e, 0, (LPSTR)&buf, 0, NULL);
    printf("CreateProcess failed: %lu - %s\n", e, buf ? buf : "");
    LocalFree(buf);
}

C# 里对应的是 Win32Exception(Marshal.GetLastWin32Error()).Message;Python 的 subprocess 抛 FileNotFoundError 或 OSError: [WinError 2],那个数字就是同一个错误码。

二、高频错误码对照

官方错误码清单见 https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- ,实际最常撞上的这几个:

  • 2(ERROR_FILE_NOT_FOUND):可执行文件路径找不到。占九成案例。lpApplicationName 传 NULL 时,系统只从 lpCommandLine 的第一段解析程序名,解析规则是“先当命令、再当文件名”,路径里有空格会直接被截断。
  • 5(ERROR_ACCESS_DENIED):权限不足。目标 exe 在 Program Files 等受保护目录、或需要管理员权限(manifest 里 requireAdministrator)、或被安全软件拦截。UAC 开启时从非提权进程创建提权程序,返回的就是 5 而不是弹窗。
  • 740(ERROR_ELEVATION_REQUIRED):明确要求提升。必须用 ShellExecuteEx 带 SEE_MASK_NOCLOSEPROCESS 走 runas,CreateProcess 本身无法触发 UAC 提权。
  • 193(ERROR_BAD_EXE_FORMAT):32/64 位不匹配。64 位进程去加载 32 位 exe 或反过来,或目标文件其实是脚本/损坏文件。
  • 1260(ERROR_ACCESS_DISABLED_BY_POLICY):组策略/SRP/AppLocker 拦截。企业环境里排查顺序应先看 gpedit.msc → 计算机配置 → Windows 设置 → 安全设置 → 应用程序控制策略。
  • 87(ERROR_INVALID_PARAMETER):参数结构没填。最常见是 STARTUPINFOW.cb 忘记 ZeroMemory 后设成 sizeof(STARTUPINFOW)。
  • 267(ERROR_DIRECTORY):lpCurrentDirectory 传了个不存在或不是目录的路径。

三、参数写法的四个经典陷阱

1. 命令行引号。CreateProcess 的第二个参数是可写缓冲区(老式 API 要求,W 版建议传可修改的 wchar_t 数组而不是常量字符串),且路径含空格必须整体加引号:

wchar_t cmd[] = L""C:\\Program Files\\app\\tool.exe" -arg1 "空格参数"";
CreateProcessW(NULL, cmd, ...);

不加引号会被解析成程序名 C:\Program → 错误码 2。

2. 扩展名不能省。与 cmd 不同,CreateProcess 不会自动补 .exe。传 notepad 会失败,必须 notepad.exe 或写全路径。

3. 打开的不是可执行文件。对 .txt、.docx 直接 CreateProcess 一定失败(193 或无效参数)。这类“用关联程序打开文档”的需求应该用 ShellExecuteEx,它走的是注册表文件关联。

4. 工作目录与 PATH。lpCurrentDirectory 为空时新进程继承调用者当前目录;相对路径的程序名解析依赖调用者的 PATH,而不是硬编码 System32。服务进程(LocalSystem)里 PATH 常常只有 System32,导致“命令行手动跑没问题、服务里失败”。

四、语言层封装的差异

  • C#:Process.Start() 默认内部走 ShellExecute(UseShellExecute = true),能触发 UAC、能用文件关联,但拿不到标准输入重定向。设成 false 后才是真正的 CreateProcess,此时若文件名不存在会抛 Win32Exception,NativeErrorCode 即错误码。
  • Python:subprocess.Popen 在 Windows 下默认 shell=False 走 CreateProcess;传一个字符串而不是列表时,Windows 不做参数转义,含空格/引号会错位——这是 FileNotFoundError 与“参数被吞”两大坑的成因。shell=True 则变成先起 cmd.exe /c,报错会变成 cmd 的退出码而非 2。
  • Go:exec.Command 的 Err 可用 errors.As(&ee, &exec.ExitError{}) 判断退出码,创建失败则是 *os.PathError,里面带 syscall.Errno。

五、排查顺序(按命中率排)

  • ① 先用资源管理器确认那个 exe 双击能打开;
  • ② 打印错误码,别看“CreateProcess 失败”这五个字就猜;
  • ③ 路径加引号 + 写全扩展名 + 绝对路径三件事一次改完;
  • ④ 用管理员身份重试,区分是权限还是文件问题;
  • ⑤ 打开 Process Monitor(微软官方 Sysinternals 工具包),按 Process Name 过滤,看 NAME NOT FOUND、PATH NOT FOUND、ACCESS DENIED 的具体路径——它能直接告诉你系统在找哪个文件、因为什么被拒;
  • ⑥ 最后再查杀软白名单和组策略。

六、常见问题 FAQ

问:为什么代码里手动指定 System32 路径就能跑,换目录就报 5?
答:可能是目标 exe 需要写自身目录(临时文件、日志),受保护目录写入被拒;把程序放到可写目录,或在 lpCurrentDirectory 指定可写目录。

问:CreateProcess 成功但子进程一闪就没了?
答:说明创建没问题,是子进程自己退出。用 WaitForSingleObject(pi.hProcess, INFINITE) + GetExitCodeProcess 取退出码;或让它挂到控制台(CREATE_NEW_CONSOLE)看到报错。

问:怎么让控制台程序的输出被我读走?
答:CREATE_NO_WINDOW(0x08000000)配合 STARTUPINFOW 的 hStdInput/hStdOutput/hStdError 指向匿名管道,同时 dwFlags |= STARTF_USESTDHANDLES、bInheritHandles = TRUE。少任一项,重定向都会静默失败。

问:服务里创建进程为什么看不到界面?
答:Session 0 隔离。Windows Vista 起服务运行在会话 0,GUI 无法显示在用户桌面,需要用户端进程或计划任务的交互模式来承载界面。

一句话:CreateProcess 失败 的万能解法是“错误码 + Process Monitor”。拿到 2 就查路径与引号,拿到 5/740 就查提权,拿到 193 就查位数,剩下交给 Procmon。

生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »