AddIns in SpeedCommander 11 (Teil 1)

Seit einigen Jahren wünschen sich die Anwender immer wieder eine Pluginschnittstelle im SpeedCommander. Bisher hatte ich mich immer etwas dagegen gesperrt, weil ich vom Pluginkonzept anderer Anwendungen nicht ganz überzeugt war. Ein Plugin muss sich in die Anwendung integrieren können, es muss Befehle zur Verfügung stellen und es muss auch die Möglichkeit bestehen, diese Befehle aus Menüs und Symbolleisten aufzurufen. Die Konfiguration sollte möglichst einfach gehalten werden und nicht über viele Dialoge verstreut sein.

Nach vielen Überlegungen bin ich auf das AddIn-Konzept von Microsoft gestoßen, das unter anderem in den Office-Produkten und in Visual Studio eingesetzt wird. Diese AddIns können mit jeder Sprache entwickelt werden, mit der sich COM-Objekte erzeugen lassen. Dies ist ein großer Vorteil gegenüber dem klassischen Dll-Konzept mit einem Satz von exportierten Funktionen, da sich auch mit Visual Basic und dem .NET-Framework COM-Objekte programmieren lassen. Beim klassischen Dll-Konzept bleiben Visual Basic und .NET nämlich außen vor.

Ein AddIn ist ein COM-Objekt und bietet verschiedene Schnittstellen an. Zwingend erforderlich ist lediglich die Schnittstelle IDTExtensibility2, welche aus fünf Methoden besteht:

  • OnConnection
  • OnStartupComplete
  • OnBeginShutdown
  • OnDisconnection
  • OnAddInsUpdate

OnConnection wird aufgerufen, wenn das AddIn geladen wird. Das AddIn erhält jeweils einen Zeiger auf das Application-Interface von SpeedCommander und auf das Interface des AddIn-Objektes. Dazu bekommt es noch die Information, wie das AddIn geladen wurde. Dies kann einerseits gleich beim Starten von SpeedCommander geschehen oder später durch nachträgliche Installation oder Aktivierung des AddIns. Über das Application-Interface kann das AddIn später auf das umfangreiche Objektmodell von SpeedCommander zugreifen und z.B. neue Befehle oder eigene Symbolleisten erzeugen.

Wenn SpeedCommander komplett initialisiert wurde, wird bei allen geladenen AddIns die Methode OnStartupComplete aufgerufen. Hier kann das AddIn Funktionen aufrufen, die bei der Initialisierung eine Interaktion mit dem Benutzer erfordern. OnBeginShutdown wird aufgerufen, bevor SpeedCommander beendet wird. Das AddIn kann an dieser Stelle z.B. seine Einstellungen speichern.

Bevor das AddIn entladen wird, ruft SpeedCommander die Methode OnDisconnection auf. Dem AddIn wird hier auch der Grund des Entladens mitgeteilt. Das AddIn kann somit erkennen, ob es z.B. vom Anwender deinstalliert wurde und somit seine Einstellungsdaten löschen kann.

Die fünfte Methode OnAddInsUpdate wird aufgerufen, wenn ein anderes AddIn geladen wird. Wenn z.B. die AddIns A und B bereits geladen sind und später das AddIn C geladen wird, dann ruft SpeedCommander die Methode OnAddInsUpdate von AddIn A und B auf. Ist z.B. ein AddIn abhängig von einem anderen, so kann das AddIn hier prüfen, ob das andere AddIn geladen oder entladen wird.

Aufbruch in das XML-Land

Das Speichern der Programmeinstellungen in der Registrationsdatenbank hat sich zwar mittlerweile durchgesetzt, doch für mobile Anwendungen entstehen damit auch einige Nachteile. Beim Start auf fremden Systemen sieht z.B. SpeedCommander so jungfräulich frisch aus wie nach einer Neuinstallation. Man kann als Parameter zwar einen vorher exportierten Registrationsauszug übergeben, den SpeedCommander beim Start in die hiesige Registrationsdatenbank einträgt und nach dem Beenden selbständig wieder löscht. Allerdings verfallen hier wieder alle während des Betriebes geänderten Einstellungen. Auch bei einer Neuinstallation von Windows bzw. dem Zurückspielen eines Images sind alle davor geänderten Einstellungen wieder weg.

Mit dem Update des Xtreme Toolkit Pro auf die Version 9.60 führte Codejock ein cleveres System zur Speicherung von Einstellungen ein. Die Basis ist eine Klasse CXTPPropExchange, die alle grundlegenden Funktionen zum Lesen und Schreiben von Daten unterschiedlicher Typen enthält. Zur Umsetzung auf den jeweiligen Ausgabetyp sind davon dann die Klassen

  • CXTPPropExchangeArchive
  • CXTPPropExchangeRegistry
  • CXTPPropExchangeXMLNode
  • CXTPPropExchangeIniFile

abgeleitet. Sämtliche Objekte speichern ihre Daten über die Klasse CXTPPropExchange, ihnen ist es egal, ob die Serialisierung nun in eine binäre Datei, in die Registry, in eine INI-Datei oder in eine XML-Datei geschieht. Die Entscheidung dafür liegt dann in der Hand des Anwendungsprogrammierers.

Diese Idee hat mich so begeistert, dass ich für SpeedCommander 11 und Squeez 5 nun endlich einmal die Umstellung auf XML in Angriff genommen und nach einer Woche auch erfolgreich zum Abschluss gebracht habe. Fast alle Einstellungsdaten werden nun leserlich in eine XML-Datei geschrieben. Übrig bleiben nur noch die Tastenkürzel und die Einstellungen für das Menü und die Symbolleisten, die in binäre Dateien gespeichert werden. Zwar ist hier eine Umstellung auf XML auch möglich, allerdings ist es doch ein wenig Overkill, angepasste Symbole mit Base64 zu kodieren und in einer XML-Datei abzulegen.

Einstellungssache

In der letzten Zeit war ich sehr mit dem Redesign der Einstellungsdialoge von SpeedCommander beschäftigt. Man denkt eigentlich, dass es doch gar nicht so schwer ist, ein paar Dialogelemente im Fenster zu verteilen, aber da steckt einiges an Arbeit drin.

Ein Nachteil der bisherigen Implementation ist, dass die Einstellungen in einzelne Dialoge aufgeteilt sind und der Anwender somit viel zu viel mit dem Öffnen und Schließen der Dialogfenster beschäftigt ist. Ein guter Freund überzeugte mich auch, dass die derzeitige Anordnung eher so aufgebaut ist, wie es der Programmierer sieht; aber nicht so, wie es der Anwender gerne hätte. Er erklärte sich bereit, mit mir zusammen die ganze Sache einmal auf neue Füße zu stellen.

Wir haben die einzelnen Einstellungen nach dem Muster Aussehen, Verhalten und Erweitert aufgeteilt und dann in zusammengehörige Gruppen sortiert. Die zeitintensivste Arbeit war dann aber das Dialogdesign selbst. Die Dialoge sollen schließlich gut zu bedienen sein, schick aussehen und sich auch an den Styleguide von Microsoft halten.