Unbegrenzte Möglichkeiten mit Visual Studio Add-ins

Veröffentlicht: 24. Mrz 2003 | Aktualisiert: 22. Jun 2004

Von Mirko Matytschak

Viele Entwickler vermissen bei der Arbeit mit C# oder VB.NET benutzerdefinierte Build Steps, wie sie für C++ möglich sind. Es gibt aber etwas wesentlich Besseres: Mit Hilfe von ein paar Mausklicks lassen sich Erweiterungen von Visual Studio erzeugen, die in C# oder VB.NET codiert sind und kaum Wünsche offen lassen.

Auf dieser Seite

 Möglichkeiten mit Add-ins
 Erzeugen eines Add-in-Rahmens
 Abfangen der Build-Events
 Speichern der Konfiguration
 Ausgaben ins Output-Fenster
 Debugging von Add-ins
 Laden von Add-ins
 Das Build Rules Add-in

In diesem Beitrag möchten wir zunächst anhand eines konkreten Projekts aufzeigen, welche Möglichkeiten Ihnen mit Add-ins zur Verfügung stehen. Dann werden wir das Prozedere beschreiben, wie Sie einen Add-in-Rahmen erzeugen, der die verschiedenen Ereignisse des Build-Prozesses abfängt. In diesen Rahmen können Sie dann Code einfügen, der bei bestimmten Ereignissen ausgeführt werden soll. Dann gehen wir auf ein Add-in ein, das Microsoft als Beispiel entwickelt hat: Das Build Rules Add-in. In diesem Add-in können Sie für eine Solution und jedes ihrer Teilprojekte Pre- und Post- Build Rules angeben. Wir beschreiben kurz, wie das Add-in funktioniert und geben dann einige Verbesserungsvorschläge.

Möglichkeiten mit Add-ins

Wie Sie bereits an der Einführung erkennen können, stehen Ihnen mit Add-ins praktisch alle Möglichkeiten offen. Mit Hilfe des Build Rules Add-ins können Sie alle Prozesse anstoßen, die als selbständige Exe-Dateien zur Verfügung stehen. Ausgaben, die diese Prozesse mit Console.WriteLine vornehmen, landen automatisch im Output-Fenster von Visual Studio. Alle Erweiterungen, die mit diesem Add-in nicht zu bewerkstelligen sind, können Sie leicht mit eigens angepassten Add-ins vornehmen. Um Ihnen einen Eindruck darüber zu verschaffen, was alles möglich ist, beschreiben wir kurz ein Produkt, das als Add-in realisiert worden ist.

NDO (.NET Data Objects) ist ein Tool, mit dem ganz normale Klassen persistent gemacht werden können. Die Abspeicherung der Informationen beruht auf ADO.NET. Alles, was der Entwickler tun muss, ist, seine Klassen mit Hilfe des Attributs NDOPersistent zu markieren. Beziehungen zu anderen persistenten Klassen werden mit Hilfe des Attributs NDORelation markiert.

Ein Add-in, der sogenannte Enhancer, fängt nun die Build Events ab und erweitert eine eben kompilierte Assembly um zusätzliche Funktionalität. Die als Persistent markierten Klassen implementieren nach diesem Vorgang ein zusätzliches Interface, das vom Persistenz-Framework angesprochen wird, um Daten aus den Objekten zu lesen bzw. neu angelegte Objekte mit Daten aus der Datenbank zu initialisieren.

Gleichzeitig legt der Enhancer eine XML-Datei an, in der Mapping-Informationen stehen. Diese Informationen können vom Entwickler angepasst werden, so dass Klassen an vorhandene Datenbestände gekoppelt werden können. Wer eine Anwendung von null an entwickelt, kann die automatisch generierten Sql-Scripts verwenden, um Datenbanken zu erstellen, die zu den Mapping-Informationen passen. Mit Hilfe einer Benutzeroberfläche lässt sich das Add-in konfigurieren. Im Standard-Fall ist der Enhancer deaktiviert, so dass normale Projekte nicht angetastet werden.

