为什么我的 Windows 桌面进程似乎立即退出且没有标准输出?

Why does my Windows desktop process appear to exit immediately with no stdout?

我有一个 "hello world" Windows 桌面应用程序,其来源来自 this official walkthrough

当我 运行 来自 Windows 子系统的 Linux 程序时,我得到了预期的行为:shell 块,等待进程退出,并且shell 提示只会在进程退出后重新出现。然后我可以检查进程退出代码(这里设置为 3,通过从 wWinMain 返回):

jim@LAPTOP-SMUS1UJN:/mnt/c/Users/james/source/repos/DesktopApp/x64/Release$ ./DesktopApp.exe  # blocks until I close the window ...
jim@LAPTOP-SMUS1UJN:/mnt/c/Users/james/source/repos/DesktopApp/x64/Release$ echo $?
3

但是,这不是我从命令提示符或 PowerShell 运行运行程序时得到的行为。在这里,进程启动了,但是shell声称进程立即退出,立即给我新的提示!但是这个进程显然没有退出,因为它创建的window还存在,我可以和它交互

在这两种情况下(Windows Linux 的子系统和 PowerShell),进程标准输出不会打印到终端。 (使用 std::cout << "test" 验证,不会向终端打印任何内容。)

就好像原始进程已经为 运行 win32 东西派生了一个守护进程。但我不认为这是正在发生的事情,因为 Windows Linux 的子系统至少会阻塞直到退出。

当我使用 Visual Studio 创建 "console" C++ 应用程序时,它的行为符合预期。奇怪的提前退出无输出行为只发生在我的 "desktop" win32 程序中。

那么,为什么命令提示符或 PowerShell 声称进程立即退出?它的标准输出在哪里?

Jonathan Potter 在对问题的评论中提供了关键指针;详细说明:

任何给定的 Windows 申请都属于这两个相互排斥的类别之一[1]:

  • GUI-子系统应用程序,如Notepad.exe:

    • 此类应用程序通常会创建 GUI windows,有时根本不会创建 UI。

    • 由于未与 控制台关联,它们不能写入标准输出 (stdout) 或标准错误 (stderr) 流。[2]

  • 控制台子系统 应用程序,例如findstr.exe:

    • 此类应用程序要么在调用时创建一个控制台window,要么在预先存在的[=]中运行 143=] 一个,通常由命令行 shell 创建,例如 cmd.exe 或 PowerShell。

    • 他们将输出写入标准输出 (stdout) and/or 标准错误 (stderr) 输出流。

(基于控制台)命令行 shells on Windows,例如 cmd.exe 和 PowerShell:

  • 调用控制台子系统应用同步并报告它们的退出代码:在cmd.exe中通过伪环境变量%ERRORLEVEL%,在PowerShell中通过变量$LASTEXITCODE

    • 此外,此类应用程序的 stdout and/or stderr 输出默认打印到控制台,但也可以捕获、重定向或传递到其他 console-subsystem 通过管道应用程序。
  • 调用GUI-子系统应用异步 默认情况下,即启动即忘式风格。

    • 也就是说,shell默认情况下既不等待应用程序进程完成,也不报告其退出代码。

    • 但是,shell 都提供同步 调用,包括退出代码报告,选择加入 依据:

      • cmd.exe中,使用start /WAIT ...进行同步执行,然后在%ERRORLEVEL%中反映退出代码;请参阅 internal start command's documentation.

      • 在PowerShell中,使用Start-Process -Wait ...进行同步执行;要同时获取进程退出代码,请使用 $ps = Start-Process -Wait -PassThru ...,然后检查 $ps.ExitCode - 请参阅 Start-Process cmdlet's documentation.


相比之下,POSIX-like shells of WSLWindows Subsystem for Linux),例如,默认情况下,bash:

  • 调用两者控制台子系统和GUI子系统应用程序同步 默认情况下,并在之后的内置 $? 变量中反映任一应用程序类型的退出代码。

  • 要选择 异步 调用,请使用后置 & 从后台执行调用 job;例如,(Notepad.exe &)

    • 注意:(...) 抑制 & 触发的 shell 的作业控制状态消息(在 bashzshdash, 但不是 ksh);有关详细信息,请参阅

[1] GUI 和控制台子系统是与最终用户直接设计为 运行 的可执行文件相关的子系统。存在其他子系统,如本 help topic on the PE format 中所述(Windows 上的可执行文件格式)。向 James(OP)致敬,感谢他提供 link.

[2] 虽然 GUI-子系统应用程序可以按需显式创建控制台,但这样的控制台独立于控制台子系统调用者的控制台。甚至可以 附加到 控制台子系统调用者的预先存在的控制台,但调用者不会意识到这一点,因此将无法捕获、重定向或传递在 GUI-subsystem 应用程序写入该控制台的任何输出上。