3

I'm trying to open a console from a winform application from a button click. I'm doing this via the code below.

[DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool AllocConsole();

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

private void button1_Click(object sender, EventArgs e)
    {
        int userInput = 0;
        AllocConsole();

        do
        {
            Console.Clear();
            Console.WriteLine("Hello World");
            Console.WriteLine("Select 1 or 2");
            int.TryParse(Console.ReadLine(), out userInput);
        } while (userInput != 1 && userInput != 2);

        FreeConsole();
    }

The first time the console is opened, it works fine. Attempting to open the console a second time will work fine, but once Console.Clear(); is called, I get:

An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll

Additional information: The handle is invalid.

The same exception will be thrown on Console.WriteLine(); and Console.ReadLine();. I've tried the proposed solutions from this, this, and this, but I end up with the same "handle is invalid." error on Console.Clear();.

Some additional code I've tried:

    [DllImport("kernel32.dll",
        EntryPoint = "GetStdHandle",
        SetLastError = true,
        CharSet = CharSet.Auto,
        CallingConvention = CallingConvention.StdCall)]
    private static extern IntPtr GetStdHandle(int nStdHandle);

    [DllImport("kernel32.dll",
        EntryPoint = "AllocConsole",
        SetLastError = true,
        CharSet = CharSet.Auto,
        CallingConvention = CallingConvention.StdCall)]
    private static extern int AllocConsole();

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


    private const int STD_OUTPUT_HANDLE = -11;
    private const int MY_CODE_PAGE = 437;

private void button1_Click(object sender, EventArgs e)
    {
        int userInput = 0;
        AllocConsole();

        IntPtr stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
        FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
        Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE);
        StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
        standardOutput.AutoFlush = true;
        Console.SetOut(standardOutput);
        Stream streamIn = Console.OpenStandardInput();
        TextReader readerIn = new StreamReader(streamIn);
        Console.SetIn(readerIn);

        do
        {
            Console.Clear();
            Console.WriteLine("Hello World");
            Console.WriteLine("Select 1 or 2");
            int.TryParse(Console.ReadLine(), out userInput);
        } while (userInput != 1 && userInput != 2);

        FreeConsole();
    }

This seems to be ok for Console.WriteLine(); and Console.ReadLine(); but I still can't get around the exception being thrown by Console.Clear();. Can anyone tell me if I'm missing something?

Sudsy1002
  • 588
  • 6
  • 21
  • A process can be associated with only one console, https://learn.microsoft.com/en-us/windows/console/allocconsole – Tony Nov 29 '17 at 13:14
  • @Tony "A process can use the FreeConsole function to detach itself from its current console, then it can call AllocConsole to create a new console or AttachConsole to attach to another console." from your source... – Sudsy1002 Nov 29 '17 at 13:16
  • You arent checking if AllocConsole() was successful – BugFinder Nov 29 '17 at 13:22
  • @BugFinder, how would it not be succesful? I was able to get passed AllocConsole() fine and do Console.WriteLine as well as Console.ReadLine. Both are fine, the Console.Clear() specifically is what is throwing an exception. Despite this I just ran a test and can confirm that it is successful. AllocConsole() returned 1. – Sudsy1002 Nov 29 '17 at 13:27
  • Are you running from within Visual Studio? – Idle_Mind Nov 29 '17 at 13:40
  • @Idle_Mind Yes sir. – Sudsy1002 Nov 29 '17 at 13:41

1 Answers1

3

I doubt it is possible to attach/detach to console multiple times. As far as I understand the code of private static IntPtr ConsoleInputHandle (the same goes for ConsoleOutputHandle), the handle is initialized once on first usage, so when you detach the process from console and re-attach it once again the handle is invalid.

So imho it is not feasible to use Console class as you want (nor do I know any live examples of programs working both as console and Win32 app - the most applications I've seen provide two versions of .exe).

If you really need Windows console I guess you can try to provide your own console wrapper above Windows API. If you need only the looks of console you can go with rendering of your own "console-like" window.

  • If this were the case, I would expect AllocConsole to fail. Instead, it succeeds and I am able to use Console.WriteLine and Console.ReadLine. If I can use both of those methods, why not Console.Clear()? Does it somehow use a different handle than the handle used by the former two methods? – Sudsy1002 Nov 29 '17 at 14:08
  • @Sudsy1002 as far [as I see](http://referencesource.microsoft.com/#mscorlib/system/console.cs,5a089586e22b4062) `WriteLine` uses handle unconditionally (which is strange for me, it seems unsafe). The `Clear` method checks if the handle is valid (see the link above) and probably this check is the one that throws. I don't know why it was coded this way. – Sergey.quixoticaxis.Ivanov Nov 29 '17 at 14:14