2008-12-03 146 views
6

我正在编写一个应用程序,它可以作为标准WinForms应用程序启动,也可以从命令行以无人参与模式启动。该应用程序是使用VS 2k5标准WinForms模板构建的。如果从命令行启动,则输出到命令行

当从命令行执行应用程序时,我希望它输出可由执行应用程序的脚本捕获的信息。当我通过Console.WriteLine()直接执行此操作时,虽然可以通过管道将文件捕获,但不会显示输出。

另一方面,我可以强制应用程序通过在kernel32上对AllocConsole()执行P/Invoke来弹出第二个控制台。不过,这不是我想要的。我希望输出出现在应用程序被调用的同一个窗口中。

这是突出的代码,让我弹出命令行控制台:

<STAThread()> Public Shared Sub Main() 

    If My.Application.CommandLineArgs.Count = 0 Then 
     Dim frm As New ISECMMParamUtilForm() 
     frm.ShowDialog() 
    Else 
     Try 
      ConsoleControl.AllocConsole() 
      Dim exMan As New UnattendedExecutionManager(ConvertArgs()) 
      IsInConsoleMode = True 
      OutputMessage("Application started.") 
      If Not exMan.SetSettings() Then 
       OutputMessage("Execution failed.") 
      End If 
     Catch ex As Exception 
      Console.WriteLine(ex.ToString()) 
     Finally 
      ConsoleControl.FreeConsole() 
     End Try 

    End If 

End Sub 

Public Shared Sub OutputMessage(ByVal msg As String, Optional ByVal isError As Boolean = False) 
    Trace.WriteLine(msg) 
    If IsInConsoleMode Then 
     Console.WriteLine(msg) 
    End If 

    If isError Then 
     EventLog.WriteEntry("ISE CMM Param Util", msg, EventLogEntryType.Error) 
    Else 
     EventLog.WriteEntry("ISE CMM Param Util", msg, EventLogEntryType.Information) 
    End If 

End Sub 

回答

2

更新1:

正如迈克尔·伯尔答说,最近Raymond Chenposted a short article about this。我很高兴看到我的猜测并非完全错误。

更新0:

免责声明:本 “答案” 主要是炒作。我只是因为有足够的时间来证明没有多少人能够回答什么是一个基本问题而发布它。

我认为,如果应用程序是GUI或控制台的“决定”是在编译时创建的,而不是在运行时创建的。所以,如果你将应用程序编译为GUI应用程序,即使你不显示GUI,它仍然是一个GUI应用程序,并没有控制台。如果您选择将其编译为控制台应用程序,那么至少您将有一个控制台窗口闪烁,然后转至gui“模式”。我不知道在托管代码中是否可行。

问题是根本的,我认为,因为控制台应用程序必须对调用控制台应用程序进行“控制”。而且它必须在子应用程序的代码运行之前这样做。

9

雷蒙德陈最近发布(一个月的问题在这里张贴在SO)之后很短的文章关于这一点:

How do I write a program that can be run either as a console or a GUI application?

你不能,但你可以尝试假了。

每个PE应用程序在其标题中包含一个字段 ,该字段指定它被设计为运行 下的哪个 子系统。你可以说 IMAGE_SUBSYSTEM_WINDOWS_GUI,以纪念自己 作为一个Windows GUI应用程序, 或者你可以说 IMAGE_SUBSYSTEM_WINDOWS_CUI说 你是一个控制台应用程序。如果 您是GUI应用程序,那么 程序将在没有控制台的情况下运行。

子系统确定 内核如何为程序准备执行 环境。如果 程序被标记为在 控制台子系统运行,那么内核 将程序的控制台连接到 其父的控制台,创建一个新的 控制台,如果家长没有 的控制台。 (这是一个不完整的 描述,但细节不 相关的讨论。)在 另一方面,如果该计划被标记为 运行的GUI应用程序,然后 内核将运行程序 无任何控制台。

在这篇文章中他指出,另一个由俊峰张,讨论了几个方案(Visual Studio和反汇编)如何实现这一行为:

How to make an application as both GUI and Console application?

在VisualStudio的情况下,有实际上有两个二进制文件:devenv.com和devenv.exe。 Devenv.com是一个控制台应用程序。 Devenv.exe是一个GUI应用程序。当您输入devenv时,由于Win32探测规则,devenv.com被执行。如果没有输入,devenv.com将启动devenv.exe,然后退出。如果有输入,devenv.com将它们作为正常的控制台应用程序处理。

在ildasm情况下,只有一个二进制文件:ildasm.exe。它首先编译为一个GUI应用程序。稍后使用editbin.exe将其标记为控制台子系统。它的主要方法是确定它是否需要作为控制台模式或GUI模式运行。如果需要以GUI模式运行,它将自行重新启动为GUI应用程序。

在评论Raymond Chen的文章,laonianren有这个添加到的Studio如何视觉作品李俊峰张的简要说明:

devenv.com是一个通用的控制台模式的stub应用程序。当它运行时,它会创建三个管道来重定向控制台的stdin,stdout和stderr。然后它找到自己的名称(通常是devenv.com),用“.exe”替换“.com”,并使用stdin管道的读取结束和stdout的写入结束启动新应用程序(即devenv.exe)和stderr管道作为标准手柄。然后,它只是坐着等待devenv.exe退出并在控制台和管道之间复制数据。

因此,尽管devenv.exe是一个GUI应用程序,它可以使用其标准手柄读写“父”控制台。

您可以自己使用devenv.com将myapp.exe重命名为myapp.com。但你不能在实践中,因为它属于MS。