1

My code was previously using AllocConsole like this:

AllocConsole();
var arg = Console.ReadLine();
// do stuff with arg

and it was working well. However, I didn't like the fact that if I started the application from a console (like running MyApp.exe in a CMD-window), it would create a new console-instance, so I changed AllocConsole to this:

if(!AttachConsole(-1))
    AllocConsole();

Output is still printed to the console, but if I ever hit "enter", strange stuff happens. running application

It might not be too easy to see from the image, but what I did was run the application. It then outputs a few lines about usage, and asks for input ("Action [run|install|uninstall|quit]"). I wrote "run" and hit enter, but the console is directing input at CMD, not my application. So, while my application is still running, it's not getting any input. It's a bit like running an application in linux with & at the end.

Is there any way I can have my application take input from the CMD-window that started it?

[Edit] Full code:

#region Custom Launching Helpers (for when not launced as a service)

private enum Action
{
    Run,
    Install,
    Uninstall
}

private static string PrintUsage()
{
    Console.WriteLine("Usage: <application-name> [run|install|uninstall]");
    Console.WriteLine("Use run to run a single itteration of the program.");
    Console.WriteLine("Use install to install the service.");
    Console.WriteLine("Use uninstall to uninstall the service.");
    Console.Write("Action [run|install|uninstall|quit]: ");
    return Console.ReadLine();
}

[DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);

[DllImport("kernel32.dll")]
private static extern bool AllocConsole();

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

[DllImport("kernel32.dll")]
private static extern bool SetConsoleTitle(String lpConsoleTitle);

private static void RequireConsole()
{
    if (!AttachConsole(-1))
        AllocConsole();
}

#endregion


/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
    // Environment.UserInteractive will be true when launced as a normal application
    // (for instance by double-clicking, or running form a cmd-prompt).
    // 
    // Services can't run this way, thus we present the user with a menu (if no spesific action
    // was passed allong as a startup-parameter) that enables the user either to run a single
    // itteration, or to install/uninstall the service.
    if (Environment.UserInteractive)
    {
        try
        {
            // open a console
            // since AssemblyService isn't a console-application, one must be crated explicitly.
            RequireConsole();
            SetConsoleTitle("AssemblyService 2");
            Action action;

            // If no command-line arguments are provided, print usage
            // and enable the user to input an argument while running.
            var arg = args.Length == 0 ? PrintUsage() : args[0];

            while (true)
            {
                switch (arg)
                {
                    case "run":
                        action = Action.Run;
                        goto execute;

                    case "install":
                        action = Action.Install;
                        goto execute;

                    case "uninstall":
                        action = Action.Uninstall;
                        goto execute;

                    case "quit":
                        return;

                    default:
                        // If the argument is invalid, keep asking the user
                        // untill a valid argument is given.
                        arg = PrintUsage();
                        Console.WriteLine("Got arg: {0}", arg);
                        break;
                }
            }

        execute:
            ; // empty statement so it compiles
            // unimportant, does stuff;
        }
        finally
        {
            // Release the console.
            Console.WriteLine();
            FreeConsole();
        }
    }
    else
    {
        // Run as service (only in non-interactive session).
        //ServiceBase.Run(new AssemblyService());
        // rather just exit
    }
}

[Edit 2] I tried adding the following:

var inputHandle = GetStdHandle((int) StdHandle.Stdin);
var safeInputHandle = new SafeFileHandle(inputHandle, true);
var input = new FileStream(safeInputHandle, FileAccess.Read);
Console.SetIn(new StreamReader(input));

it resulted in nothing beeing printed to the console at all (so I had no idea what was happening). Then I tried to add setters for StdOut and StdErr as well:

var outputHandle = GetStdHandle((int) StdHandle.Stdout);
var safeOutputHandle = new SafeFileHandle(outputHandle, true);
var output = new FileStream(safeOutputHandle, FileAccess.Write);
Console.SetOut(new StreamWriter(output));

var errHandle = GetStdHandle((int) StdHandle.Stderr);
var safeErrHandle = new SafeFileHandle(errHandle, true);
var err = new FileStream(safeErrHandle, FileAccess.Write);
Console.SetError(new StreamWriter(err));

And still no output to console at all.

[Edit 3] Starting the application with

start /wait "AssemblyService 2.exe"

causes there to be a new window created which shows the CMD-prompt, and still doesn't let me enter input to the application. It also doesn't show output. using start cmd

Alxandr
  • 12,345
  • 10
  • 59
  • 95
  • I assume AllocConsole and AttachConsole are pinvoke wrappers around their win32 api counterparts? Did you implement [GetStdHanlde](http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx) as well? – rene Jul 22 '13 at 07:55
  • @rene I added the "full" code (only the relevant code). It should show both `AttachConsole` and `AllocConsole`. No `GetStdHandle` though. – Alxandr Jul 22 '13 at 07:59
  • @rene but as said, output works like it's supposed to. The problem seems to be that cmd does not wait for the application to exit before accepting input again. – Alxandr Jul 22 '13 at 08:01
  • I would do a GetStdHandle(STD_INPUT_HANDLE), convert it to a stream and hook in the Console.SetIn(input). I assume that will enable your app to consume the input. StdOut is probably automagically mapped... – rene Jul 22 '13 at 08:06
  • @rene I tried doing that, see updated question. – Alxandr Jul 22 '13 at 08:22

0 Answers0