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

Сер*_*гей 11 c# console-application

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

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

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我怎么能阻止这个?据我所知,控制台窗口上的所有属性/事件都与控制此行为无关.

如你所见,当我在窗口内点击时出现光标.当我按任意键 - 光标消失,应用程序继续工作 暂停的应用程序

Jim*_*hel 15

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

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

您可以在程序中禁用控制台上的"快速编辑模式",但这样做需要调用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.
    }
}
Run Code Online (Sandbox Code Playgroud)

如果沿着这条路走下去,那么在程序启动时保存原始控制台模式设置可能是个好主意,并在程序退出时恢复它.所以在启动时:

GetConsoleMode(GetConsoleWindow(), ref saveConsoleMode);
Run Code Online (Sandbox Code Playgroud)

当你的程序终止时:

SetConsoleMode(GetConsoleWindow(), saveConsoleMode);
Run Code Online (Sandbox Code Playgroud)

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


Sar*_*kal 6

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


吉姆的答案对我没有用,我不知道为什么。我四处寻找并找到了可行的解决方案,因此我将分享我的发现,希望能帮助处于相同情况的某人。

问题出在我从中获取的句柄GetConsoleWindow(),它给出了Win32错误(0x6),其中当我尝试使用它时该句柄无效。的呼吁SetConsoleMode()无济于事。

为了获得有效的句柄,我曾经GetStdHandle()获得过控制台的Input句柄。将此添加到Jim的代码中:

public const int STD_INPUT_HANDLE = -10;

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

然后更换GetConsoleWindow()GetStdHandle(STD_INPUT_HANDLE)DisableQuickEdit()EnableQuickEdit()吉姆的代码。

调用后DisableQuickEdit(),将在控制台中禁用选择。

谢谢吉姆!