Archive for the ‘.NET’ Category

Erstellung eines AddIns für Word 2007 – Teil 4 (Erweiterung des Ribbons)

Wednesday, September 3rd, 2008

Hallo,

in diesem posting beschreibe ich, wie man dem Word Ribbon eine eigene RibbonGroup hinzufügt, mit dem man die AddIn-spezifische Funktionalität aufrufen kann.

Dazu muss man dem Projekt ein neues Objekt vom Typ “Multifunktionsleiste (Visueller Designer)” hinzufügen:

Add Ribbon 1

oclwordaddin_addribbon2.png

Der visuelle Designer hat gewisse funktionale Einschränkungen gegenüber der Erstellung einer XML-Datei zur Ribbon-Erweiterung aber für unsere Zwecke ist er völlig ausreichend und wir nutzen gerne den erheblichen Produktivitätsvorteil.

Der Designer zeigt uns nun das neue RibbonTab an. Wir setzen noch sein label ( “OCL” – der Anhang “(Integriert)” wird von Visual Studio hinzugefügt und ist nur im design mode sichtbar) und das label der automatisch erstellten RibbonGroup (”Check”):

Ribbon editieren

Projekt erstellen, Starten, und tatsächlich, unser RibbonTab wird angezeigt:

Leeres OCL Ribbon in Word

Jetzt wollen wir die buttons zum Laden einer XMI-Datei und zum anstoßen der Prüfung aller im Dokument enthaltenen OCL-Ausdrücke hinzufügen. VSTO stellt in der Toolbox den eigenen Abschnitt “Steuerelemente für Office-Multifunktionsleiste” bereit, aus dem wir uns bedienen können.

Wie gewohnt lassen sich die buttons mittels drag and drop dem ribbon hinzufügen, konfigurieren und mit einem Handler für das ‘Click’-Ereignis versehen.

An diesem Punkt stellt sich die erste Design-Frage. Die Eventhandler in der von Visual Studio erzeugten OfficeRibbon-Klasse werden zwar gerufen, aber die dabei implizit erzeugte Instanz hat keine Verbindung zu der ebenfalls impliziten Instanz der eigentlichen AddIn-Klasse “ThisAddin”. Es stellt sich also die Frage, wer die diversen events und Informationen orchestriert und wie events und Informationen zwischen den verschiedenen Instanzen ausgetauscht werden.

Hier gibt es sicherlich verschiedenste Wege, hier der den ich gewählt habe:

  • Die Orchestrierung soll durch die Klasse ThisAddIn erfolgen. Diese Klasse hat bereits Zugriff auf das Word-Objektmodell und bietet sich auch als zentrale Steuerklasse an.
  • Um Informationen von der OfficeRibbon-Instanz zur This AddIn-Instanz zu transportieren, werden der OfficeRibbon-Klasse mehrere events hinzugefügt. Weiterhin wird eine statische Property angelegt, die Zugriff auf die Instanz ermöglicht. Damit kann die ThisAddIn-Klasse in ihrer Initialisierung die nötigen eventhandler anmelden.
  • Diese Vorgehen hat den Nachteil, dass man sich von der zeitlichen Reihenfolge der Konstruktion und Initialisierung abhängig macht. Konkret geht man davon aus, dass im ThisAddIn_Startup()-Ereignis die OfficeRibbon-Instanz bereits erzeugt wurde. Es scheint mir legitim, anzunehmen dass alle Objekte die Visual Studio implizit erzeugt, vor dem Aufruf des ersten events konstruiert wurden.

Damit sieht die Initialisierungsroutine des AddIns so aus:

private void ThisAddIn_Startup(object sender, System.EventArgs e)
 {
     Application.DocumentOpen += new Microsoft.Office.Interop.Word.ApplicationEvents4_DocumentOpenEventHandler(application_DocumentOpen);
     ribbonTabOcl.Instance.OnLoadXmi += this.OnLoadXmi;
     ribbonTabOcl.Instance.OnCheckAllExpressions += this.OnCheckAllExpressions;
     ribbonTabOcl.Instance.OnDisplayResultTaskPane += this.OnDisplayResultTaskPane;
     ConfigureLog4Net();
}

Viele Grüße,
Andreas

Technorati Tags: , ,

C# Compiler Kauderwelsch

Tuesday, May 13th, 2008

Hallo,

ich hatte mich ja kürzlich über die Qualität des Microsoft C#-compilers positiv geäußert.

Leider habe ich nun auch etwas schlechtere Erfahrungen gemacht. Folgender code

using System;
using System.Collections.Generic;
using System.Text;
 
namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
           List aList = new List();
           foreach (string str in aList)
           {
                Console.WriteLine(str);
           }
 
           string str = "Hallo";
    }
}
}

erzeugt folgende Fehlermeldung:


Fehler 1 Eine lokale Variable mit dem Namen "str" kann in diesem Bereich nicht deklariert werden, weil dadurch "str" eine andere Bedeutung erhalten würde, was bereits im Bereich "untergeordnet" in anderer Bedeutung verwendet wird. c:\Temp\ConsoleApplication1\Program.cs 18 20 ConsoleApplication1

Alles klar?

Man könnte nun annehmen, dass der C#-Compiler anders als ein ANSI-konformer C++ Compiler den scope der ersten Deklaration von str nicht auf die foreach-Schleife beschränkt. Dann müsste man aber bei der Zuweisung an str nach der foreach-Schleife einfach die Deklaration weglassen können, was aber nicht der Fall ist.

Fehler 1 Der Name "str" ist im aktuellen Kontext nicht vorhanden. c:\Temp\ConsoleApplication1\Program.cs 18 13 ConsoleApplication1

Dieser Forumsbeitrag legt nahe, dass dieses Verhalten Absicht ist, die Dokumentation auf der MSDN website beschreibt aber einen wesentlich nachvollziehbareren Fall. Meiner Meinung nach ist das ein klarer Compiler-bug.

Liebe Leser, was meinen Sie dazu?

Viele Grüsse,
Andreas

Technorati Tags: ,

Microsoft 70-536 .NET Framework – Application Development Foundation bestanden

Thursday, May 8th, 2008

Hallo,

am 06.05.08 habe ich den Test 70-536 bestanden, den ersten Schritt auf dem Weg zu angestrebten Zertifizierung als Microsoft Certified Professional Developer.

Wie erwartet war der Test recht schwer, da viele Fragen eine detaillierte Kenntnis verschiedener APIs (d.h. z.B. Methodennamen und Position von Parametern) voraussetzten – Informationen die normalerweise die IDE bereitstellt.

Trotzdem hilft dieser Test einen gewissen Überblick über Bereiche des .NET Frameworks zu gewinnen, mit denen man sich normalerweise nicht beschäftigen würde, z.B. verschiedene kryptographische Anwendungen.

Viele Grüsse,
Andreas

Technorati Tags: , ,

Erstellung eines AddIns für Word 2007 – Teil 3 (Erstellung des AddIns)

Wednesday, April 16th, 2008

Hallo,

dieses posting ist der dritte Artikel in einer Serie von Artikeln die die Erstellung eines AddIns für Microsoft Word 2007 mit C# beschreiben.

In diesem posting werde ich die eigentliche Erstellung des AddIns als Projekt in Visual Studio 2008 beschreiben.

Als Vorbereitung habe ich den Artikel Setting Up Development Environments for the 2007 Microsoft Office System auf der MSDN website gelesen. Diese Dokument bezieht sich in weiten Teilen auf die Installation der Entwicklungsumgebung, was bei Verwendung von Visual Studio 2008 nicht relevant ist. Visual Studio 2008 bringt alle erforderlichen Komponenten mit.

Interessant waren die Hinweise zur Erweiterung des Ribbons und auf das Konzept “document level AddIn” und “application level AddIn“. Ein “application level AddIn” ist ein AddIn welches bereits beim Laden der Anwendung – ohne dass ein Dokument geladen wurde – aktiv ist. Der in dem screen shot weiter unten gewählte Projekttyp erstellt ein application lavel AddIn (erst möglich mit VSTO 3.0, enthalten in Visual Studio 2008).

Beim Anlegen des Projektes muss der passende Typ ausgewählt werden:

Projekt erstellen, breakpoints in ThisAddIn_Startup() und ThisAddIn_Shutdown() gesetzt und F5 (Debuggen) gedrückt. Wie erwartet startet Word und nach dem Start wird der breakpoint in der ThisAddIn_Startup() Methode getroffen. Nach beenden von Word wird der breakpoint in ThisAddIn_Shutdown() getroffen. Damit ist Teilaufgabe 1 – Erstellung des AddIns – erledigt.

Da wir mit jedem Word-Dokument ein eigenes XMI-file assoziieren wollen, ist es nötig das Öffnen, schließen und Neuanlegen eines Dokumentes zu überwachen. Diese event handler werden wir später hinzufügen.

