Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - OLE Excel und Threads


Bergmann89 - Di 29.03.11 17:23
Titel: OLE Excel und Threads
Hey,

ich steh mal wieder vor nem Problem mit Threads (wie so oft in letzter Zeit). Ich hab ne Art ThreadPool (ist selbst ein Thread), der ein OLEExcelObject anlegt. In diesem Objekt legen die Workthreads dann ihre Daten ab. Wenn ich die Tabelle dann speichern will (im Context des ThreadPools) kommt folgende Meldung: EOleSysError: 'Eine Schnittstelle, die für einen anderen Thread marshalled war, wurde von der Anwendung aufgerufen.'
Mit der Meldung kann ich aber nich wirklich was anfangen. Hier mal noch ein Auzug aus dem Code zum Excel-Objekt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//schreibt die Daten eines Tippplans in die Excel-Datei
//@TPData: Daten des Tippplans;
//@Caption: Beschreibung/Überschirft des Tippplans;
//@SheetName: Name des Sheets, in das geschrieben werden soll;
//@TipCharCount: Zeiger auf die Verteilung der einzellnen Tippzeichen;
//@result: Errorcode;
function TExcelFile.WriteTipPlanData(TPData: TTipPlanData; Caption, SheetName: String; TipCharCount: PTipCharCount): Cardinal;
var
  Sheet: Variant;
  p: PSheetData;
  Col, Row, i, j: Integer;
  WaitResult: Cardinal;
  Arr: TArray3b;
begin
  WaitResult := WaitForSingleObject(fWriteTipPlanDataSem, SEMAPHORE_WAIT_TIME);
  try
    case WaitResult of
      WAIT_OBJECT_0: begin
        {mit Excelobject arbeiten und Daten ablegen...}
      WAIT_TIMEOUT: begin
        result := EXCEL_FILE_WAIT_FOR_SEM_TIMEOUT;
      end;
    else
      result := EXCEL_FILE_WAIT_FOR_SEM_ERROR;
    end;
  finally
    if not ReleaseSemaphore(fWriteTipPlanDataSem, 1nilthen
      result := EXCEL_FILE_RELEASE_SEM_ERROR;
  end;
end;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//speichert die Exceldatei
//@Filename: Datei in der gespeichert werden soll;
procedure TExcelFile.Save(Filename: String);
begin
  fWorkbook.SaveAs(Filename);
end;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//erzeugt das Objekt
constructor TExcelFile.Create(ReqTipChars: TTipPlan; GameCount: Byte);
begin
  inherited Create;

  fWriteTipPlanDataSem := CreateSemaphore(nil11'');
  {...}
  CoInitializeEx(nil, COINIT_MULTITHREADED);
  fExcel := CreateOleObject('Excel.Application');
  fExcel.Application.SheetsInNewWorkBook := 1;
  fExcel.Workbooks.Add;
  fWorkbook := fExcel.Workbooks[1];
end;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//gibt das Objekt frei
destructor TExcelFile.Destroy;
var
  i: Integer;
begin
  for i := 1 to fExcel.Workbooks.Count do
    fExcel.Workbooks[i].Close(False);
  fExcel.Quit;
  {...}
  inherited Destroy;
end;

Weiß da jmd was ich falsch gemacht hab und kann mir da weiterhelfen?

MfG & Thx Bergmann.


Chemiker - Mi 30.03.11 00:45

Hallo Bergmann89,

ob der Fehler im Thread liegt kann ich an Hand des Quelltextes nicht sagen. Aufgefallen ist mir allerdings das eine Verbindung zu einem Worbook nicht ordnungsgemäß getrennt wird.

Mit dieser Quellcodezeile wird der Variable fWorkbook ein Workbook zugewiesen.


Delphi-Quelltext
1:
fWorkbook:= fExcel.Workbooks[1];                    

aber in Destroy nicht wieder freigeben. Die Verbindung wird auch nach einem Close weiter aufrecht erhalten. Wenn jetzt die Verbindung zu Excel beendet wird bleibt die Verbindung weiter bestehen. (Kannst Du überprüfen wenn Du den Taskmanager aufrufst)
Um die Verbindung zu schließen bittet sich an das wie folgt zu realisieren:

Delphi-Quelltext
1:
2:
3:
4:
5:
if (NOT VarIsEmpty(fWorkbook)) then
  begin
    fWorkbook.Close;
    fWorkbook:= Unassigned;
  end;
anschließend dann mit:


Delphi-Quelltext
1:
2:
3:
4:
5:
if  NOT VarIsEmpty(fExcel) then
begin
  fExcel.Quit;
  fExcel:= Unassigned;
end;
Excel zu schließen.


Bis bald Chemiker


Bergmann89 - Mi 30.03.11 09:21

Hey,

danke für den Tipp, aber das hab ich im destructor alles drin stehen und Excel wird auch richtig beendet (kein Prozess mehr im Taskmanager). Den Test auf VarIsEmpty werd ich noch einfügen, den hab ich vergessen. Der Feler tritt ja beim Speichern auf, nicht beim schließen...

MfG Bergmann.