Home » Tipps & Tricks » COM/OLE/DDE » Andere Office-Anwendungen » Excel mit OLE-Automation steuern

Excel mit OLE-Automation steuern

Wie kann ich Excel starten?

Die Komponenten (seit Delphi 5) machen das Starten von Excel sehr einfach. Setzen Sie eine ExcelApplication-Komponente auf Ihr Formular. Falls sie in der Toolpalette nicht zu finden ist, muss im Menü Komponente / Packages installieren ein Haken gesetzt werden vor „Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server“.
Wenn die AutoConnect-Eigenschaft true ist, wird Excel automatisch gestartet, wenn Ihr Programm startet; wenn die Eigenschaft false ist, rufen Sie einfach

ExcelApplication1.Connect;

auf, wenn Sie Excel starten wollen. Um eine laufende Instanz von Excel zu verwenden, sofern es eine gibt, setzen Sie die ConnectKind-Eigenschaft von TExcelApplication auf ckRunningOrNew oder auf ckRunningInstance, wenn Sie keine neue Instanz starten wollen, wenn Excel nicht läuft.
Ist Excel einmal gestartet, können Sie andere Komponenten wie TExcelWorkbook, verbinden, indem Sie deren ConnectTo-Methode verwenden:

ExcelWorkbook1.ConnectTo(ExcelApplication1.ActiveWorkbook);
  ExcelWorksheet1.ConnectTo(ExcelApplication1.ActiveSheet as _Worksheet);
  ExcelWorksheet2.ConnectTo(ExcelApplication1.Worksheets.Item['Sheet2'] as _Worksheet);

Beachten Sie, dass ein Workbook oder ein Worksheet geöffnet werden muss, bevor Sie es verbinden können! Mehr dazu weiter unten.
Ich empfehle Ihnen jedoch, Excel nicht auf eine andere Art zu starten, als durch die Verwendung der Application-Komponente. Zumindest auf einigen Installationen verursacht der Aufruf der Connect-Methode (nicht der ConnectTo-Methode!) einer Workbook- oder einer Worksheet-Komponente eine Exception.

Excel öffnen (frühe Bindung)

Bevor Sie diese Methode nutzen können, müssen Sie die Typbibliothek (Excel8.olb für Excel 97) importieren.
Eine Art, Excel zu starten, ist der Versuch (try), GetActiveObject aufzurufen, um eine laufende Instanz von Excel zu erhalten, setzen Sie jedoch einen Aufruf von CoApplication.Create in den except-Abschnitt. Aber except-Klauseln sind langsam und können bei Leuten, die gerne „Bei Exceptions anhalten“ aktiviert haben, Probleme innerhalb der IDE auslösen. Der folgende Code benötigt keine try-except-Konstruktion, weil die Verwendung von OleCheck bei GetActiveObject im Fall, dass Excel nicht läuft, vermieden wird.

uses Windows, ComObj, ActiveX, Excel_TLB;

  var
    Excel: _Application;
    AppWasRunning: boolean; // tells you if you can close Excel when you've finished
    lcid: integer;
    Unknown: IUnknown;
    Result: HResult;
  begin
    lcid := LOCALE_USER_DEFAULT;
    AppWasRunning := False;

    {$IFDEF VER120}      // Delphi 4
    Result := GetActiveObject(CLASS_Application_, nil, Unknown);
    if (Result = MK_E_UNAVAILABLE) then
      Excel := CoApplication_.Create

    {$ELSE}              // Delphi 5
    Result := GetActiveObject(CLASS_ExcelApplication, nil, Unknown);
    if (Result = MK_E_UNAVAILABLE) then
      Excel := CoExcelApplication.Create
    {$ENDIF}

    else begin
      { make sure no other error occurred during GetActiveObject }
      OleCheck(Result);
      OleCheck(Unknown.QueryInterface(_Application, Excel));
      AppWasRunning := True;
    end;
    Excel.Visible[lcid] := True;
    ...

