- MSDN 说 ProcessStartInfo.UseShellExecute==True 表示在启动进程时使用 shell,这是否意味着进程的启动与我运行 cmd.exe 并从该命令提示符运行程序一样?这样做有什么意义?以这种方式启动的进程是否有自己的控制台?
其实是这样的:
- 如果 UseShellExecute 为 false,应用程序将使用 CreateProcess API 启动;此 API 允许您指定许多启动选项,其中可以重定向 stdin/stdout/stderr,但只会启动可执行文件。如果您尝试使用 CreateProcess 启动文件(例如 word 文档),它将失败,因为 word 文档不是可执行文件。
- 如果 UseShellExecute 为真,进程将使用ShellExecuteEx API 启动;当您双击文件夹中的文件时,此功能与 Windows 资源管理器(“the shell”,至少在 Microsoft 术语中)使用的功能相同;它的主要优点是它“知道”如何启动文档(使用相关程序打开它们),它知道 shell 文件夹,......因为它使用了很多 shell 工具。但是它有一些主要缺点:与裸 CreateProcess 相比,它相当重量级(因为它必须执行很多额外的工作),如果文件关联/shell 扩展/...出现问题,它甚至无法打开可执行文件。无法重定向标准输入/标准输出/标准错误。并不是说理论上是不可能的:毕竟 ShellExecuteEx 在内部调用了 CreateProcess;问题是它没有公开这个功能。
所以,这两种创建流程的方法真的很不一样; Process 类在展平它们方面做得很好,但是绝对不能用 ShellExecuteEx 函数重现的一个特性是 IO 流重定向,因为它不是由 ShellExecuteEx 函数提供的,它只能在进程启动时通过 CreateProcess 启用。
启动程序对控制台的使用是另一个问题。控制台在 CreateProcess 内部分配/重用(实际上 IIRC 它与 windows PE 加载器有关,它检查 PE 标头中所需的子系统);指定控制台创建/重用规则here。
如果启动的应用程序是 GUI 应用程序,则根本不会创建控制台;另一方面,如果启动控制台应用程序,它会重用其父进程的控制台,除非它在 CreateProcess 调用中指定 CREATE_NEW_CONSOLE 标志。默认情况下不指定此标志,但我不确定 ShellExecuteEx 对控制台应用程序有什么作用,而且我手头没有要检查的 Windows 框。我将把这个作为练习留给读者。 :P
其他答案
嗨,Matteo,还有一个问题。如果我创建一个新进程作为没有附加控制台的分离进程。现在进程的标准输出在哪里?进程的输出到哪里去了?进程的标准输出与控制台的输出不同吗?
我不清楚你的意思。当您使用 CreateProcess/ShellExecuteEx 启动一个新进程时,控制台会根据需要分配,即,如果 exe 是控制台可执行文件(如 PE 标头中指定),Windows 会为其提供一个控制台(即新的控制台或父级的控制台,具体取决于根据我上面指定的规则);如果 exe 是 GUI 应用程序,则不会为其分配控制台。
IIRC,对于 GUI 应用程序,stdout/stdin/stderr 只是位桶,即它们没有指向任何有用的东西,并且对它们的所有 IO 都被丢弃;顺便说一句,nothing stops 一个 GUI 应用程序分配了一个控制台并将它自己的 std* 流重定向到它。
请记住,控制台、Windows 标准流和 CRT 标准流是三个独立的东西。控制台只是一个接口,默认情况下,控制台应用程序方便地绑定到 Windows 标准流。
Windows std 流是在 CreateProcess 中指定 stdin/stdout/stderr 重定向时被重定向的流;您可以使用GetStdHandle 函数获取它们的句柄,并使用SetStdHandle 重定向它们。
最后,CRT 标准输入/标准输出/标准错误是另一个抽象,由 C 运行时库构建;它们默认绑定到 Windows 标准流。
所有这些通常都可以无缝运行,您甚至不必担心 Windows std 流和 CRT 流之间的区别;但是,当您开始考虑流重定向时,这种差异就变得很重要。
如果进程的标准输出流与控制台的输出不同,Shell 会为我们绑定这两个流。进程的输出是否会从 process.stdout 复制到 console.output?效率高吗?
shell 不参与此过程,它是执行管道的内核,遵循 CreateProcess 中使用的指令(或随后使用其他 API 从新进程内部修改)。对于您的性能问题,像这样的分层结构是唯一的方法;而且,复制部分(如果它真的是一个副本,我怀疑整个事情只是传递指针的问题)是该过程中最快的部分,真正的瓶颈是控制台窗口绘画/滚动/等。事实上,如果你想运行一个控制台应用程序让它全速生成数据,你通常会将其标准输出重定向到文件或通过管道。
非常感谢。意大利马泰奥。你是一个很好的问题解决者。 :D
谢谢。 :)