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

Python 有没有办法在pywin32中解码数字COM错误代码,python,windows,excel,com,pywin32,Python,Windows,Excel,Com,Pywin32,以下是最近运行的一个用Python编写的不可靠应用程序的堆栈跟踪的一部分,该应用程序控制另一个用Excel编写的应用程序: pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None) 显然出了点问题。。。但是什么呢?[1]这些COM错误代码似乎过于神秘 如何解码此错误消息?有没有一个表格可以让我把这个数字错误代码转换成更有意义的代码 [1] 实际上

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

pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
显然出了点问题。。。但是什么呢?[1]这些COM错误代码似乎过于神秘

如何解码此错误消息?有没有一个表格可以让我把这个数字错误代码转换成更有意义的代码


[1] 实际上我知道在这个例子中出现了什么问题,它试图访问一个没有Name属性的Range对象上的Name属性。。。并不是所有的bug都这么容易找到

是尝试win32api模块:

import win32api
e_msg = win32api.FormatMessage(-2147352567)

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

您没有做错任何事情。堆栈跟踪中的第一项(编号)是COM对象返回的错误代码。第二项是与错误代码相关联的描述,在本例中为“异常发生”。pywintypes.com_错误已经为您调用了与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)通常表示“无错误”,0x8000002表示E_OUTOFMEMORY。有时,HRESULT代码由对象返回,有时由COM基础结构返回

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

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

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

以DISP_uu开头的错误是IDISPATCH错误代码。这个错误大致意味着“对象抛出了一个COM异常”,更多的信息打包在其他地方(虽然我不知道在哪里;我必须查找它)

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

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

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

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

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

这个问题不是在Python或它的Excel接口上解决的。Excel本身没有解释发生了什么,甚至对VBA也没有解释

然而,上述一般程序仍然有效。如果您将来从Excel中得到错误,您可能会得到一条更好的错误消息,您可以用同样的方法进行跟踪

祝你好运

这样做:

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

更多关于分解pythoncom.com_error对象的信息,请参见此处:

特别是对于pythoncom,产生的错误代码不仅仅是神秘的。这是因为pythoncom在内部将它们表示为32位有符号整数,而正确的表示形式是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
同样,这是因为
>>> hex(my_hr)
'-0x7ffdfff7'
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
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
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)