c - 系统异常处理: try to get the string message with arguments

标签 c windows winapi exception interrupt

在 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_VIOLATIONEXCEPTION_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[] 数组时使用 +1EXCEPTION_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/

相关文章:

c - 为什么这不会打印?

c++ - 如何检测是否有任何应用程序进入全屏或退出全屏?

c++ - 如何获取当前用户权限组?

c - 如何在主线程退出时让子线程退出?

c++ - 如何将 Visual Studio 2010 WinForm 对象添加到 C++ 控制台应用程序项目

c - 如何从 Win32 中的 func ptr 获取模块句柄?

c - 我的 Win32 应用程序不会退出主循环

c - 从函数返回 this 指针

c - 这段代码符合 MISRA C 标准吗?

c++ - 如何将 for 循环变成数学方程式?