在 Windows XP 上,我尝试在 CPU 引发异常(中断)时打印官方字符串消息。这里我有一段代码试图访问
#include <stdio.h>
#include <windows.h>
LONG WINAPI e(LPEXCEPTION_POINTERS ExceptionInfo) {
printf("Exception Handled ...\n");
char buf[8192];
memset(buf, 0, 8192);
void * pArgs[ExceptionInfo->ExceptionRecord->NumberParameters];
for (int i = 0; i < ExceptionInfo->ExceptionRecord->NumberParameters; i++) {
printf("arg[%d] = %d\n", i, ExceptionInfo->ExceptionRecord->ExceptionInformation[i+1]);
pArgs[i] = (void *) ExceptionInfo->ExceptionRecord->ExceptionInformation[i+1];
}
HMODULE Hand = LoadLibrary("NTDLL.DLL");
int res = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE,
Hand,
ExceptionInfo->ExceptionRecord->ExceptionCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf,
8192,
(va_list *) pArgs);
printf("res=%d\n", res);
FreeLibrary(Hand);
printf("ExceptionCode=0x%08x (%s)\n", ExceptionInfo->ExceptionRecord->ExceptionCode, buf);
printf("ExceptionFlags=%d\n", ExceptionInfo->ExceptionRecord->ExceptionFlags);
printf("ExceptionAddress=0x%08x\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);
printf("NumberParameters=%d\n", ExceptionInfo->ExceptionRecord->NumberParameters);
printf("ExceptionInformation=%s\n", ExceptionInfo->ExceptionRecord->ExceptionInformation);
return EXCEPTION_EXECUTE_HANDLER;
}
int main() {
LPTOP_LEVEL_EXCEPTION_FILTER p = SetUnhandledExceptionFilter(e);
for (int i = 10; i < 256; i++) {
int *p = (int *) i;
printf("address pointed by p = 0x%08x\n", *p);
}
}
它产生以下输出:
Exception Handled ...
arg[0] = 10
arg[1] = 65599
res=22
ExceptionCode=0xc0000005 (The instruction at "0x)
ExceptionFlags=0
ExceptionAddress=0x004018da
NumberParameters=2
ExceptionInformation=
如您所见,消息被截断了。
ntdll.dll
中有字符串消息:
jlouis@didi /c/WINDOWS/system32
$ strings ntdll.dll | grep instruction
The instruction at %p referenced memory at %p.
The instruction at %p tried to %s
知道获取完整消息的正确方法是什么吗?谢谢。
最佳答案
ExceptionInformation[]
数组中的参数与 NTDLL 消息字符串中的格式说明符不匹配。如果您阅读 documentation , EXCEPTION_ACCESS_VIOLATION
和 EXCEPTION_IN_PAGE_ERROR
在第一个数组元素中提供读/写标志,在第二个数组元素中提供内存地址,但是您尝试使用的消息字符串都期望2个内存地址代替。基本上,FormatMessage()
不适合用于格式化这两个特定异常。对于其他异常,ExceptionInformation[]
的内容是未定义的,因此您不应将它们传递给 FormatMessage()
。您需要查看 ExceptionCode
,然后相应地格式化您的消息,例如:
http://flylinkdc.googlecode.com/svn-history/r10225/trunk/windows/ExceptionDlg.h
std::wstring FormatExceptionMessage()
{
std::wstring str;
LPCWSTR pFmt = NULL;
if (m_pException->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
DWORD_PTR dwAddress = 0;
if (m_pException->ExceptionRecord->NumberParameters == 2)
{
if (m_pException->ExceptionRecord->ExceptionInformation[0] == 0)
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation reading 0x%08Ix.";
else if (m_pException->ExceptionRecord->ExceptionInformation[0] == 8)
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation DEP 0x%08Ix.";
else
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation writing 0x%08Ix.";
dwAddress = m_pException->ExceptionRecord->ExceptionInformation[1];
}
else
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation";
str.resize(95); //TODO
wsprintf(&str.at(0), pFmt, m_pException->ExceptionRecord->ExceptionAddress, dwAddress);
dcassert(str.size() > (size_t)lstrlen(str.c_str()));
}
else if (m_pException->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR)
{
DWORD_PTR dwAddress = 0;
DWORD_PTR dwCode = 0;
if (m_pException->ExceptionRecord->NumberParameters == 3)
{
if (m_pException->ExceptionRecord->ExceptionInformation[0] == 0)
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault reading 0x%08Ix with code 0x%08Ix.";
else if (m_pException->ExceptionRecord->ExceptionInformation[0] == 8)
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault DEP 0x%08Ix with code 0x%08Ix.";
else
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault writing 0x%08Ix with code 0x%08Ix.";
dwAddress = m_pException->ExceptionRecord->ExceptionInformation[1];
dwCode = m_pException->ExceptionRecord->ExceptionInformation[3];
}
else
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault";
str.resize(115); //TODO
wsprintf(&str.at(0), pFmt, m_pException->ExceptionRecord->ExceptionAddress, dwAddress, dwCode);
dcassert(str.size() > (size_t)lstrlen(str.c_str()));
}
else
{
LPWSTR pMessage = NULL;
int iMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, GetModuleHandle(L"ntdll.dll"), m_pException->ExceptionRecord->ExceptionCode, 0, (LPWSTR) & pMessage, 0, NULL);
if (pMessage)
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0x%08x: %s";
else
pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0x%08x";
str.resize(115 + iMsgLen); // 55 -> 115 - http://code.google.com/p/flylinkdc/issues/detail?id=571
wsprintf(&str.at(0), pFmt, m_pException->ExceptionRecord->ExceptionAddress, m_pException->ExceptionRecord->ExceptionCode, pMessage);
dcassert(str.size() > (size_t)lstrlen(str.c_str()));
if (pMessage)
LocalFree(pMessage);
}
return str;
}
话虽如此,为什么在索引到 ExceptionInformation[]
数组时使用 +1
? EXCEPTION_ACCESS_VIOLATION
提供 2 个数组元素,EXCEPTION_IN_PAGE_ERROR
提供 3 个。您正在跳过第一个数组元素并访问不存在的最后一个数组元素。
调用 FormatMessage()
时,您也没有指定 FORMAT_MESSAGE_ARGUMENT_ARRAY
标志。如果没有该标志,最后一个参数必须是正确的 va_list
如果不是 NULL。您没有使用 va_list
,因此在传递序数数组时必须指定 FORMAT_MESSAGE_ARGUMENT_ARRAY
。
关于c - 系统异常处理: try to get the string message with arguments,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22837787/