在窗口应用程序中写入命令行

时间:2009-03-20 16:08:35

标签: c# .net user-interface console

我正在尝试让我的基于WinForm的C#与命令行合作,但是我很难让它发挥得很好。例如,我有这段代码:

    [STAThread]
    static void Main(string[] args) {
        foreach (string s in args) {
            System.Windows.Forms.MessageBox.Show(s);
            Console.WriteLine("String: " + s);
        }

        Mutex appSingleton = new System.Threading.Mutex(false, "WinSyncSingalInstanceMutx");
        if (appSingleton.WaitOne(0, false)) {
            try {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                //start logger
                Logger.singleton.makeOpen(true);
                Application.Run(new MainForm(false));
            } catch (Exception) {
            } finally {
                appSingleton.Close();
                Logger.singleton.makeOpen(false); 
            }
        } else {
            System.Windows.Forms.MessageBox.Show("Sorry, only one instance of WinSync can be ran at once.");
        }
    }
}

它应该使用Console.WriteLine写入控制台,但我什么也看不见,只显示MessageBox。

我做错了什么?

6 个答案:

答案 0 :(得分:5)

尝试AttachConsole(-1)重定向Console.Out,为我工作:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace PEFixer
{
    static class Program
    {
        [DllImport("kernel32.dll")]
        private static extern bool AttachConsole(int dwProcessId);

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static int Main(string[] args)
        {
            if (args.Length > 0)
            {
                AttachConsole(-1);
                return Form1.doTransformCmdLine(args);
            }
            else
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
            return 0;
        }
    }
}

答案 1 :(得分:2)

Windows应用程序模型让它有点痛苦。您需要将应用程序类型更改为Console(在属性中),它应该神奇地开始工作。无论如何,您的表单都会出现,因为它是由自动生成的代码显式创建的。

答案 2 :(得分:2)

我的建议是创建一个Windows应用程序,然后P / Invoke来获取一个控制台。 IE:

public Form1()
{
   [DllImport("kernel32.dll")]
   public static extern bool AllocConsole();

   [DllImport("kernel32.dll")]
   public static extern bool FreeConsole();

   public Form1()
   {
      AllocConsole();
      Console.WriteLine("Whatever.");
   }
}

答案 3 :(得分:1)

在玩了一下之后,看到了AllocConsole和AttachConsole的警告,我认为最好的想法是拥有两个.NET exe文件,比如foo.exe和fooconsole.exe。当您需要控制台输出时,请使用fooconsole.exe。

您的主要功能以外的所有代码都可以放入生成DLL(类库)的.NET项目中。这包括所有获胜表格,包括您的主窗口。 exe项目中唯一剩下的就是一个小的main函数,它反过来调用DLL中的静态函数。所有这一切都可以在不诉诸P-Invoke的情况下完成。

在您的DLL类库中:

using System;
using System.Windows.Forms;

namespace MyNamespace
{
    public class MainEntry
    {
        private static bool mIsConsole = false;

        private MainEntry() { }

        public static bool IsConsoleApp
        {
            get { return mIsConsole; }
        }

        public static int DoMain(string[] args, bool isConsole)
        {
            mIsConsole = isConsole;
            try
            {
                // do whatever - main program execution
                return 0; // "Good" DOS return code
            }
            catch (Exception ex)
            {
                if (MainEntry.IsConsoleApp)
                {
                    Console.Error.WriteLine(ex.Message);
                }
                else
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return 1; // "Bad" DOS return code indicating failure
            }
        }
    }
}

在发出Windows EXE(foo.exe)的项目中,这是唯一的代码:

using System;

namespace MyNamespace
{
    static class Program
    {
        [STAThread]
        static int Main(string[] args)
        {
            return MainEntry.DoMain(args, false);
        }
    }
}

在发出控制台EXE(fooconsole.exe)的项目中,这是唯一的代码:

using System;

namespace MyNamespace
{
    static class Program
    {
        [STAThread]
        static int Main(string[] args)
        {
            return MainEntry.DoMain(args, true);
        }
    }
}

当然,在两个EXE项目中,您需要在同一解决方案中引用DLL项目。在项目属性的“应用程序”选项卡上,可以更改项目类型 - Windows应用程序(EXE),控制台应用程序(EXE)或类库(DLL)。

请注意,可以以编程方式确定EXE文件是否使用Windows或控制台子系统,但它可能不值得 - 许多P-Invoke并且您必须查看EXE文件的PE头的字节。

此外,AllocConsole / AttachConsole方法很有趣,如果您从命令行或批处理文件运行程序,并尝试将输出(stdout和/或stderr)重定向到文件,它将无法工作 - 它不会去文件。见http://www.nabble.com/WIN32:-Spawning-a-command-line-process-td21681465.html

同样,可以解决,但它需要更多的P-Invoke,可能不值得。

作为一种最佳实践,我从未在EXE项目中投入比简单的Main函数更多的东西,而main函数又调用DLL中的静态函数。有很多原因,但有一个很好的理由是单元测试工具使用DLL比使用EXE更好。

还要注意Main函数的形式,它接受args的字符串数组,并返回一个int。这是最好的表单,因为您可以在批处理文件中使用ERRORLEVEL来处理您的EXE返回的任何数字 - 传统上0表示成功,大于0表示失败。

答案 4 :(得分:0)

您是从Visual Studio在调试模式(F5)中运行它,还是从命令行运行它?在VS中,您将在“输出”选项卡中看到控制台输出(Ctrl + Alt + O)。否则,没有可写入的控制台。

答案 5 :(得分:0)

您需要将它用于什么?

如果是用于调试(因为用户永远不会看到控制台),那么我建议使用Debug.WriteLine()。然后应该没有问题地进入控制台......