Ответ 1
Да. Это NTSTATUS
, поэтому используйте FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE
и передайте HMODULE
из LoadLibrary("NTDLL.DLL")
Мне неохотно приходится иметь дело с структурными исключениями Win32. Я пытаюсь создать строку, описывающую исключение. Большинство из них просты, но я зациклился на чем-то базовом: как я могу преобразовать код исключения (результат GetExceptionCode()
или ExceptionCode
члена EXCEPTION_RECORD
) в строку, описывающую исключение?
Я ищу что-то, что преобразует, например, 0xC0000005 в "Нарушение доступа". Это просто вызов FormatMessage()
?
Да. Это NTSTATUS
, поэтому используйте FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE
и передайте HMODULE
из LoadLibrary("NTDLL.DLL")
Структурированные коды исключений определяются через номера 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
it unsigned long
, unsigned long
, char*
)), И этот подход имеет большой недостаток: любое изменение количества, порядка или размера аргументов в ntdll.dll
может нарушить ваш код.
Так что это безопаснее и проще жестко закодировать строки в свой собственный код. Я считаю опасным использовать струны, подготовленные кем-то другим, без координации со мной:) и, кроме того, для других функций. Это еще одна возможность для сбоя.
Сложно правильно управлять форматом потока, который имеет некоторые из строк 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;
}
Я предлагаю вам использовать Bugslayer. Просто вызовите GetFaultReason
с помощью EXCEPTION_POINTERS
.
Кроме того, вы можете пройтись по стеку, используя GetFirstStackTraceString
и GetNextStackTraceString
.