简体   繁体   English

当用户单击控制台窗口时,代码停止执行

[英]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 当我按任意键 - 光标消失,应用程序继续工作 暂停的应用程序

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. 您可以在程序中禁用控制台上的“快速编辑模式”,但这样做需要调用GetConsoleModeSetConsoleMode API函数。 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. 如果对GetConsoleMode的调用失败,则不希望恢复控制台模式。

I just saw that this answer linked in the comments of OP's question contained what I found by myself. 我刚刚看到OP的问题评论中的这个答案包含了我自己发现的内容。 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. 问题是我从GetConsoleWindow()获得的句柄,它给出了Win32错误(0x6),当我尝试使用它时句柄无效。 The call to SetConsoleMode() did nothing. SetConsoleMode()的调用什么也没做。

To get a working handle, I used GetStdHandle() to get the Input handle for the console. 为了得到一个工作句柄,我使用GetStdHandle()来获取控制台的Input句柄。 Add this to Jim's code : 将此添加到Jim的代码中:

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. 然后更换GetConsoleWindow()GetStdHandle(STD_INPUT_HANDLE)DisableQuickEdit()EnableQuickEdit()在吉姆的代码。

After calling DisableQuickEdit() , the selection is disabled in the console. 调用DisableQuickEdit() ,在控制台中禁用选择。

Thanks Jim ! 谢谢吉姆!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM