如何编程禁用C#控制台应用程序的快速编辑模式?

36

我尝试了几个解决方案,比如这个 ->

http://www.pcreview.co.uk/forums/console-writeline-hangs-if-user-click-into-console-window-t1412701.html

但是,我注意到在GetConsoleMode(IntPtr hConsoleHandle, out int mode)中,模式对于不同的控制台应用程序是不同的。 它是不固定的。

我能否禁用控制台应用程序上的鼠标点击(右键/左键),以实现相同的情况。 我发现可以使用IMessageFilter来完成此操作,但仅适用于窗体应用程序,而不适用于控制台应用程序。


1
据我所知,它默认是关闭的。如果用户想要启用它,那你又有什么资格告诉他们不同呢? - M.Babcock
实际上,昨天发生了同样的情况,当我们的一个基于控制台的C#应用程序作为服务器运行时。实施团队中的某个人可能错误地打开了快速编辑模式,然后无意中单击了窗口。这会导致UI线程冻结。在实际发现问题不是技术问题之前,我们花费了2个小时的时间。 :) - Himanshu
6个回答

79

对于像我这样喜欢复制/粘贴的不费脑筋代码的人,这里是受采纳答案启发而来的代码:

using System;
using System.Runtime.InteropServices;

static class DisableConsoleQuickEdit {

   const uint ENABLE_QUICK_EDIT = 0x0040;

   // STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
   const int STD_INPUT_HANDLE = -10;

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

   [DllImport("kernel32.dll")]
   static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);

   [DllImport("kernel32.dll")]
   static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);

   internal static bool Go() {

      IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);

      // get current console mode
      uint consoleMode;
      if (!GetConsoleMode(consoleHandle, out consoleMode)) {
         // ERROR: Unable to get console mode.
         return false;
      }

      // Clear the quick edit bit in the mode flags
      consoleMode &= ~ENABLE_QUICK_EDIT;

      // set the new mode
      if (!SetConsoleMode(consoleHandle, consoleMode)) {
         // ERROR: Unable to set console mode
         return false;
      }

      return true;
   }
}

7
太完美了! - Jordec
3
绝对完美! - Allen Underwood
2
这正是我一直在寻找的。谢谢。 - tedebus
1
也适用于 .Net Core。 - Noobie3001
如果有需要,这里有一个Python的等效版本:https://dev59.com/SVsX5IYBdhLWcg3wY-rX#76855923 - Maicon Mauricio

18
如果您想禁用快速编辑模式,需要调用GetConsoleMode来获取当前模式。然后清除启用快速编辑的位并调用SetConsoleMode。假设您有未管理函数的托管原型,则编写如下所示:
const int ENABLE_QUICK_EDIT = 0x0040;

IntPtr consoleHandle = GetConsoleWindow();
UInt32 consoleMode;

// get current console mode
if (!GetConsoleMode(consoleHandle, out consoleMode))
{
    // Error: Unable to get console mode.
    return;
}

// Clear the quick edit bit in the mode flags
mode &= ~ENABLE_QUICK_EDIT;

// set the new mode
if (!SetConsoleMode(consoleHandle, consoleMode))
{
    // ERROR: Unable to set console mode
}

如果您想禁用鼠标输入,您需要清除鼠标输入位。

const int ENABLE_MOUSE_INPUT = 0x0010;

mode &= ~ENABLE_MOUSE_INPUT;

谢谢,这正是我正在寻找的内容。 - Himanshu
@M.Babcock:是的,这就是链接代码试图做的事情。但是链接的代码获取的是标准输入句柄,而不是控制台窗口句柄(可能不同)。它还硬编码了控制台模式,这可能会更改许多不同的设置。此代码仅更改一个设置。 - Jim Mischel
@Jim Mischel - 使用IntPtr consoleHandle = GetConsoleWindow()会导致进入if条件并返回。因此,我使用了public const int STD_INPUT_HANDLE = -10; IntPtr handle = GetStdHandle(STD_INPUT_HANDLE); GetConsoleMode(handle, out consoleMode)来获取模式。我是否遗漏了什么? - Himanshu
这是一些时间以前的内容,但有一些额外的提示: 来源其中包含重要信息:要禁用QuickEdit模式,必须在SetConsole上设置Extended标志。如果您在不使用QUICK和EXTENDED的情况下调用SetConsole,则标志会更改,但quickedit仍然启用。要禁用: mode&=〜ENABLE_QUICK_EDIT; mode | = ENABLE_EXTENDED_FLAGS; - Gabor
下投票者:习惯了要提供下投票的原因。这个答案有什么问题吗? - Jim Mischel
@JimMischel我不是那个给这个投票打负分的人。但是,我认为你的代码中有一个小错误:mode &= ~ENABLE_QUICK_EDIT; 这一行应该是 consoleMode &= ... 我觉得你可能想纠正一下。 - David I. McIntosh

10

阅读以上答案,不能使用GetConsoleWindow()。必须使用GetStdHandle()。

因此,这里提供一个复制粘贴类来启用/禁用快速编辑模式。调用ConsoleWindow.QuickEditMode(false);来禁用控制台窗口的快速编辑模式。

using System;
using System.Runtime.InteropServices;

public static class ConsoleWindow
{
    private static class NativeFunctions
    {
        public enum StdHandle : int
        {
            STD_INPUT_HANDLE = -10,
            STD_OUTPUT_HANDLE = -11,
            STD_ERROR_HANDLE = -12,
        }

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

