Thomas Wölfers Baustatik-Blog

Thomas Wölfers Baustatik Blog

Note to self...


Nachdem ich es nun ungefähr zum hundertsten mal rausgesucht habe.... Blockauswahl im VS geht mit ALT+Maus.


Wie man im VS 2005 eigenen Kommandos Bitmaps zuweist


Ich habe nun nur etwa 3 Jahre gebraucht um drauf zu kommen... vielleicht hilft es ja an anderer Stelle auch weiter :-)

Wenn man das Visual Studio per Macro um einen eigenen Befehl erweitert, dann erscheint der zunächst als NameOfModule.NameOfHabeIchVergessen.NameOfFunction im Menü bzw. der Werkzeugleiste: Jedenfalls als ein etwas hässlicher FQN des Kommandos. Wenn man nun will, das statt dessen ein Bitmap angezeigt wird, dann ist das _gaaaanz_ einfach, zumindest, wenn man drauf kommt.

Es geht so:

1.) Den "Customize" Dialog öffnen. (z.B. per Rechtsklick auf die Werkzeugleiste.

2.) Dann bei geöffnetem Dialog das Kommando anklicken, das man optisch aufpeppen will.

3.) Was wiederum dazu führt, das der "Modify Selection" Drop-Down Button enabled wird - und man z.b. einen anderen Button auswählen kann.


Probleme mit Deployment-Projekten im Visual Studio


Im Visual Studio (2005) gibt es eine Projektart vom Typ "Deployment" - damit kann man "ganz einfache" Installationsprogramme auf Basis des Windows Installers erzeugen. Nachdem wir zwar ein sehr großes Projekt haben, dessen Installation aber im wesentlichen eigentlich auch per XCopy durchgeführt werden könnte, nehmen wir keines der großen Installer-Pakete her, sondern verwenden einfach das im Studio eingebaute.

Der einzige Luxus der einem dabei geboten wird, sind "Custom Setup Actions": Man kann eine (so man will, managed) DLL produzieren die verschiedene Eintrittspunkte für Installationsereignisse wie "Install", "Uninstall" oder "Rollback" hat. Man muss nicht alle implementieren - und wir benutzen auch nur einen davon: Beim installieren der Software überprüft der "Custom" Step, ob sich an der Stelle auf dem Quellmedium, an der sich auch der Installer befindet, eine Lizenzdatei befindet. Ist das der Fall, dann wird die Datei in einen Ordner des Zielsystems kopiert.

Soweit, so gut - und eigentlich völlig klar. Wenn es da nicht ein Problem wäre: Hat man bereits eine Programmversion installiert und versucht dann, eine neue zu installieren - dann schlägt das fehl. Oder um genau zu sein: Die Installation der Folgeversion klappt auch, aber wenn man dann versucht selbige zu starten, dann startet statt dessen der Windows Installer und bietet an, die Installation zu reparieren.

Ich habe eine ganze Menge Stunden gebraucht um dahinter zu kommen, was genau passiert - denn wenn man vor der Installation der "Nachfolgeversion" die "Vorgängerversion" manuell deinstalliert, dann klappt alles.

Nun arbeite ich genauso mit dem Windows Installer wie jeder andere lokale Installer-Guru, nur eben ohne jegliches Fachwissen: Die im folgenden geschilderten Vorgänge können also stimmen oder nicht - ich habe sie mir in der Tat einfach nur so "zusammengereimt".

Folgendes passiert: Wenn eine Nachfolgeversion installiert wird und die passende Eigenschaft gesetzt ist (was bei mir der Fall war), dann deinstalliert der Windows Installer zunächst die Vorgängerversion und installiert dann die Nachfolgeversion. Das geschieht aber in einem Programmlauf.