Es gibt jedoch ein Problem, vor dem Sie sich hüten sollten: Excel mit GetActiveObject zu starten, kann dazu führen, dass Excels Hauptfenster angezeigt wird, sein Client-Gebiet jedoch verborgen bleibt. Obwohl Sie das Client-Gebiet dadurch wiederherstellen können, indem Sie Excel auf Vollbild und wieder zurück setzen, ist das ein offensichtlich unattraktives Verhalten. Es scheint nur aufzutreten, wenn Excel zum Zeitpunkt des GetActiveObject-Aufrufs unsichtbar läuft.
Excel mag unsichtbar laufen, auch wenn Sie denken, Sie hätten es freigegeben, wenn eine Ihrer Workbook- oder Worksheet-Variablen noch „leben“. (Prüfen Sie dies, indem Sie Strg+Alt+Entf drücken und die Liste der laufenden Tasks durchsehen.) Wenn Sie sicher sind, alle Excel-Variablen freigegeben zu haben, wenn Sie die Anwendung beenden, wird Excel sauber beendet werden. Der folgende Start der Anwendung verursacht normalerweise keine Probleme, vor allem wenn die Anwender Ihrer Software Excel nicht durch eine andere Methode unsichtbar laufen haben.
Wenn Ihre Anwender jedoch evtl. andere Software verwenden, die Excel unsichtbar laufen lässt, sollten Sie es vorziehen, alle Aufrufe von GetActiveObject (oder GetActiveOleObject) zu vermeiden und einfach CoApplication.Create zu verwenden.

Ohne Verwendung der Typbibliothek

Automation ist sehr viel schneller und einfacher, wenn Typbibliotheken (frühe Bindung) verwendet werden, deshalb sollten Sie es, wenn immer möglich, vermeiden, ohne sie zu arbeiten. Aber wenn es wirklich nicht anders geht, hier, wie vorzugehen ist:

var
    Excel: Variant;
  begin
    try
      Excel := GetActiveOleObject('Excel.Application');
    except
      Excel := CreateOleObject('Excel.Application');
    end;
    Excel.Visible := True;

Wie kann ich Excel schließen?

Eine Application-Variable Excel und eine Integer-Variable LCID, der Sie den Wert GetUserDefaultLCID zugewiesen haben, vorausgesetzt:

Frühe Bindung:

{ Uncomment the next line if you want Excel to quit without asking
    whether to save the worksheet }
  // Excel.DisplayAlerts[LCID] := False;
  Excel.Quit;

Wenn Sie die D5-Server-Komponenten verwenden, sollten Sie die Verbindung lösen:

Excel.Disconnect;

Wenn Sie eine _Application-Interface-Variable verwenden, sollten Sie diese stattdessen auf nil setzen:

Excel := nil;

Späte Bindung:

{ Uncomment the next line if you want Excel to quit without asking
    whether to save the worksheet }
  // Excel.DisplayAlerts := False;
  Excel.Quit;
  Excel := Unassigned;

Beachten Sie jedoch, dass Excel unsichtbar laufend im Hauptspeicher verbleibt, wenn Sie nicht alle Ihre Workbook- und Worksheet-Variablen freigegeben haben. Trennen Sie die Verbindung aller Komponenten (disconnect), setzen Sie alle Interface-Variablen auf nil und setzen Sie Variant-Variablen auf unassigned, um dies zu verhindern. Wie kann ich ein Workbook erzeugen?
Eine Application-Variable Excel und eine Integer-Variable LCID, der Sie den Wert GetUserDefaultLCID zugewiesen haben, vorausgesetzt:

Frühe Bindung:

var
    WBk: _Workbook;
  ...
    WBk := Excel.Workbooks.Add(EmptyParam, LCID);

EmptyParam als ersten Parameter zu verwenden, bedeutet, dass ein neues Workbook mit einer Zahl leerer Sheets erzeugt wird. Wenn Sie als ersten Parameter einen Dateinamen übergeben, wird diese Datei als Vorlage für das neue Workbook verwendet. Alternativ können Sie eine der folgenden Konstanten verwenden: xlWBATChart, xlWBATExcel4IntlMacroSheet, xlWBATExcel4MacroSheet oder xlWBATWorksheet. Das erzeugt ein neues Workbook mit einem einzigen Sheet des angegebenen Typs.

Späte Bindung:

Bei später Bindung müssen Sie keine optionalen Parameter oder LCIDs angeben, weshalb Sie einfach das verwenden können:

WBk := Excel.WorkBooks.Add;

Wenn Sie nicht die Typbibilothek verwenden, aber eine der oben erwähnten Konstanten verwenden wollen, können Sie diese in Ihrem Code etwa so definieren:

