ylim - python plot text bbox




有沒有辦法解碼pywin32中的數字COM錯誤代碼 (4)

這是最近運行的一個用Python編寫的不可靠應用程序的堆棧跟踪的一部分,該應用程序控制用Excel編寫的另一個應用程序:

pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)

顯然出現了問題...但是什麼?[1]這些COM錯誤代碼似乎過於神秘。

如何解碼此錯誤消息? 是否有一個表允許我將這個數字錯誤代碼轉換為更有意義的東西?

[1]我實際上知道在這種情況下出了什麼問題,它試圖訪問一個沒有Name屬性的Range對像上的Name屬性...並不是所有的bug都很容易找到!


你沒有做錯任何事。 堆棧跟踪中的第一項(數字)是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收到錯誤,您可能會收到更好的錯誤消息,您可以以相同的方式跟踪。

祝你好運!



沒有人提到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 ,它將返回None :(


特別是對於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

要了解為什麼會出現此問題,請查看以下錯誤代碼:

>>> 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.txthttp://www.megos.ch/support/doserrors_e.txt

此外,既然你有它,fix_com_hresult()也應該在你的擴展錯誤代碼(-2146788248)上運行,但正如Euro Micelli所說,它在這個特定的實例中沒有幫助你:)







pywin32