我不情愿地又得处理Win32结构化异常了。我试图生成一个描述异常的字符串。大部分都很简单,但是我困在一个基本的问题上:我如何将异常代码(GetExceptionCode()
的结果或EXCEPTION_RECORD
的ExceptionCode
成员)转换为描述异常的字符串?
我正在寻找一种将例如0xC0000005转换为“访问冲突”的方法。只需要调用FormatMessage()
吗?
FORMAT_MESSAGE_FROM_SYSTEM
用于将GetLastError()的结果转换为字符串,所以在这里没有意义。使用标志FORMAT_MESSAGE_FROM_HMODULE
和ntdll.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.
FormatMessage() 将 %0
视为转义序列,表示消息终止符,而不是插入项。插入项为 %1 至 %99。这就是为什么标志 FORMAT_MESSAGE_IGNORE_INSERTS
不起作用的原因。
ntdll.dll
加载字符串,并将其传递给 vprintf(),但你需要按照字符串指定的参数准备参数(例如对于 EXCEPTION_ACCESS_VIOLATION
,它是 unsigned long
、unsigned long
和 char*
)。而这种方法有一个主要弱点:在 ntdll.dll
中参数的数量、顺序或大小的任何更改都可能导致代码出错。是的。这是一个NTSTATUS
,所以请使用FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE
,并将来自LoadLibrary("NTDLL.DLL")
的HMODULE
传递进去。
正确管理一些NTSTATUS字符串的流格式比较复杂。您应该考虑使用RtlNtStatusToDosError()将其转换为Win32消息,该函数在Winternl.h头文件中提供。您需要在链接器输入中包含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;
}
我建议你使用bugslayer。只需使用EXCEPTION_POINTERS
调用GetFaultReason
。
此外,你还可以使用GetFirstStackTraceString
和GetNextStackTraceString
来遍历堆栈。
FormatMessage
应该可以解决问题。 - avakar