如何将Win32异常代码转换为字符串?

时间:2021-03-21 07:04:37

I'm reluctantly having to deal with Win32 structured exceptions again. I'm trying to generate a string describing an exception. Most of it is straightforward, but I'm stuck on something basic: how can I convert an exception code (the result of GetExceptionCode(), or the ExceptionCode member of an EXCEPTION_RECORD) into a string describing the exception?

我不情愿地再次处理Win32结构化异常。我正在尝试生成描述异常的字符串。大部分都是直截了当的,但我坚持一些基本的东西:如何将异常代码(GetExceptionCode()的结果或EXCEPTION_RECORD的ExceptionCode成员)转换为描述异常的字符串?

I'm looking for something that will convert eg 0xC0000005 to "Access Violation". Is it just a call to FormatMessage()?

我正在寻找能够将例如0xC0000005转换为“访问冲突”的东西。它只是对FormatMessage()的调用吗?

3 个解决方案

#1


4  

Yes. It's a NTSTATUS, so use FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE, and pass the HMODULE from LoadLibrary("NTDLL.DLL")

是。这是一个NTSTATUS,所以使用FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,并从LoadLibrary传递HMODULE(“NTDLL.DLL”)

Source

#2


8  

Structured exception codes are defined through NTSTATUS numbers. Although someone from MS suggests using FormatMessage() to convert NTSTATUS numbers to strings, I would not do this. Flag FORMAT_MESSAGE_FROM_SYSTEM is used to convert result of GetLastError() into a string, so it makes no sense here. Using flag FORMAT_MESSAGE_FROM_HMODULE along with ntdll.dll will lead to incorrect results for some codes. E.g., for EXCEPTION_ACCESS_VIOLATION you will get The instruction at 0x, which is not very informative :) .

结构化异常代码通过NTSTATUS编号定义。虽然MS的某些人建议使用FormatMessage()将NTSTATUS数字转换为字符串,但我不会这样做。标志FORMAT_MESSAGE_FROM_SYSTEM用于将GetLastError()的结果转换为字符串,因此这里没有意义。使用标志FORMAT_MESSAGE_FROM_HMODULE和ntdll.dll将导致某些代码的结果不正确。例如,对于EXCEPTION_ACCESS_VIOLATION,您将获得0x处的指令,这不是非常有用的信息:)。

When you look at the strings that are stored in ntdll.dll it becomes obvious that many of them are supposed to be used with the printf() function, not with the FormatMessage(). For example, the string for EXCEPTION_ACCESS_VIOLATION is:

当您查看存储在ntdll.dll中的字符串时,很明显其中许多字符串应该与printf()函数一起使用,而不是与FormatMessage()一起使用。例如,EXCEPTION_ACCESS_VIOLATION的字符串是:

The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.

0x%08lx处的指令引用0x%08lx处的内存。内存不能是%s。

%0 is treated by FormatMessage() as the escape sequence meaning message terminator, not an insert. Inserts are %1 to %99. That's why flag FORMAT_MESSAGE_IGNORE_INSERTS does not make any difference.

FormatMessage()将%0视为转义序列,表示消息终止符,而不是插入。插入是%1到%99。这就是为什么标志FORMAT_MESSAGE_IGNORE_INSERTS没有任何区别。

You might want to load the string from ntdll.dll and pass it to vprintf but you will need to prepare arguments exactly as the string specifies (e.g. for EXCEPTION_ACCESS_VIOLATION it's unsigned long, unsigned long, char*). And this approach has major drawback: any change in the number, order or size of arguments in ntdll.dll may break your code.

您可能希望从ntdll.dll加载字符串并将其传递给vprintf,但您需要准确准备与字符串指定的参数(例如,对于EXCEPTION_ACCESS_VIOLATION,它是unsigned long,unsigned long,char *)。这种方法有一个主要缺点:ntdll.dll中参数的数量,顺序或大小的任何更改都可能会破坏您的代码。

So it's safer and easier to hard code the strings into your own code. I find it dangerous to use strings prepared by someone else without coordination with me :) and moreover for other function. This is just one more possibility for malfunction.

因此,将字符串硬编码到您自己的代码中更安全,更容易。我发现使用别人准备的字符串而不与我协调是危险的:)而且还有其他功能。这只是故障的另一种可能性。

#3


1  

