【问题标题】:Output to command-line if started from command line如果从命令行启动,则输出到命令行
【发布时间】:2008-12-03 22:16:22
【问题描述】:

我正在编写一个应用程序,它既可以作为标准 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

【问题讨论】:

    标签: command-line .net-2.0


    【解决方案1】:

    Raymond Chen 最近发布了(问题在 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 和 ildasm)如何实现这种行为:

    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 在张俊峰对 Visual Studio 工作原理的简要描述中添加了以下内容:

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

    因此,即使 devenv.exe 是一个 gui 应用程序,它也可以使用其标准句柄读取和写入“父”控制台。

    您可以自己将 devenv.com 用于 myapp.exe,方法是将其重命名为 myapp.com。但实际上你不能,因为它属于 MS。

    【讨论】:

      【解决方案2】:

      更新 1:

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

      更新 0:

      免责声明:这个“答案”主要是猜测。我发布它只是因为已经有足够的时间来确定没有多少人能够回答看似基本的问题。

      我认为应用程序是 gui 还是控制台的“决定”是在编译时而不是在运行时做出的。因此,如果您将应用程序编译为 gui 应用程序,即使您不显示 gui,它仍然是一个 gui 应用程序并且没有控制台。如果您选择将其编译为控制台应用程序,那么在进入 gui“模式”之前,您至少会有一个控制台窗口闪烁。而且我不知道在托管代码中是否可能。

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

      【讨论】:

        【解决方案3】:

        如果您想检查您的应用程序是否从 .NET 中的命令行启动,您可以使用 Console.GetCursorPosition()。 这样做的原因是,当您从命令行启动它时,光标会从初始点 ((0, 0)) 移开,因为您在终端中输入了一些内容(应用程序的名称)。 您可以通过相等检查(C# 中的代码)来做到这一点:

        class Program
        {
            public static void Main
            {
                if (Console.GetCursorPosition() == (0, 0))
                {
                    //something GUI
                }
                else
                {
                    //something not GUI
                }
            }
        }
        

        注意:您必须将输出类型设置为控制台应用程序,因为其他输出类型会使Console.GetCursorPosition() 抛出异常。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-01-29
          • 2011-12-22
          • 1970-01-01
          • 2013-02-04
          • 1970-01-01
          • 2016-07-10
          • 2013-03-02
          • 1970-01-01
          相关资源
          最近更新 更多