【问题标题】:Code stops executing when a user clicks on the console window当用户单击控制台窗口时代码停止执行
【发布时间】:2017-01-08 01:55:20
【问题描述】:

我有一个控制台应用程序,无需用户交互即可执行我的代码。如果用户有意或无意地在控制台窗口内单击,所有执行都会停止。

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

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

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

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

如何防止这种情况发生?据我所知,控制台窗口上的所有属性/事件都与控制此行为无关。

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

【问题讨论】:

  • 请停止编辑这个,我有一个重写,希望能澄清发生了什么。 @ShannonHolsinger 问题是你不能准确地告诉用户不要不小心点击控制台窗口......
  • 我不确定,但这可能是 Windows 10 中的新功能。如果您的客户端计算机上有一个长时间运行的控制台应用程序,并且他们在试图聚焦窗口。而且没有任何迹象表明发生了什么,或者如何让应用程序再次运行。
  • this,可能不是一个确切的例子,但包含有用的提示。
  • @Mahdi 什么操作系统?我相信这种变化对于 Windows 10 来说是新的。
  • @Mahdi 在这里也一样,我绝对可以复制。

标签: c# console-application


【解决方案1】:

如果您在控制台窗口中启用了快速编辑模式,就会发生这种情况。如果您右键单击标题栏并选择属性,然后选择选项选项卡,您可以检查是否启用了快速编辑模式。如果您禁用快速编辑模式,则在您单击窗口时滚动不会停止。

滚动停止的原因是在窗口中单击鼠标用于选择文本。

您可以在程序的控制台上禁用快速编辑模式,但这样做需要调用GetConsoleModeSetConsoleMode 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 的调用失败,您不会想恢复控制台模式。

【讨论】:

  • 这似乎只在从 Visual Studio 调试运行时才有效。自行启动程序时,即使以管理员身份运行,问题仍然存在。有什么想法吗?
  • @tayoung - 检查每个系统调用的错误返回,并在发生错误时尝试调用Marshal.GetLastWin32Error(),例如:var error = Marshal.GetLastWin32Error(); var errorMessage = new Win32Exception(error).Message;
  • @tayoung:这对我有用,即使在独立运行时也是如此。如果您能以其他方式说明,请发布带有示例的问题。
  • @jiMMischel 我面临着类似的问题。我检查了你的两个解决方案(这个和你的旧帖子上都有)。我检查了快速编辑模式的开启和关闭。 (两者都得到相同的结果)。我还没有找到解决方案。有什么办法可以解决这个错误。
  • @SMH 这是关闭QuickEditModeExtendedFlags 的位掩码。假设QuickEditMode = 1ExtendedFlags = 2。这些不是实际值,但它们会这样做。所以(QuickEditMode | ExtendedFlags) = 1 | 2。那是3,或者二进制的00000011~(非)运算符翻转这些位,这样就变成了二进制的11111100。现在,假设mode01011011。如果我用&amp;(和)11111100,那么我得到011011000:它会清除mode 值中的QuickEditModeExtendedFlags 位。请参阅tutorialspoint.com/csharp/csharp_bitwise_operators.htm 了解更多信息。
【解决方案2】:

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


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

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

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

谢谢吉姆!

【讨论】:

    猜你喜欢
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    相关资源
    最近更新 更多