Wenn man nun eine CustomInstallerStep.dll verwendet, dann muss diese DLL zum deinstallieren vom Installer geladen werden, damit der "Uninstall" Step daraus durchgeführt werden kann. Handelt es sich um eine "managed code" DLL, dann kann der Installer diese DLL aber nicht mehr entladen, nachdem der Custom Step durchgeführt wurde. Grund: Die Assembly der DLL ist in den vom Installer verwendeten CLR Raum geladen - und von dort kann man Assemblies nicht entladen. Sie verschwinden erst, wenn auch der Prozess der die CLR nutzt verschwindet.

Nun soll die "alte" DLL aber im Zuge der Deinstallation entfernt werden. Das geht aber nicht, weil Sie noch geladen ist. Dem Installer-Prozess bleibt also nichts anderes übrig, als sich zu "merken", das diese DLL "ganz am Ende" noch gelöscht werden muss. (Ich vermute, das der Installer dazu in zwei Prozesse aufgeteilt ist, wobei der "innere" die CLR nutzt, der äußere aber nicht.).

Ist nun die Deinstallation vollzogen, dann befindet sich die CustomStep DLL weiterhin ungelöscht auf der Platte. Es folgt nun die Installation der neuen Programmversion. Ich vermute, das in deren Zuge die "alte" DLL von der neuen überschrieben wird. (Auch stark geraten: Glaube, das man nicht löschen, aber sehr wohl überschreiben kann.). Danach ist dann die Installation vollständig, und die benutzte CLR wird vom Installer entladen. (Oder aber, der "innere" Installer wird beendet und in diesem Zuge wird die CLR entladen - der Effekt ist aber der gleiche.).

Nachdem die CLR jetzt nicht mehr geladen ist, ist auch die Custom Step DLL nicht mehr blockiert - und also wird sie nun im allerletzten Schritt der Installation gelöscht. Ergebnis: Die Installation hat zwar "funktioniert" - sieht aber mangels einer fehlenden Datei nicht so aus, wie sie aussehen sollte. Genau darum bemängelt der Installer die Installation dann später.

Wie gesagt: Alles ein wenig geraten - wenn mir jemand besser erklären kann, wie das zusammenhängt: Immer her mit der Erklärung :-)

Achso - es gibt eine Lösung: Man markiert die CustomStep DLL im Setup-Projekt so, das sie eben nicht mit deinstalliert wird: dann bleibt zwar beim deinstallieren ein wenig "Schmutz" auf der Platte zurück - dafür kann man aber überhaupt eine Deinstallation durchführen.


Lob an den SourceGear Support


Ich habe ja schon ein paarmal erwähnt, das ich SourceGear Vault super finde. Läuft prima und performant, und auch der Support hat überzeugt. Jetzt hatte ich in den letzten Tagen ein Problem mit unserem Webserver, auf dem,ohne das ein User angemeldet ist, der Vault Client die "aktuellen" Versionen der Dokumentation zur Baustatik abholt und veröffentlicht. Das Problem war: Das Script zum abholen lief zwar prima wenn man angemeldet war, aber eben nicht, wenn niemand angemeldet war - was dummerweise der Normalzustand ist.

Ich war definitiv nicht in der Lage herauszufinden, woran es lag: Nun habe ich gerade eine etwa 45 Minuten lange Online-Session (per Microsoft Shared View) mit dem Support von SourceGear hinter mir - und die Sache läuft jetzt. Bin echt beeindruckt.


SourceGear Vault: Jetzt schneller


Ich hatte vor kurzem darüber berichtet, das wir von SourceSafe auf Vault umgestiegen sind. Mir war aufgefallen, das ein automatisches Checkout innerhalb von VS2005 doch relativ lange dauerte - beim mir etwa 20 Sekunden.

Laut dem SourceGear Support tritt dieses Problem wohl nur in größeren Solutions bzw. Projekten auf, und das trifft auf die Baustatik wohl zu: Die Solution enthält zur Zeit knapp 70 Projekte und etwa 10.000 Dateien.

