这里是最近一次用Python编写的不可靠的应用程序运行的堆栈跟踪的一部分,该应用程序控制另一个用Excel编写的应用程序:
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
显然有什么问题…但是什么?[1]这些COM错误代码似乎过于隐晦。
我怎样才能解码这个错误信息? 有没有一个表,让我把这个数字错误代码转换成更有意义的东西?
[1]我真的知道在这种情况下出了什么问题,它试图访问没有名称属性的Range对象的名称属性…并非所有的错误都很容易find!
你没有做错什么。 堆栈跟踪中的第一项(数字)是COM对象返回的错误代码。 第二项是与在这种情况下是“发生异常”的错误代码相关联的描述。 pywintypes.com_error已经为你调用了win32api.FormatMessage(errCode)的等价物。 我们会在一分钟之内看第二个数字。
顺便说一下,您可以使用Visual Studio(C:\ Program Files \ Microsoft Visual Studio 9.0 \ Common7 \ Tools \ ErrLook.exe)中的“Error Lookup”实用程序作为快速启动板来检查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“:应用程序定义或对象定义的错误 ”(我刚刚通过尝试访问一个Range的名称属性从VBA在Excel中)。 这不是很有帮助。
这个问题不在Python上路由,或者是与Excel的接口。 Excel本身并不能解释发生了什么,甚至是VBA。
但是,上述一般程序仍然有效。 如果您将来从Excel中收到错误,则可能会得到一个更好的错误消息,您可以按照相同的方式进行跟踪。
祝你好运!
像这样做:
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
是的尝试win32api模块:
import win32api e_msg = win32api.FormatMessage(-2147352567)
您可以抓取从异常中返回的任何代码,并将它们传递给FormatMessage。 你的例子有2个错误代码。
特别是对于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
再一次,这是因为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所说的那样,它在这个特殊的实例中并没有帮助你:)
没有人提到pywintypes.com_error
异常的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)
注意,如果你有一个非标准的HRESULT
🙁