Im nächsten Teil wird es spannender, dort wollen wir das Ribbon erweitern um in unserem AddIn spezifische Funktionalität aufzurufen.

Viele Grüße,
Andreas

Erstellung eines AddIns für Word 2007 – Teil 2 (zu lösende Probleme)

Saturday, April 12th, 2008

Hallo,

dieses posting ist das zweite in einer Serie von postings über die Erstellung eines AddIns für Microsoft Word 2007. In Teil 1 habe ich die Hintergründe und Anforderungen an das AddIn beschrieben.

In diesem posting möchte ich nun eine genauere Vorstellung gewinnen, welche Probleme gelöst werden müssen.

Erstellung des AddIns

Dazu zählt das Erstellen des Projektes in Visual Studio 2008 (also mit VSTO 3.0), das starten von Word und das Abfangen zentraler Ereignisse. Damit wäre der Rahmen für die Programmierung und das Debuggen des AddIns geschaffen.

Erweiterung des Word Ribbons

Zweiter Schritt wäre die Erweiterung des Word-Ribbons. Es soll ein neues RibbonTab mit zwei buttons hinzugefügt werden. Die buttons haben die Funktionalitäten “Load XMI-file” und “Check OCL fragments”. Breakpoints in den Ereignishandlern dieser buttons verifizieren den korrekten Anschluss an das GUI. Auf diesen Punkt bin ich besonders neugierig, verspricht Visual Studio 2008 doch einen visuellen Designer zur Erweiterung des Ribbons.

Finden der OCL-Fragmente

Hierzu muss auf das Word-Objektmodell zugegriffen werden. Ich gehe davon aus, dass es eine relativ einfache Aufgabe ist, alle Fragmente eines Dokumentes zu finden, die eine bestimmte Formatvorlage haben. Diese Funktionalität ist ja bereits in der normalen, interaktiven Suche enthalten.

Aktivierung von log4net

Die verwendeten Komponenten benutzen log4net und es wäre sinnvoll, wenn das AddIn das Schreiben der logs unterstützen würde.

Implementierung der Prüfung der OCL-Ausdrücke

Diese Aufgabe ist unabhängig von Word 2007 zu lösen. Eine derartige Funktionalität wurde bereits mehrfach implementiert und ist mehr oder weniger trivial in dem AddIn einzubinden (unter Verwendung der bestehenden Komponenten, versteht sich).

Präsentation der Ergebnisse

In einer ersten Version werden die Ergebnisse lediglich in einem separaten Dialogfenster, welches das AddIn bereitstellt, angezeigt. Ergonomischer – aber auch aufwendiger – wäre die Darstellung in einer Word Task Pane.

Persistenz des Namens des XMI-files

Hierzu müsste das AddIn dem Dokument eigene Daten hinzufügen. Ich gehe davon aus, dass eine solche Funktionalität existiert und dass sie relativ einfach zu nutzen ist.

Deployment

Hierzu zählt das Erstellen einer einfachen Dokumentation und eines Setup-Programms.

Im nächsten posting werden ich die Erstellung des AddIns beschreiben.

Viele Grüße,
Andreas

Technorati Tags: , ,

Erstellung eines AddIns für Word 2007 – Teil 1 (Anforderungen)

Saturday, April 5th, 2008

Hallo,

die Entwicklung von sog. “Office Based Applications” – d.h. Lösungen für Geschäftsprobleme die durch Erweiterung der Microsoft Office Programme erstellt werden – scheint derzeit groß in Mode zu sein. Die “Visual Studio Tools for Office” (VSTO) versprechen, die Erstellung von AddIns für Microsofts Office Anwendungen einfacher zu machen und dabei die gesamte Bandbreite .NET basierter Frameworks zu Verfügung zu stellen. Diese neuen technischen Möglichkeiten möchte ich gerne näher erkunden und werde dazu eine kleine, aber praxisnahe Anwendung als AddIn für Word 2007 erstellen. Dieses Projekt wird das Thema meiner nächsten blog-postings sein.

Thema

Da der Aufwand sicher nicht ganz gering sein wird, wäre es schön wenn das Ergebnis auch einen praktischen Nutzen hätte. Andererseits möchte ich mich im Rahmen dieser Blog-Postings nicht mit der Lösung eines Geschäftsproblems beschäftigen sondern mit den Möglichkeiten der Erweiterung und Integration von bzw. mit Microsoft Word.

