求助Python实现模拟真实按键突破第三方应用截图限制
问题
某第三方应用存在行为检测,阻止常规截图操作:
- 使用
keyboard库、pyautogui或普通按键模拟工具无法完成截图 - 手动按
PrtScr键或屏幕键盘按键时,应用允许截图并自动保存至api/screenshots目录(只需窗口处于激活状态) - 测试发现模拟数字键"3"有效,但模拟
PrtScr无效
已尝试的方法
keyboard库模拟:
keyboard.press('print_screen') keyboard.release('print_screen')
pyautogui截图:
pyautogui.screenshot()
- 基于
ctypes的虚拟键模拟:
import ctypes from ctypes import wintypes import time user32 = ctypes.WinDLL('user32', use_last_error=True) INPUT_MOUSE = 0 INPUT_KEYBOARD = 1 INPUT_HARDWARE = 2 KEYEVENTF_EXTENDEDKEY = 0x0001 KEYEVENTF_KEYUP = 0x0002 KEYEVENTF_UNICODE = 0x0004 KEYEVENTF_SCANCODE = 0x0008 MAPVK_VK_TO_VSC = 0 VK_TAB = 0x09 VK_MENU = 0x12 VK_SNAPSHOT = 0x2C key_3 = 0x33 wintypes.ULONG_PTR = wintypes.WPARAM class MOUSEINPUT(ctypes.Structure): _fields_ = (("dx", wintypes.LONG), ("dy", wintypes.LONG), ("mouseData", wintypes.DWORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.ULONG_PTR)) class KEYBDINPUT(ctypes.Structure): _fields_ = (("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.ULONG_PTR)) def __init__(self, *args, **kwds): super(KEYBDINPUT, self).__init__(*args, **kwds) if not self.dwFlags & KEYEVENTF_UNICODE: self.wScan = user32.MapVirtualKeyExW(self.wVk, MAPVK_VK_TO_VSC, 0) class HARDWAREINPUT(ctypes.Structure): _fields_ = (("uMsg", wintypes.DWORD), ("wParamL", wintypes.WORD), ("wParamH", wintypes.WORD)) class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): _fields_ = (("ki", KEYBDINPUT), ("mi", MOUSEINPUT), ("hi", HARDWAREINPUT)) _anonymous_ = ("_input",) _fields_ = (("type", wintypes.DWORD), ("_input", _INPUT)) LPINPUT = ctypes.POINTER(INPUT) def _check_count(result, func, args): if result == 0: raise ctypes.WinError(ctypes.get_last_error()) return args user32.SendInput.errcheck = _check_count user32.SendInput.argtypes = (wintypes.UINT, # nInputs LPINPUT, # pInputs ctypes.c_int) # cbSize def PressKey(hexKeyCode): x = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=hexKeyCode)) user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x)) def ReleaseKey(hexKeyCode): x = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=hexKeyCode, dwFlags=KEYEVENTF_KEYUP)) user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x)) def Press_function(): PressKey(key_3) time.sleep(0.1) ReleaseKey(key_3) time.sleep(0.1) PressKey(VK_SNAPSHOT) time.sleep(0.1) ReleaseKey(VK_SNAPSHOT) if __name__ == "__main__": time.sleep(1) Press_function()
解决方案
原因分析
该应用大概率通过**按键事件的扫描码(Scan Code)**或事件来源标记区分真实按键与软件模拟。普通虚拟键(VK)模拟的事件缺少真实按键的硬件扫描码特征,导致PrtScr模拟未通过检测,而数字键"3"的模拟刚好符合应用的检测规则。
修改后的模拟代码
改用扫描码+KEYEVENTF_SCANCODE标记模拟PrtScr,让事件更接近真实硬件触发:
import ctypes from ctypes import wintypes import time user32 = ctypes.WinDLL('user32', use_last_error=True) INPUT_KEYBOARD = 1 KEYEVENTF_SCANCODE = 0x0008 KEYEVENTF_KEYUP = 0x0002 # 标准PC键盘PrtScr的扫描码,若无效可自行适配当前键盘布局 PRTSCR_SCANCODE = 0x2C wintypes.ULONG_PTR = wintypes.WPARAM class KEYBDINPUT(ctypes.Structure): _fields_ = (("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.ULONG_PTR)) class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): _fields_ = (("ki", KEYBDINPUT),) _anonymous_ = ("_input",) _fields_ = (("type", wintypes.DWORD), ("_input", _INPUT)) LPINPUT = ctypes.POINTER(INPUT) def _check_count(result, func, args): if result == 0: raise ctypes.WinError(ctypes.get_last_error()) return args user32.SendInput.errcheck = _check_count user32.SendInput.argtypes = (wintypes.UINT, LPINPUT, ctypes.c_int) def press_scan_code(scan_code): # 按下按键:仅传入扫描码,不指定虚拟键 input_down = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wScan=scan_code, dwFlags=KEYEVENTF_SCANCODE)) user32.SendInput(1, ctypes.byref(input_down), ctypes.sizeof(input_down)) def release_scan_code(scan_code): # 释放按键:添加KEYEVENTF_KEYUP标记 input_up = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wScan=scan_code, dwFlags=KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP)) user32.SendInput(1, ctypes.byref(input_up), ctypes.sizeof(input_up)) def simulate_prtscr(): press_scan_code(PRTSCR_SCANCODE) time.sleep(0.1) # 模拟真实按键的按压间隔 release_scan_code(PRTSCR_SCANCODE) # 自动激活目标窗口(可选) def activate_window(window_title): hwnd = user32.FindWindowW(None, window_title) if hwnd: user32.SetForegroundWindow(hwnd) return True return False if __name__ == "__main__": # 替换为你的目标应用窗口标题 target_window_title = "目标应用名称" # 给3秒手动切换窗口,或使用activate_window自动激活 time.sleep(3) # if activate_window(target_window_title): # time.sleep(0.5) simulate_prtscr()
补充说明
- 若
0x2C扫描码无效,可通过以下代码获取当前键盘布局下的PrtScr扫描码:
VK_SNAPSHOT = 0x2C MAPVK_VK_TO_VSC = 0 prtscr_scancode = user32.MapVirtualKeyExW(VK_SNAPSHOT, MAPVK_VK_TO_VSC, 0)
- 自动激活窗口功能需替换为目标应用的准确窗口标题,避免手动切换。
内容的提问来源于stack exchange,提问作者Maciek
相关产品推荐
相关产品推荐

