有没有办法在 pywin32 中解码数字 COM 错误代码
Posted
技术标签:
【中文标题】有没有办法在 pywin32 中解码数字 COM 错误代码【英文标题】:Is there a way to decode numerical COM error-codes in pywin32 【发布时间】:2010-10-06 00:36:19 【问题描述】:这是最近运行的一个用 Python 编写的不可靠应用程序的堆栈跟踪的一部分,该应用程序控制着另一个用 Excel 编写的应用程序:
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
显然出了点问题……但是什么?[1] 这些 COM 错误代码似乎过于神秘。
如何解码此错误消息?是否有一个表格可以让我将此数字错误代码转换为更有意义的内容?
[1] 我实际上知道在这种情况下出了什么问题,它试图访问没有 Name 属性的 Range 对象上的 Name 属性...并非所有错误都这么容易找到!
【问题讨论】:
【参考方案1】:你没有做错任何事。堆栈跟踪中的第一项(数字)是 COM 对象返回的错误代码。第二项是与错误代码相关的描述,在这种情况下是“发生异常”。 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 自动化或 IDispatch 的机制)添加了一些需要剥离的层才能获取信息。 Excel 通常通过后期绑定进行操作。
现在让我们再看看你的情况。作为第一个数字,您得到的是一个相当通用的错误代码:DISP_E_EXCEPTION。 注意:您通常可以通过谷歌搜索数字来找出 HRESULT 的正式名称,尽管有时您必须使用十六进制版本才能找到有用的信息。
以 DISP_ 开头的错误是 IDISPATCH 错误代码。该错误大致意味着“对象抛出了一个 COM 异常”,更多信息包含在其他地方(虽然我不太清楚在哪里;我必须查一下)。
根据我对 pywintypes.com_error 的了解,您消息中的最后一个数字是异常期间对象返回的实际错误代码。这是您从 VBA 的Err.Number
中得到的实际数字代码。
不幸的是,第二个代码 -2146788248 (0x800A9C68) 在为自定义应用程序定义的错误消息保留的范围内(在 VBA 中:VbObjectError + someCustomErrorNumber
),因此没有集中的含义。对于不同的程序,相同的数字可能意味着完全不同的东西。
在这种情况下,我们走到了死胡同:
错误代码是“自定义”,应用程序需要记录它是什么,除了 Excel 没有。此外,Excel(或错误的实际来源)似乎没有通过 IErrorInfo 提供更多信息。
Excel 因来自自动化的神秘错误代码和导致它们的晦涩情况而臭名昭著(至少对我而言)。对于可以考虑“设计时错误”(“您应该比调用对象中不存在的方法”)的错误尤其如此。你得到的是“Run-time error '1004': Application defined or object-defined error”,而不是一个很好的“无法读取 Name 属性”(我只是通过尝试从 Excel 中的 VBA 访问 Range 上的 Name 属性获得)。这不是很有帮助。
问题不是在 Python 上路由,也不是在 Excel 接口上。 Excel 本身并没有解释发生了什么,甚至对 VBA 也是如此。
但是,上述一般程序仍然有效。如果您以后从 Excel 中收到错误,您可能会收到一条更好的错误消息,您可以通过相同的方式进行跟踪。
祝你好运!
【讨论】:
非常感谢!对于使用 Visual Studio 10.0+ 的任何人,您可以从:microsoft.com/en-us/download/details.aspx?id=100432 下载错误查找。然后使用终端(cmd / powershell)执行。您可以搜索错误代码,例如“./Err_这样做:
try:
[whatever code]
except pythoncom.com_error as error:
print(win32api.FormatMessage(error.excepinfo[5]))
更多关于在此处消化 pythoncom.com_error 对象的信息:https://web.archive.org/web/20170831073447/http://docs.activestate.com/activepython/3.2/pywin32/com_error.html
【讨论】:
链接已损坏。 通过回程机固定链接【参考方案3】:是的,试试 win32api 模块:
import win32api
e_msg = win32api.FormatMessage(-2147352567)
您可以获取从异常返回的任何代码并将它们传递给 FormatMessage。您的示例有 2 个错误代码。
【讨论】:
第一条消息是“发生异常”,第二条消息引发错误:系统在 %2 的消息文件中找不到消息号 0x%1 的消息文本。我做错了吗?【参考方案4】:特别是对于 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 位有符号整数,然后将 that 解释为无符号整数:
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 所说,它在这种特定情况下对你没有帮助:)
【讨论】:
【参考方案5】:还没有人提到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)
请注意,如果您有非标准的HRESULT
,它将返回None
:(
【讨论】:
error.strerror
只返回第一个代码的字符串-2147352567,已经在问题中了。以上是关于有没有办法在 pywin32 中解码数字 COM 错误代码的主要内容,如果未能解决你的问题,请参考以下文章
如果某个任务正在任务管理器中运行,我如何在 python 中使用 pywin 或 win32com.client 检查?
pywin32 - Python 在Windows COM 编程模块
有没有使用 pywin32 createprocessasuser 并获取输出的好例子?