Search code examples
pythonwindowsoperating-systemkeyboardkeyboard-events

Block and replace OS operations. I can't figure out how to block the Windows OS ctrl+v paste function and replace it with something else


I'm making a custom paste script and need to block OS pasting on ctrl+v so that I can replace it with my own behaviour.

This is some test code that is supposed to block OS pasting but it doesn't:

import keyboard

block_os_paste = False # Flag to toggle blocking
print("Unblocked OS Ctrl+V. Default behavior.")

def toggle_blocking():
    """Toggle the blocking of OS Ctrl+V."""
    global block_os_paste
    block_os_paste = not block_os_paste
    if block_os_paste:
        print("Blocking OS Ctrl+V. Custom behavior enabled.")
    else:
        print("Unblocking OS Ctrl+V. Default behavior restored.")

def handle_ctrl_v(e):
    """Custom handler for Ctrl+V."""
    if block_os_paste:
        e.suppress = True
        print("cookies")  # Custom behavior
    else:
        keyboard.send("ctrl+v")  # Send the default behavior

keyboard.add_hotkey("f9", toggle_blocking) # Assign hotkey to toggle blocking
keyboard.add_hotkey("ctrl+v", lambda: handle_ctrl_v(False)) # Intercept Ctrl+V
keyboard.wait("esc")  # Exit the script when "Esc" is pressed

I have tried many other ways as well like using global keyboard listeners from pynput but nothing I've come up with is working. Any help would be greatly appreciated.


Solution

  • For anyone else looking into this I found the solution. Unfortunately it is extremely aids so implementing my own functionality into it and customizing it will be horrible. But possible.

    import ctypes
    import ctypes.wintypes
    import keyboard
    import win32api
    import win32con
    
    # Define virtual key codes if not available in win32con
    VK_V = 0x56  # Virtual key code for 'V'
    VK_ESCAPE = 0x1B  # Virtual key code for 'Esc'
    
    # Variable to toggle blocking
    block_os_paste = False
    hHook = None  # Initialize hHook as a global variable
    
    def toggle_blocking():
        """Toggle the blocking of OS Ctrl+V."""
        global block_os_paste
        block_os_paste = not block_os_paste
        if block_os_paste:
            print("Blocking OS Ctrl+V")
        else:
            print("Unblocking OS Ctrl+V")
    
    # Hook callback prototype
    def low_level_keyboard_handler(nCode, wParam, lParam):
        """Low-level keyboard hook handler."""
        if nCode == 0:  # If wParam is WM_KEYDOWN or WM_KEYUP
            # Extract virtual key code from lParam
            key_info = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long)).contents
            vk_code = key_info.value  # Extract the virtual key code
            
            isCtrlPressed = win32api.GetAsyncKeyState(win32con.VK_CONTROL) < 0
            
            # Check for only WM_KEYDOWN
            if wParam == win32con.WM_KEYDOWN:
                if (vk_code == VK_V) and isCtrlPressed:
                    if block_os_paste:
                        print("Pasting has been blocked!")  # Print only once
                        return 1  # Block the Ctrl+V event
    
            # Check for Esc key press to exit
            if vk_code == VK_ESCAPE:
                print("Exiting...")
                ctypes.windll.user32.PostQuitMessage(0)  # Exit the message loop
    
        return ctypes.windll.user32.CallNextHookEx(hHook, nCode, wParam, lParam)
    
    # Set keyboard hook
    def set_keyboard_hook():
        """Set a low-level keyboard hook."""
        global hHook
        WH_KEYBOARD_LL = 13
        # Use ctypes.CFUNCTYPE to define the correct function pointer
        LOW_LEVEL_KEYBOARD_HOOK = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
        low_level_handler = LOW_LEVEL_KEYBOARD_HOOK(low_level_keyboard_handler)
        hHook = ctypes.windll.user32.SetWindowsHookExA(WH_KEYBOARD_LL, low_level_handler, None, 0)
    
        msg = ctypes.wintypes.MSG()
        while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), 0, 0, 0) != 0:
            ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
            ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
    
    if __name__ == "__main__":
        keyboard.add_hotkey('f9', toggle_blocking)
        
        print("Press F9 to toggle blocking, Esc to exit.")
        try:
            set_keyboard_hook()  # Set the keyboard hook
        except KeyboardInterrupt:
            pass
        finally:
            if hHook is not None:
                ctypes.windll.user32.UnhookWindowsHookEx(hHook)
    

    This will block pasting and replace it with printing a statement that prints on key down. Can alter it so that it does something on key down and something else on key up.