当用户单击控制台时代码停止执行 window

Code stops executing when a user clicks on the console window

我有一个控制台应用程序可以在没有用户交互的情况下执行我的代码。如果用户有意或无意地在控制台 window 内单击,所有执行都会停止。

这与从控制台复制文本有关window。应用程序再次开始执行的唯一方法是用户选择文本,然后右键单击控制台 window,将其复制到剪贴板。

要查看实际效果,请创建一个控制台应用程序并添加以下代码。

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();
    }
}

当您单击控制台 window 时,任务线程停止执行。这根本不是理想的行为,我想防止这种情况在我的控制台应用程序中发生。

我该如何防止这种情况发生?据我所知,控制台上的 properties/events window 的 None 与控制此行为有任何关系。

如您所见,当我在 window 内单击时会出现光标。当我按任意键时 - 光标消失,应用程序继续工作

如果您在控制台上启用了快速编辑模式,就会发生这种情况 window。如果您在标题栏上 right-click 和 select 属性,然后 select 选项选项卡,您可以检查是否启用了快速编辑模式。如果禁用快速编辑模式,则当您单击 window.

时滚动不会停止

滚动停止的原因是因为在 window 中单击的鼠标习惯于 select 文本。

您可以在程序的控制台上禁用快速编辑模式,但这样做需要调用 GetConsoleMode and SetConsoleMode API 函数。以下是您的操作方式:

[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.
    }
}

如果您走这条路,最好在程序启动时保存原始控制台模式设置,并在程序退出时恢复它。所以在启动时:

GetConsoleMode(GetConsoleWindow(), ref saveConsoleMode);

当您的程序终止时:

SetConsoleMode(GetConsoleWindow(), saveConsoleMode);

当然有适当的错误处理。如果对 GetConsoleMode 的调用失败,您不想恢复控制台模式。

我刚刚看到这个 answer 链接在 OP 问题的评论中包含我自己发现的内容。我会保留我的答案,因为人们可能不会看到它,就像我一样,这会节省他们很多时间。


Jim 的回答对我不起作用,我不明白为什么。 我四处寻找并找到了一个可行的解决方案,所以我将分享我的发现,希望能帮助处于相同情况的人。

问题出在我从 GetConsoleWindow() 获得的句柄上,当我尝试使用它时出现 Win32 错误 (0x6),句柄无效。对 SetConsoleMode() 的调用没有任何作用。

为了获得工作句柄,我使用 GetStdHandle() 来获得控制台的输入句柄。将此添加到 Jim 的代码中:

public const int STD_INPUT_HANDLE = -10;

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

然后将 GetConsoleWindow() 替换为 GetStdHandle(STD_INPUT_HANDLE) 中的 DisableQuickEdit() 和 Jim 代码中的 EnableQuickEdit()

调用 DisableQuickEdit() 后,控制台中的选择被禁用。

谢谢吉姆!