有没有办法解码pywin32中的数字COM错误代码

时间:2009-02-06 19:18:02

标签: python windows excel com pywin32

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

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

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

如何解码此错误消息?是否有一个表允许我将这个数字错误代码转换为更有意义的东西?

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

5 个答案:

答案 0 :(得分:39)

你没有做错任何事。堆栈跟踪中的第一项(数字)是COM对象返回的错误代码。第二项是与错误代码相关的描述,在这种情况下是“Exception Occurred”。 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 Automation或IDispatch的机制)添加了一些需要剥离的图层以获取信息。 Excel通常通过后期绑定来操纵。

现在让我们再看看你的情况。你得到的第一个数字是一个相当通用的错误代码:DISP_E_EXCEPTION。 注意:您通常可以通过Google搜索数字找出HRESULT的官方名称,但有时您必须使用十六进制版本来查找有用的内容。

以DISP_开头的错误是IDISPATCH错误代码。错误松散地意味着“对象抛出了一个COM异常”,其他地方包含更多信息(尽管我不知道在哪里;我将不得不查找它。)

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

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

在这种情况下,我们已达到死胡同:

  

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

Excel因为自动化和导致它们的模糊情况的隐秘错误代码而臭名昭着(至少对我而言)。对于可以考虑“设计时错误”的错误尤其如此(“你应该知道比调用对象中不存在的方法更好”)。你得到“ 运行时错误'1004':应用程序定义或对象定义错误 ”而不是一个好的“无法读取名称属性”(我只是通过尝试从Excel中的VBA访问Range上的Name属性获得。这不是很有用。

问题不在Python或它的Excel界面上路由。 Excel本身并没有解释发生了什么,甚至是VBA。

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

祝你好运!

答案 1 :(得分:9)

这样做:

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

有关消化pythoncom.com_error对象的更多信息,请访问:http://docs.activestate.com/activepython/3.2/pywin32/com_error.html

答案 2 :(得分:7)

是的尝试win32api模块:

import win32api
e_msg = win32api.FormatMessage(-2147352567)

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

答案 3 :(得分:3)

特别是对于pythoncom,结果的错误代码不仅仅是神秘的。这是因为当正确的表示是32位 unsigned 整数时,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

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

答案 4 :(得分:1)

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

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 :(