Sal*_*ley 26 python windows com excel pywin32
这是最近运行的一个用Python编写的不可靠应用程序的堆栈跟踪的一部分,该应用程序控制用Excel编写的另一个应用程序:
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
Run Code Online (Sandbox Code Playgroud)
显然出现了问题...但是什么?[1]这些COM错误代码似乎过于神秘.
如何解码此错误消息?是否有一个表允许我将这个数字错误代码转换为更有意义的东西?
[1]我实际上知道在这种情况下出了什么问题,它试图访问一个没有Name属性的Range对象上的Name属性...并不是所有的bug都很容易找到!
Eur*_*lli 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':应用程序定义或对象定义错误 "(我刚刚尝试从VBA访问Range上的Name属性而获得)而不是一个很好的"无法读取Name属性" 在Excel中).这不是很有帮助.
问题不是在Python或它的Excel界面上路由的.Excel本身并没有解释发生了什么,甚至VBA.
但是,上述一般程序仍然有效.如果将来从Excel收到错误,您可能会收到更好的错误消息,您可以以相同的方式跟踪.
祝好运!
像这样做:
try:
[whatever code]
except pythoncom.com_error as error:
print(win32api.FormatMessage(error.excepinfo[5]))
Run Code Online (Sandbox Code Playgroud)
有关消化pythoncom.com_error对象的更多信息,请访问:http://docs.activestate.com/activepython/3.2/pywin32/com_error.html
是的尝试win32api模块:
import win32api
e_msg = win32api.FormatMessage(-2147352567)
Run Code Online (Sandbox Code Playgroud)
您可以获取从异常返回的任何代码并将它们传递给FormatMessage.您的示例有2个错误代码.
特别是对于 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
Run Code Online (Sandbox Code Playgroud)
要了解为什么会出现问题,让我们查看以下错误代码:
>>> DISP_E_EXCEPTION = 0x80020009
>>> DISP_E_EXCEPTION
2147614729L
>>> my_hr = -2147352567
>>> my_hr == DISP_E_EXCEPTION
False
Run Code Online (Sandbox Code Playgroud)
同样,这是因为 python 将常量声明为正数,而 pythoncom 的错误声明将其解释为负数。当然,最明显的解决方案失败了:
>>> hex(my_hr)
'-0x7ffdfff7'
Run Code Online (Sandbox Code Playgroud)
解决方案是正确解释数字。幸运的是,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
Run Code Online (Sandbox Code Playgroud)
所以,把它们放在一起,你需要在 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]
Run Code Online (Sandbox Code Playgroud)
然后可以按您期望的方式使用:
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
Run Code Online (Sandbox Code Playgroud)
我找不到列出所有 HRESULT 的 MSDN 文档,但我找到了这个:http : //www.megos.ch/support/doserrors_e.txt
此外,既然你有它, fix_com_hresult() 也应该在你的扩展错误代码(-2146788248)上运行,但正如 Euro Micelli 所说,它在这个特殊情况下对你没有帮助:)