2009-02-06 4 views
25

hier zu dekodieren ist Teil einer Stapels-Spur aus einem aktuellen Laufe einer unzuverlässigen Anwendung in Python geschrieben, die in Excel geschrieben andere Anwendung steuert:Gibt es eine Möglichkeit numerische COM-Fehler-Codes in pywin32

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

Offensichtlich ist etwas schiefgelaufen ... aber was? [1] Diese COM-Fehlercodes scheinen übertrieben kryptisch zu sein.

Wie kann ich diese Fehlermeldung decodieren? Gibt es irgendwo eine Tabelle, die es mir erlaubt, diesen numerischen Fehlercode in etwas Sinnvolleres umzuwandeln?

[1] Ich weiß tatsächlich, was in diesem Fall schief gelaufen ist, es wurde versucht, auf ein Name-Objekt zuzugreifen, das keine Name-Eigenschaft hatte ... nicht alle Bugs sind so einfach zu finden!

Antwort

37

Sie machen nichts falsch. Das erste Element in Ihrer Stapelüberwachung (die Nummer) ist der Fehlercode, der vom COM-Objekt zurückgegeben wird. Der zweite Punkt ist die Beschreibung, die dem Fehlercode zugeordnet ist, der in diesem Fall "Ausnahme aufgetreten" ist. pywintypes.com_error hat bereits den Gegenwert von win32api.FormatMessage (errCode) für dich genannt. Wir werden uns die zweite Nummer in einer Minute ansehen.

Übrigens können Sie das Dienstprogramm "Error Lookup", das in Visual Studio (C: \ Programme \ Microsoft Visual Studio 9.0 \ Common7 \ Tools \ ErrLook.exe) kommt, als eine schnelle Startleiste zu überprüfen COM-Fehlercodes. Dieses Dienstprogramm ruft auch FormatMessage für Sie auf und zeigt das Ergebnis an. Nicht alle Fehlercodes funktionieren mit diesem Mechanismus, aber viele werden es tun. Das ist normalerweise meine erste Station.

Fehlerbehandlung und Berichterstattung in COM ist ein bisschen chaotisch. Ich werde versuchen, dir einen Hintergrund zu geben.

Alle COM-Methodenaufrufe geben einen numerischen Code namens HRESULT zurück, der den Erfolg oder Fehler anzeigen kann. Alle Formen der Fehlerberichterstattung in COM bauen darüber hinaus auf.

Die Codes werden häufig in Hex ausgedrückt, obwohl Sie sie manchmal als große 32-Bit-Zahlen sehen, wie in Ihrem Stack-Trace. Es gibt alle Arten von vordefinierten Rückgabecodes für allgemeine Ergebnisse und Probleme, oder das Objekt kann benutzerdefinierte numerische Codes für spezielle Situationen zurückgeben. Zum Beispiel bedeutet der Wert 0 (S_OK genannt) allgemein "Kein Fehler" und 0x80000002 ist E_OUTOFMEMORY. Manchmal werden die HRESULT-Codes vom Objekt zurückgegeben, manchmal von der COM-Infrastruktur.

Ein COM-Objekt kann auch auswählen, um wesentlich umfangreichere Fehlerinformationen bereitzustellen, indem eine Schnittstelle namens IErrorInfo implementiert wird. Wenn ein Objekt IErrorInfo implementiert, kann es alle Arten von Details zu dem Vorfall bereitstellen, z. B. eine detaillierte benutzerdefinierte Fehlermeldung und sogar den Namen einer Hilfedatei, die das Problem beschreibt. In VB6 und VBA. Das Objekt Err ermöglicht Ihnen den Zugriff auf alle diese Informationen (Err.Description usw.).

Um komplizierter zu machen, fügen spät gebundene COM-Objekte (die einen Mechanismus namens COM Automation oder IDispatch verwenden) einige Layer hinzu, die entfernt werden müssen, um Informationen zu erhalten. Excel wird normalerweise durch späte Bindung manipuliert.

Nun schauen wir uns Ihre Situation noch einmal an. Was Sie als erste Zahl erhalten, ist ein ziemlich allgemeiner Fehlercode: DISP_E_EXCEPTION.Hinweis: Sie können den offiziellen Namen eines HRESULT normalerweise herausfinden, indem Sie die Nummer googlen, obwohl Sie manchmal die Hex-Version verwenden müssen, um etwas Nützliches zu finden.

Fehler, die mit DISP_ beginnen, sind IDISPATCH-Fehlercodes. Der Fehler bedeutet locker "Es gab eine COM-Ausnahme, die vom Objekt ausgelöst wurde", mit mehr Informationen an anderer Stelle (obwohl ich nicht ganz weiß, wo; ich muss nachsehen).

Von dem, was ich von pywintypes.com_error verstehe, ist die letzte Zahl in Ihrer Nachricht der tatsächliche Fehlercode, der von dem Objekt während der Ausnahme zurückgegeben wurde. Es ist der tatsächliche numerische Code, den Sie von VBA's Err.Number bekommen würden.

