在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
知道什么应该是获得完整信息的正确方法?感谢。
答案 0 :(得分:3)
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;
}
话虽如此,为什么在索引+1
数组时使用ExceptionInformation[]
? EXCEPTION_ACCESS_VIOLATION
提供了2个数组元素,EXCEPTION_IN_PAGE_ERROR
提供3.您正在跳过第一个数组元素并访问不存在的最后一个数组元素。
调用FORMAT_MESSAGE_ARGUMENT_ARRAY
时,您也没有指定FormatMessage()
标志。如果没有该标志,则最后一个参数必须是正确的va_list
,如果不是NULL。您没有使用va_list
,因此在传递序数数组时必须指定FORMAT_MESSAGE_ARGUMENT_ARRAY
。
答案 1 :(得分:1)
我遇到了同样的问题,我无法解决它,但我可以提供一些提示。
“字符串”工具没有显示正确的字符串,因为它正在查找ASCII文本,但真实文本存储在UTF-16(即Windows本机)中。
您应该从FormatMessage获取的文本如下所示:
"The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s."
显然,FormatMessage将%0
定义为转义字符。它用空字符替换它然后停止。我检查了调试器中的字符串,以确认它在%0
之后停止。标志FORMAT_MESSAGE_IGNORE_INSERTS
应该禁用此行为,但它也没有帮助。
如果您确实想要获取该错误文本,可以从ntdll.dll
中的消息表中检索它。答案显示了如何:https://stackoverflow.com/a/24127220/35951。我尝试了它,它确实返回ID为0xc0000005
的完整字符串。
编辑:原来已经解释过问题的人(但他们也没有提供好的解决方案):https://stackoverflow.com/a/33044673/35951