Destruktor nicht gerufen in mixed mode C++ Anwendungen mit Visual C++ 2008

February 9th, 2009

Hallo,

in C++ gibt es ja das Resource Aquisition Is Initialization (RAII) pattern. Der Kern dieses Patterns besteht darin, dass ein Objekt bereits bei der Konstruktion die Ownership für eine Resource übernimmt und diese Resource bei seiner Zerstörung wieder frei gibt. Man wendet dieses pattern an, um Resourcen wie allokierten Speicher oder file handles sicher freizugeben. Dabei mancht man sich zu nutze, dass in C++ der Destruktor eines Objektes deterministisch aufgerufen wird, z. B. auch im Falle eine exception.

Dieses pattern ist sehr wichtig, um zuverlässige und robuste Software in C++ zu schreiben und ich nutze es regelmäßig. Nun hatte ich vor einiger Zeit in einem der Unittests in der Software, die ich für mein Unternehmen EmPowerTec schreibe, einen seltsamen Fehler in einem Unittest. Nach einer längeren Debugging-Session sah es so aus, als würden die Destruktoren für Objekte die in einer Methode auf dem Stack angelegt waren, beim Verlassen der Methode nicht mehr gerufen. Ein solches Verhalten führt mit Sicherheit zu Fehlern, wenn das RAII Pattern genutzt wird. Zunächst war ich darüber verblüfft, da ich mir nicht vorstellen konnte, dass ein derartig fundamentales Verhalten vom Microsoft C++ Compiler nicht richtig implementiert war. Tatsächlich war das aber der Fall, wie in folgendem Knowledge Base Eintrag beschrieben:

http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/8324a598-ad05-40e1-a271-6f64ce3b6008

Wie man dem Thread entnehmen kann, tritt das Problem auf, wenn man von einer C++/CLI Library eine native C++ library aufruft und diese mit inkompatiblen Einstellungen für das Exception handling übersetzt hat.

Und die Moral von der Geschicht,
Trau’ keinem Compiler nicht,

Unittests sollst Du machen,
Sonst hast Du nichts zu lachen

Viele Grüße,
Andreas

Technorati Tags: ,

.NET Assembly-Ladevorgang loggen

January 26th, 2009

Hallo,

kürzlich hatte ich folgende Situation:

In einer C#-Solution wurde eine in Managed C++ erstellte .NET Assembly referenziert. Sobald die C#-Anwendung eine Klasse aus der referenzierten Library benutzen wollte, gab es eine Exception der Form:


System.IO.FileNotFoundException: Die Datei oder Assembly "etocl, Version=1.0.3306.20332, Culture=de, PublicKeyToken=null" oder eine Abhängigkeit davon wurde nicht gefunden. Das System kann die angegebene Datei nicht finden.
Dateiname: "etocl, Version=1.0.3306.20332, Culture=de, PublicKeyToken=null"

Die referenzierte Assembly fand sich aber wie es sein sollte im Arbeitsverzeichnis des C#-Programms. Um ein Protokoll des Ladevorgangs zu bekommen kann man nun folgenden Registry-Eintrag anlegen:


[HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) = 1

Danach enthält die Fehlermeldung eine Aufstellung der Pfade, die der Loader durchsucht hat:


LOG: Download von neuem URL file:///K:/Develop/et/etroot/src/apps/etoclide/bin/Debug/de/etocl.DLL.
LOG: Download von neuem URL file:///K:/Develop/et/etroot/src/apps/etoclide/bin/Debug/de/etocl/etocl.DLL.
LOG: Download von neuem URL file:///K:/Develop/et/etroot/src/apps/etoclide/bin/Debug/de/etocl.EXE.
LOG: Download von neuem URL file:///K:/Develop/et/etroot/src/apps/etoclide/bin/Debug/de/etocl/etocl.EXE.

und das Problem wird schnell sichtbar: der loader sucht im Unterverzeichnis “de”. Mit diesem Hinweis ausgerüstet sieht man schnell, dass das AssemblyInfo.cpp-file der referenzierten Library einen expliziten Eintrag für das locale enthielt:


[assembly:AssemblyCultureAttribute("de")]