It is complicated to correctly manage the stream format some of the NTSTATUS strings have. You should consider converting it into a Win32 message with RtlNtStatusToDosError(), which comes in header Winternl.h. You'll need to have ntdll.lib in your linker input.

正确管理一些NTSTATUS字符串所具有的流格式很复杂。您应该考虑使用RtlNtStatusToDosError()将其转换为Win32消息,该消息位于Winternl.h标头中。您需要在链接器输入中使用ntdll.lib。

Example implementation:

// 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;
}

#1


4  

Yes. It's a NTSTATUS, so use FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE, and pass the HMODULE from LoadLibrary("NTDLL.DLL")

是。这是一个NTSTATUS,所以使用FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,并从LoadLibrary传递HMODULE(“NTDLL.DLL”)

Source

#2


8  

Structured exception codes are defined through NTSTATUS numbers. Although someone from MS suggests using FormatMessage() to convert NTSTATUS numbers to strings, I would not do this. Flag FORMAT_MESSAGE_FROM_SYSTEM is used to convert result of GetLastError() into a string, so it makes no sense here. Using flag FORMAT_MESSAGE_FROM_HMODULE along with ntdll.dll will lead to incorrect results for some codes. E.g., for EXCEPTION_ACCESS_VIOLATION you will get The instruction at 0x, which is not very informative :) .

结构化异常代码通过NTSTATUS编号定义。虽然MS的某些人建议使用FormatMessage()将NTSTATUS数字转换为字符串,但我不会这样做。标志FORMAT_MESSAGE_FROM_SYSTEM用于将GetLastError()的结果转换为字符串,因此这里没有意义。使用标志FORMAT_MESSAGE_FROM_HMODULE和ntdll.dll将导致某些代码的结果不正确。例如,对于EXCEPTION_ACCESS_VIOLATION,您将获得0x处的指令,这不是非常有用的信息:)。

When you look at the strings that are stored in ntdll.dll it becomes obvious that many of them are supposed to be used with the printf() function, not with the FormatMessage(). For example, the string for EXCEPTION_ACCESS_VIOLATION is:

当您查看存储在ntdll.dll中的字符串时,很明显其中许多字符串应该与printf()函数一起使用,而不是与FormatMessage()一起使用。例如,EXCEPTION_ACCESS_VIOLATION的字符串是:

The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.

0x%08lx处的指令引用0x%08lx处的内存。内存不能是%s。

%0 is treated by FormatMessage() as the escape sequence meaning message terminator, not an insert. Inserts are %1 to %99. That's why flag FORMAT_MESSAGE_IGNORE_INSERTS does not make any difference.

FormatMessage()将%0视为转义序列,表示消息终止符,而不是插入。插入是%1到%99。这就是为什么标志FORMAT_MESSAGE_IGNORE_INSERTS没有任何区别。

You might want to load the string from ntdll.dll and pass it to vprintf but you will need to prepare arguments exactly as the string specifies (e.g. for EXCEPTION_ACCESS_VIOLATION it's unsigned long, unsigned long, char*). And this approach has major drawback: any change in the number, order or size of arguments in ntdll.dll may break your code.

您可能希望从ntdll.dll加载字符串并将其传递给vprintf,但您需要准确准备与字符串指定的参数(例如,对于EXCEPTION_ACCESS_VIOLATION,它是unsigned long,unsigned long,char *)。这种方法有一个主要缺点:ntdll.dll中参数的数量,顺序或大小的任何更改都可能会破坏您的代码。

So it's safer and easier to hard code the strings into your own code. I find it dangerous to use strings prepared by someone else without coordination with me :) and moreover for other function. This is just one more possibility for malfunction.

因此,将字符串硬编码到您自己的代码中更安全,更容易。我发现使用别人准备的字符串而不与我协调是危险的:)而且还有其他功能。这只是故障的另一种可能性。

#3


1  

It is complicated to correctly manage the stream format some of the NTSTATUS strings have. You should consider converting it into a Win32 message with RtlNtStatusToDosError(), which comes in header Winternl.h. You'll need to have ntdll.lib in your linker input.

正确管理一些NTSTATUS字符串所具有的流格式很复杂。您应该考虑使用RtlNtStatusToDosError()将其转换为Win32消息,该消息位于Winternl.h标头中。您需要在链接器输入中使用ntdll.lib。

Example implementation:

// 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;
}