【问题标题】:How to enable Windows console QuickEdit Mode from python?如何从 python 启用 Windows 控制台快速编辑模式?
【发布时间】:2016-09-26 18:33:15
【问题描述】:

我想在运行 python 脚本时在控制台中强制使用 QuickEdit 模式,然后在终止之前将其禁用。有没有办法做到这一点?

【问题讨论】:

    标签: python windows console prompt


    【解决方案1】:

    您可以使用ctypes 呼叫GetConsoleModeSetConsoleMode

    ctypes 定义:

    import msvcrt
    import atexit
    import ctypes
    from ctypes import wintypes
    
    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
    
    # input flags
    ENABLE_PROCESSED_INPUT = 0x0001
    ENABLE_LINE_INPUT      = 0x0002
    ENABLE_ECHO_INPUT      = 0x0004
    ENABLE_WINDOW_INPUT    = 0x0008
    ENABLE_MOUSE_INPUT     = 0x0010
    ENABLE_INSERT_MODE     = 0x0020
    ENABLE_QUICK_EDIT_MODE = 0x0040
    ENABLE_EXTENDED_FLAGS  = 0x0080
    
    # output flags
    ENABLE_PROCESSED_OUTPUT   = 0x0001
    ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 # VT100 (Win 10)
    
    def check_zero(result, func, args):    
        if not result:
            err = ctypes.get_last_error()
            if err:
                raise ctypes.WinError(err)
        return args
    
    if not hasattr(wintypes, 'LPDWORD'): # PY2
        wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)
    
    kernel32.GetConsoleMode.errcheck= check_zero
    kernel32.GetConsoleMode.argtypes = (
        wintypes.HANDLE,   # _In_  hConsoleHandle
        wintypes.LPDWORD,) # _Out_ lpMode
    
    kernel32.SetConsoleMode.errcheck= check_zero
    kernel32.SetConsoleMode.argtypes = (
        wintypes.HANDLE, # _In_  hConsoleHandle
        wintypes.DWORD,) # _Out_ lpMode
    

    以下将底层 WinAPI 函数包装为 get_console_modeset_console_mode。我将包装器限制为仅在控制台的活动输入缓冲区或活动输出缓冲区上运行,即\\.\CONIN$\\.\CONOUT$。我认为这比不必担心文件描述符和句柄要简单。值得注意的是,sys.stdinsys.stdout 可能会重定向到其他地方,C 运行时的标准 I/O FILE 流、文件描述符和您可以从 GetStdHandle 获得的 Windows 标准句柄也可能是这种情况。在这些情况下,您仍然可以打开 CONIN$CONOUT$,只要进程附加到控制台。

    def get_console_mode(output=False):
        '''Get the mode of the active console input or output
           buffer. Note that if the process isn't attached to a
           console, this function raises an EBADF IOError.
        '''
        device = r'\\.\CONOUT$' if output else r'\\.\CONIN$'
        with open(device, 'r+') as con:
            mode = wintypes.DWORD()
            hCon = msvcrt.get_osfhandle(con.fileno())
            kernel32.GetConsoleMode(hCon, ctypes.byref(mode))
            return mode.value
    
    def set_console_mode(mode, output=False):
        '''Set the mode of the active console input or output
           buffer. Note that if the process isn't attached to a
           console, this function raises an EBADF IOError.
        '''
        device = r'\\.\CONOUT$' if output else r'\\.\CONIN$'
        with open(device, 'r+') as con:
            hCon = msvcrt.get_osfhandle(con.fileno())
            kernel32.SetConsoleMode(hCon, mode)
    

    update_console_mode结合了后面的功能,让你传入你要设置的flags和要修改的标志mask。这包括要清除的标志。它还允许通过注册atexit function 来恢复以前的模式。

    def update_console_mode(flags, mask, output=False, restore=False):
        '''Update a masked subset of the current mode of the active
           console input or output buffer. Note that if the process
           isn't attached to a console, this function raises an
           EBADF IOError.
        '''
        current_mode = get_console_mode(output)
        if current_mode & mask != flags & mask:
            mode = current_mode & ~mask | flags & mask
            set_console_mode(mode, output)
        else:
            restore = False
        if restore:
            atexit.register(set_console_mode, current_mode, output)
    

    例子:

    if __name__ == '__main__':
        import os
        import sys
        import time
    
        if sys.stderr is None:
            os.close(2)
            sys.stderr = open('stderr.txt', 'w', buffering=1)
    
        print("%#06x, %#06x" % (get_console_mode(),
                                get_console_mode(output=True)))    
    
        flags = mask = ENABLE_EXTENDED_FLAGS | ENABLE_QUICK_EDIT_MODE
        update_console_mode(flags, mask, restore=True)
    
        print("%#06x, %#06x" % (get_console_mode(),
                                get_console_mode(output=True)))    
    
        time.sleep(10) # check console properties
    

    【讨论】:

    • 谢谢!我会试试看。
    • 我必须使用模式 r 而不是 r+ 打开 CONIN$,否则我会遇到异常 File or stream is not seekable,使用新的 Windows 10 控制台。
    • 我还应该注意,根据MS docs,您还需要设置 ENABLE_EXTENDED_FLAGS 以启用或禁用快速编辑模式。当我尝试在 PowerShell 窗口中执行此操作时,这让我大吃一惊。
    【解决方案2】:

    对于任何试图仅对当前控制台禁用快速编辑和插入模式并且无法找到简单解决方案的人:

    import ctypes
    
    kernel32 = ctypes.windll.kernel32
    kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), 128)
    

    【讨论】:

    • 非常感谢!
    【解决方案3】:

    这对于尝试在 Windows 中仅启用和禁用快速编辑模式而不禁用其他功能的人可能会有所帮助。

    def quickedit(enabled=1): # This is a patch to the system that sometimes hangs
            import ctypes
            '''
            Enable or disable quick edit mode to prevent system hangs, sometimes when using remote desktop
            Param (Enabled)
            enabled = 1(default), enable quick edit mode in python console
            enabled = 0, disable quick edit mode in python console
            '''
            # -10 is input handle => STD_INPUT_HANDLE (DWORD) -10 | https://docs.microsoft.com/en-us/windows/console/getstdhandle
            # default = (0x4|0x80|0x20|0x2|0x10|0x1|0x40|0x200)
            # 0x40 is quick edit, #0x20 is insert mode
            # 0x8 is disabled by default
            # https://docs.microsoft.com/en-us/windows/console/setconsolemode
            kernel32 = ctypes.windll.kernel32
            if enabled:
                kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), (0x4|0x80|0x20|0x2|0x10|0x1|0x40|0x100))
                print("Console Quick Edit Enabled")
            else:
                kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), (0x4|0x80|0x20|0x2|0x10|0x1|0x00|0x100))
                print("Console Quick Edit Disabled")
    
    quickedit(0) # Disable quick edit in terminal
    

    只需禁用快速编辑的 0x40 标志

    【讨论】:

      猜你喜欢
      • 2020-07-08
      • 1970-01-01
      • 2017-11-09
      • 2012-11-19
      • 1970-01-01
      • 1970-01-01
      • 2014-03-15
      • 1970-01-01
      • 2016-04-23
      相关资源
      最近更新 更多