Nach Entfernung dieses Eintrags und erneuter Erstellung der library  funktionierte dann auch alles wieder.

Fairerweise muss ich sagen, dass bereits die ursprüngliche Meldung einen Hinweis auf das Problem enthielt, allerdings nicht so deutlich:


System.IO.FileNotFoundException: Die Datei oder Assembly "etocl, Version=1.0.3306.20332, Culture=de, PublicKeyToken=null" oder eine Abhängigkeit davon wurde nicht gefunden. Das System kann die angegebene Datei nicht finden.
Dateiname: "etocl, Version=1.0.3306.20332, Culture=de, PublicKeyToken=null"

Microsoft empfiehlt, den Registry-Eintrag wieder zu löschen da er zu einer Performance-Einbusse führen könnte.

Viele Grüße,
Andreas

Technorati Tags: ,

Bessere UML Shapes für Visio

January 18th, 2009

Hallo,

wer Microsoft Visio für die Erstellung von UML-Diagrammen nutzt kann sich mal diese Shapes auf softwarestencils.com anschauen. Sie sind angeblich besser als die mit Visio mitgelieferten Shapes.

Viele Grüße,
Andreas

Technorati Tags: ,

Gemeine Entwickler…

November 23rd, 2008

gibt es wohl auf entwicklerGemein.de.

Liebe Leute, man muss nicht jede Mode mitmachen.
(Über die Qualität dieser site will ich hier ausdrücklich keine Aussage machen).

Viele Grüße,
Andreas

C#/.NET ist populärer als Java

November 19th, 2008

zumindest wenn man den tags auf stackoverflow.com glaubt.

Erstaunt hat mich auch die Popularität von Python im Vergleich zu Perl.

Viele Grüße,
Andreas

Erstellung eines AddIns für Word 2007 – Teil 6 (Anzeige der Ergebnisse in einem CustomTaskPane)

November 16th, 2008

Hallo,

im letzten Teil dieser Serie habe ich beschrieben wie man bestimmte Fragmente eines Dokumentes anhand der Formatvorlage (engl. “style”) findet. Eine genaue Beschreibung der Überprüfung der damit gefunden OCL-Fragmente ist im Rahmen von postings über Microsoft Office Programmierung weniger interessant.

Relevanter wird es wieder bei der Frage nach der Darstellung der Ergebnisse.

Hierfür gibt es in den von mir verwendeten OCL libraries bereits ein UserControl “ErrorList”, das im Rahmen des Office-AddIns wiederverwendet werden soll. Dieses control stellt Fehler tabellarisch dar und signalisiert einen Doppelklick auf einen Fehlereintrag mit einem event:

Um ein solches control mit einem Dokument zu assoziieren bietet sich eine CustomTaskPane an.

Eine solche CustomTaskPane ist ein container für ein UserControl der gleichzeitig zu dem Dokument angezeigt wird:

CustomTaskPane mit UserControl in Word 2007 Dokument

Die CustomTaskPane kann vom Anwender per drag and drop an jeder Seite des Dokumentes gedockt werden. Bei Bedarf kann man davon auch per event benachrichtigt werden um z.B. das Layout im gehosteten UserControl anzupassen.

Unser AddIn gibt dem Benutzer die Möglichkeit die CustomTaskPane mit den Ergebnisse des OCL checks unsichtbar oder sichtbar zu machen (mit der “Display check results” checkbox im “OCL” tab des ribbons – siehe obiges Bild).

Dadurch wird das handling nicht völlig trivial, weshalb ich im folgenden einen möglichen Ansatz für den Umgang mit der CustomTaskPane vorschlagen will. Eine Überlegung besteht darin, dass die Steuerung der Sichtbarkeit nicht in den Eventhandlern des ribbons erfolgen sollte, sondern in der Klasse “ThisAddIn” die die Steuerung des AddIns zentral implementieren sollte.
Um dies zu erreichen, wurde für die generierte Ribbon-Klasse ein neuer event “OnDisplayResultTaskPane” eingeführt. Falls nun die checkbox “Display check result” geclickt wird, erzeugt das ribbon lediglich einen entsprechenden event. Die ThisAddIn-Klasse meldet sich für diesen event an und reagiert dann entsprechend indem sie die CustomTaskPane sichtbar oder unsichtbar macht.

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    ....
    ribbonTabOcl.Instance.OnDisplayResultTaskPane += this.OnDisplayResultTaskPane;
    ....
}