const
    xlWBATChart = $FFFFEFF3;
    xlWBATExcel4IntlMacroSheet = $00000004;
    xlWBATExcel4MacroSheet = $00000003;
    xlWBATWorksheet = $FFFFEFB9;

Wie kann ich ein Workbook öffnen?

Eine Application-Variable Excel und eine Integer-Variable LCID, der Sie den Wert GetUserDefaultLCID zugewiesen haben, vorausgesetzt:

Frühe Bindung:

var
    WBk: _Workbook;
    WS: _WorkSheet;
    Filename: OleVariant;
  ...
    Filename := 'C:Test.xls';
    WBk := Excel.Workbooks.Open(Filename, EmptyParam, EmptyParam,
                                EmptyParam, EmptyParam, EmptyParam,
                                EmptyParam, EmptyParam, EmptyParam,
                                EmptyParam, EmptyParam, EmptyParam,
                                EmptyParam, LCID);
    WS := WBk.Worksheets.Item['Sheet1'] as _Worksheet;
    WS.Activate(LCID);

Späte Bindung:

Bei später Bindung müssen Sie keine optionalen Parameter angeben, deshalb können Sie es so machen:

var
    WBk, WS, SheetName: OleVariant;
  ...
    WBk := Excel.WorkBooks.Open('C:Test.xls');
    WS := WBk.Worksheets.Item['SheetName'];
    WS.Activate;

Wie kann ich ein Workbook schließen?

Eine _Workbook-Variable WBk und eine Integer-Variable LCID, der Sie den Wert GetUserDefaultLCID zugewiesen haben, vorausgesetzt:

Frühe Bindung:

var
    SaveChanges: OleVariant;
  ...
    SaveChanges := True;
    WBk.Close(SaveChanges, EmptyParam, EmptyParam. LCID);

Wenn Sie EmptyParam als SaveChanges-Parameter verwenden, wir der Anwender gefragt werden, ob das Workbook gespeichert werden soll. Der zweite Parameter erlaubt es Ihnen einen Dateinamen anzugeben, und der dritte gibt an, ob das Workbook an den nächsten Empfänger weitergeleitet werden soll.

Späte Bindung:

Bei später Bindung ist es nicht notwendig, optionale Parameter oder die LCID anzugeben, weshalb Sie es so machen können:

WBk.Close(SaveChanges := True);

oder so:

WBk.Close;

Wie kann ich Daten eingeben?

Eine _Worksheet-Variable WS vorausgesetzt:

WS := Excel.ActiveSheet as _Worksheet;

  WS.Range['A1', 'A1'].Value := 'The meaning of life, the universe, and everything, is';
  WS.Range['B1', 'B1'].Value := 42;

Sie können Daten in viele Zellen auf einmal eingeben:

WS.Range['C3', 'J42'].Formula := '=RAND()';

Sie können auf Zellen über die Zeilen- und Spaltennummer oder über Variablen zugreifen:

var
    Row, Col: integer;
  ...
    WS.Cells.Item[1, 1].Value := 'The very first cell';
    WS.Cells.Item[Row, Col].Value := 'Some other cell';

Wie kann ich Daten kopieren?

Von einem Bereich zu einem anderen:

var
    DestRange: OleVariant;
  begin
    DestRange := Excel.Range['C1', 'D4'];
    Excel.Range['A1', 'B4'].Copy(DestRange);

Stellen Sie sicher, dass Sie nicht gerade eine Zelle bearbeiten, wenn Sie den Bereich kopieren wollen, oder Sie bekommen einen ‚Call was rejected by callee‘-Fehler.
Von einem Sheet in ein anderes:
Dieses Beispiel kopiert die erste Spalte eines Sheets in die zweite Spalte eines anderen:

var
    DestSheet: _Worksheet;
    DestRange: OleVariant;
  begin
    DestSheet := Excel.WorkBooks['Test.xls'].Worksheets['Sheet1'] as _Worksheet;
    DestRange := Destsheet.Range['B1', 'B1'].EntireColumn;
    Excel.Range['A1', 'A1'].EntireColumn.Copy(DestRange);

Über die Zwischenablage:
Das Verwenden von Copy ohne Ziel-Parameter kopiert die Zellen in die Windows-Zwischenablage:

Excel.Range['A1', 'B4'].Copy(EmptyParam);

Sie können diese Zellen dann in einen anderen Bereich einfügen:

