C#关闭标准输出

8

我希望能够像wget -b一样将我的程序与控制台分离。代码片段可能如下:

static void Main(string[] args)
{
    var settings = new Settings(args);
    if (settings.Background)
    {
        /*Tell the user what's going on.*/
        System.Console.WriteLine("Detatching from console. The program will still be running.");
        System.Console.Out.Close();
    }
    /*do work then exit.*/
}

但是System.Console.Out.Close();并不能实现正确的效果。

需要澄清的是,所谓的“正确的效果”是,在控制台中运行此程序时,提示符应该重新出现。或者,在从explorer.exe运行此程序时,控制台窗口应该关闭。

如果我表达不清楚,请告诉我。


1
如果您想要一个长时间运行的应用程序并保持在后台,那么您需要一个Windows服务。如果您只是想定期启动您的应用程序,您可以考虑使用任务计划程序来启动它。 - Jeff LaFay
1
我不认为那是正确的做法。关闭stdout并不意味着执行会返回到命令提示符。程序终止会导致这种情况。如果你想启动程序并返回命令提示符,你可以在命令行上使用start - James Michael Hare
据我所知,有一个Windows API函数可以从控制台中分离。 - CodesInChaos
“程序终止会导致这种情况。” 毫无疑问,但您可以运行 wget -b 并获得我想要的行为。 我不希望程序从一开始就是无头的。 例如,我希望程序先向控制台打印一行,然后分离,使用户再次获得命令解释器。 - lmat - Reinstate Monica
@ChrisShain 感谢您的提问。GNU发布了很多Windows相关的东西,例如http://gnuwin32.sourceforge.net/packages/wget.htm。也一定要查看http://gnuwin32.sourceforge.net/packages/coreutils.htm。 - lmat - Reinstate Monica
显示剩余7条评论
4个回答

1

不必创建控制台应用程序,然后在调用FreeConsole时遇到问题,您可以通过创建一个普通的 Windows 应用程序(没有 GUI),根据需要使用AllocConsole/AttachConsole/FreeConsole 来获得所需的内容。

这样,您就有了一个可以在后台运行但可以根据需要显示和隐藏控制台窗口的应用程序。尽管据我所记,这些控制台窗口与普通的 Windows 命令提示符之间存在一些差异,因此可能无法满足您的要求。


谢谢你的回答。打开一个新的控制台窗口确实是个好主意,但我希望只与现有的控制台窗口交互。我不认为我在使用FreeConsole时遇到了问题;它的表现正常。我知道有AllocConsole等函数。问题真正的原因是命令解释器(cmd或powershell)在返回到命令解释器之前等待进程结束。再次感谢。 - lmat - Reinstate Monica

1

这不起作用。static void Main(string[] args){System.Console.Out.Close(); System.Console.In.Close(); System.Console.Error.Close(); System.Console.WriteLine("detatched.");} 表明控制台没有分离。 - lmat - Reinstate Monica
我尝试了 FreeConsole。它似乎几乎可以工作。释放后,我不能再使用控制台写入标准输出,但命令解释器仍然无法重新获取控制台。 - lmat - Reinstate Monica
@LimitedAtonement 你是通过运行cmd.exe,然后运行应用程序来启动应用的吗?为了回到shell,你需要连接到同一控制台的cmd.exe副本。 - Chris Shain
我运行PowerShell,然后运行我的应用程序。你说的“连接到同一控制台的cmd.exe副本”是什么意思?似乎已经有一个连接到控制台(在我的进程运行时暂停),为什么我还需要另一个? - lmat - Reinstate Monica

1

你所参考的 wget 源代码可以在这里获得。你会发现,在 mswindows.c 的 193-314行中,实现了一个fork过程。它们会生成 wget 的新实例,并将相同的参数传递给它。

注释也是有意义的:

Windows 不支持 fork() 调用;因此我们通过使用相同参数调用另一个Wget副本来伪造它。

并且在第102行上:

在 Windows 9x 下,如果我们从16位进程启动...父进程应立即恢复。在 NT 下...这是徒劳的,因为父进程将等待我们终止后才恢复。

简而言之,答案是“不要这样做”。


是的 - 你可以假装或者非常接近,但这是非常严格“不鼓励”的。 - Chris Carew
你能想到另一种实现方式吗? - lmat - Reinstate Monica
如果您不介意我问一下,打印/返回任何内容到控制台的目的是什么?这样做在执行其他控制台操作之前有意义吗?将其分开是否明智?运行服务并让命令行组件发送命令然后终止是否有意义? - Chris Carew
@CrisCarew 嗯,对于我个人的程序来说,这样做是没有意义的。我创建了一个控制台程序来监视文件系统并在文件更改时运行命令。这不应该是一个服务。问题很快从具体实例变成了一般性问题(事实上,我认为对于这个特定项目而言,将其分叉到后台并不是一个好主意,我只是在考虑它),然后我开始思考,“我怎么做才能实现这个?”你的建议在某些情况下可能是有意义的。 - lmat - Reinstate Monica

0
为了澄清,“正确的事情”是,当从控制台运行此程序时,提示应该重新出现。或者,当从explorer.exe运行此程序时,控制台窗口应该关闭。
你不是想要“关闭控制台窗口”,而是希望它不存在。有一种非常简单的方法可以实现你的愿望:不要构建控制台应用程序。 :-)
如果从命令行构建,请使用CSC /target:winexe ...。这将创建一个Windows程序,永远不会与任何命令行相关联。

1
这并不是我的本意。我想要一个控制台一段时间,然后再分离它。我之前编辑了问题以澄清这个需求。现在代码示例可能更加清晰。 - lmat - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接