Das Erzeugen der CustomTaskPane ist so implementiert:

private ErrorList CreateCustomTaskPane()
{
    if (this.CustomTaskPanes.Contains(m_checkResultsTaskPane))
    {
        this.CustomTaskPanes.Remove(m_checkResultsTaskPane);
    }
 
    ErrorList el = new ErrorList();
 
    el.OnErrorSelected += this.OnErrorSelected;
    m_checkResultsTaskPane = this.CustomTaskPanes.Add(el, "OCL check results");
    m_checkResultsTaskPane.DockPosition =                            Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionBottom;
    m_checkResultsTaskPane.VisibleChanged += new EventHandler(m_checkResultsTaskPane_VisibleChanged);
 
    return (el);
}

In Zeile 11 wird das CustomTaskPane-Objekt erzeugt. Im Konstruktor wird dabei das UserControl übergeben, das in der CustomTaskPane dargestellt werden soll. Es empfiehlt sich, das UserControl an den Aufrufer zurückzugeben, damit dieser dort Eventhandler registrieren kann und properties des UserControls setzen oder lesen kann.

Ich habe die Beobachtung gemacht, dass das managed CustomTaskPane-Objekt gelegentlich aufgrund der Benutzerinteraktionen sein natives Objekt “verliert” ohne das man dies durch einen event gemeldet bekommt. In einem solchen Fall gäbe es eine Exception beim Zugriff auf das CustomTaskPane-Objekt. Deshalb wird die Methode CreateCustomTaskPane() nach jedem click auf den “Check all expressions” button aufgerufen, obwohl dies streng genommen nicht nötig wäre.

Ein Schliessen der CustomTaskPane durch klicken auf ihren close button zerstört das Objekt nicht. In einem solchen Fall wird ein VisibleChanged event generiert den man benutzen kann um das GUI synchron zu halten (in unserem Fall die “Display check results” checkbox zu deselektieren).

Insgesamt bieten die CustomTaskPanes eine elegante Möglichkeit, beliebige controls mit einem Word-Dokument zu assoziieren.

Viele Grüße,
Andreas

Technorati Tags: , , ,

Interessantes UML Statement

October 27th, 2008

Hallo,

ein nicht namentlich bekannter Softwareentwickler (vielleicht Kishore Kumar?) hat einige lesenwerte Artikel zum Thema UML auf seinem blog veröffentlicht. Die Artikel sind nicht nur sehr liebevoll geschrieben, mit vielen Bildern und Zitaten, sondern scheinen mir auch sehr profund und nachvollziehbar.

Einige seiner Aussagen lauten:

  • Die UML ist ein wertvolles Mittel um Softwaresysteme zu beschreiben
  • Die MDA ist vom Konzept her Unsinn, weil man die eigentliche Kodierung immer brauchen wird. Ein Modell kann lediglich einen Routenplan ersetzen aber gehen (= kodieren) muss man immer noch selbst.
  • Es ist wichtig, dass Diagramme optisch gut gestaltet sind, da man sie sonst kaum versteht. Um dies zu erreichen ist ein geeignetes UML-tool eine wichtige Voraussetzung.
  • Ein gegebenes Diagramm sollte nur einen einzigen Aspekt beschreiben.
  • UML Diagramme sollten den Regeln der UML Spezifikation genügen, diese zu lernen ist jedoch nicht leicht.
  • Um gut modellieren zu können braucht man also Erfahrung und Ausdauer beim lernen.

In diesem Artikel spricht er einen Effekt an, den ich selbst sehr gut bestätigen kann. Unabhängig von dem dauerhaften Wert der Modelle schafft allein der Akt des Modellierens ein besseres Verständnis des Systems. Man denkt eigentlich automatische tiefer und weiter als wenn man zu früh mit dem Kodieren anfängt.

Viele Grüße,
Andreas

Technorati Tags: ,

