以下是最近运行的一个用Python编写的不可靠应用程序的堆栈跟踪的一部分,该应用程序控制用Excel编写的另一个应用程序:
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
显然出现了问题...但是什么?[1]这些COM错误代码似乎过于神秘。
如何解码此错误消息?是否有一个表允许我将这个数字错误代码转换为更有意义的东西?
[1]我实际上知道在这种情况下出了什么问题,它试图访问一个没有Name属性的Range对象上的Name属性...并不是所有的bug都很容易找到!
答案 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
Exception的strerror
属性。这将返回错误代码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
:(