Deshalb wird die angestrebte Lösung eine Prüfung von OCL-Fragmenten auf syntaktische Korrektheit und Typkonformität implementieren. Falls ein solches AddIn eine gewisse Reife erreichen würde, könnte ich es als kostenlosen download auf meiner Firmenwebsite anbieten und damit neue Besucher gewinnen und evtl. Feedback und Diskussionspartner finden. Die komplexe Funktionalität ist für andere Entwicklungsprojekte bereits implementiert und kann ohne großen Aufwand wiederverwendet werden.

Die Verwendung von OCL erlaubt es, Geschäftsregeln formal zu definieren, bevorzugt im Kontext eines UML-modells.

Anforderungsdefinition

Die genauen Anforderungen definieren wir folgendermaßen:

  • Zu einem Word-Dokument kann ein UML-modell als XMI-Datei geladen werden. Mit jedem geladenen Dokument wird eine eigene XMI-Datei assoziiert.
  • In dem Word-Dokument können OCL-Fragmente definiert werden. Diese werden mit einer speziellen Formatvorlage formatiert. Diese Formatvorlage dient sowohl zur visuellen Gestaltung des OCL codes als auch zur Erkennung des codes durch das AddIn.
  • Dem Word-Ribbon wird ein neues RibbonTab hinzugefügt, das die Funktionalität des AddIns bereitstellt.
  • Diese besteht aus zwei buttons, einem zum laden des assoziierten XMI-files, einen zum Auslösen des checks aller OCL-Fragmente im gesamten Dokument. Nach einem click auf diesen button wird das gesamte Dokument nach OCL-Fragmenten durchsucht und diese im Kontext des durch das XMI-file definierten Modells geprüft.
  • Die Ergebnisse werden in einem separaten Dialogfenster angezeigt.

Die oben genannten Anforderungen sind die Minimalanforderungen, die in jedem Fall implementiert sein müssen, damit das AddIn einen gewissen praktischen Nutzen hat.

Folgende weitere Anforderungen wären sinnvoll, sind aber eventuell zu aufwendig (”nice to have”):

  • Beim Klick mit der rechten Maustaste in ein als OCL-Fragement formatierten Text erscheint ein Kontextmenü, mit dem eine Prüfung nur für dieses Fragment angestoßen werden kann. Alternativ kann auch im Ribbon ein button “Check current fragment” zur Verfügung gestellt werden.
  • Die Ergebnisse der Prüfung sollten als eigenes Fenster in der Word-Anwendung (”Task pane”) erscheinen. Damit können die Fehler bearbeitet werden und die Prüfungsergebnisse bleiben weiterhin verfügbar. Weiterhin wäre es sinnvoll, wenn man direkt von einer Fehlermeldung an die fehlerhafte Stelle im Dokument navigieren könnte.
  • Das gewählte XMI-file wird dauerhaft mit dem Dokument assoziiert, d.h. beim nächsten Laden des Dokumentes lädt das AddIn automatisch die XMI-Datei. Hierzu müsste dem Dokument eine “user defined property” mit dem entsprechenden Dateinamen/Pfad hinzugefügt werden.

Ressourcen:

Visual Studio Tools for Office Developer Portal (Microsoft)

Microsoft VSTO Forum

Im nächsten posting werde ich die konkreten zu lösenden Problem definieren.

Viele Grüße,
Andreas

Technorati Tags: , ,

Unreachable code in C# for loop – intelligenter compiler

Thursday, March 20th, 2008

Hallo,

kürzlich hat mich Microsofts C# compiler mit seiner intelligenten Analyse verblüfft. Ich hatte sinngemäß folgenden code geschrieben:

for (int jj = 0; jj < parts.Length - 1; ++jj)
{
    part = parts[jj];
    int count = result.getRefCount(refAttr);
    for (int ii = 0; ii < count; ++ii)
    {
        EOInstance packagedElement = result.getRefObject(refAttr, ii);
        if (packagedElement.getValue("Name") == part)
        {
            result = packagedElement;
            continue;
        }

    }
    string msg = "Cannot resolve path '" + parts.ToString() +
                     "' for attribute '" + refAttr + ". Unknown part is '" +
                     parts + "'.";

    throw (new Exceptions.UnknownPathException(msg));
}

Der C# compiler meldete:

Fehler CS0162: Warnung als Fehler: Unerreichbarer Code wurde entdeckt.

und die IDE markierte die Inkrement-Anweisung ‘++jj in der äußeren for-Schleife.

