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, ¶meters, &device ); if (FAILED(hr)) { hr = d3d->CreateDevice( adapter, D3DDEVTYPE_HAL, parameters.hDeviceWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, ¶meters, &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; }
关键修改点说明
传入桌面窗口句柄:
原代码中CreateDevice的窗口句柄为NULL,这在窗口模式下是不规范的。改为GetDesktopWindow()让设备关联到桌面,从而可以捕获全屏应用的画面。使用当前显示器的格式:
原代码硬编码D3DFMT_A8R8G8B8,但有些全屏游戏可能使用其他格式(比如D3DFMT_X8R8G8B8),改为mode.Format可以保证和当前显示模式完全匹配。添加兼容性标志:
D3DCREATE_MULTITHREADED确保设备在多线程环境下稳定,D3DCREATE_FPU_PRESERVE避免影响其他程序的浮点运算;同时添加了硬件顶点处理失败时的软件处理 fallback,提升兼容性。完善错误检查:
每一步DirectX调用都检查返回值,方便快速定位问题,同时在资源释放时判空,避免崩溃。只读锁定表面:
锁定表面时添加D3DLOCK_READONLY标志,告诉DirectX我们只读取数据,不需要写入,提升性能和兼容性。
额外注意事项
- 如果遇到某些采用独占全屏模式的游戏仍然无法捕获,可能需要启用Windows的“游戏栏”捕获权限,或者使用更底层的捕获方式(比如Windows 8及以上推荐的
DXGI桌面复制API)。 - 确保你的程序以管理员权限运行,避免权限不足导致无法访问全屏应用的缓冲区。
内容的提问来源于stack exchange,提问作者user2420655




