jam*_*her 1 c++ windows powershell winapi stdout
我有一个“hello world”Windows桌面应用程序,其来源来自这个官方演练。
当我从适用于 Linux 的 Windows 子系统运行此程序时,我得到了预期的行为: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
Run Code Online (Sandbox Code Playgroud)
但是,这不是我从命令提示符或 PowerShell 运行程序时得到的行为。在这里,进程启动了,但是 shell 声称进程立即退出,立即给我一个新的提示!但该进程显然没有退出,因为它创建的窗口仍然存在,我可以与它交互。
在这两种情况下(适用于 Linux 的 Windows 子系统和 PowerShell),进程 stdout 不会打印到终端。(使用 进行验证std::cout << "test",它不会向终端打印任何内容。)
就好像原始进程生成了一个守护进程来运行 win32 的东西。但我不认为这是正在发生的事情,因为 Linux 的 Windows 子系统至少会阻塞直到退出。
当我使用 Visual Studio 创建“控制台”C++ 应用程序时,它的行为符合预期。这种奇怪的提前退出且无输出的行为只发生在我的“桌面”win32 程序中。
那么,为什么命令提示符或 PowerShell 声称进程立即退出呢?它的标准输出在哪里?
乔纳森·波特(Jonathan Potter)在对该问题的评论中提供了关键的指导;详细说明:
任何给定的 Windows 应用程序都属于这两个相互排斥的类别之一[1]:
GUI 子系统应用程序,例如Notepad.exe:
此类应用程序通常创建GUI 窗口,有时根本不创建 UI。
由于不与控制台关联,它们无法写入标准输出(stdout)或标准错误(stderr)流。[2]
控制台子系统应用程序,例如findstr.exe:
此类应用程序要么在调用时创建一个控制台窗口,要么在预先存在的窗口中运行,通常是由命令行 shell(例如cmd.exePowerShell)创建的窗口。
它们将输出写入标准输出(stdout)和/或标准错误(stderr)输出流。
Windows 上的(基于控制台的)命令行 shell,例如cmd.exePowerShell:
同步调用控制台子系统应用程序并报告其退出代码%ERRORLEVEL%:通过中的伪环境变量cmd.exe以及通过$LASTEXITCODEPowerShell 中的变量。
默认情况下,以“启动后不管”的方式异步调用GUI 子系统应用程序。
也就是说,默认情况下 shell 既不等待应用程序进程完成,也不报告其退出代码。
然而,这两个 shell 都提供同步调用,包括退出代码报告,基于选择加入:
在 中cmd.exe,用于start /WAIT ...同步执行并在之后反映退出代码%ERRORLEVEL%;请参阅内部start命令的文档。
在PowerShell中,用于Start-Process -Wait ...同步执行;要获取进程退出代码,请使用$ps = Start-Process -Wait -PassThru ...,然后检查$ps.ExitCode- 请参阅Start-Processcmdlet 的文档。
相比之下,WSL(Linux 的 Windows 子系统)的类似 POSIX 的 shell,默认情况下例如:bash
默认情况下同步调用控制台子系统和 GUI 子系统应用程序,然后在内置$?变量中反映任一应用程序类型的退出代码。
要选择异步调用,请使用后置从后台作业&执行调用;例如,(Notepad.exe &)
(...)抑制 shell&触发的作业控制状态消息(在 、 和 中有效bash,zsh但dash在 中无效ksh);有关详细信息,请参阅此答案。[1] GUI 和控制台子系统是设计供最终用户直接运行的可执行文件的相关子系统。还存在其他子系统,如有关 PE 格式(Windows 上的可执行文件格式)的帮助主题中所述。向 James(OP)致敬,感谢他提供了链接。
[2] 虽然 GUI 子系统应用程序可以根据需要显式创建控制台,但此类控制台独立于控制台子系统调用者的控制台。甚至可以附加到控制台子系统调用者预先存在的控制台,但调用者不会意识到这一点,因此无法捕获、重定向或传递由 GUI 写入该控制台的任何输出- 子系统应用程序。