Сергей Сергей - 3 months ago 28
C# Question

Code stops executing when a user clicks on the console window

I've got a console application that executes my code without user interaction. If the user clicks within the console window, on purpose or on accident, all execution stops.

This has something to do with copying text from the console window. The only way for the application to start executing again is if the user selects text and then right-clicks on the console window, copying it to the clipboard.

To see this in action, create a console application and add the following code.

class Program
{
static void Main(string[] args)
{
var task = Task.Run(async () =>
{
int i = 0;
while (true)
{
Console.WriteLine(i++);
await Task.Delay(1000);
}
});
Console.ReadLine();
}
}


When you click on the console window, the Task thread stops executing. This is not desirable behavior at all, and I want to prevent this from happening in my console application.

How can I prevent this? None of the properties/events on the console window have anything to do with controlling this behavior, as far as I can see.

As you can see, when i'm click within window appear cursor. When i press any key - cursor gone and app continue working
Paused app

Answer

This happens if you have Quick Edit Mode enabled on the console window. If you right-click on the title bar and select Properties, then select the Options tab, you can check to see if Quick Edit Mode is enabled. If you disable Quick Edit Mode, then the scrolling doesn't stop when you click in the window.

The reason scrolling stops is because a mouse clicked in the window is used to select text.

You can disable Quick Edit Mode on the console in your program, but doing so requires calling the GetConsoleMode and SetConsoleMode API functions. Here's how you would do it:

[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetConsoleWindow();

[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool GetConsoleMode(
    IntPtr hConsoleHandle,
    ref int lpMode);

[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleMode(
    IntPtr hConsoleHandle,
    int ioMode);

/// <summary>
/// This flag enables the user to use the mouse to select and edit text. To enable
/// this option, you must also set the ExtendedFlags flag.
/// </summary>
const int QuickEditMode = 64;

// ExtendedFlags must be combined with
// InsertMode and QuickEditMode when setting
/// <summary>
/// ExtendedFlags must be enabled in order to enable InsertMode or QuickEditMode.
/// </summary>
const int ExtendedFlags = 128;

void DisableQuickEdit()
{
    IntPtr conHandle = GetConsoleWindow();
    int mode;

    if (!GetConsoleMode(conHandle, out mode))
    {
        // error getting the console mode. Exit.
        return;
    }

    mode = mode & ~(QuickEditMode | ExtendedFlags);

    if (!SetConsoleMode(conHandle, mode))
    {
        // error setting console mode.
    }
}

void EnableQuickEdit()
{
    IntPtr conHandle = GetConsoleWindow();
    int mode;

    if (!GetConsoleMode(conHandle, out mode))
    {
        // error getting the console mode. Exit.
        return;
    }

    mode = mode | (QuickEditMode | ExtendedFlags);

    if (!SetConsoleMode(conHandle, mode))
    {
        // error setting console mode.
    }
}

If you go down this route, it's probably a good idea to save the original console mode setting when your program starts, and restore it when your program exits. So at startup:

GetConsoleMode(GetConsoleWindow(), out saveConsoleMode);

and when your program terminates:

SetConsoleMode(GetConsoleWindow(), saveConsoleMode);

With appropriate error handling, of course. You wouldn't want to restore the console mode if the call to GetConsoleMode failed.