python windows excel com pywin32

python - ¿Hay alguna manera de decodificar códigos de error COM numéricos en pywin32



windows excel (5)

Específicamente para pythoncom, los códigos de error que resultan son más que crípticos. Esto se debe a que pythoncom los representa internamente como un entero con signo de 32 bits, cuando la representación correcta es un entero sin signo de 32 bits. Como resultado, la conversión que termina viendo en el seguimiento de la pila es incorrecta.

En particular, su excepción, de acuerdo con pythoncom, es -2147352567, y su (a falta de una palabra mejor) Err.Number es -2146788248.

Sin embargo, esto causa algunos problemas cuando se buscan errores específicos, como a continuación:

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

Para ver por qué esto tiene problemas, veamos estos códigos de error:

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

De nuevo, esto se debe a que python ve la constante declarada como positiva, y la declaración incorrecta de pythoncom la interpretó como negativa. Por supuesto, la solución más obvia falla:

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

La solución es interpretar correctamente el número. Afortunadamente, la representación de pythoncom es reversible. Necesitamos interpretar el número negativo como un entero de 32 bits con signo, y luego interpretarlo como un entero sin signo:

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

Entonces, al ponerlo todo junto, necesitas ejecutar fix_com_hresult () en ese resultado de pythoncom, esencialmente todo el tiempo.

Como normalmente debe hacer esto cuando busca excepciones, creé estas funciones:

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]

que luego puede usarse como espera:

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

No pude encontrar un documento de MSDN que enumerara todos HRESULT, pero encontré esto: http://www.megos.ch/support/doserrors_e.txt

Además, dado que lo tiene, fix_com_hresult () también debe ejecutarse en su código de error extendido (-2146788248), pero como dijo Euro Micelli, no le ayuda en esta instancia en particular :)

Aquí hay parte de un seguimiento de pila de una ejecución reciente de una aplicación no confiable escrita en Python que controla otra aplicación escrita en Excel:

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

Obviamente, algo salió mal ... ¿pero qué? [1] Estos códigos de error COM parecen ser excesivamente crípticos.

¿Cómo puedo decodificar este mensaje de error? ¿Hay alguna tabla en alguna parte que me permita convertir este código de error numérico en algo más significativo?

[1] De hecho, sé lo que salió mal en este caso, fue intentar acceder a una propiedad Name en un objeto Range que no tenía una propiedad Name ... ¡no todos los errores son tan fáciles de encontrar!



Nadie ha mencionado aún el atributo strerror de la excepción pywintypes.com_error . Esto devuelve el resultado de FormatMessage para el código de error. Entonces, en lugar de hacerlo tú mismo como este

try: [whatever code] except pythoncom.com_error as error: print(win32api.FormatMessage(error.excepinfo[5]))

Puedes hacer esto:

try: [whatever code] except pythoncom.com_error as error: print(error.strerror)

Tenga en cuenta que devolverá None si tiene un HRESULT no estándar :(


No estás haciendo nada malo. El primer elemento en su rastro de pila (el número) es el código de error devuelto por el objeto COM. El segundo elemento es la descripción asociada con el código de error que en este caso es "Excepción ocurrida". pywintypes.com_error ya ha llamado el equivalente de win32api.FormatMessage (errCode) por usted. Veremos el segundo número en un minuto.

Por cierto, puede usar la utilidad "Error Lookup" que viene en Visual Studio (C: / Archivos de programa / Microsoft Visual Studio 9.0 / Common7 / Tools / ErrLook.exe) como una plataforma de inicio rápido para verificar los códigos de error COM. Esa utilidad también llama a FormatMessage por usted y muestra el resultado. No todos los códigos de error funcionarán con este mecanismo, pero muchos lo harán. Esa suele ser mi primera parada.

El manejo e informe de errores en COM es un poco complicado. Trataré de darle algunos antecedentes.

Todas las llamadas al método COM devolverán un código numérico llamado HRESULT que puede indicar éxito o falla. Todas las formas de informes de errores en la compilación COM además de eso.

Los códigos comúnmente se expresan en hexadecimal, aunque a veces los verá como números grandes de 32 bits, como en su rastro de pila. Hay todo tipo de códigos de retorno predefinidos para resultados y problemas comunes, o el objeto puede devolver códigos numéricos personalizados para situaciones especiales. Por ejemplo, el valor 0 (llamado S_OK) significa universalmente "Sin error" y 0x80000002 es E_OUTOFMEMORY. Algunas veces los códigos HRESULT son devueltos por el objeto, a veces por la infraestructura COM.

Un objeto COM también puede elegir proporcionar información de error mucho más rica implementando una interfaz llamada IErrorInfo. Cuando un objeto implementa IErrorInfo, puede proporcionar todo tipo de detalles sobre lo que sucedió, como un mensaje de error personalizado detallado e incluso el nombre de un archivo de ayuda que describe el problema. En VB6 y VBA. el objeto Err permite acceder a toda esa información ( Err.Description , etc.).

Para complicar las cosas, los objetos COM enlazados tarde (que usan un mecanismo llamado Automatización COM o IDispatch) agregan algunas capas que necesitan ser despegadas para obtener información. Excel generalmente se manipula a través de enlace tardío.

Ahora veamos tu situación nuevamente. Lo que está obteniendo como primer número es un código de error bastante genérico: DISP_E_EXCEPTION. Nota: generalmente puede averiguar el nombre oficial de HRESULT buscando en Google el número, aunque a veces tendrá que usar la versión hexadecimal para encontrar algo útil.

Los errores que comienzan con DISP_ son códigos de error IDISPATCH. El error significa "Hubo una excepción COM lanzada por el objeto", con más información empaquetada en otra parte (aunque no sé muy bien dónde, tendré que buscarla).

Por lo que entiendo de pywintypes.com_error, el último número en su mensaje es el código de error real que fue devuelto por el objeto durante la excepción. Es el código numérico real que obtendría del número de Err.Number de VBA.

Desafortunadamente, ese segundo código -2146788248 (0x800A9C68) está en el rango reservado para los mensajes de error definidos por la aplicación personalizada (en VBA: VbObjectError + someCustomErrorNumber ), por lo que no hay un significado centralizado. El mismo número puede significar cosas completamente diferentes para diferentes programas.

En este caso, hemos llegado a un callejón sin salida:

El código de error es "personalizado", y la aplicación necesita para documentar lo que es, excepto que Excel no lo hace. Además, Excel (o la fuente real del error) no parece proporcionar más información a través de IErrorInfo.

Excel es notorio (al menos para mí) por los códigos de error crípticos de la automatización y las situaciones poco claras que los causan. Esto es especialmente así para los errores que uno podría considerar como "errores de tiempo de diseño" ("debería haber sabido mejor que llamar a un método que no existe en el objeto"). En lugar de una buena "No se pudo leer la propiedad Name", se obtiene " Error en tiempo de ejecución ''1004'': Error definido por la aplicación o definido por el objeto " (que acabo de obtener al intentar acceder a una propiedad Name en un Range, desde VBA en Excel). Eso NO es muy útil.

El problema no se enruta en Python o su interfaz a Excel. Excel en sí no explica lo que sucedió, incluso a VBA.

Sin embargo, el procedimiento general anterior sigue siendo válido. Si recibe un error de Excel en el futuro, es posible que reciba un mensaje de error mejor que indique que puede realizar el seguimiento de la misma manera.

¡Buena suerte!


Sí, prueba el módulo win32api:

import win32api e_msg = win32api.FormatMessage(-2147352567)

Puede tomar los códigos devueltos por la excepción y pasarlos a FormatMessage. Tu ejemplo tiene 2 códigos de error.