3

I'd like to create a C# application that behaves like a console application when you run it from a console window (and can output text to the console) but that does not create a console window when run externally. I've done a bit of research and the usual suggestion is to create a C# console application, then change its project type to a Windows Application. However that stops the Console.WriteLine messages from being written when run from a console.

Is there any way to run a program like this? I don't need it to take any console input. Perhaps there's some other method I can use to get text to display when run from the console?

RandomEngy
  • 14,931
  • 5
  • 70
  • 113
  • Is this something in your direction: http://stackoverflow.com/questions/9009333/how-to-check-if-the-program-is-run-from-a-console – rene Oct 19 '13 at 22:00
  • "However that stops the Console.WriteLine messages from being written when run from a console" --> Use [AttachConsole](http://www.pinvoke.net/default.aspx/kernel32.attachconsole) with ATTACH_PARENT_PROCESS to attach the console from which it was run. – Idle_Mind Oct 19 '13 at 22:03
  • @Idle_Mind That's a good start; I can see the output in the console window, however it doesn't exit back the console prompt, even after calling FreeConsole(). – RandomEngy Oct 19 '13 at 22:17
  • Not sure I follow...can you give more details? – Idle_Mind Oct 19 '13 at 22:25
  • @Idle_Mind Oh it does exit, it just does it immediately, so all the console output shows up oddly after the prompt: http://i.imgur.com/OHQOchH.png – RandomEngy Oct 19 '13 at 22:32
  • Gotcha...right. That's normal behavior for a **Windows** application (you changed the output type) as they don't block the Console. I don't have a good answer for you there, sorry. – Idle_Mind Oct 19 '13 at 22:49

3 Answers3

3

so all the console output shows up oddly after the prompt

Using AttachConsole() just doesn't work that well in practice. The command processor looks at your .exe file and does two different things, depending if it is a console mode app or a GUI app. If it is console mode app then it waits for the program to exit, then displays the prompt. If it is a GUI app then it doesn't wait, it assumes that the program will display its own window.

Which is what happened, it displayed the prompt after starting your program and waits for input. Your Console.Write() output now intermingles with the command processor output. You'll also have a problem with Console.ReadLine(), the first line that the user types goes to the command processor, not to you. Greatly confuzzling the user.

The workaround is to start your program differently. You have type

  start /wait yourapp.exe args...

Which makes the command processor wait for your program to exit, just like it would for a console mode app.

This is not very practical, the user of your app just won't think to use start /wait. No simple fix for this, you'll need to use AllocConsole(). If that's a deal breaker then you'll just need to create another little EXE project that's a console app. Which starts your GUI app, passing the command line. If you give it the same name as your GUI exe but give the .com filename extension then it will always be found first. It can look like this, works for any gui app:

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

class Program {
    static void Main(string[] args) {
        try {
            var path = Assembly.GetExecutingAssembly().Location;
            var dir = Path.GetDirectoryName(path);
            var file = Path.GetFileNameWithoutExtension(path);
            path = Path.Combine(dir, file + ".exe");

            var prc = Process.Start(path, Environment.CommandLine);
            if (args.Length > 0) prc.WaitForExit();
        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
            Environment.Exit(1);
        }
    }
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • That's almost there; I'd need to forward the arguments and standard output as well. Though for my purposes I think I'm going to just leave the CLI as a Console Application and make a small wrapper windows application which passes on the arguments and hides the console window. I can use that when I need to launch without a window and the CLI project will still work normally. Still, that's a useful trick with the .com extension. – RandomEngy Oct 20 '13 at 02:59
0

Honestly I needed this too and was looking for an answer and found this in

Show Console in Windows Application?

public static void ShowConsoleWindow()
{
    var handle = GetConsoleWindow();

    if (handle == IntPtr.Zero)
    {
        AllocConsole();
    }
    else
    {
        ShowWindow(handle, SW_SHOW);
    }
}

public static void HideConsoleWindow()
{
    var handle = GetConsoleWindow();

    ShowWindow(handle, SW_HIDE);
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;

This looks like a nice solution to all the others that have been suggested in this thread.

Community
  • 1
  • 1
user2888973
  • 583
  • 3
  • 15
  • I don't want to create a new console from a Windows application. I want to run it normally from a console and with no window when launched outside of a command prompt. – RandomEngy Oct 19 '13 at 22:22
  • I see, sorry, I have no answer for this. I wouldn't even know how to begin. – user2888973 Oct 19 '13 at 22:29
0

For reference, here's what I did. I made a simple "Windowless CLI" executable. I created a new console application, changed it to a Windows Application and passed through the arguments:

static void Main(string[] args)
{
    string programFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    string passArguments = string.Join(" ", args.Select(WrapArgument));

    Process.Start(new ProcessStartInfo(Path.Combine(programFolder, "VidCoderCLI.exe"))
    {
        Arguments = passArguments,
        WindowStyle = ProcessWindowStyle.Hidden
    });
}

private static string WrapArgument(string arg)
{
    if (arg.EndsWith(@"\"))
    {
        return "\"" + arg + "\\\"";
    }

    return "\"" + arg + "\"";
}

Anything that needs to make a call without a window showing up goes through there. It's nice because I avoid dealing with output redirection and console shenanigans.

Though it's not technically what I asked so I won't accept this answer.

RandomEngy
  • 14,931
  • 5
  • 70
  • 113