使用DirectX11捕获屏幕时触发读取访问违例异常的问题
DirectX11屏幕捕获读取访问违例(DesktopResource=0xFFFFFFFFFFFFFFFF)修复方案
错误原因分析
DesktopResource=0xFFFFFFFFFFFFFFFF是无效指针访问,说明屏幕捕获的核心资源初始化失败,常见诱因包括:
- D3D11设备创建参数不兼容DXGI屏幕复制需求
- DXGI输出设备选择错误(如选中非活跃显示器)
IDXGIOutputDuplication创建失败未做错误处理,直接使用空指针- Windows权限不足或其他软件占用屏幕捕获资源
核心修复步骤
1. 确保D3D11设备支持DXGI特性
创建设备时必须启用DEBUG层(方便排查驱动级错误),并指定兼容的Feature Level:
D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; UINT numFeatureLevels = ARRAYSIZE(featureLevels); ID3D11Device* pDevice = nullptr; ID3D11DeviceContext* pDeviceContext = nullptr; D3D_FEATURE_LEVEL featureLevel; HRESULT hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_DEBUG, // 启用DEBUG层,发布版可移除 featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &pDevice, &featureLevel, &pDeviceContext ); if (FAILED(hr)) { // 处理设备创建失败,fallback到WARP设备 hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_WARP, nullptr, 0, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &pDevice, &featureLevel, &pDeviceContext ); }
2. 正确遍历并选择DXGI输出设备
不能硬编码输出索引,需确认目标显示器为活跃状态:
IDXGIDevice* pDxgiDevice = nullptr; IDXGIAdapter* pAdapter = nullptr; IDXGIOutput1* pOutput1 = nullptr; pDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&pDxgiDevice); pDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&pAdapter); // 遍历所有输出设备,找到主显示器 for (UINT i = 0; ; i++) { IDXGIOutput* pOutput = nullptr; HRESULT hr = pAdapter->EnumOutputs(i, &pOutput); if (hr == DXGI_ERROR_NOT_FOUND) break; DXGI_OUTPUT_DESC outputDesc; pOutput->GetDesc(&outputDesc); if (outputDesc.AttachedToDesktop) { pOutput->QueryInterface(__uuidof(IDXGIOutput1), (void**)&pOutput1); pOutput->Release(); break; } pOutput->Release(); }
3. 严格处理DuplicateOutput的返回值
IDXGIOutputDuplication创建失败是导致空指针的核心原因,必须检查HRESULT并处理常见错误:
IDXGIOutputDuplication* pDesktopDuplication = nullptr; HRESULT hr = pOutput1->DuplicateOutput(pDevice, &pDesktopDuplication); if (FAILED(hr)) { switch (hr) { case DXGI_ERROR_ACCESS_DENIED: MessageBox(nullptr, L"请以管理员权限运行", L"权限错误", MB_OK); break; case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE: MessageBox(nullptr, L"屏幕捕获资源被占用", L"资源冲突", MB_OK); break; default: char errMsg[256]; sprintf_s(errMsg, "DuplicateOutput失败: 0x%X", hr); MessageBoxA(nullptr, errMsg, "错误", MB_OK); break; } // 释放资源并退出 pOutput1->Release(); pAdapter->Release(); pDxgiDevice->Release(); pDevice->Release(); pDeviceContext->Release(); return false; }
4. 捕获帧时的安全检查
获取桌面资源前必须判断AcquireNextFrame的返回值,避免空指针:
DXGI_OUTDUPL_FRAME_INFO frameInfo; IDXGIResource* pDesktopResource = nullptr; HRESULT hr = pDesktopDuplication->AcquireNextFrame(500, &frameInfo, &pDesktopResource); if (hr == DXGI_ERROR_WAIT_TIMEOUT) { // 超时,跳过本次捕获 continue; } if (FAILED(hr)) { // 资源丢失,重新初始化Duplication pDesktopDuplication->ReleaseFrame(); // 此处添加重新创建Duplication的逻辑 continue; } // 后续处理pDesktopResource...
转换为OpenGL/ImGui可渲染纹理
捕获到的DXGI资源需转换为CPU可读格式,再导入到OpenGL/ImGui:
1. 将捕获帧转换为CPU可访问纹理
ID3D11Texture2D* pAcquiredTexture = nullptr; pDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&pAcquiredTexture); // 创建Staging纹理(CPU可读) D3D11_TEXTURE2D_DESC desc; pAcquiredTexture->GetDesc(&desc); desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.Usage = D3D11_USAGE_STAGING; desc.BindFlags = 0; desc.MiscFlags = 0; ID3D11Texture2D* pStagingTexture = nullptr; pDevice->CreateTexture2D(&desc, nullptr, &pStagingTexture); pDeviceContext->CopyResource(pStagingTexture, pAcquiredTexture); // 映射纹理获取像素数据 D3D11_MAPPED_SUBRESOURCE mappedResource; pDeviceContext->Map(pStagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource); // mappedResource.pData 即为像素数据,格式为 DXGI_FORMAT_B8G8R8A8_UNORM
2. 导入到ImGui渲染
// 全局存储ImGui纹理ID static ImTextureID g_ImGuiScreenTexture = 0; const int width = desc.Width; const int height = desc.Height; if (!g_ImGuiScreenTexture) { // 创建新纹理 ImGui_ImplOpenGL3_CreateTextureFromPixels( (const void*)mappedResource.pData, width, height, 4, // 4通道BGRA &g_ImGuiScreenTexture ); } else { // 更新现有纹理 ImGui_ImplOpenGL3_UpdateTextureFromPixels( g_ImGuiScreenTexture, (const void*)mappedResource.pData, width, height, 4 ); } // 渲染到ImGui窗口 ImGui::Begin("Screen Capture"); ImGui::Image(g_ImGuiScreenTexture, ImVec2(width, height)); ImGui::End(); // 释放临时资源 pDeviceContext->Unmap(pStagingTexture, 0); pStagingTexture->Release(); pAcquiredTexture->Release(); pDesktopResource->Release(); pDesktopDuplication->ReleaseFrame();
内容的提问来源于stack exchange,提问作者Tim van Naarden
相关产品推荐
相关产品推荐

