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


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

我正在寻找能够将例如0xC0000005转换为“访问冲突”的东西。这只是一个电话 FormatMessage()


2661
2017-10-27 11:27


起源

是, FormatMessage 应该做的伎俩。 - avakar


答案:


是。它是 NTSTATUS,所以使用 FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE,并通过 HMODULE 从 LoadLibrary("NTDLL.DLL") 

资源


4
2017-10-27 11:39



Win32是另一组错误代码。 - MSalters
谢谢,这几乎是有效的。不幸的是,NTDLL.DLL中的字符串似乎没有使用FormatMessage的正确格式代码。 0xc0000005的字符串是'%p处的指令引用%p处的内存'。我认为,哪个FormatMessage转换为'0x'处的指令(原文如此)。另请参阅此内容 相关问题。 - Alan Stokes
呃,你 没有 传递实际地址? FormatMessage 看到两个 %p 参数,输出看起来无法格式化第一个地址。 - MSalters
我已尝试传入实际地址并传入FORMAT_MESSAGE_IGNORE_INSERTS,但它没有任何区别。我使用了特殊套管访问违规(和IN_PAGE_ERROR)。 - Alan Stokes
请参阅我的答案,我解释了为什么FormatMessage不能用于此目的。 - 4LegsDrivenCat


结构化异常代码通过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() 作为转义序列意味着消息终止符,而不是插入。插入是%1到%99。这就是旗帜的原因 FORMAT_MESSAGE_IGNORE_INSERTS 没有任何区别。

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

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


8
2017-10-09 18:27



感谢您的回答! (尽管这个问题现在很老了。)如果你看看我对另一个答案的评论,你会发现我发现了你提到的问题。我只是特别提到麻烦的消息;我认为我更喜欢按照你的建议对所有内容进行硬编码。 (特别是因为将来可能会添加更多错误。) - Alan Stokes
这当然是品味的问题。我选择打印代码编号(对于可能出现的新错误很有用),其字符串表示形式(例如“EXCEPTION_INVALID_DISPOSITION”)和EXCEPTION_RECORD结构中的其他值。根据我的需要,这已经足够了。我认为向最终用户显示详细描述并不合理。无论如何,他们中的大多数人都很难理解。即使是高级版也不会有用,它们无法修复你的程序。最终用户应该将该信息传递给开发人员进行调查。我作为开发人员可以阅读Internet中错误代码的最新描述。 - 4LegsDrivenCat


正确管理一些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;
}

1
2018-05-14 06:47



给出0xC0000005时该怎么办? - Alan Stokes
@AlanStokes它将被转换为ERROR_NOACCESS(在英文本地化:“对内存位置的无效访问。”)。 - Laa Laa