Mit VBA aus Powerpoint in ein Word-Dokument schreiben

20. Dezember 2008 Keine Kommentare »

Mit folgendem Code ist es möglich, aus Powerpoit heraus mit Hilfe von VBA ein Word-Dokument zu öffnen und Text in das Dokument einzufügen. In das Word-Dokument werden dazu Textfelder eingefügt, die dann über einen Index zugegriffen werden kann.

[code]

Sub openDocument()
'Deklaration eines Word-Objects
Dim oWord As Object
'Erzeugen einer Word-Object-Instanz
Set oWord = CreateObject("Word.Application")
'Öffnen eines bestehenden Word-Objects
oWord.Documents.Open "C:\Test.doc"
'Zugriff auf die Textfelder des Word-Dokuments und Schreiben des Inhalts
oWord.ActiveDocument.Shapes(1).TextFrame.TextRange.Text = Wert
'Word in den Vordergrund holen
oWord.Visible = True
End Sub

[/code]

Tags:, , , , ,

Bookmarken bei... del.icio.us:Mit VBA aus Powerpoint in ein Word-Dokument schreiben furl:Mit VBA aus Powerpoint in ein Word-Dokument schreiben Y!:Mit VBA aus Powerpoint in ein Word-Dokument schreiben

Debug-Funktionen für JavaScript

20. November 2007 1 Kommentar »

Häufig sieht man sich bei der Arbeit mit JavaScript mit der Situation konfrontiert, dass man statt mit einer ausgereiften Entwicklungsumgebung mit integriertem Debugger mit einem blanken Texteditor Vorlieb nehmen muss. Insbesondere bei der Arbeit mit DOM-Objekten wird die Fehlersuche dann weniger komfortabel als man es sich vielleicht wünscht :-)

JavaScript bietet nun allerdings die Möglichkeit, bequem Eigenschaften und Methoden von Objekten zur Laufzeit zu erfragen. Damit kann man sich zwei hilfreiche Funktionen basteln, mit denen man Eigenschaftswerte von Objekten sichtbar machen kann.

[code lang="javascript"]
function obj2str (p_Obj) {
var strText = "";
var i = 0;
for (prop in p_Obj) {
strText += i++ + " " + prop + ": " + p_Obj[prop] + "\n";
}
return strText;
}

