29

这是最近运行的一个用 Python 编写的不可靠应用程序的堆栈跟踪的一部分,该应用程序控制着另一个用 Excel 编写的应用程序:

pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)

显然出了点问题……但是什么?[1] 这些 COM 错误代码似乎过于神秘。

如何解码此错误消息?是否有表格可以让我将此数字错误代码转换为更有意义的内容?

[1] 我实际上知道在这种情况下出了什么问题,它试图访问没有 Name 属性的 Range 对象上的 Name 属性...并非所有错误都这么容易找到!

4

5 回答 5

43

你没有做错任何事。堆栈跟踪中的第一项(数字)是 COM 对象返回的错误代码。第二项是与错误代码相关的描述,在这种情况下是“发生异常”。pywintypes.com_error 已经为您调用了相当于 win32api.FormatMessage(errCode) 的内容。我们将在一分钟内查看第二个数字。

顺便说一句,您可以使用 Visual Studio 中的“错误查找”实用程序 (C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\ErrLook.exe) 作为快速启动板来检查 COM 错误代码。该实用程序还为您调用 FormatMessage 并显示结果。并非所有错误代码都适用于这种机制,但很多都可以。这通常是我的第一站。

COM 中的错误处理和报告有点混乱。我会试着给你一些背景。

所有 COM 方法调用都将返回一个称为 HRESULT 的数字代码,它可以指示成功或失败。COM 中所有形式的错误报告都建立在此之上。

这些代码通常以十六进制表示,尽管有时您会看到它们是大的 32 位数字,就像在您的堆栈跟踪中一样。对于常见的结果和问题,有各种预定义的返回码,或者对象可以针对特殊情况返回自定义的数字码。例如,值 0(称为 S_OK)通常表示“无错误”,而 0x80000002 是 E_OUTOFMEMORY。有时 HRESULT 代码由对象返回,有时由 COM 基础结构返回。

COM 对象还可以选择通过实现称为 IErrorInfo 的接口来提供更丰富的错误信息。当一个对象实现 IErrorInfo 时,它可以提供有关所发生事情的各种详细信息,例如详细的自定义错误消息,甚至是描述问题的帮助文件的名称。在 VB6 和 VBA 中。该Err对象允许您访问所有这些信息(Err.Description等)。

更复杂的是,后期绑定的 COM 对象(使用称为 COM 自动化或 IDispatch 的机制)添加了一些需要剥离以获取信息的层。Excel 通常通过后期绑定进行操作。

现在让我们再看看你的情况。作为第一个数字,您得到的是一个相当通用的错误代码:DISP_E_EXCEPTION。注意:您通常可以通过谷歌搜索数字来找出 HRESULT 的正式名称,尽管有时您必须使用十六进制版本才能找到任何有用的信息。

以 DISP_ 开头的错误是 IDISPATCH 错误代码。该错误大致意味着“对象抛出了一个 COM 异常”,更多信息打包在其他地方(尽管我不太清楚在哪里;我必须查一下)。

根据我对 pywintypes.com_error 的了解,消息中的最后一个数字是异常期间对象返回的实际错误代码。这是您从 VBA 中获得的实际数字代码Err.Number

不幸的是,第二个代码 -2146788248 (0x800A9C68) 在为自定义应用程序定义的错误消息保留的范围内(在 VBA 中:)VbObjectError + someCustomErrorNumber,因此没有集中的含义。对于不同的程序,相同的数字可能意味着完全不同的东西。

在这种情况下,我们走到了死胡同:

错误代码是“自定义”,应用程序需要记录它是什么,除了 Excel 没有。此外,Excel(或错误的实际来源)似乎没有通过 IErrorInfo 提供更多信息。

Excel 因来自自动化的神秘错误代码和导致它们的晦涩情况而臭名昭著(至少对我而言)。对于可以考虑“设计时错误”(“您应该比调用对象中不存在的方法”)的错误尤其如此。而不是一个很好的“无法读取名称属性”,而是“运行时错误'1004':应用程序定义或对象定义错误”(我只是通过尝试从 VBA 访问范围上的名称属性得到的在 Excel 中)。这不是很有帮助。

问题不是在 Python 上路由,也不是在 Excel 的接口上。Excel 本身并没有解释发生了什么,甚至对 VBA 也是如此。

但是,上述一般程序仍然有效。如果将来您从 Excel 中收到错误,您可能会收到一条更好的错误消息,您可以以相同的方式跟踪。

