27

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

Сергей
  • 423
  • 1
  • 4
  • 7
  • What is "workspace app"? How does the output looks like? How does the code producing output looks like? – Sinatr Aug 31 '16 at 13:02
  • workspace means console window, app output - logs of processing operations like "Move file A to storage B..." – Сергей Aug 31 '16 at 13:05
  • "Doctor, it hurts when I do this!" "Well, then, don't do that!" – Shannon Holsinger Aug 31 '16 at 13:06
  • 2
    Stop editing this please, I've got a rewrite that will hopefully clarify what's going on. @ShannonHolsinger the problem is that you can't exactly tell users not to accidentally click on the console window... –  Aug 31 '16 at 13:08
  • @ShannonHolsinger Oh, such smart. It's customer wishes. – Сергей Aug 31 '16 at 13:08
  • 3
    I'm not sure, but this might be new in windows 10. The behavior is very suboptimal if you've got a long running console application sitting on a client's machine and they accidently stop executing it when trying to focus the window. And there's no indication of what is going on, or how to get the application running again. –  Aug 31 '16 at 13:13
  • 1
    See [this](http://stackoverflow.com/q/13656846/1997232), may not be an exact case, but contains useful hints. – Sinatr Aug 31 '16 at 13:14
  • 1
    @Will Thanks for edit. Customer, like you say, accidentally clicked within the window, and think that app is freezed. After explaining that app was just paused, customer wishes disable this behavior to avoid such situations – Сергей Aug 31 '16 at 13:18
  • I cannot reproduce it. The code you provided works fine. – Mahdi Aug 31 '16 at 13:20
  • 2
    @Mahdi what operating system? I believe this change is new to windows 10. –  Aug 31 '16 at 13:22
  • @Sinatr that's definitely the same issue. –  Aug 31 '16 at 13:23
  • @Will Windows 10 Pro. – Mahdi Aug 31 '16 at 13:24
  • 2
    @Mahdi same here, and I can definitely repro. –  Aug 31 '16 at 13:24
  • You could also run the process from another process using System.Diagnostics and hide the console window altogether. – Shannon Holsinger Aug 31 '16 at 13:28
  • Here's how to turn it off via interop calls http://stackoverflow.com/q/13656846/1997232 –  Aug 31 '16 at 13:29

2 Answers2

37

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,
    out 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(), ref 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.

dbc
  • 104,963
  • 20
  • 228
  • 340
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • This seems to only work when running in debug from Visual Studio. When launching the program on its own, even when running as administrator, the problem still occurs. Any ideas? – tayoung Jan 11 '18 at 19:50
  • @tayoung - Check the error returns from each system call, and try calling `Marshal.GetLastWin32Error()` in the event of an error, e.g.: `var error = Marshal.GetLastWin32Error(); var errorMessage = new Win32Exception(error).Message;` – dbc Jan 11 '18 at 22:05
  • 1
    @tayoung: This works for me, even when running standalone. If you can illustrate otherwise, please post a question with an example. – Jim Mischel Jan 14 '18 at 14:09
  • @jimMischel I'm Facing a similar issue. I checked your both solution(this one and which is available on your old post). I checked with the quick edit mode on and off. (Both getting the same results). I did not found a solution yet. Is there any solution to how can I solve this error. – Nikunj Chaklasiya Aug 18 '20 at 08:29
  • @JimMischel this worked for me, running standalone. Thank you. – Sergio Porres Mar 19 '21 at 05:13
  • Can someone please explain what does `mode = mode & ~(QuickEditMode | ExtendedFlags);` mean? – bzmind Jul 22 '21 at 15:42
  • 1
    @SMH It's a bit mask that's turning off `QuickEditMode` and `ExtendedFlags`. Assume that `QuickEditMode = 1` and `ExtendedFlags = 2`. Those aren't the actual values, but they'll do. So `(QuickEditMode | ExtendedFlags) = 1 | 2`. That's `3`, or `00000011` in binary. The `~` (not) operator flips the bits, so that becomes `11111100` in binary. Now, say that `mode` is `01011011`. If I `&` (and) that with `11111100`, then I get `011011000`: it clears the `QuickEditMode` and `ExtendedFlags` bits in the `mode` value. See https://www.tutorialspoint.com/csharp/csharp_bitwise_operators.htm for more info. – Jim Mischel Jul 28 '21 at 15:52
15

I just saw that this answer linked in the comments of OP's question contained what I found by myself. I will keep my answer because people might not see it, just like me, and it would spare them a lot of time.


Jim's answer did not work for me, I couldn't figure out why. I dug around and found a solution that works, so I'll share my findings, hopefully helping someone in the same situation.

The problem was with the handle that I got from GetConsoleWindow(), it gave a Win32 error (0x6) where the handle is invalid when I tried to use it. The call to SetConsoleMode() did nothing.

To get a working handle, I used GetStdHandle() to get the Input handle for the console. Add this to Jim's code :

public const int STD_INPUT_HANDLE = -10;

[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);

Then replace GetConsoleWindow() by GetStdHandle(STD_INPUT_HANDLE) in DisableQuickEdit() and EnableQuickEdit() in Jim's code.

After calling DisableQuickEdit(), the selection is disabled in the console.

Thanks Jim !

Sarkal
  • 151
  • 1
  • 4