Как преобразовать код исключения Win32 в строку? - PullRequest
12 голосов
/ 27 октября 2011

Я неохотно вынужден снова иметь дело со структурированными исключениями Win32.Я пытаюсь сгенерировать строку, описывающую исключение.Большая часть этого проста, но я застрял на чем-то простом: как я могу преобразовать код исключения (результат GetExceptionCode() или ExceptionCode член EXCEPTION_RECORD) в строку, описывающую исключение?

Я ищу что-то, что конвертирует, например, 0xC0000005 в "Нарушение прав доступа".Это просто звонок на FormatMessage()?

Ответы [ 4 ]

9 голосов
/ 09 октября 2015

Структурированные коды исключений определяются через номера NTSTATUS. Хотя кто-то из MS предлагает , используя FormatMessage () для преобразования чисел NTSTATUS в строки, я бы не стал этого делать. Флаг 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.

%0 обрабатывается FormatMessage () как escape-последовательность, означающая терминатор сообщения, а не вставка. Вставки от% 1 до% 99. Вот почему флаг FORMAT_MESSAGE_IGNORE_INSERTS не имеет значения.

Возможно, вы захотите загрузить строку из ntdll.dll и передать ее в vprintf, но вам нужно будет подготовить аргументы в точности так, как указано в строке (например, для EXCEPTION_ACCESS_VIOLATION это unsigned long, unsigned long, char*) , И у этого подхода есть существенный недостаток: любое изменение числа, порядка или размера аргументов в ntdll.dll может нарушить ваш код.

Так что безопаснее и проще кодировать строки в ваш собственный код. Я считаю опасным использование строк, подготовленных кем-то другим, без согласования со мной :) и, более того, для других функций. Это еще одна возможность неисправности.

5 голосов
/ 27 октября 2011

Да.Это NTSTATUS, поэтому используйте FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE и передайте HMODULE из LoadLibrary("NTDLL.DLL")

Источник: KB259693 (в архиве)

3 голосов
/ 14 мая 2017

Сложно правильно управлять форматом потока, который есть у некоторых строк NTSTATUS. Вы должны рассмотреть возможность преобразования его в сообщение Win32 с помощью RtlNtStatusToDosError () , которое входит в заголовок 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;
}
0 голосов
/ 27 апреля 2019

Я предлагаю вам использовать Bugslayer . Просто позвоните GetFaultReason с EXCEPTION_POINTERS.

Кроме того, вы можете пройтись по стеку, используя GetFirstStackTraceString и GetNextStackTraceString.

...