Programm hinterlässt Prozess, obwohl beendet
-
Hallo,
meine Frage heute ist, warum mein Programm ein Excel-Prozess im Task-Manager hinterlässt, obwohl ich diesen beendet habe...
und zwar mach ich folgendes:
ich erstelle erstmal ein Excel-Objekt und öffne die Anwendung:Variant m_vExcel; m_vExcel = CreateOleObject("Excel.Application"); if( bVisible ) { m_vExcel.OlePropertySet("Visible",true); }Nach dem ich meine Arbeit im Excel getan habe, möchte ich Excel schliessen, das mache ich dann so:
m_vExcel.OleProcedure("Quit");Jedoch, wenn ich mir den Task-Manager nachher anschaue, ist der EXCEL-Prozess immer noch drin, wenn ich jetzt paar Excel-Sheets öffne und schliesse, weil die Anwendung diese Arbeit verlangt, habe ich ja ne ganze Latte an Excel-Prozessen...

Weiss jemand Rat für mich, muss ich irgendwie das Handle an Windows zurückübergeben?
Wenn ich Excel nämlich nicht aus der Anwendung heraus schliesse, sondern das Excel selber, bleibt der Excel-Prozess trotzdem im Task-Manager.Gruss,
chullain
-
keine Ahnung, aber kann das etwas damit zu tun haben ?
http://www.c-plusplus.net/forum/viewtopic-var-t-is-170076.html
-
Hallo,
ich habe im Moment genau das gleiche Problem.
Falls jemand einen Lösungsansatz gefunden hat, würde mich das auch interessieren.Die Verwendung von ClearVariant(&var) ist mir nich klar.
Danke für jede Hilfe
-
Hallo,
Hast du mal probiert, ob das "Quit" überhaupt ausgeführt wird? Ich verwende normalerweise die Wrapperklasse aus der FAQ (etwas geändert und erweitert). Da wird das
var_Excel.OleFunction("Quit");im Destruktor aufgerufen, so dass das auf keinem Fall schief gehen kann.
-
wie in meinem topic geschrieben, solltest du nicht immer neue variants anlegen sonder lieber EINEN variant für ALLES benutzen.
Initialisier dein variant im ctor des programm quit ihn im dtor und wenn du zwischendurch etwas neues speichern willst lösch ihn vorher immer mit VariantClear();ich hab nur nen standard VARIANT benutzt ich weis nicht was sich bei so nem Excelvariant noch für Probleme ergeben, aber vielleicht hilft das.
-
Hallo,
Danke für die schnellen Antworten.
@Braunstein: Ich benutze die gleiche Wrapperklasse (TMLExcel).Haben die Erweiterungen irgendetwas mit dem Speichern oder Scließen zu tun?
Zur Erklärung:
- Es wird eine Excel-Datei mit mehreren Tabellen geöffnet (Visible = false).
- Es wird in einer oder mehreren Tabellen dieser Arbeitsmappe nach einer bestimmten Spaltenüberschrift gesucht und von dieser Spalte der Inhalt aller Zeilen in einer Stringliste gespeichert.
- In dieser (oder diesen) Stringlisten wird ein bestimmter Wert gesucht
- Wenn gefunden, wird die Zeilennummer und der Tabellenindex an das Hauptprogramm übergeben.
- Es wird also in der Arbeitsmappe nichts geändert.
- Es wird die Methode CloseWorkbook() aus der Wrapperklasse aufgerufen (Rückgabewert ist true!)
- danach wird die Instanz gelöscht (delete ...);
- Der Prozess bleibt erhalten.Gruß Udo_11
-
Ich habe die Verwaltung der Workbooks und Worksheets ein wenig geändert. Mir hat das ganze gecaste mit der TList nicht behagt, so dass ich hier auf vector umgestellt habe. Das vereinfacht vieles. Dann habe ich noch das Auslesen von Shapes aus einem Excel-sheet mit eingebaut. Wenn du willst kann ich dir diese Dateien mal schicken.
Hier mal ein beispiel für die Veränderung einer Funktion
// alt void __fastcall TMLWorkbook::UpdateSheetList() { FSheetNames->Clear(); if(Connect) { if(!(WorksheetCount ==FSheetNames->Count || WorksheetCount ==FWorksheets->Count )) { FSheetNames->Clear(); for( int i=FWorksheets->Count-1;i>=0;i--) { TMLWorksheet* sheet= reinterpret_cast <TMLWorksheet*>( FWorksheets->Items[i]); if(sheet) delete sheet; } FWorksheets->Clear(); if(WorksheetCount>0) { for( int i=1;i<=WorksheetCount;i++) { TMLWorksheet * book=new TMLWorksheet(this,(Variant)var_Book.OlePropertyGet("WorkSheets",i)); FWorksheets->Add((void*)book); FSheetNames->Add(var_Book.OlePropertyGet("Worksheets",i).OlePropertyGet("Name")); } } // ShowMessage(FSheetNames->Text); } } } //neu void __fastcall TMLWorkbook::UpdateSheetList() { if(Connect) { int count = GetWorksheetCount(); FWorksheets.clear(); FSheetNames.clear(); if(count > 0) { for( int i=1;i<=WorksheetCount;i++) { FWorksheets.push_back(TMLWorksheet(this,(Variant)var_Book.OlePropertyGet("WorkSheets",i))); FSheetNames.push_back(var_Book.OlePropertyGet("Worksheets",i).OlePropertyGet("Name")); } } } }
-
Hallo,
@ Braunstein: Es wäre schön, wenn Du mir die Dateien schicken würdest.
Ich habe noch ein paar Tests gemacht und folgendes festgestellt:
1. Wenn ich die Eigenschaft Saved des geöffneten Excel-Workbooks auf false setze und somit beim Beenden von Excel der Abfragedialog zur Datenspeicherung erscheint und ich diesen Dialog mit JA bzw. NEIN verlasse, wird der Prozess ordnungsgemäß beendet.
2. Wenn ich die Eigenschaft Saved des geöffneten Excel-Workbooks auf true setze und somit Excel ohne Abfragedialog zur Datenspeicherung beendet wird bleibt der Prozess erhalten.
Ich hatte wohl noch nicht erwähnt, das die ganze Aktion in einem eigenen Thread abläuft und der Thread nach dem Schliessen von Excel beendet wird.
Ich hatte auch schon ein Sleep nach dem delete Excel eingebaut, das hat aber auch nichts gebracht.
Gruß Udo_11
-
Hallo,
Ich bin trotz mehrerer Versuche zu keinem Erfolg gekommen.
Ich denke, das ist ein Zeitproblem im Thread.Wenn ich nämlich die Excel-Instant außerhalb des Threads erstelle, dann wird diese beim Beenden des Threads immer beendet und auch der Prozess ist weg.
Das Problem ist nur, das ich dann im Thread nicht auf die Excel-Instanz zugreifen kann.void __fastcall Twk_satz::w_selectCompleted(TObject *Sender, bool &Cancel) { Cancel = dest_list->Items->Count < 2; if (!Cancel) { // ShowMessage("searched = "+BoolToStr(searched,true)); if (go_xls == NULL && !searched) { Excel = new TMLExcel; Excel->OpenWorkbook(MainForm->datapath+"excel\\s_u_test.xls"); Excel->CloseExcelOnTerminate = true; Application->ProcessMessages(); // Sleep(5000); go_xls = new XLSSearch(Excel->Books[0]); found1 = false; found2 = false; go_xls->OnTerminate = DeleteThread; } } else { . . . } } //---------------------------------------------------------------------------aus der .h-Datei des Threads:
class XLSSearch : public TThread { private: . . . TMLWorkbook *Excel; protected: void __fastcall Execute(); public: __fastcall XLSSearch(TMLWorkbook *Xls); };aus der .cpp-Datei des Threads:
__fastcall XLSSearch::XLSSearch(TMLWorkbook *Xls) : TThread(false) { Priority = tpNormal; FreeOnTerminate = true; } //---------------------------------------------------------------------------@Braunstein: Wenn Du mir die Dateien schicken willst --> udo.knirsch@stahlwerk-thueringen.de
Gruß Udo_11
-
Hallo,
Wieso kannst du im Thread nicht auf deine ExcelInstanz zugreifen? Du brauchst sie doch nur zu übergeben.
Um das Löschen der Excelinstanz sicher zu machen, würde ich hier die Verwendung eines Smartpointer empfehlen. Dann wird im Destruktor dein Excelprozess zuverlässig geschlossen. In deinem Fall empfehle ich boost::shared_ptr.
Die Dateien schicke ich dir gleich.Ciao
-
Hallo,
@Braunstein: Danke für die Mail, ist angekommen.
Ich habe mich im letzten Posting falsch ausgedrückt. Ich kann auf die Excel-Instanz zugreifen, aber der Zugriff scheint ins leere zu gehen. Jeder Versuch,
eine Eigenschaft zu verwenden, führt zu nichts. Die Funktionen werden nicht ausgeführt, es kommt keine Fehlermeldung, der Aufruf von GetLastError() liefert 0, der Thread wird beendet.Ich weiß echt keinen Rat mehr.
Gruß Udo_11
-
Ich würde deinen Thread etwas umorganisieren
class XLSSearch : public TThread { private: . . . TMLWorkbook *WorkBook; //Bezeichnung Excel ist etwas mißdeutlich protected: void __fastcall Execute(); public: __fastcall XLSSearch(TMLWorkbook *Xls); }; __fastcall XLSSearch::XLSSearch(TMLWorkbook *Xls) : TThread(false), WorkBook(Xls) // WorkBook sollte schon im Konstruktor initialisiert werden, woanders geht es ja nicht { Priority = tpNormal; FreeOnTerminate = true; }Hier muß natürlich sichergestellt werden, dass WorkBook immer auf eine gültige Instanz zeigt. Also Excel vorher nicht löschen.
-
Hallo,
@Braunstein: Habe Deinen Änderungsvorschlag eingearbeitet. Am Verhalten ändert sich jedoch nichts.
an einem Breakpoint im Debugger bekomme ich bei überwachten Ausdrücken folgende Ausgabe:
WorkBook: :01B2D2B0
WorkBook->WorksheetCount: E2122 Funktionsaufruf durch unbehandelte Exception 0xeedfade an Adresse 0x7c812a5b beendet
WorkBook->Sheets[0]->Name: E2122 Funktionsaufruf durch unbehandelte Exception 0xeedfade an Adresse 0x7c812a5b beendetDas bedeutet doch, das Workbook auf eine Speicheradresse zeigt, aber nicht verwendet werden kann ???
Gruß Udo_11
-
Nun, das Workbook muß natürlich existieren. Debugge mal von dem Punkt aus, wo das Workbook erzeugt wird.
-
Hallo,
Beim Debuggen ab dem Zeitpunkt der Erstellung der Excel-Instanz sieht das folgendermaßen aus:
Breakpoint Excel-Initialisierung --> Ok
Breakpoint im Konstruktor des Threads: WorkBook existiert (Adresse: 01B2D2B0) und ich kann in überwachten Ausdrücken alle Funktionen des WorkBook aufrufen.Breakpoint am Beginn der Execute-Methode des Threads: WorkBook-Adresse ist die gleiche, aber beim Zugriff auf die Funktionen des WorkBooks kommen die im letzten Posting gezeigten Fehler.
Gruß Udo_11
-
Dann scheint das Workbook irgendwo dazwischen freigegeben worden zu sein. Schau dir mal alle Funktionen an, mit denen man das Workbook freigebn kann. Setze gegebenenfalls Breakpoints in die Destruktoren von TMLWorkBook sowie TMLExcel und schau ob und wann die aufgerufen werden.
-
Hallo,
@Braunstein: Nach Deinem Hinweis von gestern habe habe ich den Debugger nochmal bemüht und folgendes festgestellt:
Im Konstruktor des Threads ist ein Zugriff auf WorkBook->Name möglich
Die Excel-Instanz wird bei mir nur in der Funktion gelöscht, welche ich dem OnTerminate-Ereignis des Threads zugewiesen habe.
OnTerminate wird laut Builder-Hilfe ausgelöst, wenn die Execute-Methode des Threads zurückkehrt.
Die Execute-Methode meines Threads wird beendet, weil sie einen Fehler beim Zugriff auf WorkBook->Name produziert.
-
Ok, ich ahne was los ist. Übergib mal statt der Workbookinstanz die Excelinstanz an deinen Thread. Die Excelinstanz verwaltet ja die Workbooks und da kann es evtl. vorkommen, dass der Pointer auf ein Workbook intern gelöscht und neu erzeugt wird. Dann hättest du einen ungültigen Pointer in deinem Thread.
-
Hallo,
Bei der Übergabe der Excel-Instanz an den Thread zeigt sich das gleiche Fehlerbild.
Der Fehler wird ausgelöst in der TMLExcel-Klasse in der Funktion GetWorkbookCount()
int TMLExcel::GetWorkbookCount() { if(!Connect) return 0; else return var_Excel.OlePropertyGet("Workbooks").OlePropertyGet("Count"); //in dieser Zeile !!! }Gruß Udo_11
-
Hallo,
Ich habe jetzt folgende Fehlermeldung:
Fehler:
Eine Schnittstelle, die für einen anderen Thread marshalled war, wurde von der Anwendung aufgerufenDas ganze hat mit Marshalling und COM+ zu tun. Ich habe auch schon etliche Artikel im Netz gefunden, weiß aber nicht so recht wie man das im konkreten Fall umsetzen muß.
Wenn dazu jemand etwas sagen kann, wäre das wunderbar.Gruß Udo_11