You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

DirectX全屏应用(游戏)屏幕捕获失败问题求助

解决DirectX捕获全屏游戏画面的问题

我来帮你搞定这个全屏游戏捕获的难题,你的代码在桌面环境正常但无法抓取全屏应用,主要是设备创建的参数配置和兼容性问题导致的——下面是具体的修复方案和解释:

核心问题分析

你遇到的错误码0x88760868对应DirectX的D3DERR_INVALIDMODE,意思是你设置的全屏显示模式参数无效。直接把parameters.Windowed改为false不可行,因为全屏游戏通常会独占显示设备,此时你无法创建第二个全屏DirectX设备,而且参数不匹配也会导致设备创建失败。

更稳妥的方案是使用窗口模式的DirectX设备来捕获全屏内容,只要调整参数和窗口句柄就能实现兼容。

修复后的完整代码

下面是加入错误检查、兼容全屏捕获的修改版代码:

#include <d3d9.h>
#include <windows.h>
#include <d3dx9.h>

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

void save_screen(UINT width, UINT height, UINT pitch, LPBYTE data, const WCHAR* filename, const GUID& format) {
    // 保留你原有的save_screen实现逻辑
    D3DXSaveSurfaceToFile(filename, format, NULL, data, pitch);
}

void get_screen() {
    UINT adapter = D3DADAPTER_DEFAULT;
    IDirect3D9* d3d = nullptr;
    IDirect3DDevice9* device = nullptr;
    IDirect3DSurface9* surface = nullptr;
    D3DPRESENT_PARAMETERS parameters = { 0 };
    D3DDISPLAYMODE mode;
    D3DLOCKED_RECT rc;
    UINT pitch;
    LPBYTE shots = nullptr;
    HRESULT hr;

    // 初始化Direct3D
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (!d3d) {
        MessageBox(NULL, L"Failed to create Direct3D9 object", L"Error", MB_ICONERROR);
        goto cleanup;
    }

    // 获取当前显示器的显示模式
    hr = d3d->GetAdapterDisplayMode(adapter, &mode);
    if (FAILED(hr)) {
        MessageBox(NULL, L"Failed to get adapter display mode", L"Error", MB_ICONERROR);
        goto cleanup;
    }

    // 配置Present参数:窗口模式,关联桌面窗口
    parameters.Windowed = TRUE;
    parameters.BackBufferFormat = mode.Format; // 使用当前显示器原生格式,而非硬编码
    parameters.BackBufferHeight = mode.Height;
    parameters.BackBufferWidth = mode.Width;
    parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    parameters.hDeviceWindow = GetDesktopWindow(); // 传入桌面窗口句柄,关键修改!

    // 创建设备:添加兼容性标志,硬件处理失败时 fallback 到软件处理
    hr = d3d->CreateDevice(
        adapter,
        D3DDEVTYPE_HAL,
        parameters.hDeviceWindow,
        D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
        &parameters,
        &device
    );
    if (FAILED(hr)) {
        hr = d3d->CreateDevice(
            adapter,
            D3DDEVTYPE_HAL,
            parameters.hDeviceWindow,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
            &parameters,
            &device
        );
        if (FAILED(hr)) {
            MessageBox(NULL, L"Failed to create Direct3D device", L"Error", MB_ICONERROR);
            goto cleanup;
        }
    }

    // 创建离屏表面:使用系统内存,方便后续读取
    hr = device->CreateOffscreenPlainSurface(
        mode.Width,
        mode.Height,
        mode.Format,
        D3DPOOL_SYSTEMMEM,
        &surface,
        nullptr
    );
    if (FAILED(hr)) {
        MessageBox(NULL, L"Failed to create offscreen surface", L"Error", MB_ICONERROR);
        goto cleanup;
    }

    // 获取前端缓冲区数据
    hr = device->GetFrontBufferData(0, surface);
    if (FAILED(hr)) {
        MessageBox(NULL, L"Failed to get front buffer data", L"Error", MB_ICONERROR);
        goto cleanup;
    }

    // 锁定表面并复制数据:添加只读锁定提升兼容性
    hr = surface->LockRect(&rc, NULL, D3DLOCK_READONLY);
    if (FAILED(hr)) {
        MessageBox(NULL, L"Failed to lock surface", L"Error", MB_ICONERROR);
        goto cleanup;
    }

    pitch = rc.Pitch;
    shots = new BYTE[pitch * mode.Height];
    CopyMemory(shots, rc.pBits, pitch * mode.Height);

    surface->UnlockRect();

    // 保存截图
    WCHAR file[100] = L"test.png";
    save_screen(mode.Width, mode.Height, pitch, shots, file, GUID_ContainerFormatPng);

cleanup:
    // 安全释放资源:判空避免空指针访问
    if (surface) surface->Release();
    if (device) device->Release();
    if (d3d) d3d->Release();
    if (shots) delete[] shots;
}

关键修改点说明

  1. 传入桌面窗口句柄
    原代码中CreateDevice的窗口句柄为NULL,这在窗口模式下是不规范的。改为GetDesktopWindow()让设备关联到桌面,从而可以捕获全屏应用的画面。

  2. 使用当前显示器的格式
    原代码硬编码D3DFMT_A8R8G8B8,但有些全屏游戏可能使用其他格式(比如D3DFMT_X8R8G8B8),改为mode.Format可以保证和当前显示模式完全匹配。

  3. 添加兼容性标志
    D3DCREATE_MULTITHREADED确保设备在多线程环境下稳定,D3DCREATE_FPU_PRESERVE避免影响其他程序的浮点运算;同时添加了硬件顶点处理失败时的软件处理 fallback,提升兼容性。

  4. 完善错误检查
    每一步DirectX调用都检查返回值,方便快速定位问题,同时在资源释放时判空,避免崩溃。

  5. 只读锁定表面
    锁定表面时添加D3DLOCK_READONLY标志,告诉DirectX我们只读取数据,不需要写入,提升性能和兼容性。

额外注意事项

  • 如果遇到某些采用独占全屏模式的游戏仍然无法捕获,可能需要启用Windows的“游戏栏”捕获权限,或者使用更底层的捕获方式(比如Windows 8及以上推荐的DXGI桌面复制API)。
  • 确保你的程序以管理员权限运行,避免权限不足导致无法访问全屏应用的缓冲区。

内容的提问来源于stack exchange,提问作者user2420655

火山引擎 最新活动