2011-10-27 50 views
11

我不情願再次處理Win32結構化異常。我試圖生成一個描述異常的字符串。大部分內容很簡單,但我堅持一些基本的東西:如何將異常代碼(GetExceptionCode()ExceptionCode的成員EXCEPTION_RECORD的成員)轉換爲描述異常的字符串?如何將Win32異常代碼轉換爲字符串?

我正在尋找的東西,將例如0xC0000005轉換爲「訪問衝突」。這只是致電​​嗎?

+0

是,'FormatMessage'應該做的伎倆。 – avakar

回答

3

是的。這是一個NTSTATUS,所以使用FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE,並通過HMODULELoadLibrary("NTDLL.DLL")

Source

+0

Win32是另一組錯誤代碼。 – MSalters

+0

謝謝,這非常有效。不幸的是,NTDLL.DLL中的字符串似乎沒有爲FormatMessage使用正確的格式代碼。 0xc0000005的字符串是'%p處的指令引用了%p處的內存',我想這是FormatMessage轉換爲'0x'(原文如此)的指令。另請參閱此[相關問題](http://stackoverflow.com/questions/321898/how-to-get-the-name-description-of-an-exception)。 –

+0

呃,你是否在傳遞實際的地址? 'FormatMessage'看到兩個'%p'參數,輸出看起來像是沒有格式化第一個地址。 – MSalters

7

結構化異常代碼通過NTSTATUS數字定義。儘管MS suggests使用FormatMessage()將NTSTATUS數字轉換爲字符串,但我不會這樣做。 Flag FORMAT_MESSAGE_FROM_SYSTEM用於將GetLastError()的結果轉換爲字符串,因此這裏沒有意義。使用標記FORMAT_MESSAGE_FROM_HMODULEntdll.dll將導致某些代碼的結果不正確。例如,對於EXCEPTION_ACCESS_VIOLATION,您將獲得The instruction at 0x,這不是非常有用的信息:)。

當您查看存儲在ntdll.dll中的字符串時,很明顯它們中的很多應該與printf()函數一起使用,而不是FormatMessage()。例如,對於EXCEPTION_ACCESS_VIOLATION該字符串是:

The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.

%0FormatMessage()視爲轉義序列意消息終止子,而不是一個插入件。插入是%1到%99。這就是爲什麼國旗FORMAT_MESSAGE_IGNORE_INSERTS沒有任何區別。

你可能想從ntdll.dll加載字符串,並把它傳遞給vprintf,但你需要準備完全相同的參數爲指定字符串(例如,用於EXCEPTION_ACCESS_VIOLATION它的unsigned longunsigned longchar*)。這種方法有一個主要缺點:ntdll.dll中參數的數量,順序或大小發生任何變化都可能會破壞您的代碼。

因此,將字符串硬編碼到自己的代碼中會更安全,更容易。我發現使用別人編寫的字符串而不與我協調:)是危險的,而且還有其他功能。這只是另一種故障的可能性。

+0

感謝您的回答! (即使這個問題現在已經很老了)。如果你看看我對另一個答案的評論,你會發現我發現你提到的問題。我只是特別介紹了麻煩的信息;我認爲我更喜歡按照您的建議對所有內容進行硬編碼。 (特別是因爲將來可能會增加更多的錯誤。) –

+0

這是一個當然的味道問題。我選擇打印代碼號(對於可能出現的新錯誤非常有用),其字符串表示形式(例如「EXCEPTION_INVALID_DISPOSITION」)和EXCEPTION_RECORD結構體中的其他值。爲了我的需要就夠了。我認爲向最終用戶顯示詳細描述是沒有意義的。無論如何,他們中的大多數人都很難理解。即使是先進的,它也不會有用,他們不會修復你的程序。最終用戶應該將這些信息傳遞給開發人員進行調查。作爲開發人員,我可以閱讀Internet上最新的錯誤代碼描述。 – 4LegsDrivenCat

1

正確管理某些NTSTATUS字符串所具有的流格式很複雜。你應該考慮將它轉換成一個Win32消息,其中包含頭文件Winternl.h中的RtlNtStatusToDosError()。你需要在你的鏈接器輸入中有ntdll.lib。

實現示例:

// Returns length of resulting string, excluding null-terminator. 
// Use LocalFree() to free the buffer when it is no longer needed. 
// Returns 0 upon failure, use GetLastError() to get error details. 
DWORD FormatNtStatus(NTSTATUS nsCode, TCHAR **ppszMessage) { 

    // Get handle to ntdll.dll. 
    HMODULE hNtDll = LoadLibrary(_T("NTDLL.DLL")); 

    // Check for fail, user may use GetLastError() for details. 
    if (hNtDll == NULL) return 0; 

    // Call FormatMessage(), note use of RtlNtStatusToDosError(). 
    DWORD dwRes = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, 
     hNtDll, RtlNtStatusToDosError(nsCode), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
     (LPTSTR)ppszMessage, 0, NULL); 

    // Free loaded dll module and decrease its reference count. 
    FreeLibrary(hNtDll); 

    return dwRes; 
} 
+0

給定0xC0000005時會做什麼? –

+0

@AlanStokes它將被轉換爲ERROR_NOACCESS(英文本地化:「對內存位置的訪問無效。」)。 –

相關問題