Doch damit nicht genug. Ein zweites Add-in, das zu NDO gehört, kümmert sich darum, für private Felder sogenannte Accessor-Properties zu generieren. Um nämlich die Kontrolle über die persistenten Felder halten zu können, müssen persistente Felder privat sein. Um auf diese Felder von außen zugreifen zu können, werden Properties benötigt. Das Add Accessor Add-in erzeugt im Tools-Menü einen neuen Menüpunkt. Alles, was der Entwickler nun tun muss, ist, den Cursor in eine Zeile zu setzen, in der ein privates Feld deklariert wird. Nach Aufruf des Menüpunkts steht unterhalb der Deklaration eine Property-Deklaration. Ist das Feld vom Typ IList und als Beziehung markiert, wird statt des Properties ein Funktionspaar mit Addxxx- und Removexxx-Funktionen erzeugt.

Diese kurze Beschreibung sollte reichen, um Ihnen einen Eindruck von der Leistungsfähigkeit der Add-ins zu vermitteln:

  • Einhängen eigenen Codes in den Build-Ablauf (Beispiel Enhancer)

  • Manipulation von Dateien, die im Build-Prozess verwendet werden (Beispiel Enhancer)

  • Erzeugen von Code innerhalb eines offenen Projektes (Beispiel Add Accessor Add-in)

  • Analysieren von Code mit dem sogenannten Code-DOM (Beispiel Add Accessor Add-in)

Mehr Info über NDO finden Sie unter www.NetDataObjects.de.

 

Erzeugen eines Add-in-Rahmens

Starten Sie Visual Studio und wählen Sie den Menüpunkt zur Erzeugung eines neuen Projekts. Es erscheint ein Dialog wie im Bild 1 dargestellt. Sie sehen in Bild 1, dass Sie den Projekttyp nicht wie gewohnt unter den Sprachen finden, sondern unter Other Projects | Extensibility Projects.

Visual Studio Add-ins haben eine gewisse Verwandtschaft mit Office-Add-ins. Sie können theoretisch Add-ins schreiben, die sowohl Office als auch Visual Studio erweitern - wie wäre es mit einer Rechtschreibung für C#-Programme, die dann gleich bei der Dokumentation eingesetzt werden kann?

BuildSteps_01

Bild 1: Anlegen eines neuen Add-in-Projekts

Aber Spaß beiseite: Am besten wählen Sie Visual Studio .NET Add-in. Dies führt zum Start eines Wizards, den wir im Folgenden kurz durchgehen:

  • Programmiersprache - hier wählen Sie die Sprache aus (Bild 2)

  • Hosts (VSMacros, Visual Studio .NET) - hier wählen Sie normalerweise VS.NET

  • Name & Description - Der Name wird als Namespace und als erster Teil der ProgrID in der Registry verwendet; der zweite Teil der ProgID ist immer Connect. Wenn Sie als Name "NDOEnhancer" angeben, dann erhalten Sie als ProgID NDOEnhancer.Connect.

  • Choose Add-in Options (Bild 3) - Hier ist eigentlich nur wichtig, dass Sie den ersten Punkt anwählen, wenn Sie einen Menüeintrag unter Tools haben wollen. Alle weiteren Einstellungen kann der Anwender des Add-ins selbst anpassen.

  • 'Help About' Information - Hier werden ggf. Informationen übernommen, die in der Registry unter Ihrem Firmennamen eingetragen sind.

BuildSteps_02

Bild 2: Auswahl der Programmiersprache

BuildSteps_03

Bild 3: Add-in-Optionen