Nach konzentrierter Durchsicht des codes ist mit der Fehler aufgefallen: die throw Anweisung sollte in die innere Schleife verschoben werden. So wie der code geschrieben war, wurde die äußere Schleife tatsächlich nur einmal durchlaufen und die Inkrement-Anweisung nie ausgeführt da immer am Ende des ersten Durchlaufs eine exception geworfen wurde.

Der C++ compiler von Visual Studio findet dieses Problem nicht, auch nicht im release build (wo der compiler weitergehende semantische Prüfungen machen kann unter Ausnutzung der Analyse-Ergebnisse des optimizers).

UnreachableCode - 0 Fehler, 0 Warnung(en)
==== Erstellen: 1 erfolgreich, Fehler bei 0, 0 aktuell, 0 übersprungen ====

Bleibt noch die Frage, was PC-Lint (das ich seit mehr als 10 Jahren für alle C++-Entwicklungen verwende) zu der Sache meint.
Enttäuschenderweise meldet es diesen Fehler auch nicht, nicht mal mit zwei Durchläufen (-passes(2)):

PC-lint for C/C++ (NT) Vers. 8.00w, Copyright Gimpel Software 1985-2007

--- Module:   UnreachableCode.cpp (C++)

/// Start of Pass 2 ///

--- Module:   UnreachableCode.cpp (C++)

--- Global Wrap-up

error 900: (Note -- Successful completion, 0 messages produced)

Für diese Test habe ich folgenden mutmaßlich äquivalenten code verwendet:

        for (int jj = 0; jj < 10; ++jj)
	{
		for (int ii = 0; ii < 10; ++ii)
		{
			if (ii == 5)
			{
				continue;
			}
		}

		throw(std::exception("Huh"));
	}

Also wirklich eine beeindruckende Leistung des C# compilers.

Der code enthält übrigens noch einen weiteren logischen Fehler, auf den ich erst durch die Meldung des C# compilers aufmerksam wurde, was aber für die Intention dieses postings nicht relevant ist.

Viele Grüße,
Andreas

Technorati Tags: , , ,

Nachtrag vom 04.05.2008: ein kluger Kollege hat mich darauf hingewiesen, dass Microsoft Visual C++ bei Verwendung von warning level 4 den gewünschten Hinweis tatsächlich meldet. Für meine Produktionssoftware verwende ich /W4, für das kleine Beispielprogramm habe ich es aber bei den defaults belassen.

Versionsinformation in managed C++ assemblies

Thursday, February 14th, 2008

Guten Tag,

kürzlich ist mir aufgefallen, dass in einer managed C++ assembly die in Visual Studio erzeugt wurde keine Versionsinformation im klassischen Sinne vorliegt. D.h. tools die die Versionsinformation einer DLL auswerten – wie z.B. Programme die Setups erzeugen – finden keine Versionsinformation und auch der Windows Explorer zeigt keine Versionsinformation an. Dies ist etwas irritierend, da das Projekt ja eine Datei AssemblyInfo.cpp enthält, die genau die Informationen beinhaltet, die man als Versionsinformation betrachten würde. Bei einer C# assembly klappt es ja auch! Dort wird der Inhalt von AssemblyInfo.cpp wie erwartet von allen tools als Versionsinformation erkannt.

Wie man z.B. hier lesen kann wird die Versionsinformation als resource in einem bestimmten Format in der DLL erwartet. In einer managed C++ assembly wird eine solche resource nicht automatisch erzeugt!
Die Information aus AssemblyInfo.cpp wird lediglich für die .NET-interne Verwaltung von assemblies benutzt.

Als Lösung bleibt lediglich selbst eine Versions-resource hinzuzufügen. Dies geht einfach über das Kontexmenü für die assembly: Einfügen/Resource dann im erscheinenden Dialog “Resource hinzufügen” als Typ “Version” auswählen.

Das eigentlich Problem bei dieser Vorgehensweise ist dass die Version nun redundant in der assembly vorhanden ist und es dem Entwickler obliegt die Information konsistent zu halten. Glücklicherweise liegen beide Quellen als normale Textfiles vor so dass man ein kleines Skript oder Programms schreiben kann, dass als “Präbuildereignis”gestartet werden kann und bei Bedarf eine der beiden Dateien aus der anderen aktualisiert (sinnvollerweise sollte man AssemblyInfo.cpp als “master” verwenden da dort noch weitere Information eingetragen ist).
Es wäre interessant zu wissen, ob Microsoft für diese Entscheidung Gründe hatte oder ob das Erzeugen der Versions-resource lediglich vergessen wurde.

Viele Grüße,
Andreas