D3D11代码未初始化COM对象存活警告的成因及解决方法
D3D11 ReportLiveDeviceObjects触发未显式初始化COM对象存活警告
问题场景
在64位Windows系统下使用Visual Studio Community 2019编写D3D11 C++代码,调用ReportLiveDeviceObjects后触发多个未显式创建的COM对象存活警告。手动释放ID3D11Device两次后警告消失,但不理解背后原因。
代码示例
#define _INCLUDE_LIBS__ #include "stdafx.h" #define WINDOW_NAME L"main window" #define WS WS_OVERLAPPEDWINDOW LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { HWND hwnd = NULL; { WNDCLASS wc = { 0 }; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 1)); wc.hCursor = NULL; wc.hIcon = NULL; wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.lpszClassName = L"MainClass"; wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; if (!RegisterClass(&wc)) OutputDebugString(L"ERROR : window class registration failed!"); hwnd = CreateWindow(wc.lpszClassName, WINDOW_NAME, WS, 0, 0, 1000, 600, NULL, NULL, hInstance, NULL); if(!hwnd) OutputDebugString(L"ERROR : window Creation failed!"); } IDXGIFactory1* factory = nullptr; { CreateDXGIFactory1(IID_PPV_ARGS(&factory)); } IDXGIAdapter* adapter = nullptr; { factory->EnumAdapters(0, &adapter); DXGI_ADAPTER_DESC desc; adapter->GetDesc(&desc); OutputDebugString(L"\n"); OutputDebugString(desc.Description); OutputDebugString(L"\n\n"); } ID3D11Device* device = nullptr; ID3D11DeviceContext* context = nullptr; { D3D_FEATURE_LEVEL lvl[] = { D3D_FEATURE_LEVEL_10_0 }; UINT flag = D3D11_CREATE_DEVICE_DEBUG; D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flag, lvl, ARRAYSIZE(lvl), D3D11_SDK_VERSION, &device, NULL, &context); } ID3D11Debug* debug = nullptr; { device->QueryInterface(IID_PPV_ARGS(&debug)); } if (context) context->Release(); if (device) device->Release(); if (adapter) adapter->Release(); if (factory) factory->Release(); debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
警告输出
D3D11 WARNING: Live ID3D11Device at 0x0000020454692FF0, Refcount: 2 [ STATE_CREATION WARNING #441: LIVE_DEVICE] D3D11 WARNING: Live ID3D11Context at 0x0000020454695020, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #2097226: LIVE_CONTEXT] D3D11 WARNING: Live ID3DDeviceContextState at 0x000002045626F000, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #3145742: LIVE_DEVICECONTEXTSTATE] D3D11 WARNING: Live ID3D11BlendState at 0x0000020456266170, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #435: LIVE_BLENDSTATE] D3D11 WARNING: Live ID3D11DepthStencilState at 0x0000020456266370, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #436: LIVE_DEPTHSTENCILSTATE] D3D11 WARNING: Live ID3D11RasterizerState at 0x0000020456266570, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #437: LIVE_RASTERIZERSTATE] D3D11 WARNING: Live ID3D11Sampler at 0x0000020456266780, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #434: LIVE_SAMPLER] D3D11 WARNING: Live ID3D11Query at 0x0000020456266E60, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #438: LIVE_QUERY]
原因解析
- COM引用计数规则:调用
QueryInterface获取COM接口时,会同时增加目标对象的引用计数。这里通过device->QueryInterface获取ID3D11Debug接口,导致ID3D11Device的引用计数从1(创建时)增加到2。你只调用了一次device->Release(),计数降到1,设备并未销毁。 - 默认状态对象:警告中的
BlendState、DepthStencilState等是D3D11设备初始化时自动创建的默认状态对象,它们被设备上下文内部持有(IntRef:1代表内部引用)。只要设备存活,这些内部对象就不会被释放。 - 释放顺序问题:你在调用
ReportLiveDeviceObjects前释放了设备,但此时设备因引用计数未归0仍存活,连带内部默认对象也处于存活状态,触发警告。手动调用两次Release()把计数降到0,设备销毁后内部对象才被释放,警告消失。
修复方案
方案1:正确管理ID3D11Debug的引用
QueryInterface创建的ID3D11Debug接口本身需要调用Release(),这会自动减少ID3D11Device的引用计数,让一次device->Release()就能完成销毁:
// ... 原有代码 ... ID3D11Debug* debug = nullptr; { device->QueryInterface(IID_PPV_ARGS(&debug)); } // 先释放除debug、device外的其他对象 if (context) context->Release(); if (adapter) adapter->Release(); if (factory) factory->Release(); // 调用设备对象报告 debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); // 释放debug接口,自动减少device的引用计数 debug->Release(); // 释放device,此时计数归0,设备销毁 if (device) device->Release(); return 0;
方案2:使用COM智能指针(推荐)
使用Microsoft::WRL::ComPtr(Windows Runtime Library)管理COM对象,它会自动处理引用计数,避免手动Release的错误:
// 包含头文件 #include <wrl/client.h> using namespace Microsoft::WRL; // 替换裸指针为ComPtr ComPtr<IDXGIFactory1> factory; ComPtr<IDXGIAdapter> adapter; ComPtr<ID3D11Device> device; ComPtr<ID3D11DeviceContext> context; ComPtr<ID3D11Debug> debug; // 创建对象时无需手动管理引用 CreateDXGIFactory1(IID_PPV_ARGS(&factory)); factory->EnumAdapters(0, &adapter); // ... 其他创建逻辑 ... device->QueryInterface(IID_PPV_ARGS(&debug)); // 调用报告 debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); // 无需手动调用Release,ComPtr超出作用域时自动释放
内容的提问来源于stack exchange,提问作者unicus
相关产品推荐
相关产品推荐

