Is there a way to decode numerical COM error-codes in pywin32

前端 未结 5 1081
别那么骄傲
别那么骄傲 2020-12-28 15:26

Here is part of a stack-trace from a recent run of an unreliable application written in Python which controls another application written in Excel:

pywintype         


        
5条回答
  •  余生分开走
    2020-12-28 16:02

    Specifically for pythoncom, the errors codes that result are more than cryptic. This is because pythoncom represents them internally as a 32bit signed integer, when the correct representation is a 32bit unsigned integer. As a result, the conversion that you end up seeing in the stack trace is incorrect.

    In particular, your exception, according to pythoncom, is -2147352567, and your (for lack of a better word) Err.Number is -2146788248.

    This however causes some issues when watching for specific errors, like below:

    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
    

    To see why this has issues, lets look into these error codes:

    >>> DISP_E_EXCEPTION = 0x80020009
    >>> DISP_E_EXCEPTION
    2147614729L
    >>> my_hr = -2147352567
    >>> my_hr == DISP_E_EXCEPTION
    False
    

    Again, this is because python sees the constant declared as positive, and pythoncom's incorrect declaration interpreted it as negative. Of course, the most obvious solution fails:

    >>> hex(my_hr)
    '-0x7ffdfff7'
    

    The solution is to properly interpret the number. Luckily, pythoncom's representation is reversible. We need to interpret the negative number as a 32 bit signed integer, then interpret that as an unsigned integer:

    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
    

    So, putting it all together, you need to run fix_com_hresult() on that result from pythoncom, essentially all the time.

    Since normally you need to do this when checking for exceptions, I created these functions:

    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]
    

    which can then be used how you expect:

    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
    

    I was unable to find a MSDN document listing all HRESULTs, but I found this: http://www.megos.ch/support/doserrors_e.txt

    Also, since you have it, fix_com_hresult() should also be run on your extended error code (-2146788248), but as Euro Micelli said, it doesn't help you in this particular instance :)

提交回复
热议问题