function printObject (p_Obj) {
var wnd = window.open ();
var str = obj2str (p_Obj);
str = str.replace (/\ str = str.replace (/\>/g, ">");
wnd.document.write ("

<pre><br/>\n " + str + " \n<br/></pre>

");
wnd.focus ();
}

[/code]

Durch den Aufruf von printObject (obj) öffnet sich ein Browserfenster, das alle Eigenschaften von obj inklusive ihrer Inhalte ausgibt.

Zum Debugging von Funktionsaufrufen ist die folgende Funktion hilfreich:

[code lang="javascript"]
Function.prototype.callString=function(){
var args = this.arguments;
var str = this.caller + " \n --calls--> \n \t (";
for (var i = 0; i < args.length - 1; i++) {
str += args[i] + ", ";
}

str += args[args.length-1] + ") \n";
str += this.toString ();
return str;
};
[/code]

Der Aufruf erfolgt über

[code lang="javascript"]
function callString (p_strFuncName) {
var fn = eval (p_strFuncName);
alert (p_strFuncName + ": " + fn.callString ());
}
[/code]

CallString gibt den Code einer Funktion samt Aufrufparameter aus.

Tags:, ,

Bookmarken bei... del.icio.us:Debug-Funktionen für JavaScript furl:Debug-Funktionen für JavaScript Y!:Debug-Funktionen für JavaScript

Funktionale Erweiterung von JavaScript

19. November 2007 2 Kommentare »

JavaScript ist eine objektbasierte Skriptprache mit dynamischer Typisierung, mit deren Hilfe man Webseiten mit clientseitigem “Verhalten” versehen kann. Im Zeiten von Web 2.0 und insbesondere Ajax kann man beobachten, dass Seiten zunehmend dynamischer werden. Ihre Programmlogik und die damit verbundene Entwicklungsarbeit werden immer umfangreicher. Es stellt sich daher die Frage, inwieweit die Sprache für solche Anforderungen geeignet ist.

An dieser Stelle möchte ich nun einige bekannte Eigenschaften von JavaScript betrachten. Im Zusammenhang mit der Eignung für umfangreiche Entwicklungen sind zwei Eigenschaften von JavaScript interessant. Diese verleihen Programmen einen – mitunter überraschend – hohen Grad an Dynamik.

Zunächst ist die Sprache objektbasiert im folgenden Sinne. Vererbungsbeziehungen werden über eine sogenannte prototypische Vererbung ad-hoc aufgebaut. Das bedeutet nichts anderes, als dass jedes Objekt zu jeder Zeit um beliebige Eigenschaften und Methoden erweitert werden kann. Weiterhin gibt es keine mit den “klassischen” objektorientierten Sprachen vergleichbaren sprachlichen Mittel, um Daten zu kapseln. Es gibt zwar Wege, eine Kapselung über lokale Variablen in Konstruktorfunktionen zu erreichen, allerdings gibt es hierfür keine explizite Ausdrucksweise wie etwa in C++ oder Java.

Zum anderen nimmt der Interpreter zu keiner Zeit eine Prüfung von Funktionsaufrufen vor. Weder Typ noch die Anzahl (!) der übergebenen Parameter werden mit der Definition einer Funktion verglichen.

Beispiel:

Eine Funktion

[code lang="JavaScript"]
function test (p_1, p_2, p_3) {
alert (p_1); alert (p_2); alert (p_3);
}
[/code]

kann als test (1), test (1,2) oder test (1,2,3) aufgerufen werden. Nicht übergebene Parameter haben innerhalb der Funktion den Wert “undefined”. Diese und andere Eigenschaften führen dazu, dass es bei umfangreichen Programmteilen mitunter schwierig ist, abzuschätzen, wie sich ein Stück Code bei seinem Aufruf verhält.

JavaScript ist natürlich nicht ohne Grund so geraten. Vielmehr eignen sich dynamische Skriptsprachen gut zur Entwicklung von Prototypen oder einfacher Programmlogiken. Bei umfangreichen Entwicklungen ist – zumindest meiner Erfahrung nach – ein “ordendliches”, sprich: statisches Typsystem durchaus eine große Hilfe. Die Dinge sind nun aber wie sie sind und kann man versuchen, das Beste daraus zu machen. In diesem Artikel möchte ich daher zeigen, wie man JavaScript etwas von dem Feeling einer funktional Sprache verleihen kann. Funktionale Sprachen zeichnen sich in gewissem Sinne dadurch aus, dass Programme auf einer abstrakteren Ebene formuliert und dadurch kompakter werden. Davon verspreche ich mir zweierlei: Zum einen wird die Lesbarkeit des Codes verbessert, zum anderen werden Wiederverwendbarkeit und Robustheit erhöht, da für häufig verwendete Konzepte benannte Strukturen definiert werden.

Funktionale Programmiersprachen zeichnen sich im wesentlich durch zwei Konzepte aus:

  1. Referentielle Transparenz
  2. Funktionen sind First-Class-Objects

Referentielle Transparenz bedeutet, dass der Wert einer Variablen nur von ihrer Umgebung abhängt und nicht vom Zeitpunkt ihrer Verwendung. Variablen werden daher eher “im mathematischen Sinn” aufgefasst und weniger als benannte Speicherplätze. Insbesondere ist eine Variable, für die referentielle Transparenz gilt, unabhängig von Seiteneffekten. JavaScript bietet dieses Konzept nicht. Bei Funktionsaufrufen kann man ihm aber durch Wertkopien und die ausschließliche Verwendung lokaler Variablen nahekommen.

Werden Funktionen als First-Class-Objects aufgefasst, können sie genauso wie Werte behandelt werden und damit auch Rückgabewerte oder Parameter von Funktionen sein oder zur Laufzeit erzeugt werden. Das geht über das Konzept eines einfachen Funktionszeigers in C hinaus – hier sind bspw. keine anonymen oder partiell evaluierten Funktionen möglich. In JavaScript sind Funktionen Objekte. Sie können zur Laufzeit – auch anonym – erzeugt und verarbeitet werden. Darüber hinaus sind Funktionen in JavaScript Closures. Das bedeutet, eine lokal definierte Funktion “erbt” das sie umgebende lexikalische Scope.

[code lang="JavaScript"]
function testClosure () {
var myInt = 1;
var closure = function (e) {
return e + myInt;
}
alert (closure ( 2));
}

[/code]

Innerhalb von closure () ist myInt sichtbar, obwohl es nicht global deklariert ist. Mit Hilfe dieser Eigenschaft ist es bspw. auch möglich, Eventhandler zu parametrieren, ohne globale Variablen zu verwenden.

JavaScript bietet mit diesen Eigenschaften nun die Möglichkeit, sogenannte Funktionale zu definieren. Das sind im syntaktischen Sinne gewöhnliche Funktionen mit der Besonderheit, dass sie eine oder mehrere Funktionen als Parameter entgegen nehmen und deren Ausführung steuern. Ich möchte hier exemplarisch drei Beispiele für Funktionale zeigen, die auch in anderen Sprachen verfügbar sind. Diese sind map, filter und foldr.

Map nimmt eine Liste l und eine unäre Funktion f als Parameter und wendet f auf jedes Element von l an. Die Parameter von Filter sind eine Liste l und ein unäres Prädikat p. Die Rückgabe von filter sind dann alle Element von l, für die p gilt. Foldr ist etwas komplizierter. Die Parameter sind eine Liste l, eine binäre Funktion f und ein für die Funktion neutrales Element n. Die Rückgabe von foldr ist ein einziges Element, das folgendermaßen bestimmt wird: f wird auf das letzte Element von l und n angewendet. Das Ergebnis nennen wir e1 := f (l[-1], n). Dann wird f auf e1 und das vorletzte Element von l angewendet: e2 := f (l[-2], e1), u.s.w. bis nur noch ein Element übrigbleibt. Mit Hilfe von foldr kann man Elemente kumulieren, Maxima ermitteln und Listen sortieren – vorausgesetzt man definiert eine entsprechende Funktion.

Im folgenden zeige ich, wie diese Funktionale in JavaScript realisiert werden können. Durch sein Konzept der prototypischen Vererbung bietet JavaScript die Möglichkeit, vorhandene “Klassen” auch nachträglich um eigene Methoden zu erweitern. Unsere Funktionale arbeiten auf Listen – es bietet sich daher an, sie dem eingebauten “Listen”-typ – dem Array als Methoden in die Schuhe zu schieben. Die vorgestellten Implementierungen werden daher als Erweiterungen des Array-Prototyps realisiert. Um eine Art referentielle Transparenz zu erhalten, legen alle Methoden Kopien der übergebenen Felder an.

Eine Implementierung von map () lässt sich in JavaScript folgendermaßen realisieren:

  1. p_Func ist die Funktion, die auf jedes Element der Array-Instanz angewendet wird

[code lang="JavaScript"]
Array.prototype.map = function (p_Func) {
var ret = new Array (this.length);
for (var i = 0; i < this.length; i++) {
ret[i] = p_Func (this[i], i);
}
return ret;
};

[/code]

Im Fall von Filter kann das Ergebnis dann so aussehen:

  1. p_Func ist das Prädikat, das auf das Array angewendet werden soll
  2. p_bReturnIndex gibt an, ob das Ergebnis-Array die Indices oder die Elemente beinhalten soll
  3. p_bPreserveIndex gibt an, ob nicht enthaltene Elemente ausgespart werden sollen
  4. p_Neutral ist ein neutraler Wert, der für nicht enthaltene Werte gesetzt wird, sofern p_bPreserveIndex gesetzt ist

[code lang="javascript"]
Array.prototype.filter = function (p_Func, p_bReturnIndex, p_bPreserveIndex, p_Neutral) {
var ret = new Array ();
for (var i = 0; i < this.length; i++) {
if (p_Func (this[i], i)) {
if (p_bPreserveIndex) {
(p_bReturnIndex) ? ret[i] = i : ret[i] = this[i];
} else {
(p_bReturnIndex) ? ret.push (i) : ret.push (this[i]);
}
} else if (p_bPreserveIndex) {
(p_bReturnIndex) ? ret[i] = -1 : ret[i] = p_Neutral;
}
}
return ret;
};
[/code]

Map kann auf die folgende Weise umgesetzt werden:

  1. p_Func ist die Funktion, die auf jedes Element angewendet werden soll. An dieser Stelle wird eine binäre Funktion übergeben, die als zweites Argument den aktuellen Index des Elements erhält.

[code lang="javascript"]
Array.prototype.map = function (p_Func) {
var ret = new Array (this.length);
for (var i = 0; i < this.length; i++) {
ret[i] = p_Func (this[i], i);
}
return ret;
};
[/code]

Die Umsetzung von Foldr ist wie folgt:

  1. p_Func ist eine binäre Funktion
  2. p_Neutral ist ein neutrales Element dieser Funktion

[code lang="javascript"]
Array.prototype.foldr = function(p_Func, p_Neutral) {
return (this.length == 0) ? p_Neutral :
p_Func(this[0], this.slice(1).foldr(p_Func, p_Neutral));
};
[/code]

Die Funktionalität gegenüber einer “einfachen” Schleife ist hier nicht eingeschränkt. Lokal definierte Funktionen in JavaScript sind sogenannte Closures. Das bedeutet, dass Variablen aus dem umgebenden Scope im inneren einer solchen Funktion sichtbar sind.

Beispiel:

[code lang="javascript"]
var myArray = new Array (1,2,3,4);
var myInt = 3;
var smaller = myArray.filter (function (p_El) { return p_El < myInt; } );
[/code]

Die Variable myInt ist innerhalb der anonymen Funktion sichtbar, obwohl sie außerhalb definiert wurde.

Was erreichen wir damit? Zum einen erhält man eine bessere Wiedererkennbarkeit von Codeabschnitten. In einer Schleife kann im Prinzip alles mögliche getan werden – bei einem Funktional ist auf einen Blick klar, was seine Aufgabe ist. Zum anderen bieten zwingt man sich letztendlich auch dazu, seinen Code besser durch Funktionen zu strukturieren. Neben anonymen Funktionen können die Funktionale auch benannte Funktionen mit einer passenden Signatur entgegen nehmen. Das trägt widerum zu einer besseren Lesbarkeit und darüber hinaus zu einer höheren Wiederverwendbarkeit verglichen mit einem einfachen Schleifenrumpf bei.

Beispiel:

In dem Array myArray sollen alle geraden Zahlen mal 2 genommen werden.

[code lang="javascript"]
var myArray = new Array (1,2,3,4);
[/code]

In einer einfachen Schleife sieht das etwa so aus:

[code lang="javascript"]
var newArray = new Array ();
for (var i = 0; i < myArray.length; i++) {
if (myArray[i] % 2 == 0) {
newArray.push (2 * myArray[i]);
}
}
[/code]

Der funktionale Ausdruck ist hingegen deutlich kompakter als die Schleife:

[code lang="javascript"]
var twotime = function (p_Par) { return p_Par * 2; }
var even = function (p_Par) { return p_Par % 2 == 0; }
var newArray = myArray.map (twotime).filter (even);
[/code]

Der auf diese Art veränderte Array-Prototyp wirkt sich auf alle Instanzen aus, die im Anschluss an die Deklarationen gebildet werden. Insbesondere bei der Arbeit mit DOM-Objekten muss man allerdings beachten, dass diese bereits im Browser instatiiert sind, bevor externe JavaScript-Quellen geladen werden. Um also Arrays, wie sie von DOM-Objekten geliefert werden, auf diese Art verwenden zu können, muss man eine neue Array-Instanz bilden. Das kann man am einfachsten mit einer Art Konstruktor-Funktion erreichen:

[code lang="JavaScript"]
Array.prototype.construct = function (a) {
for (var i = 0; i < a.length; i++) {
this.push (a[i]);
}
return this;
};
[/code]

Gehen wir nun davon aus, dass wir es mit einer Html-Tabelle zu tun haben, die in jeder ihrer Zellen eine Textbox beinhaltet. Der Einfachheit halber sollen zunächst lediglich die Werte dieser Textboxen beschafft werden. Mit Hilfe von Funktionalen kann diese Aufgabe lösen.

Wir brauchen zunächst eine Funktion, welche die Textboxen ermittelt.

[code lang="JavaScript"]
var fnGetTextbox = function (nd, pos) {
return nd.getElementsByTagName("input")[0];
};
[/code]

[code lang="JavaScript"]
function getTextboxes (p_Table) {
var vndCells = new Array ().construct (p_Table.getElementsByTagName ("td"));
var vtxtInput = vndCells.map (fnGetTextbox);
return vtxtInput;
}
[/code]

Weiterhin definieren wir eine Funktion, die den Wert einer Textbox liest.

[code lang="JavaScript"]
var fnGetValue = function (nd, pos) {
return (nd ? nd.value : '');
};

[code lang="JavaScript"]
var vtxtInput = getTextboxes (row);
var vValues = vtxtInput.map (fnGetValue);
[/code]

Um aufwändigere Aufgaben zu erledigen, kann man map () mit einer lokalen Funktion parametrieren.

[code lang="JavaScript"]
var vtxtInput = getTextboxes (row);
var newValue = "Inhalt: ";
var vValues = vtxtInput.map (function (p_Nd, p_iPos) {
p_Nd.value = newValue + p_iPos;
return p_Nd;
});
[/code]

Selbstverständlich sind Funktionale nicht wirklich mächtiger als Schleifen. Der entscheidende Unterschied, der nicht zuletzt beim Einsatz mit DOM zum Tragen kommt, ist allerdings der folgende: Das Wissen um die Strukturen innerhalb der Listenelemente (sprich: wie liegen Eigenschaften von Elementen innerhalb von Zellen einer Tabelle) ist innerhalb einer Funktion gekapselt. Auf diese Weise wird Code, der für die Traversierung einer Struktur verantwortlich ist, getrennt von der Logik, die einzelne Elemente betrifft.

Tags:,

Bookmarken bei... del.icio.us:Funktionale Erweiterung von JavaScript furl:Funktionale Erweiterung von JavaScript Y!:Funktionale Erweiterung von JavaScript

PDFs in C# unter Windows ausdrucken

10. September 2007 12 Kommentare »

Um pdf-Dokumente unter Windows aus einer .NET-Anwendung heraus auszudrucken, gibt es die verschiedensten Möglichkeiten. Es gibt zunächst einmal eine Vielzahl freier oder kostenpflichtiger Bibliotheken, mit denen mehr oder weniger komfortabel pdf-Dokumente aus .NET heraus erstellt werden können. Geht es allerdings darum, ein Dokument auszudrucken, genügen freie Angebote offenbar nicht mehr und man müsste auf eine kostenpflichtige Komponente zurückgreifen. Will man lediglich ein vorhandenes Dokument ausdrucken, schiesst man damit vermutlich mit Kanonen auf Spatzen. Ein einfacher Weg ist hier, eine vorhandene Installation von Acrobat oder dem Reader einzusetzen. Beide sind in der Lage, Dokumente auszudrucken und warum sollte man davon nicht Gebrauch machen? (Vorausetzung ist hier wie gesagt, dass eine der beiden Anwendungen auf Zielrechner vorhanden ist.)

Nun bieten der Acrobat, wie auch der Reader einem Entwickler durchaus die Möglichkeit, gegen ihre Dlls zu linken und so auf dem “offiziellen” Weg auf ihre Dienste zurückzugreifen. Da ist zunächst einmal nichts geheimnisvolles dabei. Adobe bietet zudem eine relativ umfangreiche Dokumentation der Objektmodelle des Acrobats (die sogar ein kleines Kapitel über die Anbindung an .NET beinhaltet). Der Haken hierbei ist jedoch, dass sich die Objektmodelle beider Anwendungen (und vermutlich sogar einzelner Versionen) voneinander unterscheiden. So muss bei der Erstellung der eigenen Anwendung berücksichtigt werden, welche Acrobat (Reader)-Version später auf dem Zielrechner vorgefunden wird. Jeder normale Mensch würde jetzt vermutlich erstmal an eine späte Bindung denke, um diese Unterscheidung zur Laufzeit vornehmen zu können. Das Problem hierbei ist allerdings, dass die Namen von Klassen und Methoden der installierten Acrobat-Version dann spätestens zur Laufzeit bekannt sein müssen. Sicher kriegt man das irgendwie hin – es geht aber auch einfacher.

Es gibt nämlich ausser unserer Anwendung auf dem Zielrechner noch jemanden, der das gleiche Problem hat: die Shell, resp. der Explorer. Über das Kontextmenü kann ein Dokument ausgewählt und ein Druckauftrag angestossen werden. Die Shell muss also auch wissen, welche Anwendung sich für einen bestimmten Dateityp verantwortlich fühlt, ohne einzelne Versionen berücksichtigen zu müssen. Zu diesem Zweck sind in Windows (genau genommen in der Registry) bekanntlich Dateitypen mit Anwendungen verknüpft. In .NET kann man nun genau diese Verknüpfung ausnutzen. Dazu erstellt man eine Instanz von Process aus dem Namespace System.Diagnostics. Parametriert wird diese Instanz über eine Property StartInfo. Zu diesen Parametern gehört nicht nur der Dateiname der zu öffnenden Datei, sondern auch ein sogenanntes Verb. Verbs sind die Befehle, welche die Shell an eine Anwendung beim Start (per COM, DDE oder als Kommandozeilenparameter) überträgt. Die Anwendung auf der Gegenseite sollte natürlich im Stande sein, dieses Verb zu verstehen. Windows ist hier lediglich bei der Übetragung involviert. Ein Verb wie print wird allerdings im Allgemeinen bei der Installation einer Anwendung angelegt.

Versucht man nun, aus dem Anwendungscode heraus über die Shell ein pdf Dokument auszudrucken, tritt das Problem auf, dass eine Instanz des Acrobat (Readers) in der Taskleiste erhalten bleibt. Über Process.StartInfo kann ihr das abgewöhnt werden. Der folgende Code ermöglicht es, ein pdf auszudrucken und beendet anschließend den Acrobat Reader.

Process proc = new Process ();
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.Verb = “print”;
proc.StartInfo.FileName = “C:\\test.pdf”;
proc.Start();
proc.WaitForExit(10000);
proc.CloseMainWindow();
proc.Close();

Sollen mehrere Dokumente hintereinander gedruckt werden, ist hier eine kleine Optimierung möglich: Verzichtet man auf die Beendigung des Prozesses, bleibt die Instanz des Acrobat auch nach dem Druckauftrag noch (minimiert) erhalten. Bei weiteren Druckaufträgen wird dann keine neue Instanz gebildet, sondern das Verb an diese übertragen. Man spart dadurch die Zeit für den Programmstart des Acrobat.

Tags:, , ,

Bookmarken bei... del.icio.us:PDFs in C# unter Windows ausdrucken furl:PDFs in C# unter Windows ausdrucken Y!:PDFs in C# unter Windows ausdrucken

Automatisierung von Excel mit C#

27. August 2007 2 Kommentare »

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:

  1. Die .NET-Anwendung muss gegen die entsprechende “Excel-DLL” linken
  2. 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).
  3. Es wird eine Instanz von Excel.Application erzeugt. Über diese Instanz können Workbooks erzeugt oder geladen werden.
  4. 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.

Tags:, , ,

Bookmarken bei... del.icio.us:Automatisierung von Excel mit C# furl:Automatisierung von Excel mit C# Y!:Automatisierung von Excel mit C#