Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to close / open console within C# using Win32 calls?

The following program throws an error on "Console.ReadKey()".

How do I re-enable the console after disabling it?

    using System;
    using System.Threading;
    using System.Runtime.InteropServices;

    namespace ConsoleApplication
    {
        class Program
        {
            static void Main(string[] args)
            {
                ThreadPool.QueueUserWorkItem((o) =>
                {
                    Thread.Sleep(1000);
                    IntPtr stdin = GetStdHandle(StdHandle.Stdin);
                    CloseHandle(stdin);
                });
                Console.ReadLine();
                Console.Write("ReadLine() successfully aborted by background thread.\n");
                Console.Write("[any key to exit]");
                Console.ReadKey(); // Throws an exception "Cannot read keys when either application does not have a console or when console input has been redirected from a file. Try Console.Read."
            }

            // P/Invoke:
            private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
            [DllImport("kernel32.dll")]
            private static extern IntPtr GetStdHandle(StdHandle std);
            [DllImport("kernel32.dll")]
            private static extern bool CloseHandle(IntPtr hdl);
        }
    }

Extra for Experts

If you're wondering, I need to be able to kill a background thread running ReadLine(), within C#. This appears to be the only way (thread.Abort won't work because ReadLine() runs deep within the bowels of the operating system, in unmanaged code). There is a lot of talk on this topic on StackOverflow, nobody has really discovered (or posted) a satisfactory method of aborting Console.ReadLine() yet. I think this code is on the right track - if only we can re-enable the console after disabling it.

like image 961
Contango Avatar asked Mar 08 '12 14:03

Contango


1 Answers

Use PostMessage to send [enter] into the current process:

    class Program
    {
        [DllImport("User32.Dll", EntryPoint = "PostMessageA")]
        private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

        const int VK_RETURN = 0x0D;
        const int WM_KEYDOWN = 0x100;

        static void Main(string[] args)
        {
            Console.Write("Switch focus to another window now to verify this works in a background process.\n");

            ThreadPool.QueueUserWorkItem((o) =>
            {
                Thread.Sleep(4000);

                var hWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
                PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0);
            });

            Console.ReadLine();

            Console.Write("ReadLine() successfully aborted by background thread.\n");
            Console.Write("[any key to exit]");
            Console.ReadKey();
        }
    }

This answer also works around the fact that calling .Abort on ReadLine() won't work, as the code is running in unmanaged code deep within the Windows kernel.

This answer is superior to any answers that only work if the current process has the focus, such as SendKeys and Input Simulator.

This answer is superior to the method of closing the current console handle, as the act of closing the current console handle results in future calls to ReadLine() throwing an error.

like image 90
Contango Avatar answered Nov 15 '22 03:11

Contango