        public enum ConsoleMode : uint
        {
            ENABLE_ECHO_INPUT = 0x0004,
            ENABLE_EXTENDED_FLAGS = 0x0080,
            ENABLE_INSERT_MODE = 0x0020,
            ENABLE_LINE_INPUT = 0x0002,
            ENABLE_MOUSE_INPUT = 0x0010,
            ENABLE_PROCESSED_INPUT = 0x0001,
            ENABLE_QUICK_EDIT_MODE = 0x0040,
            ENABLE_WINDOW_INPUT = 0x0008,
            ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200,

            //screen buffer handle
            ENABLE_PROCESSED_OUTPUT = 0x0001,
            ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
            ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
            DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
            ENABLE_LVB_GRID_WORLDWIDE = 0x0010
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
    }

    public static void QuickEditMode(bool Enable)
    {
        //QuickEdit lets the user select text in the console window with the mouse, to copy to the windows clipboard.
        //But selecting text stops the console process (e.g. unzipping). This may not be always wanted.
        IntPtr consoleHandle = NativeFunctions.GetStdHandle((int)NativeFunctions.StdHandle.STD_INPUT_HANDLE);
        UInt32 consoleMode;

        NativeFunctions.GetConsoleMode(consoleHandle, out consoleMode);
        if (Enable)
            consoleMode |= ((uint)NativeFunctions.ConsoleMode.ENABLE_QUICK_EDIT_MODE);
        else
            consoleMode &= ~((uint)NativeFunctions.ConsoleMode.ENABLE_QUICK_EDIT_MODE);

        consoleMode |= ((uint)NativeFunctions.ConsoleMode.ENABLE_EXTENDED_FLAGS);

        NativeFunctions.SetConsoleMode(consoleHandle, consoleMode);
    }
}

1
对于使用vb.net的人
Const ENABLE_QUICK_EDIT As UInteger = &H40
Const STD_INPUT_HANDLE As Integer = -10


   <DllImport("kernel32.dll", SetLastError:=True)>
Public Function GetStdHandle(ByVal nStdHandle As Integer) As IntPtr
End Function

<DllImport("kernel32.dll")>
Private Function GetConsoleMode(ByVal hConsoleHandle As IntPtr, <Out> ByRef lpMode As UInteger) As Boolean
End Function

<DllImport("kernel32.dll")>
Private Function SetConsoleMode(ByVal hConsoleHandle As IntPtr, ByVal dwMode As UInteger) As Boolean
End Function 

    Friend Function Go() As Boolean
    Dim consoleHandle As IntPtr = GetStdHandle(STD_INPUT_HANDLE)
    Dim consoleMode As UInteger

    If Not GetConsoleMode(consoleHandle, consoleMode) Then
        Return False
    End If

    consoleMode = consoleMode And Not ENABLE_QUICK_EDIT

    If Not SetConsoleMode(consoleHandle, consoleMode) Then
        Return False
    End If

    Return True
End Function

sub main()
go()
end sub

2
但问题是关于C#。 - Jeremy Caney
但是这个答案适用于使用vb.net的人。 - user736893

0
通过下面的代码组合,我可以启用或禁用快速编辑模式。
const int ENABLE_QUICK_EDIT = 0x0040;

// STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
const int STD_INPUT_HANDLE = -10;

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

[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int lpMode);

[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, int dwMode);

要启用,只需执行 currentConsoleMode &= ENABLE_QUICK_EDIT;

要禁用,请执行 currentConsoleMode &= ~ENABLE_QUICK_EDIT

然后调用 SetConsoleMode


-1

我刚好在我的控制台应用程序中启用快速编辑模式时遇到了同样的问题,该应用程序是用C编写的,并且在Windows 7 32位下工作了很长时间。将其(实际上不是移植,而是适应一些代码行)移植到Windows 10 64位(仍然是32位应用程序)后,我观察到了相同的行为。所以我开始寻找解决方案。

但由于某种未知的原因,代码的工作方式完全相反,即在模式参数中设置位ENABLE_QUICK_EDIT_MODE实际上会禁用快速编辑模式。重置位则启用快速编辑模式...???

这是我的代码:

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

BOOLEAN EnableQuickEdit()
{
    HWND conHandle = GetStdHandle(STD_INPUT_HANDLE);
    int mode;
    DWORD dwLastError = GetLastError();
    if (!GetConsoleMode(conHandle, &mode))
    {
        // error getting the console mode. Exit.
        dwLastError = GetLastError();
        return (dwLastError == 0);
    }
    else 
        dwLastError = 0;
    mode = mode & ~QuickEditMode;

    if (!SetConsoleMode(conHandle, mode | ExtendedFlags))
    {
        // error setting console mode.
        dwLastError = GetLastError();
    }
    else
        dwLastError = 0;
    return (dwLastError == 0);
}

BOOLEAN DisableQuickEdit()
{
    HWND conHandle = GetStdHandle(STD_INPUT_HANDLE);
    int mode;
    DWORD dwLastError = GetLastError();

    if (!GetConsoleMode(conHandle, &mode))
    {
        // error getting the console mode. Exit.
        dwLastError = GetLastError();
        return (dwLastError == 0);
    }
    else
        dwLastError = 0;

    mode = mode | QuickEditMode;

    if (!SetConsoleMode(conHandle, mode))
    {
        // error getting the console mode. Exit.
        dwLastError = GetLastError();
    }
    else
        dwLastError = 0;
    return (dwLastError == 0);
}

你好,Wolfgang


问题是关于C#的。 - Benjineer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接