52

I currently have an application with a GUI.

Would it be possible to use this same application from the commandline (without GUI and with using parameters).

Or do I have to create a separate .exe (and application) for the commandline tool?

Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
PeeHaa
  • 71,436
  • 58
  • 190
  • 262

5 Answers5

68
  1. Edit your project properties to make your app a "Windows Application" (not "Console Application"). You can still accept command line parameters this way. If you don't do this, then a console window will pop up when you double-click on the app's icon.
  2. Make sure your Main function accepts command line parameters.
  3. Don't show the window if you get any command line parameters.

Here's a short example:

[STAThread]
static void Main(string[] args)
{
    if(args.Length == 0)
    {
        Application.Run(new MyMainForm());
    }
    else
    {
        // Do command line/silent logic here...
    }
}

If your app isn't already structured to cleanly do silent processing (if all your logic is jammed into your WinForm code), you can hack silent processing in ala CharithJ's answer.

EDIT by OP Sorry to hijack your answer Merlyn. Just want all the info here for others.

To be able to write to console in a WinForms app just do the following:

static class Program
{
    // defines for commandline output
    [DllImport("kernel32.dll")]
    static extern bool AttachConsole(int dwProcessId);
    private const int ATTACH_PARENT_PROCESS = -1;

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // redirect console output to parent process;
        // must be before any calls to Console.WriteLine()
        AttachConsole(ATTACH_PARENT_PROCESS);

        if (args.Length > 0)
        {
            Console.WriteLine("Yay! I have just created a commandline tool.");
            // sending the enter key is not really needed, but otherwise the user thinks the app is still running by looking at the commandline. The enter key takes care of displaying the prompt again.
            System.Windows.Forms.SendKeys.SendWait("{ENTER}");
            Application.Exit();
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new QrCodeSampleApp());
        }
    }
}
Community
  • 1
  • 1
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • `if all your logic is jammed into your WinForm code`. It is :P It's a good thing though, since I'm a complete novice, and because it 'bites me in the ass' now I'll remember it for future projects. I think I have some cleaning to do. – PeeHaa Aug 26 '11 at 01:16
  • last question before I go to sleep: will I be able to write a message to the commandline when for example the program is finished running? Or is it either a separate app which can write to commandline or don't write to commandline? – PeeHaa Aug 26 '11 at 01:25
  • @PeeHaa: I can't seem to get it to work, but try googling: "AttachConsole Console.WriteLine" *or* "AllocConsole Console.WriteLine". I'd suggest using a logging library instead, though, such as: http://netcommon.sourceforge.net/ (paired with NLog). – Merlyn Morgan-Graham Aug 26 '11 at 02:15
  • Tho even with that logging solution, you might want to do console output when acting like a console app. In that case try those `AllocConsole`/`FreeConsole` and `AttachConsole` options I mentioned. – Merlyn Morgan-Graham Aug 26 '11 at 02:23
  • 1
    Don't mind at all. Note that `Console.WriteLine` will go the bit-bucket with this solution if you double-clicked on the app. Also, you can check `Marshal.GetLastWin32Error` if you care when the attach failed. – Merlyn Morgan-Graham Aug 26 '11 at 18:11
  • Any thoughts on how to make the Console App exit without needing to press Enter? I tried calling FreeConsole() before my Environment.Exit(0) function call but no luck. – blak3r Sep 04 '12 at 21:51
  • @blak3r: Don't run it in the debugger and you won't have to hit enter. Try double clicking on the EXE itself in explorer and see if you still have to. – Merlyn Morgan-Graham Sep 04 '12 at 22:47
  • @MerlynMorgan-Graham I was running it from the cmd.exe... manually running it from the command line. If I run it as a GUI app, then no I don't have any issues. I'm concerned that if I run it as a scheduled task the process will not quit. – blak3r Sep 04 '12 at 23:03
  • 1
    @PeeHaa Thanks, this works quite well, however it seems that for me it is running in a separate thread (e.g. the command prompt returns immediately without waiting for the processing that started in Main to complete. Is that expected? – Kohanz Jul 10 '13 at 14:24
  • 1
    'using System.Runtime.InteropServices;' is needed to be able to use 'DllImport'. – Kai Hartmann Jun 24 '14 at 08:52
  • 3
    The drawback with `AttachConsole` is that the cmd call will return immediately before the console is even attached and therefore before the output is written. This is also the reason why you need an additional enter key at the end. If you want to pipe or redirect the output somewhere, you will only get an empty output. I have not found a real solution this far. The given solution is not applicable for me but is the closest one. – Robert S. Mar 21 '16 at 16:04
  • 1
    So I'm attempting to do just this but what I'm experiencing is when I run my application from a command prompt with command line arguments it runs as it should, however, it returns control to the command prompt right away. I'm expecting it to run until the end and return an exit code. I'm using Environment.Exit(-1) for errors and (0) for success but with it returning right away that's not going to work out correctly. – Chris Ward Jul 23 '18 at 19:59
  • @ChrisWard Did you ever find a good solution for this issue? I'm in the same boat. – Haydentech May 10 '19 at 21:47
10

In your program.cs class keep the Main method as it is but add string[] Args to the main form. For example...

    [STAThread]
    static void Main(string[] Args)
    {
        ....
        Application.Run(new mainform(Args));
    }

In mainform.cs constructor

    public mainform(string[] Args)
    {
        InitializeComponent();

        if (Args.Length > 0)
         {
             // Do what you want to do as command line application.
             // You can hide the form and do processing silently.
             // Remember to close the form after processing.
         }
    }
CharithJ
  • 46,289
  • 20
  • 116
  • 131
  • 2
    +1; But don't even bother doing `Application.Run` if you're running as a console app. Hiding the window will work, but it is a hack :) – Merlyn Morgan-Graham Aug 26 '11 at 00:59
  • 3
    The drawback is that you won't be able to see any output in the console window as there is no real console window attached to the application. `Console.WriteLine` will not produce any visible output. – Robert S. Mar 21 '16 at 16:01
