Problem mit try bei WordApp
-
Hallo,
ich habe eine WordApplication (im C++ Builder 2009) und das Problem, das ab und zu Fehler auftauchen und manchmal nicht! Ich denke, das liegt daran, dass die Befehle nicht abgewartet werden, bis sie fertig ausgeführt worden sind, bzw. ausgeführt werden, aber Word noch etwas länger braucht, und dann neue Befehle einfach schon versucht werden abzuarbeiten, aber es nicht geht:
Fehler:
"Die Schnittstelle ist unbekannt!" oder
"Der Remoteprozessaufruf ist fehlgeschlagen!"WordApp->Connect(); WordApp->set_Visible(true); try { WordApp->Documents->Open(OleVariant(StringToOleStr(DateiOeffnen))); } catch (...) { throw("scheiße, geht nicht!") ; return ; } // ... Befehle zum Bearbeiten des Worddokuments ... WordApp->ActiveDocument->SaveAs(OleVariant(StringToOleStr(DateiSpeichern)) , OleVariant(16) ) ; //OleVariant(wdFormatDocumentDefault)) WordApp->Quit() ; WordApp->Disconnect() ;
So und das ist in eine Schleife gepackt, die ca. 3-4 mal zur Zeit durchlaufen wird.
Die Fehler treten immer in der Zeile
WordApp->Documents->Open(OleVariant(StringToOleStr(DateiOeffnen)));
auf, deshalb habe ich diese auch in try reingepackt, aber der Fehler tritt trotzdem auf, liegt das an meinem try ?
-
Hallo
1. try verursacht keine Fehler noch beseitigt es Fehler, sondern dient nur der Fehlerbehandlung. Die eigentliche Fehlerursache must du schon selber suchen und finden.
2. Auch die OLE-Schnittstelle arbeitet sequentiell. Dein Programm wartet also immer bis Word die Anweisung über die OLE-Schnittstelle verarbeitet hat. Es ist also nicht möglich das dein Programm schon weitere Anweisungen sendet während Word noch an der vorherigen arbeitet.
Die eigentliche Fehlerursache läßt sich aus deiner Beschreibung jedenfalls nicht bestimmen.
bis bald
akari
-
Ja ich dachte aber, dass man den Fehler mit try abfangen kann, sodass nicht das gesamte Programm abstürzt!
Das komische daran ist ja auch, dass das Programm auch manchmal ohne Fehler die Prozedur durchläuft. Also ich habe jetzt die Prozedur zig-mal getestet, immer mit denselben Eingaben, und es kommen zu ca. 50% diese Fehler ...
-
Dein Programm stürzt nicht ab, zumindest nicht in dem Quelltextausschnitt den du gezeigt hast (was jetzt auch immer abstürzen genau ist
).
Dein Try/Catch funktioniert auf jeden Fall, evtl. bekommst du die Exception trotzdem zu sehen, da du das Programm in der IDE startest, und die zeigt sie dir trotz Try/Catch.
Des weiteren machst du ja wieder ein Throw im Try/Catch, also produzierst du selber einen "Absturz" mit der Meldung "scheiße, geht nicht!"....mfg
xXx
-
ja würde dann wenigstens meine Nachricht "scheiße, geht nicht!" kommen, macht sie aber nicht, denn der alte Fehler tritt auf (bzw. einer von den beiden oben genannten Fehlern). Meinst du, wenn ich das Programm nicht aus der IDE heraus sondern als eigenständige exe starte, dass dann das try funktionieren könnte?
Ich habe mal die Fehler hochgeladen, die ich bekomme (Der 1. und der 3. tauchen am häufigsten auf!)
-
Hallo
Ja, Exception in try-catch-Blocken werden immer noch von der IDE des Builders angezeigt. Erst ohne IDE bekommt du das von dir gewünschte komplette Verschlucken der Exception, wenn du dann noch dein eigenes throw wegläßt.
Aber denk dran : Nur weil du eine Exception unterdrückst, hast du das eigentlich auslösende Problem noch lange nicht gelöst.bis bald
akari
-
ja ich hatte das zum Testen gedacht, hätte dies geklappt, wollte ich eine Schleife draus bauen, die wartet, bis der Fehler nicht mehr auftaucht. Ich habe halt die Annahme, dass WordApp->Disconnect() nicht Sequentiell aufruft. Denn ich bin die Befehle der WordApp oft genug mit dem Debugger durchgegangen und da traten diese Fehler NIE auf ...
Edit: Nach diesen Fehler bekomme ich übrigends immer eine "Abnormal program termination".
-
MichelM schrieb:
Ich habe mal die Fehler hochgeladen, die ich bekomme (Der 1. und der 3. tauchen am häufigsten auf!)
Hast du mal die in der Fehlermeldung angegebene Codezeile näherer Beachtung gewürdigt? Sie sollte so aussehen:
// word_xp_srvr.cpp, l. 910ff Word_xp::DocumentsPtr __fastcall TWordApplication::get_Documents(void) { Word_xp::DocumentsPtr prop; OLECHECK(GetDefaultInterface()->get_Documents(&prop)); // <-- return prop; }
Wenn du nach der Definition von OLECHECK() suchst, wirst du in utilcls.h fündig:
// utilcls.h, l. 125ff #if !defined(NO_PROMPT_ON_HRCHECK_FAILURE) #define PROMPT_ON_HRCHECK_FAILURE 1 #endif ... // utilcls.h, l. 150ff // Helper used to throw an exception template <class T> #if defined(ComobjHPP) void DebugHlpr_THROW(T* msg, HRESULT hr, T* file, bool /*assertFailed*/) { // NOTE: This does not retrieve rich error information, the way Delphi and VB environments // do. // // You can specialize [T = TCHAR] 'DebugHlpr_THROW' to retrieve rich error // information and throw a VCL exception class, if you're using VCL classes already, // or throw a custom exception class. // // NOTE: Use the assertFailed parameter to distinguish between Assertion and // OLECHECK failures. (Maybe throw something different??) throw Comobj::EOleException(msg, hr, file, _T(""), 0); } ... // utilcls.h, l. 173ff // Implementation of OLECHECK - Throw an exception if !SUCCEEDED(hr) template <class T> HRESULT DebugHlpr_HRCHECK(HRESULT hr, T* expr, T* file, int line) { if (!SUCCEEDED(hr)) { TCHAR szMsg[_MAX_PATH*2]; TCHAR lfile[_MAX_PATH*2]; if (file) ::wsprintf(lfile, (sizeof(TCHAR)==sizeof(T)) ? _T("%s") : _T("%S"), file); else ::wsprintf(lfile, _T("")); LPVOID msg = 0; if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), LPTSTR(&msg), 0, 0) && (msg != 0)) { ::wsprintf(szMsg, (sizeof(TCHAR)==sizeof(T)) ? _T("'%s': %s @ %s/%d") : _T("'%S': %s @ %s/%d") , expr, LPTSTR(msg), lfile, line); ::LocalFree(msg); } else ::wsprintf(szMsg, (sizeof(TCHAR)==sizeof(T)) ? _T("(%s) Error: %lX (%ld) @ %s/%d") : _T("(%S) Error: %lX (%ld) @ %S/%d"), expr, LONG(hr), LONG(hr), file, line); #if !defined(PROMPT_ON_HRCHECK_FAILURE) int i = IDYES; #else int i = DebugHlpr_PROMPT(_T("HRCHECK: "), szMsg); #endif if (i == IDYES) DebugHlpr_THROW(szMsg, hr,lfile, false); else if (i == IDCANCEL) ::DebugBreak(); // NOTE: IDNO - implies we keep chugging along } return hr; } ... // utilcls.h, l. 263ff #if !defined(OLECHECK) #define OLECHECK(hrexpr) DebugHlpr_HRCHECK(hrexpr, #hrexpr, __FILE__, __LINE__) #endif
Das ermöglicht die folgenden Schlüsse:
- Wenn du in den Projektoptionen NO_PROMPT_ON_HRCHECK_FAILURE definierst, wird keine Messagebox angezeigt, sondern direkt eine Exception geworfen.
- DebugHlpr_HRCHECK<>() und DebugHlpr_THROW<>() sind Template-Funktionen. Wie der Kommentar in DebugHlpr_THROW<>() erwähnt, ist es möglich, sie mittels einer Template-Spezialisierung für TCHAR "umzubiegen" (mir fällt gerade keine schöne deutsche Entsprechung für "to override" ein) und etwa mit einer Exception zu reagieren, die mittels GetErrorInfo() detaillierte Informationen zum Fehler abruft.
-
Weiß denn jemand, warum der Fehler auftritt?
Das NO_PROMPT_ON_HRCHECK_FAILURE hab ich in den Projektoptionen definiert. Aber das mit den Templatefunktionen hab ich noch nicht richtig verstanden. Ich glaube, verstanden zu haben, dass man mittels GetErrorInfo() sich die genaue Art von Fehler ermittelt.
Und DebugHlpr_THROW() ist sowas ähnliches wie das throw() bei try ?
-
MichelM schrieb:
Weiß denn jemand, warum der Fehler auftritt?
Das kann ich dir leider nicht sagen; ich kenne mich mit der OLE-Schnittstelle von Word nicht aus. Vermutlich könnte man aber mit GetErrorInfo() mehr herausfinden.
MichelM schrieb:
Das NO_PROMPT_ON_HRCHECK_FAILURE hab ich in den Projektoptionen definiert.
Dann sollte ja deine Anwendung wenigstens nicht mehr mit "abnormal program termination" abstürzen.
MichelM schrieb:
Aber das mit den Templatefunktionen hab ich noch nicht richtig verstanden. Ich glaube, verstanden zu haben, dass man mittels GetErrorInfo() sich die genaue Art von Fehler ermittelt.
Ja, so etwa. Ich werde mal schauen, ob ich dazu ein schönes Beispiel finde.
MichelM schrieb:
Und DebugHlpr_THROW() ist sowas ähnliches wie das throw() bei try ?
DebugHlpr_THROW() ist eine Funktion, deren Definition ich oben auch zitiert habe. Sie tut, wie du dort sehen kannst, tatsächlich nichts anderes, als ein "throw" (das eigentlich ein Sprachkonstrukt ist) mit einem bestimmten Exception-Typ zu initiieren.
-
audacia schrieb:
MichelM schrieb:
Aber das mit den Templatefunktionen hab ich noch nicht richtig verstanden. Ich glaube, verstanden zu haben, dass man mittels GetErrorInfo() sich die genaue Art von Fehler ermittelt.
Ja, so etwa. Ich werde mal schauen, ob ich dazu ein schönes Beispiel finde.
Es ist im Grunde recht einfach. Ich habe das mal exemplarisch implementiert, und es funktioniert für dich so:
- In den Quelldateien, wo du mit den OLE-Automationsobjekten arbeitest, füge irgendwo folgende Deklaration ein:
template <> HRESULT DebugHlpr_HRCHECK (HRESULT hr, const char* expr, const char* file, int line);
- Füge ferner diese Datei deinem Projekt hinzu.
Damit solltest du eine aussagekräftigere Fehlermeldung bekommen, nämlich genau die Fehlermeldung, die Word selbst verursacht.
(Die Implementierung ist eigentlich auch ganz übersichtlich:
Comobj::EOleSysError* __fastcall _createSafeCallException (HRESULT hr) { typedef DelphiInterface<IErrorInfo> _di_IErrorInfo; _di_IErrorInfo errorInfo; System::WideString source, desc, helpFile; DWORD helpCtx = 0; if (GetErrorInfo (0, &errorInfo) == S_OK) { errorInfo->GetSource (&source); errorInfo->GetDescription (&desc); errorInfo->GetHelpFile (&helpFile); errorInfo->GetHelpContext (&helpCtx); return new Comobj::EOleException (desc, hr, source, helpFile, helpCtx); } else return new Comobj::EOleSysError ("", hr, 0); } template <> HRESULT DebugHlpr_HRCHECK (HRESULT hr, const char* expr, const char* file, int line) { if (!SUCCEEDED (hr)) throw _createSafeCallException (hr); return hr; }
Die Einschränkung mit "eigentlich" mache ich, weil die Datei, die ich oben verlinkt habe, optional noch ein wenig mit Assemblercode zaubert, um den Call-Stack etwas zu beschönigen. Das kann ggf. den Komfort erhöhen, braucht dich aber nicht weiter kümmern.)
-
Anmerkung aus Gründen der Vollständigkeit:
Der von mir beschriebene Workaround mittels Spezialisierung vonDebugHlpr_HRCHECK<>()
ist ab C++Builder XE nicht mehr nötig, da die VCL jetzt standardmäßig eine solche Spezialisierung verwendet. Bei OLE-Fehlern wird also nicht mehr diese Debug-Message-Box angezeigt, sondern eine Exception mit der richtigen Fehlermeldung geworfen.