Seit heute gibts ein Update für den integrierten Client, und der ist in der Tat dramatisch viel schneller: Statt 20 Sekunden dauert es nun weniger als 1 Sekunden. Prima! Das Update umfasst einen Client-Only Installer und kann zusammen mit einem 4.1.x Server eingesetzt werden. Zu haben ist es zur Zeit hier, aber wer lieber auf etwas "offizielleres" warten soll: In der nächsten Woche soll es das offizielle Release geben.

Update:  Es gibt ein neues Download hier. Darin ist nun auch das hinzufügen von Dateien zu einem Projekt dramatisch viel schneller geworden.


Action und Converter: Generic Delegates


Sie stammen nicht aus dem "neuen" Visual Studio 2008 Paket, sondern es gibt sie schon seit .Net 2.0 und dem Visual Studio 2005: Die beiden Generic Delegates "Action" und "Converter". Und obwohl sie nun schon einige Jahre auf dem Buckel haben, habe ich es bis zu dieser Woche geschafft, die beiden vollkommen zu übersehen. Nachdem ich annehme das ich da nicht der einzige bin, ist das Grund genug, beide kurz unter die Lupe zu nehmen.

Das Action Generic Delegate

Die Dokumentation sagt dazu lapidar "Encapsulates a method that takes a single parameter and does not return a value.". Das ist schön. Die Frage ist aber: Wozu soll das gut sein?

Ein ganz typischer Fall ist der, das man eine Sammlung von Objekten hat, und für jedes dieser Objekte einen Vorgang durchführen möchte. Gängigerweise tut man dann folgendes:

foreach( TypeOfObject obj in sammlung)
{
   DoSomethingWithObject( obj );
}

Weil man das so oft braucht, hat List<T> ein eigenes ForEach. Hat man beispielsweise eine List<string>, kann man auch folgendes Konstrukt verwenden:

list.ForEach( DoSomethingWithString );  // (DoSomethingWithString ist eine Methode die einen string als Parameter bekommt.

List<T> verwendet als Parameter den Action-Delegate. Das ganze geht dann auch anonym, also ohne explizit eine DoSomethingWithString( string s) Methode zu implementieren:

list.ForEach( delegate( string s)
{
   Console.WriteLine( s );
});

Nun muss man sich ein etwas umfangreicheres Konstrukt vorstellen: Man hat eine Sammlung an Objekten und muss für alle Objekte mehrere Aufgabe durchführen: Die Aufgaben sind im wesentlichen gleich, unterscheiden sich aber durch einen bestimmten Vorgang. Auch hier hilft das Action-Delegate.

void foo1()
{
   Action<Control> showNameDelegate = delegate(Control c) { MessageBox.Show(c.Name); };
   DoWork(showNameDelegate,
this.Controls);
}

void foo2()
{
   Action<Control> showSizeDelegate = delegate(Control c) { MessageBox.Show(c.Size.ToString()); };
   DoWork(showSizeDelegate,
this.Controls);
}

private void DoWork(Action<Control> action, System.Windows.Forms.Control.ControlCollection controls)
{
   foreach (Control control in controls)
   {
      // hier wird "allgemeingltige" arbeit mit dem
      // control durchgefhrt.

      action(control);

      // hier wird "allgemeingltige" arbeit mit dem
     // control durchgefhrt.
  
}
}

Man kann dadurch also die "allgemeingültige" Arbeit mit einer Methode versorgen, die die unterschiedlichen Aspekte übernimmt.

Das Converter Generic Delegate

Hier geht es um folgendes: Das Delegate bekommt ein Objekt von einem Typ und liefert eines von einem anderen Typ. Angenommen man hat eine Methode zum anzeigen einer Form, die, bevor sie die Form anzeigt, viele aufwendige Dinge mit der Form tut. (Im Beispiel ist das nur das Verändern des FormBorderStyle, aber die Sache kann ja auch aufwendiger werden.)