ClickOnce-Installation und der Fehler “Datei…wurde seit ihrer ersten Veröffentlichung geändert” Fehler

October 20th, 2008

Hallo,

bei einer ClickOnce-Installation einer Microsoft .NET Software kann bei der Erstellung des setup-Programmes in Visual Studio eine Option gewählt werden, die bewirkt dass vorausgesetzte Software (”redistributable prerequisites” – wie etwas das .NET framework selbst) automatisch als Teil der Installation geladen und installiert werden.

Dabei kann angegeben werden, ob die Komponenten von der Website des Hersteller oder von einer anderen Quelle geladen werden sollen.

Erforderliche Komponenten für ClickOnce Installation Dialog in Visual Studio

Im ersteren Fall kann folgendes Fehlerszenario entstehen:

Der Entwickler hat eines der vorausgesetzten Softwarepakete (z.B. das deutsche language pack vstor_lp_de_30.exe für die VSTO 3.0 runtime) lokal installiert. Beim Erstellen des setup-Programmes wird dieses Paket nicht integriert, wohl aber Information über die verwendete Version.

Nun wird die Installation auf einem beliebigen anderen Rechner gestartet. Der installer sieht dass auf der Website des Komponentenherstellers (also Microsoft für obiges Beispiel) eine andere Version der Komponente verfügbar ist. Genau dann wird diese Fehlermeldung generiert:

Error: Die Datei “C:\DOKUME~1\awa\LOKALE~1\Temp\VSD6.tmp\VSTOR30\vstor_lp_de_30.exe” wurde seit ihrer ersten Veröffentlichung geändert.

Die lokal auf dem Entwicklungsrechner mit Visual Studio 2008 SP1 installierte Version war vom 27.12.2007 während die Version auf der Microsoft Website vom 12.08.2008 war.

Dieses Verhalten ist ziemlich problematisch, kann doch ein update auf der Website des Komponentenherstellers grundsätzlich zu einem beliebigen Zeitpunkt erfolgen. Danach müssten alle ClickOnce-setups, die die aktualisierte Komponente benötigen, aktualisiert werden.

Es wäre sinnvoll, zumindest kleinere Abhängigkeiten direkt in das setup zu integrieren. Leider kann das Verhalten nicht für einzelne Abhängigkeiten individuell eingestellt werden, so dass in diesem Fall auch die .NET runtime in das setup integriert werden müsste, was nicht wirklich attraktiv ist.

Falls prerequisites ausgeschlossen werden bricht die Installation ohne konkrete Fehlermeldung ab, wenn die prerquisites nicht vorhanden sind bzw. nicht in der richtigen Version vorhanden sind.

Insgesamt würde ich empfehlen ausser den .NET runtimes alle erforderlichen prerequistes dem setup mit der Option “Erforderliche Komponenten vom demselben Speicherort wie die Anwendung herunterladen” hinzuzufügen. Die Installationsanleitung muss dann einen deutlichen Hinweis auf die erforderliche .NET runtime bzw. weitere nicht eingeschlossene prerequistes beinhalten.

Vielen Dank an Joe Wirtley für den Hinweis auf die root cause.

Viele Grüße,
Andreas

Technorati Tags: ,

Erstellung eines AddIns für Word 2007 – Teil 5 (Finden der OCL-Fragmente)

September 27th, 2008

Hallo,

wie erwartet war dieser Teil einfach zu lösen, da es im Internet genügend Beispiele gibt, wie man mit dem Word-Objektmodell arbeitet. Die OCL-Fragmente werden anhand ihrer Vorlage identifiziert. Die Vorlage muss “OCL” heißen, kann ansonsten aber beliebig gestaltet werden. Die Verwendung eines fonst mit fester Zeichenbreite (”fixed width”) empfiehlt sich.

Ein wenig mehr Zeit als nötig habe ich benötigt, weil mir anfangs nicht klar war, dass der englische Begriff für “Formatvorlage” “Style” ist und nicht “Format”.

