【问题标题】:WinAPI - CreateProcessW fails when C:\Program file existsWinAPI - C:\Program 文件存在时 CreateProcessW 失败
【发布时间】:2018-07-09 02:28:05
【问题描述】:

我运行以下 C 代码来创建一个进程。 exe文件位于C:\Program Files\Exes\Start\process1.exe

CreateProcessW(NULL, (char*) exePath, 
                           NULL, NULL, TRUE, 
                           flags, 
                           NULL, NULL, &startupInfo, &processInformation);

现在一些计算机随机有这个名为 Program 的文件位于 C:\Program,这会导致创建过程失败并出现以下错误:

%1 不是有效的 Win32 应用程序。

除了重命名文件之外,还有其他方法可以解决此问题,因为有几十个 Windows Vms 执行此 C 代码。这个错误在一些机器上随机发生。

【问题讨论】:

  • 将 EXE 路径作为 first 参数传递 - 指定 EXE 路径的那个?
  • CreateProcessW 的第二个参数必须是宽字符串。 (char *)exePath 是错误的,您应该会看到编译器错误消息。如果没有,请调整您的编译器设置。
  • @immibis 所以我将 exepath 作为应用程序名称(第一个参数)和命令行参数(第二个参数)作为 null 传递?
  • 您可以将命令行作为第二个参数传递。我不确定它是否可以为 NULL,但如果程序不关心,那么您可以使用空白字符串或程序名称。
  • 如果只是程序名,则使用CreateProcessW(L"C:\\Program Files\\Exes\\Start\\process1.exe", NULL, ...),否则,声明wchar_t buf[300];,填上,然后输入CreateProcessW(NULL, buf, ...)

标签: c winapi createprocess


【解决方案1】:

这段代码是如何编译的? CreateProcessW 的第二个参数定义为 LPWSTR,这意味着它应该只接受一个宽字符串。

下一个问题:由于演员阵容,无法确定exePath 的来源是什么。这是重要的原因是第二个参数被定义为LPWSTR,而不是LPCWSTR(即const宽字符串),原因是:CreateProcessW可能写入缓冲区.

然后是第三个问题 - 你的 exe 路径中有一个空格。当作为第二个参数(lpCommandLine)传递时,CreateProcess 有一些 derpy 逻辑来猜测 exe 名称的结束位置和命令行的开始位置。这需要对命令行的 exe 路径部分进行引号编码。

当您拥有 exe 的完整路径且没有参数时,最简单/最安全的做法是将其作为 lpApplicationName 参数传递。这是一个 const 参数,它避免了如果您的命令行源是常量字符串文字等可能导致的任何潜在的未定义行为。并且仅用作要执行的 exe 的路径,因此没有(也不能)有任何引用要求。

    CreateProcess(exePath,NULL,...);

除此之外:使用CreateProcess 的两个参数基本上可以让您将启动的应用程序的 argv[0] 设置为您想要的任何内容。因此,您可以从特定路径/exe 名称运行应用程序,但使 argv[0] 指向其他路径或 exe 名称。

要将参数传递给 exe,而不是传递完整(引号括起来)路径,您可以执行以下简单操作:

    WCHAR cmdLine[] = TEXT("console1.exe --version");
    CreateProcessW(exePath,cmdLine,...);

编辑感谢 Paul Sanders 和其他 cmets 指出了未引用的 exe 路径,这也使我完全打破了我原来的答案,这个答案在我没有意识到的情况下解决了这个问题。 感谢 RbMm 发现我的答案错误,感谢 eryksun 向我展示了近 20 年来我一直在错误地阅读文档。

【讨论】:

  • CreateProcessW 可能会写入缓冲区。 是的,很可爱,不是吗?除了您帖子中的所有好东西,但是,按照编码,他可能只需要引用路径。
  • 不要吹毛求疵,但重要的是要澄清 Windows 进程本身并不具有 argv,这是与 exec* 系列函数相关的 Unix 概念。为了兼容性,Windows CRT 的[w]main 入口点有argv,但加载程序调用的真正入口点是传递一个指向进程环境块(PEB) 的指针。这包括ProcessParameters,包括ImagePathNameCommandLine,这当然是多余的,因为应用程序可以调用GetModuleFileNameW(NULL, ...)GetCommandLineW()
  • 对于第一个参数使用封闭\" 是错误的。这里必须是准确的文件名。 WCHAR exePath[] = TEXT("\"C:\\Program Files\\Exes\\App.exe\""); CreateProcessW(exePath,NULL,...);此代码失败
  • @ChrisBecke,lpApplicationName 不是命令行。它是可执行路径,因此不应引用它,就像您不会引用传递给 CreateFile 等的文件路径一样
  • @ChrisBecke,如果lpCommandLineNULL,那么lpApplicationName 被引用为命令行。如果lpApplicationNameNULL,则通过解析lpCommandLine并调用SearchPathW在基础API的自定义搜索路径中查找可执行文件来确定图像路径。
【解决方案2】:

Chris 在那里提出了很多好的观点,您应该接受它们,但正如 immibis 所说,真正的问题是您错误地使用了 CreateProcess API。如果它包含任何空格,则您的操作方式需要引用 exepath

因此,在您解决了 Chris 提出的所有问题之后,请执行以下操作:

CreateProcessW (exePath, NULL,
                NULL, NULL, TRUE, 
                creation_flags, 
                NULL, NULL, &startupInfo, &processInformation);

即只需交换第一个和第二个参数(也许可以为flags 使用更具描述性的名称,就像我在这里所做的那样)。

如果需要,您可以在第二个参数中传递您可能需要的任何命令行参数。请read the docs


编辑(让我的代表回来:)

另外,正如 Chris 所说,exepath 在这里需要是一个宽字符串,所以我取出了你的(冗余)强制转换,它实际上会产生编译器错误(C++)或警告(C)(所以,警告,猜猜,因为你的帖子被标记为 C,谢谢@Barmak)。

无论如何,如果你现在没有得到一个,那么exepath 必须实际上已经成为一个宽字符串,所以一切都很好。如果不是,那么您显然需要解决这个问题,但我认为这整个问题只是您帖子中的一个错字,因为您显然确实让您的代码工作以报告您观察到的行为。

【讨论】:

  • 这取决于exePath 仍然是什么,如果它是一个窄字符串那么这是不好的
  • @MM 当然可以,但我在参考 Chris 的回答时说过
  • 答案应该是独立的。如果有重要的观点,需要在答案的正文中提出。如果您引用的答案被删除或编辑了,那该怎么办?
  • @David 他为什么要那样做?不,我不打算把它放进去。我去掉了演员表,这就够了。如果 OP 收到编译器警告,那么他应该采取行动。我的猜测:exepath 已经是一个很宽的字符串,而演员表(这将没有效果)只是一个疏忽。否则,代码将根本无法运行(很明显,从 OP 告诉我们的内容来看,它确实有点)。
  • @David Sigh。编辑了我的帖子(我认为这是一个好的),我希望你批准。
猜你喜欢
  • 2017-06-19
  • 1970-01-01
  • 2012-02-22
  • 2013-02-26
  • 2021-08-28
  • 1970-01-01
  • 2023-02-17
  • 1970-01-01
  • 2021-12-22
相关资源
最近更新 更多