Leider ist dieser zweite Code -2146788248 (0x800A9C68) in dem Bereich für anwenderdefinierte benutzerdefinierte Fehlermeldungen (in VBA: VbObjectError + someCustomErrorNumber) reserviert, so dass es keine zentrale Bedeutung gibt. Die gleiche Anzahl kann für verschiedene Programme völlig unterschiedliche Dinge bedeuten.

The error code is "custom", and the application needs to document what it is, except that Excel doesn't. Also, Excel (or the actual source of the error) doesn't seem to be providing any more information via IErrorInfo.

Excel ist berüchtigt (für mich zumindest) für kryptischen Fehlercodes von der Automatisierung und obskuren Situationen, die sie verursachen:

In diesem Fall haben wir in eine Sackgasse. Dies gilt insbesondere für Fehler, die man als "Entwurfszeitfehler" bezeichnen könnte ("Sie hätten es besser wissen müssen, als eine Methode aufzurufen, die im Objekt nicht existiert"). Statt eines netten "Konnte die Namenseigenschaft nicht lesen", erhalten Sie "Laufzeitfehler '1004': Anwendungsdefinierter oder objektdefinierter Fehler" (was ich gerade bekommen habe, indem ich auf eine Name-Eigenschaft auf a Bereich, von VBA in Excel). Das ist nicht sehr hilfreich.

Das Problem wird nicht auf Python oder seine Schnittstelle zu Excel weitergeleitet. Excel selbst erklärt nicht, was selbst VBA passiert ist.

Das oben beschriebene allgemeine Verfahren bleibt jedoch weiterhin gültig. Wenn Sie in Zukunft einen Fehler von Excel erhalten, erhalten Sie möglicherweise eine bessere Fehlermeldung, die Sie auf die gleiche Weise verfolgen können.

Viel Glück!

+0

+1 Großartig b reacdown der COM-Fehlercodes –

6

Ja versuchen, das win32api Modul:

import win32api 
e_msg = win32api.FormatMessage(-2147352567) 

können Sie greifen alle Codes von der Ausnahme zurückgegeben und sie Format passieren. Ihr Beispiel hatte 2 Fehlercodes.

+3

Die erste Nachricht war "Ausnahme aufgetreten" und die zweite hat einen Fehler ausgelöst: Das System kann den Nachrichtentext für die Nachrichtennummer 0x% 1 in der Nachrichtendatei für% 2 nicht finden. Mache ich etwas falsch? –

3

Speziell für pythoncom, die Fehler-Codes, die mehr als kryptische führen. Dies liegt daran, dass Pythoncom sie intern als eine 32-Bit-Ganzzahl mit Vorzeichen darstellt, wenn die korrekte Darstellung eine 32-Bit-Ganzzahl ist, die unsigned ist. Daher ist die Konvertierung, die Sie in der Stapelüberwachung sehen, falsch.

Insbesondere ist Ihre Ausnahme, nach Pythoncom, -2147352567, und Ihr (aus Mangel an einem besseren Wort) Err.Number ist -2146788248.

Dies jedoch einige Probleme verursacht, wenn für bestimmte Fehler zu beobachten, wie unten:

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 

Um zu sehen, warum diese Probleme hat, sehen wir uns in diese Fehlercodes:

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

Auch dies ist weil Python die Konstante als positiv deklariert sieht und Pythoncoms falsche Deklaration als negativ interpretiert. Natürlich schlägt die offensichtlichste Lösung fehl:

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

Die Lösung besteht darin, die Zahl richtig zu interpretieren. Glücklicherweise ist die Darstellung von Pythoncom reversibel. Wir müssen die negative Zahl als 32-Bit-Ganzzahl mit Vorzeichen interpretieren, dann interpretieren, dass als 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 setzen sie alle zusammen, müssen Sie fix_com_hresult() auf diesem Ergebnis laufen von pythoncom im Wesentlichen die ganze Zeit.

Da in der Regel müssen Sie dies tun, wenn Ausnahmen Überprüfung, habe ich diese Funktionen:

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] 

die dann verwendet werden können, wie Sie erwarten:

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 

Ich war nicht in der Lage ein finden MSDN Dokument, das alle HRESULTs auflistet, aber ich fand das: http://www.megos.ch/support/doserrors_e.txt

Auch, da Sie es haben, sollte fix_com_hresult() auch auf Ihrem erweiterten Fehlercode (-2 ausgeführt werden 146788248), aber wie Euro Micelli sagte, hilft es Ihnen in diesem speziellen Fall nicht :)

1

Niemand hat noch das strerror Attribut der pywintypes.com_error Exception erwähnt. Dies gibt das Ergebnis FormatMessage für den Fehlercode zurück. Anstatt also, es zu tun, sich wie diese

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

können Sie nur dies tun:

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

Hinweis es None zurück, wenn Sie ein Nicht-Standard haben HRESULT :(

+0

'error.strerror' gibt nur die Zeichenfolge des ersten Codes zurück, -2147352567, die bereits in der Frage ist. –