Bei der Umsetzung merkt man, dass die Kompatibilität zu Visual Basic for Applications (VBA) kein Designziel für C# war. Ein komfortabler Umgang mit den Office-Objektmodellen setzt einige features voraus, die Visual Basic.NET hat, C# aber nicht (z.B. default parameter). Deshalb sollte man grössere Anbindungen von Office-Objektmodellen in eigenen Visual Basic.NET assemblies erstellen. Für unser kleines Word-AddIn spielt das aber keine Rolle, da wir nur minimal auf die Word-Objekte zugreifen.

Um die OCL-Fragement zu finden, wird das Word “Find”-Objekt benutzt:

IList<TextFragment> FindFragmentsByStyle(Word.Document doc, Object styleName)
{
            List<TextFragment> result = new List<TextFragment>();
 
            Object start = 0;
            Object end = doc.Characters.Count;
            Word.Range range = doc.Range(ref start, ref end);
            Word.Find find = range.Find;
 
            Object style = null;
 
            try
            {
                style = doc.Styles.get_Item(ref styleName);
            }
 
            catch (Exception)
            {
                /*
                 * Style not found.
                 */
                return (result);
            }
 
            find.set_Style(ref style);
            ExecuteFind(find);
            while (find.Found)
            {
                string text = range.Text;
                int linenum = GetRelativeLineNum(doc, range);
                int pagenum =     (int)range.get_Information(Microsoft.Office.Interop.Word.WdInformation.wdActiveEndPageNumber);
 
                result.Add(new TextFragment(text, linenum, pagenum));
                ExecuteFind(find);
            }
 
            return (result);
}

Die Methode ExecuteFind() enthält einige low level Details beim Suchen nach dem nächsten Treffer:

private bool ExecuteFind(Word.Find find, Object wrapFind, Object forwardFind)
{
            // Simple wrapper around Find.Execute:
            Object forward = forwardFind;
            Object wrap = wrapFind;
 
            return find.Execute(ref missing, ref missing, ref missing,
              ref missing, ref missing, ref missing,
              ref forward, ref wrap, ref missing, ref missing, ref missing,
              ref missing, ref missing, ref missing,
              ref missing);
}

Als Ergebnis gibt die Methode FindFragmentsByStyle() eine Liste von Objekten der (selbst erstellen) Klasse TextFragment zurück. Diese Klasse speichert den eigentlichen Text und Informationen zur Position des Textes im Dokument (Seiten- und Zeilennummer).

Viele Grüße,
Andreas

Technorati Tags: , ,

stackoverflow.com: die beste online-Resource für Softwareentwickler?

September 24th, 2008

Hallo,

wer wie ich seinen Berufseinstieg in der Softwareentwicklung noch ohne Internet + Google gemacht hat, ist vielleicht immer noch dankbar wenn er nach 15 Minuten “googeln” eine Antwort auf ein Problem gefunden hat, das ihn früher vielleicht Tage gekostet hätte. Die meisten relevanten Treffer finden sich in Foren oder speziellen “Experten-sites” wie etwa Experts Exchange.

Nun tritt einen neue website an, um das teilen von Wissen zur Softwareentwicklung noch effizienter zu machen: stackoverflow.com.

Hinter der Seite steht unter anderem Joel Spolky, einer der einflussreichsten Köpfe in der IT-Szene.  Auf seiner Website erklärt er, warum stackoverflow.com die beste derartige Resource  sein soll.

In der Tat hat die site einige Alleinstellungsmerkmale, die sie zu einem Erfolg machen könnten:

  • Durch die Möglichkeit Antworten zu bewerten werden die Antworten nach Qualität sortiert
  • Durch die Möglichkeit antworten zu editieren, werden die Antworten immer besser
  • Die Möglichkeit “Reputation” zu gewinnen, fördert die Bereitschaft zu der site beizutragen
  • Antworten können kommentiert werden, ähnlich wie in einem blog
  • Last but not least: es ist keine Registrierung erforderlich!

Das Konzept scheint aufzugehen wie man etwa an enthusiastischen Diskussionen in Foren und an der bereits nach kurzer Zeit sehr hohen Zahl von Fragen sieht.

Es lohnt sich auf jeden Fall mal einen Blick auf stackoverflow.com zu werfen. In den FAQs kann man sich am schnellsten einen Überblick verschaffen.

Viele Grüße,
Andreas