Nach dem Klick auf Finish haben Sie eine Solution vorliegen, die nebst dem eigentlichen Add-in auch gleich noch ein Setup-Projekt enthält - sehr komfortabel. Wenn Sie das Add-in kompilieren, wird es gleich in der Registry eingetragen (jawohl, es ist ein COM-Objekt) und steht beim nächsten Start von Visual Studio zur Verfügung. Wenn Sie ausgewählt haben, dass ein Menüpunkt erzeugt werden soll, so finden Sie diesen Menüpunkt im Tools-Menü. Unter Tools | Add-in-Manager... können Sie das Startverhalten Ihres Add-ins konfigurieren.

Nun wollen wir dafür sorgen, dass etwas passiert, wenn der Menüpunkt ausgewählt wird. In Ihrem Add-in-Projekt befindet sich eine Datei Connect.cs, die den Code Ihres Add-ins enthält. Wenn Sie in der Datei zum Ende blättern, finden Sie eine Funktion mit dem Namen Exec. Diese Funktion sieht etwa so aus (C#-Version):

public void Exec(string commandName, EnvDTE.vsCommandExecOption executeOption,  
ref object varIn, ref object varOut, ref bool handled) 
{ 
 handled = false; 
 if(executeOption == EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault) 
 { 
  if(commandName == "NewBuildSteps.Connect.NewBuildSteps") 
  { 
   handled = true; 
   return; 
  } 
 } 
}

Vor der Zeile handled = true fügen Sie den Code ein, der ablaufen soll, wenn der Menüpunkt ausgewählt wurde.
Noch ein kurzes Wort zu dem verwendeten Symbol (Icon), das neben dem Menüpunkt auftaucht: Das Symbol stammt aus den Icons für Office. Wenn Sie es ändern wollen, müssen Sie in der Funktion OnConnection den Aufruf von AddNamedCommand ändern. Die Symbole sind durchnummeriert, per Voreinstellung wird die Nummer 59 - ein Smiley - gewählt. Mit Versuch und Irrtum können Sie andere Icons wählen. Wenn Sie nicht aufs Raten angewiesen sein wollen, müssen Sie folgendermaßen vorgehen:

  • Starten Sie Word für Windows

  • Klicken Sie mit der rechten Maustaste auf eine Symbolleiste und wählen Sie anpassen

  • Klicken Sie noch einmal mit der rechten Maustaste auf eins der Symbole in der Symbolleiste - wenn der Menüpunkt Symbol ändern verfügbar ist, wählen Sie diesen aus (Bild 4), sonst versuchen Sie es bei einem anderen Symbol.

Wie Sie in Bild 4 sehen, ist der Smiley das erste zur Verfügung stehende Symbol. Die Symbolnummern werden, beginnend mit 59, einfach hochgezählt. Der Wert 0 bedeutet, dass Sie kein Symbol benötigen.

BuildSteps_04

Bild 4: Anzeige der Symbole in Word

Natürlich werden Sie auch eigene Symbolleisten und selbst gezeichnete Symbole verwenden wollen. Dies ist möglich, wie das geht, wird aber erst in Version 2.0 dieses Artikels beschrieben... Spaß beiseite: Um den Rahmen dieses Artikels nicht zu sprengen, suchen Sie in der MSDN-Bibliothek unter den Stichworten AddCommandBar und PasteFace. Wenn Sie sich beim Namen der letzten Funktion an die Zwischenablage erinnert fühlen, dann liegen Sie richtig: Der einzige Weg, eigene Symbole in die IDE einzubringen, führt über die Zwischenablage.

 

Abfangen der Build-Events

Was nun noch für unseren Add-in-Rahmen fehlt, ist das Abfangen der Build Events. Zunächst beschreiben wir die Vorgehensweise in VB.NET, dann kommt C# dran:

  • Deklarieren Sie eine Variable vom Typ EnvDTE.BuildEvents:
Private WithEvents buildEvents As EnvDTE.BuildEvents
  • In OnConnection weisen Sie dieser Variablen einen Wert zu:
buildEvents = applicationObject.Events.BuildEvents
  • Nun können Sie die Handler-Funktionen schreiben, die folgendermaßen aussehen:
Private Sub buildEvents_OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, _ 
 ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildBegin 
 Dim globals As EnvDTE.Globals 
 If (Scope = EnvDTE.vsBuildScope.vsBuildScopeSolution) Then 
  RunSolutionRules(True) 
 End If 
End Sub 
Private Sub buildEvents_OnBuildDone(ByVal Scope As EnvDTE.vsBuildScope, _ 
 ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildDone 
 Dim globals As EnvDTE.Globals 
 If (Scope = EnvDTE.vsBuildScope.vsBuildScopeSolution) Then 
  RunSolutionRules(False) 
 End If 
End Sub 
Private Sub buildEvents_OnBuildProjConfigBegin(ByVal Project As String, _ 
 ByVal ProjectConfig As String, ByVal Platform As String, _ 
 ByVal SolutionConfig As String) Handles BuildEvents.OnBuildProjConfigBegin 
 RunProjectRules(Project, ProjectConfig, Platform, True) 
End Sub 
Private Sub buildEvents_OnBuildProjConfigDone(ByVal Project As String, _ 
 ByVal ProjectConfig As String, ByVal Platform As String, _ 
 ByVal SolutionConfig As String, ByVal Success As Boolean) _ 
 Handles BuildEvents.OnBuildProjConfigDone 
 RunProjectRules(Project, ProjectConfig, Platform, False) 
End Sub

Der Code spricht wohl für sich: Jeweils am Beginn und Ende eines Solution- bzw. Project Builds wird eine der vier Funktionen aufgerufen. Ob ein Build erfolgreich war, oder fehlgeschlagen ist, können Sie mit dem Parameter Success abfragen.

In C# sieht die Lösung etwas anders aus:

  • Variable deklarieren - hier
private EnvDTE.BuildEvents buildEvents;
  • Wert zuweisen
BuildEvents = applicationObject.Events.BuildEvents;
  • Handler zuweisen
buildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(  
 this.OnBuildBegin ); 
buildEvents.OnBuildDone += new _dispBuildEvents_OnBuildDoneEventHandler(  
 this.OnBuildDone ); 
buildEvents.OnBuildProjConfigBegin +=  
 new _dispBuildEvents_OnBuildProjConfigBeginEventHandler(   
 this.OnBuildProjConfigBegin ); 
m_buildEvents.OnBuildProjConfigDone  += new  
 _dispBuildEvents_OnBuildProjConfigDoneEventHandler( 
 this.onBuildProjConfigDone );

Die Handler-Funktionen in C# entsprechen den VB-Funktionen. Man übergibt sie als Delegates, die ebenso gut in eigenen Klassen deklariert werden können, was wir auch empfehlen würden. Damit das in VB funktioniert, muss allerdings die Schreibweise geändert werden. Statt Handles verwendet man das Schlüsselwort AddHandler:

AddHandler buildEvents.OnBuildBegin, AddressOf Me.buildEvents_OnBuildBegin

Diese Zeile entspricht exakt der Anweisung Handles BuildEvents.OnBuildBegin, nur dass nun eine Funktion aus einer anderen Klasse angegeben werden kann.
Die Funktionen RunProjectRules und RunSolutionRules erwarten je einen Boolean-Wert, der aussagt, ob ein Pre- oder Post-Build-Ereignis stattgefunden hat. Die Funktionen können Sie nun nach Gusto mit Code füllen.

 

Speichern der Konfiguration

Sehen wir uns einmal im Vorgriff das Build Rules Add-in an. Wenn man im Tools-Menü den Eintrag Solution Build Rules auswählt, erscheint ein Dialog (Bild 5), in dem man zwischen Pre- und PostBuild, sowie zwischen Solution und den einzelnen Projekten wählen kann. Drückt man auf Add, erscheint ein Fenster wie in Bild 6, in dem man einen Programmnamen und die Ausführungsparameter angeben kann. Mit Hilfe von Makro-Strings lassen sich hierbei die Solution- und Projektverzeichnisse angeben. So weit, so schön - aber wo sollen denn diese Informationen gespeichert werden, damit sie beim nächsten Start der Solution wieder zur Verfügung stehen? Die Antwort ist: Sie werden in den Konfigurationsdateien der Solution bzw. des Projekts gespeichert. Dafür sind im VS.NET Objektmodell Funktionen vorgesehen.

BuildSteps_05

Bild 5: Die Oberfläche des Build Rules Add-in

BuildSteps_06

Bild 6: Hinzufügen einer neuen Regel

Dazu dienen die Objekte vom Typ Solution und Project. Preisfrage: Wie kommt man an sie heran? Nach dem Erstellen eines neuen Projekts existiert in der Connect-Klasse eine Variable applicationObject vom Typ DTE. Dieser Typ hat ein Property Solution - das ist schon mal die halbe Miete. Die Solution wiederum hat ein Property Projects, das Sie mit For Each durchsuchen können. Die Elemente dieser Liste haben den Typ Project. Die Typen Project und Solution weisen jeweils ein Property Globals vom Typ Globals auf. Damit haben wir, was wir gesucht haben.

Die Globals verwalten Stringwerte, die Schlüsseln zugewiesen werden, ähnlich wie es in den guten alten Ini-Dateien von Windows der Fall war:

globals.VariableValue("Variablenname") = MyObject.ToString();

Wenn Sie nun in Ihrem Add-in eine Zuweisung nach obigem Muster vornehmen und den Wert nach dem Neustart von Visual Studio wieder auszulesen versuchen, geraten Sie ins Staunen: Der Wert wurde nicht abgespeichert. Es muss nämlich etwas dazu:

globals.VariablePersists("Variablenname") = True;

Man muss dem System explizit sagen, dass der Wert abgespeichert werden soll. Alles paletti? Mitnichten! Wenn Sie nämlich einen Wert überschreiben wollen, der schon einmal abgespeichert wurde, laufen Sie auf einen Fehler, weil es den Wert schon gibt. Sie dürfen VariablePersists nur dann auf True setzen, wenn die Bedingung

If globals.VariableExists("Variablenname") Then

False ergibt. Wurde die Variable einmal angelegt, reicht es, an VariableValue den neuen Wert zuzuweisen. Sie werden sicherlich elegante Möglichkeiten finden, diesen etwas umständlichen Mechanismus in Ihren Add-ins so kapseln, dass er einfacher zu handhaben ist - derweilen wenden wir uns schon einmal einem weiteren wichtigen Thema zu.

 

Ausgaben ins Output-Fenster

Add-ins können Ausgaben ins Output-Fenster schreiben. Das Output-Fenster hat mehrere Panes, im Normalfall mit dem Namen Build und Debug (oder haben Sie etwa eine deutsche Visual Studio-Version?). Weil unser Add-in in den Build-Prozess eingreift, wollen wir uns das Build-Pane besorgen:

Dim outputWindow As EnvDTE.OutputWindow 
Dim outputWindowBuildPane As EnvDTE.OutputWindowPane 
outputWindow = applicationObject.Windows.Item( _ 
 EnvDTE.Constants.vsWindowKindOutput).Object() 
outputWindowBuildPane = outputWindow.OutputWindowPanes.Item( _ 
 "{1BD8A850-02D1-11D1-BEE7-00A0C913D1F8}")

Wie wir auf diese Guid gekommen sind, ist eine abenteuerliche Geschichte. Weder in der MSDN-Dokumentation noch im gesamten Internet gibt es einen Artikel darüber. Aber das macht nichts, denn Sie können ja einfach ein kleines AddIn schreiben und sich einmal die Namen aller OutputWindowPanes zusammen mit den zugehörigen Guids auflisten lassen. Hier gleich mal zur Übung den Code, diesmal in C#:

foreach (OutputWindowPane op in outputWindow.OutputWindowPanes) 
 outputWindowBuildPane.OutputString(op.Name + " " +  
  op.Guid.ToString() + "\n");

Dieser Code ergibt folgende Ausgabe im Build-Pane:

Build {1BD8A850-02D1-11D1-BEE7-00A0C913D1F8} 
Test Run {3E29D95E-D9CE-4D0E-987C-A924F713D7EC} 
Database Output {58B84905-7074-11D3-8F3E-006097C9A866} 
Visio UML {BC5C26E6-DF2B-46C9-B74E-0E057228055D} 
Source Control {CBF36220-3F6F-11D1-8631-006008184050} 
Debug {FC076020-078A-11D1-A7DF-00A0C9110051}

Sie sehen, dass auf meinem System schon ein paar Add-ins laufen. Sie können die Output-Panes auch mit ihrem Namen indizieren, der Name unterscheidet sich jedoch in den verschiedenen Sprachversionen von Visual Studio.

Nun kommt noch ein unangenehmes Kapitel auf uns zu: Angenommen, in Ihrem Add-in tritt ein Fehler auf. Sie können nun zwar eine Fehlermeldung im Build-Pane ausgeben. Aber wenn der Build vor Ihrem Add-in fehlerfrei durchgelaufen ist, schreibt Visual Studio eine Summary-Information ins Output-Fenster - und zwar nach Ihrem Add-in, die anzeigt, dass alles paletti ist und es keine Fehler gegeben hat. Leider können die Add-ins das Resultat eines Build-Laufs nicht beeinflussen. Dies macht sich auch dann negativ bemerkbar, wenn mehrere Add-ins auf das gleiche Ereignis reagieren, aber nur laufen sollen, wenn der bisherige Build-Lauf fehlerfrei war. Angenommen im ersten Post-Build-Add-in geschieht ein Fehler. Das zweite Post-Build-Add-in läuft dann dennoch los, weil das erste Add-in keinen Einfluss auf den Parameter Success hat. Hier sollte das Erweiterungsmodell nachgebessert werden.

 

Debugging von Add-ins

Dieses Kapitel ist kurz und schmerzlos. Ihr Add-in ist eine DLL, das heißt, dass Sie fürs Debugging eine Host-Anwendung angeben müssen. Das tun Sie in den Projekt-Eigenschaften unter ConfigurationProperties | Debugging. Sie geben als Startverzeichnis „C:\Progamme\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe an“, sofern Sie Visual Studio auf der Platte C: installiert haben. Als Kommandozeilenparameter können Sie den Pfad zu einer Solution-Datei angeben. Wenn Sie nun in Ihrem Add-in-Projekt auf F5 drücken, startet eine neue Instanz von Visual Studio, die Sie mit der ersten Instanz debuggen können.

 

Laden von Add-ins

Normalerweise können Add-ins aktiviert werden, indem man unter Tools | Add-in Manager... das entsprechende Add-in anklickt. Es hat sich bei unseren Tests jedoch herausgestellt, dass komplexere Add-ins dann nicht richtig laufen - vor allem wenn sie sich in den Build-Prozess einklinken. Verzweifeln Sie also nicht, wenn Ihr Add-in sich nicht einmischen will, sondern aktivieren Sie das Add-in in der Spalte Startup des Add-in Managers. Starten Sie Visual Studio erneut - dann sollte das Add-in laufen. Bei der Entwicklung eines Add-ins müssen Sie darauf achten, dass die Visual Studio-Instanz, in der das Add-in entwickelt wird, das Add-in selbst nicht laden darf. Sonst gibt es einen Fehler, weil die Add-in-Dll nicht überschrieben werden darf. Innerhalb dieser Instanz von Visual Studio aktivieren Sie das Add-in ausschließlich für den Startup, wie es in Bild 7 für das AddAccessor-Add-in zu sehen ist. Damit wird das Add-in in einer neuen Instanz für das Debugging geladen, während es für die laufende Instanz nicht geladen ist.

Dann gibt es noch die Problemvariante, dass beim Laden von Visual Studio ein Add-in nicht gefunden wird. Im Projektverzeichnis wird eine Reg-Datei erzeugt, die ReCreateCommands.reg heißt. Diese Reg-Datei forciert beim nächsten Start von Visual Studio das Laden des Add-ins. Sollte dies nicht funktionieren, liegt es vielleicht an der Registrierung des Add-ins im Visual Studio. Diese lässt sich mit einer kleinen selbstgeschriebenen Reg-Datei erzeugen:

REGEDIT4 
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\7.0\AddIns\NewBuildSteps.Connect] 
"CommandPreload"=dword:1 
"LoadBehavior"=dword:1 
"FriendlyName"="New Build Steps" 
"Description"="New Build Steps V. 1.0"

Statt NewBuildSteps tragen Sie den Namen Ihres eigenen AddIns ein. Unter FriendlyName und Description geben Sie passende Daten an - FriendlyName erscheint übrigens im Add-in Manager als Name des Add-ins.

BuildSteps_07

Bild 7: Richtige Einstellung für das Debugging von AddAccessor

 

Das Build Rules Add-in

Nun kommen wir zum Beispiel-Add-in von Microsoft. Wie schon in den Bildern 5 und 6 gezeigt, lassen sich mit dem Add-in eigene Build Steps in den Build-Prozess integrieren. Das Add-in nutzt viel Funktionalität, so dass Sie sehen können, wie Add-ins programmiert werden. Unter anderem ist dieses Add-in die einzige mir bekannte Stelle auf der Welt, die einen Hinweis auf die Guids gibt, mit denen die Output-Panes unterschieden werden.

Der VB-Code des Add-ins ist leider etwas (hüstel) unübersichtlich, aber mit ein wenig Geduld findet man doch alle wichtigen Stellen. Das Add-in hat jedoch einen entscheidenden Nachteil. Wenn man Build Rules eingibt, dann sind die Parameter ja meist Dateinamen. Es wollte uns nun partout nicht gelingen, einen Kopiervorgang anzustoßen indem wir Program "cmd.exe" angaben und als Command Line den folgende String:

/c copy "$(SolutionFolder)\bin\debug\*.dll" "Zielverzeichnis mit Leerzeichen"

Nach langen Experimenten, die meist die Fehlermeldung "Syntaxfehler" verursachten, kamen wir dahinter, dass der Autor des Programms die beiden Strings unter Program und Command Line zu einem String zusammensetzt und ihn so abspeichert. Als Trennzeichen für die beiden Hälften des Strings verwendet er ein Anführungszeichen ("). Bei Ausführung benutzt er die Funktion Split, um den zusammengesetzten String wieder in Einzelstrings zu zerlegen... oh Schande! Denn welche Pfadangabe funktioniert heutzutage noch ohne Anführungsstriche? Ohne die Struktur des Programms anzutasten, haben wir als Trennzeichen zwei Doppelkreuze "##" angegeben - und schon waren die Syntaxfehler Vergangenheit (ja ich weiß - Sie verwenden in den meisten Ihrer Pfadnamen zwei Doppelkreuze...).

Ein weiterer Nachteil war, dass es zwar ein Makro $(SolutionFolder) gab, aber kein $(ProjectFolder). Auch konnte man zwar Build Rules anlegen und löschen, aber keine vorhandenen Build Rules abändern. An all diesen Stellen haben wir nachgebessert. Nun funktionieren die Build Rules, wie man es sich vorstellt.

Und denken Sie daran: Wenn das Build Rules Add-in einmal nicht ausreicht - das Visual Studio Extensibility Modell wartet darauf, von Ihnen erforscht zu werden.

Literatur
Les Smith, Writing Add-ins for Visual Studio .NET, Apress 2002, ISBN 1-59059-026-0