Automatisierung von Excel mit C#
Excel leicht gemacht
In diesem Beitrag möchte ich zwei Wege zeigen, wie man aus .NET heraus Excel-Worksheets erstellen kann. Im Beispiel werde ich eine bestehende Excel-Installation nutzen, um ein Worksheet zu erzeugen, einen kleinen Text hineinzuschreiben und anschließend abzuspeichern. Auf die gleiche Weise kann man dann Formeln editieren, Worksheets ausdrucken und Vieles mehr. (Prinzipiell gibt es auch die Möglichkeit, Worksheets ohne Mithilfe von Excel – aber mit Hilfe einer geeigneten Bibliothek – zu erzeugen. Das Paket NetAdvantage der Firma Infragistics bietet beispielsweise diese Möglichkeit.)
Will man aus einer .NET-Anwendung heraus auf eine bestehende Excel-Installation zugreifen, stellt sich die Frage, ob bereits während der Entwicklung eine DLL der entsprechenden Excel-Version vorhanden ist. Ist das der Fall, gestaltet sich das weitere Vorgehen recht einfacht:
- Die .NET-Anwendung muss gegen die entsprechende “Excel-DLL” linken
- In der Anwendung steht nun der Namensraum “Excel” zur Verfügung, der alle Typen des Excel-Objektmodells beinhaltet (VBA-Programmierern wird hier einiges bekannt vorkommen).
- Es wird eine Instanz von Excel.Application erzeugt. Über diese Instanz können Workbooks erzeugt oder geladen werden.
- Es folgt die weitere Bearbeitung des Workbooks und dessen Inhalte je nach Gusto.
Das Ganze kann dann etwa so aussehen:
[code lang="csharp"]
public void EarlyBinding (string p_strFileName) {
Excel.Application oApp = new Excel.Application ();
Workbook oWorkbook = (Workbook) oApp.Workbooks.Add ("Arbeitsmappe");
Worksheet oWorksheet = (Worksheet)oWorkbook.ActiveSheet;
oWorksheet.Cells[1, 1] = "Foobar"; oWorksheet.SaveAs (p_strFileName, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing);
oApp.Quit ();
Marshal.ReleaseComObject (oWorksheet);
Marshal.ReleaseComObject (oWorkbook);
Marshal.ReleaseComObject (oApp);
}
[/code]
Diese Methode setzt voraus, dass die Typen von Excel bereits während der Kompilierzeit bekannt sind. Das muss allerdings nicht immer gegeben sein. Möglicherweise wird die .NET-Anwendung auf einem Rechner installiert, dessen Excel-Version nicht im Vorfeld bekannt ist. Die Anwendung muss daher in der Lage sein, erst zur Laufzeit an die installierte Excel-Version zu binden. Dieses Vorgehen wird auch späte Bindung genannt (im Gegensatz zur oben gezeigten frühen Bindung).
Späte Bindung an eine unbekannte DLL setzt den Einsatz von Reflection voraus und sollte daher mit Bedacht eingesetzt werden.
[code lang="csharp"]
public void LateBinding (string p_strFileName) {
object oApp = null;
object oWorkbooks = null;
object oWorkbook = null;
object oWorksheets = null;
object oWorksheet = null;
object oRange = null;
object[] vParams;
try {
//Excel-Instantiieren
Type tpApp = Type.GetTypeFromProgID ("Excel.Application");
oApp = Activator.CreateInstance (tpApp);
//Lies WorkBooks-Collection
oWorkbooks = tpApp.InvokeMember ("Workbooks",
BindingFlags.GetProperty, null, oApp, null);
Type tpWorkBooks = oWorkbooks.GetType ();
//Erstelle ein neues WorkBook
oWorkbook = tpWorkBooks.InvokeMember ("Add",
BindingFlags.InvokeMethod, null, oWorkbooks, null);
Type tpWorkBook = oWorkbook.GetType ();
//Lies WorkSheets des neuen WorkBooks
oWorksheets = tpWorkBook.InvokeMember ("Worksheets",
BindingFlags.GetProperty, null, oWorkbook, null);
Type tpWorkSheets = oWorksheets.GetType ();
//Das erste WorkSheet soll verwendet werden
vParams = new Object[1];
vParams[0] = 1;
oWorksheet = tpWorkSheets.InvokeMember ("Item",
BindingFlags.GetProperty, null, oWorksheets, vParams);
Type tpWorkSheet = oWorksheet.GetType ();
//Ein Range-Object für die linke obere Zelle wird angefordert
vParams = new Object[2];
vParams[0] = "A1";
vParams[1] = Missing.Value;
oRange = tpWorkSheet.InvokeMember ("Range",
BindingFlags.GetProperty, null, oWorksheet, vParams);
Type tpRange = oRange.GetType ();
//Der Text "Foobar" wird der Value-Eigenschaft des Range-Objekts zugewiesen
vParams = new Object[1];
vParams[0] = "Foobar";
tpRange.InvokeMember ("Value", BindingFlags.SetProperty,
null, oRange, vParams);
//Speichern der aktuellen Arbeitsmappe
object s = Excel.XlSaveAction.xlSaveChanges;
tpWorkBook.InvokeMember ("SaveAs", BindingFlags.InvokeMethod, null, oWorkbook,
new object[] { p_strFileName });
tpApp.InvokeMember ("Quit", BindingFlags.InvokeMethod, null, oApp, null);
} catch (Exception ex) {
string strError = ex.Message;
} finally {
Marshal.ReleaseComObject (oApp);
Marshal.ReleaseComObject (oWorkbook);
Marshal.ReleaseComObject (oWorkbooks);
Marshal.ReleaseComObject (oWorksheet);
Marshal.ReleaseComObject (oWorksheets);
Marshal.ReleaseComObject (oRange);
}
}
[/code]
Die Schritte sind hier die gleichen wie im oberen Beispiel. Nicht zuletzt aus Performanzgründen ist die erste Methode allerdings zu bevorzugen.
Folgende Kommentare wurden auf unserem alten Blog zu diesem Beitrag veröffentlicht:
Markus schrieb Oktober 5th, 2007 at 8:32
Danke für die Anleitung. Hat mir sehr geholfen. Die Gegenüberstellung der beiden Methoden war sehr hilfreich!
Kristian schrieb December 1st, 2009 at 2:15
Endlich mal eine Anleitung, bei der man auch etwas versteht