1

I am new to c# programming. But I improvised the code hints from OP and Merlyn. The issue I faced when using their code hint was that the argument length is different when I call app.exe by double click on app.exe or when I call it from CMD. When app.exe is run as CLI from CMD then app.exe itself becomes the first arguments. Below is my improvised code which works satisfactory both as GUI double click of app.exe and as CLI from CMD.

[STAThread]
    static void Main(/*string[] args*/)
    {
        string[] args = Environment.GetCommandLineArgs();
        Console.WriteLine(args.Length);
        if (args.Length <= 1)
        {
            //calling gui part
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ACVSAppForm());
        }
        else
        {
            //calling cli part                
            string opt = args[1];
            //Console.WriteLine(args[0]);                
            if(opt == "LastBuild")
            {
                if(args.Length == 3)
                {
                    var defSettings = Properties.Settings.Default;
                    defSettings.CIBuildHistPath = args[2];
                }
                else
                {
                    //
                }
                CIBuildParser cibuildlst = new CIBuildParser();
                cibuildlst.XMLParser();
            }
        }

    }

I hope this helps someone. The only drawback of my solution is when the app.exe is run as GUI, it will open a CMD as console output window. But this is OK for my work.

MFz
  • 13
  • 2
-2

You may need to structure your Application as a Console Application, identify what you do on "Actions" - like clicking of the button - into separate class, include a form that can be shown if there were no command line arguments supplied, and handle events by routing them to the common methods in your "Action" class.

Arun
  • 2,493
  • 15
  • 12
-3

I think it is possible, just set your subsystem to "console", you will see a console window as well as the GUI window.

But in order to accept commands from the console window, I guess you will have to create an extra thread to do it.

Bill Yan
  • 3,369
  • 4
  • 27
  • 42
  • This is not a good option because it will spawn an additional console if they launch it by double-clicking on the file icon. Also, you don't necessarily have to spawn another thread - for example, if you pass your "commands" via command line parameters. – Merlyn Morgan-Graham Aug 26 '11 at 00:15