Windowcapture BitBlt失败及游戏反作弊截图黑屏问题求助
游戏反作弊导致WindowCapture BitBlt失败的解决/替代方案
问题描述
游戏反作弊机制拦截了传统GDI方式的屏幕捕获,导致BitBlt调用失败(错误返回值为0),尝试多种屏幕捕获方法均出现黑屏,现有基于win32api的捕获代码无法正常运行。
原捕获代码:
import cv2 import numpy as np import win32gui, win32ui, win32con class WindowCapture: w = 0 h = 0 hwnd = None cropped_x = 0 cropped_y = 0 offset_x = 0 offset_y = 0 def __init__(self, window_name): self.hwnd = win32gui.FindWindow(None, window_name) if not self.hwnd: raise Exception('Window not found: {}'.format(window_name)) window_rect = win32gui.GetWindowRect(self.hwnd) self.w = window_rect[2] - window_rect[0] self.h = window_rect[3] - window_rect[1] border_pixels = 1 # <-- Change this titlebar_pixels = 10 # <-- Change this self.w = self.w - border_pixels # <-- Change this self.h = self.h - titlebar_pixels - border_pixels self.cropped_x = border_pixels self.cropped_y = titlebar_pixels def get_screenshot(self): wDC = win32gui.GetWindowDC(self.hwnd) dcObj = win32ui.CreateDCFromHandle(wDC) cDC = dcObj.CreateCompatibleDC() dataBitMap = win32ui.CreateBitmap() dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h) cDC.SelectObject(dataBitMap) cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY) signedIntsArray = dataBitMap.GetBitmapBits(True) img = np.fromstring(signedIntsArray, dtype='uint8') img.shape = (self.h, self.w, 4) dcObj.DeleteDC() cDC.DeleteDC() win32gui.ReleaseDC(self.hwnd, wDC) win32gui.DeleteObject(dataBitMap.GetHandle()) img = img[...,:3] img = np.ascontiguousarray(img) return img def list_window_names(self): def winEnumHandler(hwnd, ctx): if win32gui.IsWindowVisible(hwnd): print(hex(hwnd), win32gui.GetWindowText(hwnd)) win32gui.EnumWindows(winEnumHandler, None) def get_screen_position(self, pos): return (pos[0] + self.offset_x, pos[1] + self.offset_y) wincap = WindowCapture("Window Name") screen = wincap.get_screenshot() cv2.imshow("", screen) cv2.waitKey(0)
可行方案
1. 使用Windows Graphics Capture API(WinRT)
这是微软推出的现代捕获接口,绕过GDI限制,对反作弊兼容性更好,要求Windows 10 1903及以上版本。
先安装依赖:
pip install pywinrt
示例代码:
import winrt.windows.graphics.capture as wgc import winrt.windows.graphics.imaging as wgi import numpy as np import cv2 import win32gui def get_window_hwnd(window_name): return win32gui.FindWindow(None, window_name) def capture_window(hwnd): # 初始化窗口捕获会话 interop = wgc.GraphicsCaptureInterop() capture_item = interop.create_for_window(hwnd) capture_session = wgc.GraphicsCaptureSession(capture_item) # 关闭光标捕获,仅捕获窗口内容 capture_session.is_cursor_capture_enabled = False capture_session.start_capture() # 转换捕获帧为OpenCV格式 frame = capture_session.get_next_frame() bitmap = frame.surface access = bitmap.create_async(wgi.BitmapAccessMode.READ).get_result() buffer = access.get_plane(0).buffer width = bitmap.size.width height = bitmap.size.height img = np.frombuffer(buffer, dtype=np.uint8).reshape((height, width, 4)) img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) capture_session.close() return img # 使用示例 hwnd = get_window_hwnd("Window Name") captured_img = capture_window(hwnd) cv2.imshow("Captured", captured_img) cv2.waitKey(0)
2. DXGI桌面复制方案
通过DirectX的DXGI接口直接从显卡缓冲区取帧,可绕过绝大多数GDI层面的拦截。实现思路:
- 用pywin32或ctypes调用DXGI相关API
- 创建
IDXGIOutputDuplication对象,针对游戏窗口所在显示器进行捕获 - 将捕获到的纹理数据转换为OpenCV可处理的格式
3. 调整运行权限与白名单
- 以管理员身份运行Python脚本,部分反作弊会限制普通权限进程的捕获操作
- 若游戏反作弊支持白名单,可尝试将Python进程加入(竞技类游戏通常不允许此操作)
4. 捕获全屏后裁剪游戏区域
如果窗口捕获被完全拦截,可尝试捕获整个屏幕,再裁剪出游戏窗口区域:
先安装依赖:
pip install mss
示例代码:
import mss import numpy as np import cv2 import win32gui def get_window_rect(window_name): hwnd = win32gui.FindWindow(None, window_name) return win32gui.GetWindowRect(hwnd) # 捕获全屏并裁剪游戏区域 with mss.mss() as sct: window_rect = get_window_rect("Window Name") monitor = { "top": window_rect[1], "left": window_rect[0], "width": window_rect[2] - window_rect[0], "height": window_rect[3] - window_rect[1] } img = np.array(sct.grab(monitor)) img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) cv2.imshow("Captured", img) cv2.waitKey(0)
内容的提问来源于stack exchange,提问作者CrotaL
相关产品推荐
相关产品推荐