Nun will man aber im einen Fall die Form Modal anzeigen, im anderen nicht. Wird Sie modal angezeigt, muss das DialogResult geliefert werden: Die Form wird also effektiv in ein DialogResult "umgewandelt". Im nicht-modalen Fall passiert das auch, nur ist das DialogResult dann "None".

Mit dem Converter Delegate ist das kein Problem:

        private void button1_Click_1(object sender, EventArgs e)
        {
            Converter<Form, DialogResult> showFormDelegate = delegate(Form f) { return f.ShowDialog(); };

            DoShowForm(showFormDelegate, new Form1());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Converter<Form, DialogResult> showFormDelegate = delegate(Form f) { f.Show(); return DialogResult.None; };

            DoShowForm(showFormDelegate, new Form1());
        }


        private DialogResult DoShowForm(Converter<Form, DialogResult> showFormDelegate, Form f)
        {
            f.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            // ... sowie andere taetigkeiten

            return showFormDelegate(f);
        }



 


Sourcegear vault: Erste Eindrücke


Ich arbeite nun seit einigen Tagen mit Vault, dem RCS von SourceGear. Soweit lief das alles ganz gut (bis auf ein Installationsproblem bei einem Entwickler) - und ich bin mit der Performance des Programms mehr als zufrieden. Es gibt allerdings eine Stelle die mich stört: Ein automatisches Checkout in der VS 2005 IDE dauert doch recht lange. Allerdings ist der Support von SourceGear sehr flott und kompetent - und hat mir kurzerhand mitgeteilt, das sie dieses Problem bei "großen" Solutions gehen: Nächste Woche gibts dafür ein Update.

Bin gespannt.


2 Kurzinfos zu Visual Studio 2008


Wer sich auch die Frage gestellt hat: Ja - man kann es parallel zu VS 2005 installieren.

Nicht so schön: Wenn man LINQ verwenden will, dann muss man auch das .NET Framework 3.5 verwenden. Das hätte mich nicht weiter gestört (auch wenn das Redistributable dafür ganz schön umfangreich ist) - aber die 3.5 läuft nicht mehr wie die 2.0 auf "älteren" Windows-Versionen: Nur XP, 2003 und spätere werden unterstützt.


Von SourceSafe nach Vault


Als Revision Control System haben wir in den letzten Jahren Source Safe eingesetzt. Nun ist mir bewusst, das Source Safe keinen besonders guten Ruf hat - ich kann aber wirklich nicht sagen, das wir damit irgend ein Problem gehabt hätten. (Bei der Umstellung tauchte dann zum ersten mal eines auf - aber zuvor hat die Sache für uns eigentlich recht gut funktioniert.)

Das Setup sah dabei bisher so aus, das die Oberhausener und die Münchner Niederlassung per VPN verbunden waren: Dazwischen gab es (unter anderem) eine 2MBit Standleitung, durch die die Oberhausener "durch" mussten, denn die Daten selbst waren auf einem Server in München. Das sollte anders werden, damit auch bessere Geschwindkeiten als 2MBit erzielt werden können. Ziel: Das RCS sollte auf einen Server im Internet, der mindestens mit 100 MBit erreichbar ist.

Da fing das Problem an: Bei Source Safe gibt es zwar die Möglichkeit auf die Daten per HTTP zuzugreifen - allerdings nur in sehr eingeschränktem Umfang: So kann man per HTTP beispielsweise keine Dateien löschen. Es musste also ein anderes RCS her. Ich habe mir verschiedene angesehen (und ja: Ich weiss auch das es Subversion gibt) und mich letztlich für Vault von SourceGear entschieden.

Ein RCS-Wechsel ist eine heikle Sache: Da drin befindet sich schließlich die Arbeit der letzten Jahre und zwar mit allen Änderungen: Diese Daten will man nicht verlieren. Um eines vorweg zu nehmen: Das hat mit Vault auch im großen und ganzen geklappt.

Der Wechsel zu Vault

