You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 18:10:49