DirectX11--HR巨集關於dxerr庫的替代方案
DirectX11 With Windows SDK完整目錄
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什麼問題也可以在這裡彙報。
綜述
參考文章:https://blogs.msdn.microsoft.com/chuckw/2012/04/24/wheres-dxerr-lib/
在龍書11中所使用的HR
巨集和dxerr
庫是一個比較實用的錯誤原因追蹤工具。D3D中的某些函式擁有返回值HRESULT
,通過dxerr
庫,可以將錯誤碼轉換成錯誤詳細資訊的字串。
在DirectX SDK中,包含了標頭檔案dxerr.h
和庫檔案dxerr.lib
,在以往的做法包含了DX SDK後,就可以直接使用dxerr
dxerr
庫。
此時此刻,你仍然有兩種選擇來脫離對DirectX SDK的依賴:
- 尋找較新的
dxerr.h
和dxerr.cpp
原始碼來編譯出dxerr.lib
,或者直接加入你的專案當中; - 直接拋棄
dxerr
庫
新的dxerr原始碼
微軟已經將dxerr
庫開源了,下面的連結可以下載,如果不放心的話,你也可以到上面的參考文章去下載。
在我以往的DirectX11專案中,則是從DXUT中拉過來的dxerr
:
但要注意的是,由於新的dxerr.h
僅提供了DXTrace
的Unicode字符集版本,需要將原來的__FILE__
替換為__FILEW__
,並在專案屬性頁中將字符集設定為Unicode。
拋棄dxerr庫
自Windows SDK 8.0起,HRESULT
值關於DirectX圖形API的錯誤訊息字串對映已經加入到FormatMessage
函式中。我們可以直接脫離對dxerr
的依賴,並使用該函式來直接獲取錯誤訊息字串。因此,dxerr
庫也就沒有必要在Windows SDK 8.0以上版本保留了。
FormatMessageW函式--獲取格式化訊息字串
鑑於我們只是要獲取錯誤碼對應的字串資訊,這裡就簡單提及一下該函式的部分用法:
DWORD FormatMessageW(
DWORD dwFlags, // [In]FORMAT_MESSAGE系列巨集
LPCVOID lpSource, // [In]直接填NULL
DWORD dwMessageId, // [In]傳入函式異常時返回的HRESULT
DWORD dwLanguageId, // [In]語言ID
LPTSTR lpBuffer, // [In]用於輸出訊息字串的緩衝區
DWORD nSize, // [In]WCHAR緩衝區可容納元素個數
va_list *Arguments // [In]直接填NULL
);
DXTraceW函式
這裡我將dxerr
中DXTraceW
函式的實現進行了修改,由於現在錯誤碼資訊為中文,為此也順便把錯誤視窗和輸出也漢化了。只需要包含Windows.h
和sal.h
就可以使用。
函式原型:
// ------------------------------
// DXTraceW函式
// ------------------------------
// 在除錯輸出視窗中輸出格式化錯誤資訊,可選的錯誤視窗彈出(已漢化)
// [In]strFile 當前檔名,通常傳遞巨集__FILEW__
// [In]hlslFileName 當前行號,通常傳遞巨集__LINE__
// [In]hr 函式返回的HRESULT值
// [In]strMsg 用於幫助除錯定位的字串,通常傳遞L#x(可能為NULL)
// [In]bPopMsgBox 如果為TRUE,則彈出一個訊息彈窗告知錯誤資訊
// 返回值: 傳入的HRESULT值
HRESULT WINAPI DXTraceW(_In_z_ const WCHAR* strFile, _In_ DWORD dwLine, _In_ HRESULT hr, _In_opt_ const WCHAR* strMsg, _In_ bool bPopMsgBox);
函式實現:
HRESULT WINAPI DXTraceW(_In_z_ const WCHAR* strFile, _In_ DWORD dwLine, _In_ HRESULT hr,
_In_opt_ const WCHAR* strMsg, _In_ bool bPopMsgBox)
{
WCHAR strBufferFile[MAX_PATH];
WCHAR strBufferLine[128];
WCHAR strBufferError[300];
WCHAR strBufferMsg[1024];
WCHAR strBufferHR[40];
WCHAR strBuffer[3000];
swprintf_s(strBufferLine, 128, L"%lu", dwLine);
if (strFile)
{
swprintf_s(strBuffer, 3000, L"%ls(%ls): ", strFile, strBufferLine);
OutputDebugStringW(strBuffer);
}
size_t nMsgLen = (strMsg) ? wcsnlen_s(strMsg, 1024) : 0;
if (nMsgLen > 0)
{
OutputDebugStringW(strMsg);
OutputDebugStringW(L" ");
}
// Windows SDK 8.0起DirectX的錯誤資訊已經整合進錯誤碼中,可以通過FormatMessageW獲取錯誤資訊字串
// 不需要分配字串記憶體
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
strBufferError, 256, nullptr);
WCHAR* errorStr = wcsrchr(strBufferError, L'\r');
if (errorStr)
{
errorStr[0] = L'\0'; // 擦除FormatMessageW帶來的換行符(把\r\n的\r置換為\0即可)
}
swprintf_s(strBufferHR, 40, L" (0x%0.8x)", hr);
wcscat_s(strBufferError, strBufferHR);
swprintf_s(strBuffer, 3000, L"錯誤碼含義:%ls", strBufferError);
OutputDebugStringW(strBuffer);
OutputDebugStringW(L"\n");
if (bPopMsgBox)
{
wcscpy_s(strBufferFile, MAX_PATH, L"");
if (strFile)
wcscpy_s(strBufferFile, MAX_PATH, strFile);
wcscpy_s(strBufferMsg, 1024, L"");
if (nMsgLen > 0)
swprintf_s(strBufferMsg, 1024, L"當前呼叫:%ls\n", strMsg);
swprintf_s(strBuffer, 3000, L"檔名:%ls\n行號:%ls\n錯誤碼含義:%ls\n%ls您需要除錯當前應用程式嗎?",
strBufferFile, strBufferLine, strBufferError, strBufferMsg);
int nResult = MessageBoxW(GetForegroundWindow(), strBuffer, L"錯誤", MB_YESNO | MB_ICONERROR);
if (nResult == IDYES)
DebugBreak();
}
return hr;
}
注意:上面的函式是初版修改,仍需要大量的實踐以驗證可替代性和穩定性!
HR巨集
現在的HR巨集變成了這樣:
// ------------------------------
// HR巨集
// ------------------------------
// Debug模式下的錯誤提醒與追蹤
#if defined(DEBUG) | defined(_DEBUG)
#ifndef HR
#define HR(x) \
{ \
HRESULT hr = (x); \
if(FAILED(hr)) \
{ \
DXTraceW(__FILEW__, (DWORD)__LINE__, hr, L#x, true);\
} \
}
#endif
#else
#ifndef HR
#define HR(x) (x)
#endif
#endif
測試效果如下:
在除錯輸出視窗也可以看到:
DirectX11 With Windows SDK完整目錄
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什麼問題也可以在這裡彙報。