Um den Wechsel durchführen zu können, müssen die Daten in Source Safe zunächst einmal so sauber wie möglich vorliegen: Das heist, man wirft zunächst das Analyse Tool von Source Safe an, und behandelt alle auftretenden Probleme. Im meinem Fall hat das Tool in den letzten Jahren noch nie einen Fehler gefunden, und so hatte ich auch nicht gerade jetzt damit gerechnet. Es gab aber einen, und der wurde auch nicht vom durch analyze vorgeschlagenen Vorgehen beseitigt: Eine Datei wollte einfach nicht mehr repariert werden können. Folgende Fehlermeldung trat auf:

There is a diff chain size mismatch in file 'overview.xml' (tugbaaaa) at version 7 (versions earlier than that version can no longer be retrieved from the database).

Sollte sonst mal wer über dieses Problem stolpern: Man kann es mit dem ssarc Tool beseitigen. Dieser KB-Artikel erklärt wie das geht.

Danach fand ich aber noch ein anderes Problem: Es gab Dateien die ausgecheckt waren - die aber schon seit Jahren nicht mehr Teil des Projektes waren. Es gab sie also eigentlich nicht mehr, Source Safe dachte aber, sie seien nur ausgecheckt. Ärgerlich. (Das wird in Zukunft nicht mehr passieren, da unser neuer automatischer Build-Server sowas finden und "bemängeln" wird.)

So ein Problem kann man aber auch mit einem Kommandozeilen-Tool von SourceSafe beseitigen: Man führt für die betroffenen Dateien ein Undo-Checkout unter dem SS-Account desjenigen aus, von dem SourceSafe glaubt, er oder sie hätte die Datei ausgecheckt.

Die Installation von Vault ist an sich kein besonderes Problem: Alles was man vorher braucht ist ein SQL Server (die Express-Variante tut es auch) und ein laufender IIS mit ASP.Net.

Dann kommt der spannende Teil: Das Importieren der Daten. Von SourceGear gibt es dazu ein VSSImport-Tool. Das muss auf dem Rechner installiert werden, der zuvor der SS Server war. Dann zeigt man dem Tool den Pfad auf die ss.ini und gibt noch an, wo der neue Vault Server installiert wurde.

Das Import-Tool durchläuft dann zwei Phasen: Eine "Pre-Scan" Phase sowie die eigentliche Konvertierung. Die Pre-Scan Phase ist relativ schnell durchlaufen - und wenn sie das ist, gibt es eine erste Angabe, wie lange denn der Import dauern würde: In Fall unseres "größten" Projektes (mit etwa 10.000 Files) schätzte das Import-Werkzeug den Zeitaufwand auf 22 Stunden. So lange hats dann nicht gedauert - aber 12 Stunden waren es locker. Wer nicht nur alle Änderungen sondern auch "Labels" importieren will muss sich in Geduld üben: Bei einem Projekt dessen Import nur ca. 10 Minuten gedauert hat, habe ich den "Label-Import" mal ausprobiert, aber nach etwa 5 Stunden abgebrochen. Beim großen Projekt habe ich es dann gar nicht erst versucht.

Ist die Sache importiert zeigt das Tool (möglicherweise: bei mir in einem von 3 Fällen) eine Liste an Fehlermeldungen an: Unter Umständen konnten einige Dateien nicht hochgeladen werden, oder es gab vielleicht andere Probleme: Die untersucht man besser von Hand und beseitigt sie. Danach hat man eine "funktionierende" Datenbank - man kann also ein "Get Latest" in ein frisches Verzeichnis machen, und ein Build starten.

Abgesehen von ein paar Dingen die ich für Anfangs/Umstiegsschwierigkeiten halte bin ich von Vault bisher rundrum begeistert: Ein "GetLatest" ist dramatisch viel schneller als bei SourceSafe und die Daten liegen obendrein im SQL Server vor - das macht auch das sichern deutlich einfacher.

Wer es selbst mal ausprobieren will: Die Single-User Version von Vault ist kostenlos.