var
    WS: _Worksheet;
  begin
    WS := Excel.Activesheet as _Worksheet;
    WS.Range['C1', 'D4'].Select;
    WS.Paste(EmptyParam, EmptyParam, lcid);

Wie kann ich einen Bereich formatieren?

Eine _Worksheet-Variable WS vorausgesetzt:

var
    Format: OleVariant;
  ...
    WS := Excel.ActiveSheet as _Worksheet;

Um eine Zelle im generellen Zahlenformat zu formatieren:

    Format := 'General';
    WS.Range['A1', 'A1'].NumberFormat := Format;

Um einen Bereich als „Text“ rechtsbündig zu formatieren: Sehr sonderbar: Um einem Bereich einen „Text“-Stil zuzuweisen, muss sein NumberFormat auf ‚@‘ gesetzt werden.

  with WS.Range['A1', 'M10'] do
  begin
    NumberFormat := '@';
    HorizontalAlignment := xlHAlignRight;
  end;

Um einen Zellenbereich mit dem Datumsformat „March 4, 1999“ zu formatieren:

    Format := 'mmmm d, yyyy';
    WS.Range['B1', 'C10'].NumberFormat := Format;

Um eine komplette Spalte in einem benutzerdefinierten Währungsstil zu formatieren:

    Format := '$#,##0.00_);[Red]($#,##0.00)';
    WkSheet.Range['C1', 'C1'].EntireColumn.NumberFormat := Format;

Um den Text in einer Zelle auf 20 pt Arial, fett und rosa (fuchsia) zu setzen:

    with Excel.ActiveCell.Font do
    begin
      Size := 20;
      FontStyle := 'Bold';
      Color := clFuchsia;
      Name := 'Arial';
    end;

Um die Farbe der Zelle zu verändern:

Excel.ActiveCell.Interior.Color := clBtnFace;

oder

Excel.Range['B2', 'C6'].Interior.Color := RGB(223, 123, 123);

Um die ersten drei Zeichen einer Zelle fett zu machen:

  var
    Start, Length: OleVariant;
  ...
    Start := 1;
    Length := 3;
    Excel.ActiveCell.Characters[Start, Length].Font.FontStyle := 'Bold';
    Start := 4;
    Length := 16;
    Excel.ActiveCell.Characters[Start, Length].Font.FontStyle := 'Regular';

Wie kann ich einem Workbook einen Namen zuweisen?

var
    WB: _Workbook;
    N: Excel_TLB.Name;  // or N := Excel97.Name; if you're using D5
  begin
    WB := Excel.ActiveWorkbook;
    N := WB.Names.Add('AddedName', '=Sheet1!$A$1:$D$3', EmptyParam, EmptyParam,
                 EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                 EmptyParam, EmptyParam);

Der erste Parameter ist der neue Name, der zweite, worauf sich der Name bezieht. Wenn der dritte Parameter auf false gesetzt wird, wird der Name versteckt und erscheint nicht in den Define Name- oder Goto-Dialogen.
Zwei mögliche Probleme sind hier anzumerken. Erstens: Um eine Variable des Typs „Name“ zu deklarieren, müssen Sie es wahrscheinlich ausdrücklich so machen:

N: Excel_TLB.Name;

Zweitens: Beachten Sie, dass das $-Zeichen im RefersTo-Parameter wichtig ist. Es wegzulassen, verursacht eine unterschiedliche Zahl unerwarteter Ergebnisse.

Wie kann ich einem Workbook ein Makro hinzufügen?

Sie können Excel-VBA-Code zur Laufzeit verändern, indem Sie ihn direkt in das Code-Modul des Excel-VBA-Editors schreiben, Zeilen oder Ereignisse hinzufügen oder entfernen. Das wichtige Objekt hierbei ist das CodeModule-Objekt (geschickterweise sehr schwer in der Excel-VBA-Hilfe zu finden). Es ist in der VBIDE97.pas-Datei deklariert. Hier ein Beispiel, wie es genutzen werden kann:

uses
  VBIDE97; // or VBIDE_TLB for Delphi 4
var
  LineNo: integer;
  CM: CodeModule;
begin
  CM := WBk.VBProject.VBComponents.Item('ThisWorkbook').Codemodule;
  LineNo := CM.CreateEventProc('Activate', 'Workbook');
  CM.InsertLines(LineNo + 1, '  Range("A1").Value = "Workbook activated!"');