祝你好运!

于 2009-02-10T05:01:04.257 回答
11

像这样做:

try:
    [whatever code]
except pythoncom.com_error as error:
    print(win32api.FormatMessage(error.excepinfo[5]))

有关在此处消化 pythoncom.com_error 对象的更多信息:https ://web.archive.org/web/20170831073447/http://docs.activestate.com/activepython/3.2/pywin32/com_error.html

于 2011-03-16T14:49:34.277 回答
7

是的,试试 win32api 模块:

import win32api
e_msg = win32api.FormatMessage(-2147352567)

您可以获取从异常返回的任何代码并将它们传递给 FormatMessage。您的示例有 2 个错误代码。

于 2009-02-06T19:31:24.393 回答
7

特别是对于 pythoncom,产生的错误代码不仅仅是神秘的。这是因为当正确的表示是 32 位无符号整数时,pythoncom 在内部将它们表示为 32 位有符号整数。因此,您最终在堆栈跟踪中看到的转换是不正确的。

特别是,根据pythoncom,您的例外是-2147352567,而您的(因为没有更好的词)Err.Number 是-2146788248。

但是,这在查看特定错误时会导致一些问题,如下所示:

DISP_E_EXCEPTION = 0x80020009
#...
#except pywintypes.com_error as e:
#    print repr(e)
#    #pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
#    hr = e.hresult

hr = -2147352567
if hr == DISP_E_EXCEPTION:
    pass #This never occurs
else:
    raise

要了解为什么会出现问题,让我们看看这些错误代码:

>>> DISP_E_EXCEPTION = 0x80020009
>>> DISP_E_EXCEPTION
2147614729L
>>> my_hr = -2147352567
>>> my_hr == DISP_E_EXCEPTION
False

同样,这是因为 python 看到声明为正数的常量,而 pythoncom 的错误声明将其解释为负数。当然,最明显的解决方案失败了:

>>> hex(my_hr)
'-0x7ffdfff7'

解决方案是正确解释数字。幸运的是,pythoncom 的表示是可逆的。我们需要将负数解释为 32 位有符号整数,然后将其解释无符号整数:

def fix_com_hresult(hr):
    import struct
    return struct.unpack("L", struct.pack("l", hr))[0]

>>> DISP_E_EXCEPTION = 0x80020009
>>> my_hr = -2147352567
>>> my_hr == DISP_E_EXCEPTION
False
>>> fixed_hr = fix_com_hresult(my_hr)
>>> fixed_hr
2147614729L
>>> fixed_hr == DISP_E_EXCEPTION
True

因此,将它们放在一起,您需要对来自 pythoncom 的结果运行 fix_com_hresult(),基本上是一直运行。

因为通常在检查异常时需要这样做,所以我创建了这些函数:

def fix_com_exception(e):
    e.hresult = fix_com_hresult(e.hresult)
    e.args = [e.hresult] + list(e.args[1:])
    return e

def fix_com_hresult(hr):
    import struct
    return struct.unpack("L", struct.pack("l", hr))[0]

然后可以按照您的期望使用它:

DISP_E_EXCEPTION = 0x80020009
try:
    #failing call
except pywintypes.com_error as e:
    print repr(e)
    #pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
    fix_com_exception(e)
    print repr(e)
    #pywintypes.com_error: (2147614729L, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
    if e.hresult == DISP_E_EXCEPTION:
        print "Got expected failure"
    else:
        raise

我找不到列出所有 HRESULT 的 MSDN 文档,但我发现了这个:http ://www.megos.ch/support/doserrors_e.txt

此外,既然你有它, fix_com_hresult() 也应该在你的扩展错误代码(-2146788248)上运行,但正如 Euro Micelli 所说,它在这个特定情况下对你没有帮助:)

于 2012-05-17T18:15:44.177 回答
1

还没有人提到Exceptionstrerror的属性。这将返回错误代码的结果。所以不要像这样自己做pywintypes.com_errorFormatMessage

try:
    [whatever code]
except pythoncom.com_error as error:
    print(win32api.FormatMessage(error.excepinfo[5]))

你可以这样做:

try:
    [whatever code]
except pythoncom.com_error as error:
    print(error.strerror)

None请注意,如果您有非标准的,它将返回HRESULT:(

于 2015-03-17T14:09:05.487 回答