Entwickler-Ecke
Dateizugriff - EXCEL OLE und Speicherfreigabe
JVS - Mi 20.04.16 11:43
Titel: EXCEL OLE und Speicherfreigabe
Hallo allerseits,
ich habe einen Beispielcode vom swisscenter adoptiert, um das erste Blatt eines Excelfiles in eine Listview einzulesen.
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: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81:
| function ExcelFileToLV(AXLSFile: string;lv: TListView;addLineNumbers: boolean): boolean; const xlCellTypeLastCell = $0000000B; var XLApp, MySheet: OLEVariant; RangeMatrix: OLEVariant; x, y, k, r: Integer; rstart: integer; nlv: TListItem; begin lv.Visible:=false; application.ProcessMessages; Result := False; lv.Items.Clear; lv.Columns.Clear; XLApp := CreateOleObject('Excel.Application'); try XLApp.Visible := false;
XLApp.Workbooks.Open(AXLSFile); MySheet := XLApp.Workbooks[1].WorkSheets[1];
MySheet.select;
MySheet.Cells.SpecialCells(xlCellTypeLastCell, EmptyParam).Activate; x := XLApp.ActiveCell.Row; y := XLApp.ActiveCell.Column;
RangeMatrix := XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value;
if addLineNumbers then rstart:=1 else rstart:=2;
for k:=1 to x do begin if lv.Columns.Count=0 then lv.Columns.Add;
if addLineNumbers then lv.AddItem(inttostr(k),nil) else lv.AddItem(RangeMatrix[K, 1],nil);
nlv:=lv.Items[lv.Items.Count-1]; for r := rstart to y do begin if lv.Columns.Count<r then lv.Columns.Add; try nlv.SubItems.Add(RangeMatrix[K, R]); except
end; end; end;
finally RangeMatrix := Unassigned;
if not VarIsEmpty(XLApp) then begin XLApp.DisplayAlerts := False; MySheet := Unassigned; XLApp.Workbooks[1].Close(False); XLApp.Quit; XLAPP := Unassigned; Result := True; end; end; lv.Visible:=true; end; |
Dies funktioniert bestens in folgendem Kontext:
- Delphicompiler 2010/Office 2007 unter Win 8.1
- Delphicompiler XE8/Office 2007 unter Win 8.1
- Delphicompiler 2010/Office 2010 unter Win 7 Pro
Hier kann man in Taskmanager sehen, wie die Excelinstanzen geöffnet und sofort wieder geschlossen werden.
Es funktionert nicht bei
- Delphicompiler XE8/Office 2010 unter Win 7 Pro
Dort bleiben die Excel-Instanzen im Speicher bis die Delphiapplikation geschlossen wird.
Das Problem liegt in der Nutzung der Rangematrix:
Delphi-Quelltext
1:
| RangeMatrix := XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value; |
weil offensichtlich die Freigabe nicht greift:
Delphi-Quelltext
1:
| RangeMatrix := Unassigned; |
Wenn ich die Datei nur öffne und sofort wieder schließe, funktioniert die Speicherfreigabe in allen Kontexten:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure JustOpenCloseXLS(AXLSFile: string); var ExcelApp: OLEVariant; begin ExcelApp := CreateOleObject('Excel.Application'); try ExcelApp.Visible:=false; ExcelApp.Workbooks.Open(AXLSFile); ExcelApp.Workbooks[1].close(false);
finally if not VarIsEmpty(ExcelApp) then begin ExcelApp.Quit; ExcelApp := Unassigned; end; end; end; |
Hat jemand eine Idee, warum die Speicherfreigabe mit XE8 / Office 2010 nicht funktionert?
GuaAck - Do 21.04.16 22:11
Hallo JVS,
helfen kann ich wohl nicht wirklich, meldet sich aber sonst keiner.
Habe mal eben probiert: Delphi 7, Windows 8.1, Office 2013:
Eergbnis: Excel wird sauber geschlossen. (Und die Inhalte meiner EXCEL-Tabelle erhalte ich auch irgendwie im Listview angezeigt.)
Aber: Wenn ich das "RangeMatrix := unassigend!" auskomentiere, dann geht es trotzdem!?
Baue doch mal als Test zu Beginn des Finaly-Blocks etwas wie Application.ProcessMessages und sleep(100) ein, damit alle Beteiligten genug Zeit haben, sich zu beruhigen.
Gruß
GuaAck
P.S.: Habe aktuell mit einem anderen Programm als Excel ein ähnliches Problem.
JVS - Fr 22.04.16 14:13
Hallo GuaAck,
und Danke, dass Du Dir die Mühe bemacht hast.
Du hast mit Delhi 7 getestet und ich mit D2010: in beiden Fällen funktioniert alles problemlos.
Mittlerweile muss ich sagen: mit XE8 funktioniert es überhaupt nicht, egal welches Windows oder Office.
Die im Speicher zurückbleibenden Excel-Instanzen sind aber offensichtlich "frei", wie folgende Beobachtung zeigt.
Öffnet man den Explorer mit aktivierter Detailansicht und klickt auf ein XLS-File, so wird eine Excel-Instanz vom Explorer gestartet, um die Dateivorschau zu erstellen.
Mit Schliessen des Explorerfensters verschwindet auch die Instanz.
Nun das Interessante:
liegt noch eine meiner hängengebliebenen Instanzen im Speicher, so generiert der Explorer keine neue Excel-Instanz, sondern verwendet meine.
Und noch besser:
Wird der Explorer nun wieder geschlossen, verschwindet auch meine Hängeinstanz.
Die Befehlskette
Delphi-Quelltext
1: 2: 3: 4: 5:
| RangeMatrix := Unassigned; MySheet := Unassigned; XLApp.Workbooks[1].Close(False); XLApp.Quit; XLAPP := Unassigned; |
reicht also nicht, um den Speicher zu bereinigen.
Unter .NET gibt es Aufrufe der GarbageCollection, die dies erledigen.
Aber bei Delphi/XE8 ?
Gruß,
JVS
JVS - Mi 27.04.16 10:35
Das Problem hängt tatsächlich mit der verwendeten OLE-Variablen RangMatrix zusammen.
Wird
Delphi-Quelltext
1: 2: 3:
| RangeMatrix := XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value; listview.SubItems.Add(RangeMatrix[K, R]); |
ersetzt durch den Direktzugriff
Delphi-Quelltext
1:
| listview.SubItems.Add(XLApp.Cells.Item[K, R].Value); |
so gibt es kein Speicherproblem mehr.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!