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

C# 当用户单击控制台窗口时,代码停止执行,c#,console-application,C#,Console Application,我有一个控制台应用程序,它在没有用户交互的情况下执行我的代码。如果用户故意或意外地在控制台窗口内单击,则所有执行都将停止 这与从控制台窗口复制文本有关。应用程序重新开始执行的唯一方法是,用户选择文本,然后右键单击控制台窗口,将其复制到剪贴板 要查看此操作,请创建一个控制台应用程序并添加以下代码 class Program { static void Main(string[] args) { var task = Task.Run(async () =>

我有一个控制台应用程序,它在没有用户交互的情况下执行我的代码。如果用户故意或意外地在控制台窗口内单击,则所有执行都将停止

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

要查看此操作,请创建一个控制台应用程序并添加以下代码

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();
    }
}
单击控制台窗口时,任务线程停止执行。这根本不是理想的行为,我想防止这种情况在我的控制台应用程序中发生

我怎样才能防止这种情况?据我所知,控制台窗口上的所有属性/事件都与控制此行为无关

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

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

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

您可以在程序的控制台上禁用快速编辑模式,但这样做需要调用和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.
    }
}
当您的程序终止时:

SetConsoleMode(GetConsoleWindow(), saveConsoleMode);
当然,有适当的错误处理。如果调用
GetConsoleMode
失败,您将不希望恢复控制台模式。

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


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

问题在于我从
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(标准输入\u句柄)
,并将Jim代码中的
启用快速编辑()

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


谢谢你,吉姆

请停止编辑这个,我有一个重写,希望能澄清发生了什么@ShannonHolsinger问题是你不能确切地告诉用户不要意外点击控制台窗口…我不确定,但这可能是Windows10中的新功能。如果客户机上有一个长时间运行的控制台应用程序,而他们在尝试聚焦窗口时意外停止执行,那么这种行为是非常不理想的。并且没有迹象表明发生了什么,或者如何使应用程序再次运行。请参阅,可能不是确切的情况,但包含有用的提示。@Mahdi什么操作系统?我相信这是windows 10的新变化。@Mahdi在这里也是一样的,我完全可以重新编程。这似乎只有在Visual Studio的调试中运行时才起作用。当独立启动程序时,即使以管理员身份运行,问题仍然会发生。有什么想法吗?@tayoung-检查每个系统调用返回的错误,并在发生错误时尝试调用
Marshal.GetLastWin32Error()
,例如:
var error=Marshal.GetLastWin32Error();var errorMessage=new Win32Exception(error).Message
@tayoung:这对我来说很有效,即使是在独立运行时。如果你能举例说明,请用一个例子发布一个问题。@jimMischel我面临着一个类似的问题。我检查了你的两个解决方案(这一个和你的旧帖子上提供的)。我检查了快速编辑模式的开关。(两者得到相同的结果)。我还没有找到解决办法。有什么办法可以解决这个错误吗。@JimMischel这对我来说很有效,可以独立运行。非常感谢。
public const int STD_INPUT_HANDLE = -10;

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