应用程序的输出为什么没有显示?

4
我有一个应用程序,它有以下的Main函数:
static void Main(string[] args)
{
     Console.WriteLine("started");
     if (args.Length == 0)
     {
          Application.EnableVisualStyles();
          Application.SetCompatibleTextRenderingDefault(false);
          Application.Run(new Form1());
     }
     else
     {
          File.WriteAllText("./file.txt", "hello");
     }
}

我希望它能够支持从命令行运行,以便在一些脚本中使用,同时也能像GUI应用程序一样运行。然而,如果我从命令行运行它并传递参数,我可以看到文件被创建了,但我看不到由Console.WriteLine产生的任何输出。这是为什么呢?


2
如果您的应用程序类型不是“控制台”,我认为您将无法看到使用Console.WriteLine发送的输出消息。 - Marco
1
如果你是从Visual Studio进行调试的话,我认为你需要这样做。 - Richard Ev
@RichardEv: 是的,在“输出”窗口中。 - Marco
我正在命令行中运行生成的可执行文件。 - Geo
你的项目的“输出类型”设置为什么? - M.Babcock
无论如何,我认为您可能会遇到一个重大问题:如果用户为您的应用创建了链接并在其中设置了一些命令行参数,则该链接将不会显示您的应用。我能解释清楚我的意思吗? - Marco
6个回答

3

我认为我找到了问题,如果我错了,请原谅。
当你创建一个GUI应用程序并从控制台窗口运行它时,它的标准输出流不会发送到先前打开的控制台窗口,因此不会显示出来。
但是,如果你尝试运行yourexe.exe > test.txt,你可以看到你使用Console.WriteLine写的所有内容。


1
有趣的是,yourexe.exe > test.txt 可以得到输出。 - Geo
@Tempus:嗯,> 告诉控制台将标准输出重定向到文件中...而 Console 类则在标准输出上写入 ;) - Marco
是的,我明白 : )。我不知道中介命令提示符会运行GUI应用程序。我觉得有趣的是它的输出被发送到下一个命令提示符。 - Geo

2
这段文字的意思是:“它不起作用,因为启动WinForms应用程序的控制台窗口属于cmd.exe进程,该进程与您的WinForms应用程序进程是分开的。” 作者在 这篇文章 中发现了这一点,并提供了一些使用 AttachConsole Win32方法 的解决方法。

2
似乎您想在控制台模式和GUI模式下运行应用程序。这个例子可以实现。基本上,您需要创建一个表单应用程序,并在主函数中有条件地调用AllocConsole(如果从资源管理器中运行)。如果从命令提示符中运行(您需要通过查看父进程来检测),则可以使用AttachConsole连接到其控制台。在这里,我已经硬编码了值,但您可以查看参数并决定要执行任何操作。

基本上,此应用程序有三种模式。

  • bParentConsoleMode that will use the parent console if launched from command prompt
  • bUsingOwnConsole if double clicked in explorer you will have to create a new console
  • Lastly when neither of the above is true it runs as a regular form application.

    static class Program
    {
        [DllImport("kernel32.dll")]
        static extern bool AttachConsole(int dwProcessId);
        private const int ATTACH_PARENT_PROCESS = -1;
    
    
        [DllImport("kernel32.dll")]
        private static extern bool AllocConsole(); 
    
         [STAThread]
        static void Main()
        {
            bool bParentConsoleMode = true;
            bool bUsingOwnConsole = true;
            if (bParentConsoleMode)
            {
               AttachConsole(ATTACH_PARENT_PROCESS);
                Console.WriteLine("Using parent console");
                Console.ReadLine();
    
            }
            else if (bUsingOwnConsole)
            {
                AllocConsole();
                Console.WriteLine("Using own console");
                Console.ReadLine();
            }
            else //gui mode
            {
                Console.WriteLine("This is cool");
    
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
        }
    }
    

很有趣,但这会打开一个不同的命令提示符。难道不可能让输出显示在同一个命令提示符中吗? - Geo

1

请参考 Raymond Chen 的这篇文章:如何编写既可以作为控制台应用程序又可以作为 GUI 应用程序运行的程序?

摘录:

你不能,但你可以尝试伪造它。

[...]

有些人想要编写我所谓的“机会主义”控制台程序。这些程序将使用其父级的控制台(如果可用),但如果没有,则不希望为它们创建控制台。内核不支持此类程序,但这并没有阻止一些人想出巧妙的解决方法

问题在于“附加到现有控制台窗口以进行输入和输出,还是作为无控制台的 GUI 应用程序运行?”的决定发生在进程实际启动之前。您无法编写代码,根据命令行参数做出该决定。

Windows 强制你在编译时做出决定:这个应用程序是否要使用控制台(在这种情况下,它始终有一个控制台窗口,并且如果从图标或“开始”菜单启动,则会打开一个新的控制台窗口),还是不使用控制台(在这种情况下,它无法将输入或输出直接发送到启动它的控制台窗口--但是,它可以创建一个新的控制台窗口)。如果您想始终拥有控制台,请将构建类型更改为“控制台应用程序”;如果您不想拥有控制台,请将其保留为“Windows 应用程序”。

Raymond 的帖子中提到的巧妙解决方法,以防链接失效,是 devenv(Visual Studio)和 ildasm:

在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应用程序。

1
我使用了以下代码来做类似的事情:
 Console.WriteLine("started");
 if (args.Length == 0)
 {
     ProcessForConsole(argsParser);
 }
 else
 {
     NativeMethods.FreeConsole();
     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);
     Application.Run(new Form1());                
 }    

. . . .

 [DllImport("kernel32.dll", SetLastError = true)]
    public static extern int FreeConsole();

将主应用程序设置为控制台应用程序,然后使用FreeConsole将其分离。

进程可以使用FreeConsole函数将自己从控制台中分离。如果其他进程共享控制台,则控制台不会被销毁,但调用FreeConsole的进程无法引用它。当最后一个附加到控制台的进程终止或调用FreeConsole时,控制台将关闭。

来自microsoft.


这是一个选择而非两者兼备。第一种情况下,您使用控制台而不使用表单;第二种情况下,您放弃控制台并使用表单。 - AidanO
我试图找出如何使一个应用程序在不同于C#的完全不同的编程语言中,如果传递参数,则充当控制台应用程序,并在没有传参时充当GUI应用程序。但是,搜索将我带到了这个优雅地解决我的问题的答案。 - Ryan1729

0
正如Marco所指出的那样,你尝试将应用程序的输出类型设置为“控制台应用程序”了吗?

OP希望在使用参数运行时,应用程序的行为类似于控制台,而在没有参数时则表现为GUI界面。 - Marco

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