Nachdem in einem der letzten Artikel bereits die Entwicklung eines einfachen Plugins für Windows Server Advanced Power Management beschrieben wurde, soll hier nun die Vorgehensweise zur Entwicklung eines erweiterten Plugins für WSAPM beschrieben werden.
Zur Entwicklung kommt Visual Studio 2013 zum Einsatz, die Vorgehensweise sollte aber auch auf andere Entwicklungsumgebungen übertragbar sein. Die Programmiersprache ist C#, man kann aber auch jede andere .NET-Programmiersprache verwenden.
Einfaches oder erweitertes Plugin?
Zunächst sollen noch einmal kurz die Unterschiede zwischen einem einfachen und einem erweitertem Plugin für WSAPM erläutert werden:
Beide Plugin-Typen dienen in erster Linie dazu, WSAPM mit benutzerdefinierten Richtlinien zum regelbasierten Unterdrücken des Standby-Modus zu erweitern. Einfache Plugins für WSAPM können dabei nur recht einfache Richtlinien implementieren, wie beispielsweise die Überprüfung, ob momentan Wechsellaufwerke mit dem Rechner verbunden sind.
Erweiterte Plugins für WSAPM bieten darüber hinaus auch benutzerdefinierte Einstellungen mit der dazugehörigen Oberfläche zur Konfiguration.
Genau aus diesem Grund macht die Entwicklung eines erweiterten Plugins nur dann Sinn, wenn dieses auch Einstellungen benötigt, die vom Benutzer veränderbar sein sollen. Falls dies nicht der Fall ist, sollte ein einfaches Plugin entwickelt werden, da sich die Entwicklung eines einfachen Plugins um einiges einfacher gestaltet.
Anforderungen
Das hier zu implementierende erweiterte Plugin soll folgenden Anforderungen entsprechen:
- Das Plugin soll überprüfen, ob ein (lokal) installierter Drucker eingeschaltet ist und den Standby-Modus ggf. unterdrücken. Die Beschränkung auf lokal installierte Drucker erfolgt hier deshalb, da es nicht ohne weiteres möglich ist, den Status von Netzwerkdruckern oder auf einer anderen Maschine freigegebenen Druckern zu überprüfen.
- Da bei einem Rechner durchaus mehrere Drucker installiert sein können, soll das Plugin dem Benutzer die Möglichlkeit bieten, die zu überprüfenden Drucker auszuwählen. Dazu soll eine eigene Oberfläche mittels WPF implementiert werden.
Voraussetzungen
- Windows Server Advanced Power Management muss bereits installiert sein, da die Installation die DLL enthält, die später im Projekt referenziert werden muss.
Erstellung des Grundgerüsts für das erweiterte Plugin
Zunächst wird in Visual Studio ein neues Projekt vom Typ Class Library angelegt. Wir nennen das Projekt LocalPrintersOnlinePlugin.

Die automatisch erzeugte Klasse Class1 wird in LocalPrintersOnlinePlugin umbenannt.
Anschließend wird eine Referenz auf die Datei Wsapm.Extensions.dll hinzugefügt.

Diese befindet sich im Installationsordner von Windows Server Advanced Power Management (standardmäßig unter C:\Program Files (x86)\Windows Server Advanced Power Management). Im Reference Manager muss dafür mittels Browse nach der Datei gesucht werden.

Ebenso muss eine Referenz auf System.ComponentModel.Composition hinzugefügt werden. Dies ist eine .NET Framework DLL und kann im Reference Manager unter Assemblies gefunden werden.
Implementierung der Überprüfungs-Logik
Bevor das die bestehende Klasse nun als Plugin deklariert wird, implementieren wir zunächst die Überprüfungs-Logik. Hierbei ist es empfehlenswert, die Überprüfungs-Logik in eine separate Klasse auszulagern, damit dieser Code vom technischen Plugin-Code getrennt werden kann.
Zunächst wird dem Projekt eine neue Klasse PrintHelper hinzugefügt. Diese Klasse bietet Methoden, um die lokal installierten Drucker zu finden und zu überprüfen, ob ein angegebener Drucker eingeschaltet ist. Die erforderlichen Daten werden hierbei über WMI ermittelt. Daher muss dem Projekt noch eine Referenz auf System.Management hinzugefügt werden.
Die komplette Klasse PrintHelper sieht somit folgendermaßen aus:
/// <summary> /// Helper class for managing printers. /// </summary> public static class PrintHelper { /// <summary> /// Gets a string array containing the names of the installed local printers. /// </summary> /// <returns>A string array containing the names of the installed local printers. If no local printers are installed, null is returned.</returns> public static string[] GetInstalledLocalPrinters() { var query = new ObjectQuery("SELECT * FROM Win32_Printer WHERE Local = true"); var searcher = new ManagementObjectSearcher(query); var printerNames = from ManagementObject printer in searcher.Get() select printer["Name"].ToString(); return printerNames.ToArray(); } /// <summary> /// Gets the name of the local default printer installed on the computer. /// </summary> /// <returns>The name of the local default printer installed on the computer or an empty string if no default printer is installed or the default printer is not a local printer.</returns> public static string GetDefaultLocalPrinterName() { var query = new ObjectQuery("SELECT * FROM Win32_Printer WHERE Default = true AND Local = true"); var searcher = new ManagementObjectSearcher(query); var defaultPrinterName = from ManagementObject printer in searcher.Get() select printer["Name"].ToString(); return defaultPrinterName.FirstOrDefault(); } /// <summary> /// Checks if the specified printer is online. /// </summary> /// <param name="printerName">The name of the printer.</param> /// <returns>True, if the specified printer is online, otherwise false. Also returns false if there is no printer installed at all.</returns> /// <remarks>This method does not work case sensitive.</remarks> public static bool IsPrinterOnline(string printerName) { if (string.IsNullOrEmpty(printerName)) return false; var query = new ObjectQuery("SELECT * FROM Win32_Printer"); var searcher = new ManagementObjectSearcher(query); var online = from ManagementObject printer in searcher.Get() where printer["Name"].ToString().ToLower() == printerName.ToLower() select !(bool)printer["WorkOffline"]; return online.FirstOrDefault(); } }
Implementieren der Settings-Klasse
Da ein erweitertes Plugin Einstellungen bietet, muss ebenfalls eine Settings-Klasse implementiert werden, in der sämtliche Plugin-Einstellungen gespeichert werden. Dazu fügen wir eine neue Klasse LocalPrintersOnlinePluginSettings hinzu.
Diese Klasse besitzt nur eine Eigenschaft OnlinePrinters, die eine Liste der Drucker zurück liefert, deren Status überprüft werden soll:
[Serializable] public class LocalPrintersOnlinePluginSettings { public List<string> OnlinePrinters { get; set; } }
Die Klasse (und alle Eigenschaften) müssen dabei als public deklariert werden. Ebenfalls sollte die Klasse serialisierbar sein, also das Attribut Serializable aufweisen. Diese Schritte sind notwendig, da beim Speichern der Einstellungen eine Instanz dieser Klasse mittels XML serialisiert wird.
Implementieren der Oberfläche
Als nächstes wird die Oberfläche implementiert. Die Oberfläche kann sowohl ein Windows Forms Control, als auch mittels WPF implementiert werden. Ich entscheide mich hier für ein WPF-Control. Dieses wird dem Projekt als LocalPrintersOnlinePluginSettingsControl hinzugefügt.

Damit das Projekt nun fehlerfrei gebaut werden kann, muss noch manuell eine Referenz auf System.Xaml hinzugefügt werden.
Die Oberfläche soll später eine Liste mit den lokal installierten Druckern anzeigen. Diese Liste wird dynamisch bei Laden der Oberfläche erzeugt. Daher fügen wir an dieser Stelle nur eine GroupBox und ein DockPanel hinzu, welches wiederum ein StackPanel beinhaltet. Die Oberflächen-Klasse sieht somit folgendermaßen aus:
<UserControl x:Class="LocalPrintersOnlinePlugin.LocalPrintersOnlinePluginSettingsControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Height="Auto" Width="Auto"> <DockPanel LastChildFill="True"> <GroupBox Name="groupBox" Header="Lokal installierte Drucker" Padding="10"> <StackPanel Name="stackPanel" Orientation="Vertical"></StackPanel> </GroupBox> </DockPanel> </UserControl>
In der Code-Behind-Datei der Oberfläche muss nun der Code hinzugefügt werden, damit das Control als WSAMP-SettingsControl verwendet werden kann. Dazu muss die Klasse das Interface IWsapmPluginSettingsControl implementieren. Dazu müssen zwei Methoden ausprogrammiert werden:
- GetSettingsBeforeSave: Diese Methode wird von WSAPM aufgerufen, wenn die Einstellungen des Plugins gespeichert werden sollen. An dieser Stelle muss aus dem Einstellungen an der Oberfläche eine Instanz der Settings-Klasse erstellt werden.
- SetSettings: Diese Methode wird von WSAPM aufgerufen, wenn die Oberfläche des Plugins in WSAPM angezeigt werden soll. Der Parameter liefert eine Instanz der Settings-Klasse, in der die aktuell gespeicherten Einstellungen enthalten sind. Da die Einstellungen als object geliefert werden, muss hier ein Cast auf die konkrete Einstellungs-Klasse (in diesem Fall LocalPrintersOnlinePluginSettings) erfolgen.
Neben den Methoden aus dem Interface muss an dieser Stelle auch der Code zum dynamischen Aufbereiten der Oberfläche implementiert werden (Methode BuildUI), so dass die komplette Code-Behind-Datei folgendermaßen aussieht:
/// <summary> /// Interaction logic for LocalPrintersOnlinePluginSettingsControl.xaml /// </summary> public partial class LocalPrintersOnlinePluginSettingsControl : UserControl, IWsapmPluginSettingsControl { public LocalPrintersOnlinePluginSettingsControl() { InitializeComponent(); BuildUI(); } public object GetSettingsBeforeSave() { var printerList = new List<string>(); foreach (var item in this.stackPanel.Children) { var checkBox = item as CheckBox; if (checkBox == null) continue; if(checkBox.IsChecked.Value) { var str = checkBox.Content as string; if (!string.IsNullOrEmpty(str)) printerList.Add(str); } } var pluginSettings = new LocalPrintersOnlinePluginSettings(); pluginSettings.OnlinePrinters = printerList; return pluginSettings; } public void SetSettings(object settings) { var pluginSettings = settings as LocalPrintersOnlinePluginSettings; // Always check if the settings contain data before trying to access settings properties! if (pluginSettings == null || pluginSettings.OnlinePrinters == null || pluginSettings.OnlinePrinters.Count == 0) return; foreach (var item in this.stackPanel.Children) { var checkBox = item as CheckBox; if (checkBox == null) continue; var str = checkBox.Content as string; if (string.IsNullOrEmpty(str)) continue; if (pluginSettings.OnlinePrinters.Contains(str)) checkBox.IsChecked = true; } } private void BuildUI() { this.stackPanel.Children.Clear(); var printers = PrintHelper.GetInstalledLocalPrinters(); var defaultPrinter = PrintHelper.GetDefaultLocalPrinterName(); foreach (var printer in printers) { var checkBox = new CheckBox(); checkBox.Content = printer; if (printer == defaultPrinter) checkBox.FontWeight = FontWeights.Bold; this.stackPanel.Children.Add(checkBox); } } }
Implementieren der Plugin-Klasse
Nun folgen die Schritte, um die Klasse in ein WSAPM Plugin zu wandeln und alle bis hierhin hinzugefügten Komponenten und Klassen zusammen spielen zu lassen: Zunächst wird die Klasse LocalPrintersOnlinePlugin mit dem Attribut WsapmPlugin versehen. Durch dieses Attribut wird Windows Server Advanced Power Management das Plugin als solches erkennen und einbinden. Dazu müssen drei Parameter angegeben werden, die das Plugin beschreiben:
- Der erste Parameter gibt den (internen) Namen des Plugins an. Dieser Name ist nur der interne Name des Plugins und gibt prinzipiell nur den Namen des Orders an, in dem das Plugin installiert wird.
- Der zweite Parameter gibt die Versionsnummer des Plugins an..
- Der letzte Parameter muss eine GUID sein. Diese kann einfach in Visual Studio unter Tools > Create GUID (am besten das Registry Format wählen) erzeugt werden. Es ist sehr wichtig, dass jedes Plugin eine eigene GUID besitzt, da WSAPM die Plugins anhand ihrer GUID unterscheidet. Diese GUID darf sich bei einem Plugin niemals ändern, auch wenn eine neue Version des Plugins erscheint.
Im Rahmen dieser Anleitung werde ich keine konkrete GUID angeben (sondern nur den Platzhalter {YOUR-GUID-HERE}) dass diese nicht kopiert werden kann.
Danach muss noch das Attribut System.ComponentModel.Composition.Export über die Klasse LocalPrintersOnlinePlugin geschrieben werden. Dies sorgt im Grunde genommen dafür, dass die Plugin-DLL dynamisch zur Laufzeit von WSAPM geladen werden kann. Der Parameter definiert das Plugin als WSAPM-Plugin und muss immer typeof(WsapmPluginBase) heißen, auch wenn es sich hierbei um ein erweitertes Plugin handelt.
Somit haben wir folgende Klassendefinition für die Plugin-Klasse:
[Export(typeof(WsapmPluginBase))] [WsapmPlugin("LocalPrintersOnline", "v1.0.0", "{YOUR-GUID-HERE}")] public class LocalPrintersOnlinePlugin { }
Als nächstes muss die Klasse von WsapmPluginAdvancedBase abgeleitet werden. Dies sorgt dafür, dass sechs Methoden überschrieben werden müssen:
- Initialize: Diese Methode dient zur Initialisierung des Plugins und wird genau ein mal nach dem Laden des Plugins aufgerufen und kann dazu verwendet werden, einmalige Initialisierungs-Maßnahmen vorzunehmen.
Der Rückgabewert gibt Auskunft darüber, ob die Initialisierung des Plugins erfolgreich abgeschlossen wurde. Wird hier false zurückgeliefert, wurde das Plugin nicht erfolgreich initialisiert (z.B. im Fehlerfall) und wird von WSAPM nicht in die Überprüfungs-Routinen mit einbezogen. Da keine Initialisierung für das Plugin nötig ist, wird hier einfach true zurückgeliefert. - Prepare: Diese Methode wird immer kurz vor der eigentlichen Überprüfungs-Routine aufgerufen und kann dazu verwendet werden, Initialisierungen vorzunehmen, die vor jeder Überprüfung stattfinden müssen.
Der Rückgabewert gibt wiederum Auskunft darüber, ob die Vorbereitung erfolgreich abgeschlossen wurde. Wird hier false zurückgeliefert (z.B. im Fehlerfall), wird WSAPM die Überprüfungs-Routine des Plugins im Folgenden nicht ausführen. Da das Plugin keinerlei Vorbereitungen zur Ausführung der Überprüfungs-Routine benötigt, wird hier einfach true zurückgeliefert. - CheckPluginPolicy: Dies ist die eigentliche Überprüfungs-Routine des Plugins.
Das Ergebnis der Überprüfung wird als PluginCheckSuspendResult zurückgeliefert. Der Konstruktor dieser Klasse erwartet zwei Parameter: Der erste Parameter gibt Auskunft darüber, ob der Standby-Modus von WSAPM unterdrückt werden soll (true: Standby-Modus soll unterdrückt werden; false: Standby-Modus soll nicht unterdrückt werden). Der zweite Parameter ist ein String, welcher den Grund für eine mögliche Unterdrückung des Standby-Modus angibt. Dieser Parameter wird von WSAPM nur ausgewertet, wenn der Standby-Modus tatsächlich unterdrückt werden soll.
In diesem Fall muss an dieser Stelle die Überprüfung der einzelnen Drucker erfolgen. Für alle Drucker, die in den Einstellungen ausgewählt wurden, wird hier überprüft, ob das entsprechende Gerät eingeschaltet ist. Dazu kommt wieder die Klasse PrintHelper zum Einsatz. - TearDown: Diese Methode wird jedes Mal direkt nach der Überprüfung aufgerufen und kann dazu verwendet werden, um notwendige Aufräumarbeiten durchzuführen.
Der Rückgabewert gibt wieder an, ob das Aufräumen erfolgreich beendet wurde. Da das Plugin keine solcher Aufräumarbeiten ausführen muss, wird hier wieder einfach nur true zurückgeliefert. - LoadDefaultSettings: Mit dieser Methode werden die Standard-Einstellungen des Plugins geladen und zurück geliefert. In diesem Fall handelt es sich dabei nur um eine leere Instanz der Settings-Klasse, da ohne bereits vorliegende Einstellungen kein Drucker zur Überprüfung ausgewählt sein soll.
- SettingsControl: Im Getter dieser Eigenschaft wird die Referenz auf die Oberflächen-Klasse des Plugins zurück geliefert. Wichtig dabei ist, dass immer die gleiche Instanz der Oberflächen-Klasse geliefert wird. Daher wird eine private Variable des Typs der SettingsControl-Klasse angelegt und immer nur diese eine Instanz im Getter der Eigenschaft zurückgeliefert. Diese Vorgehensweise ähnelt dem Singleton-Entwurfsmuster.
Darüber hinaus braucht die Plugin-Klasse einen parameterlosen Konstruktor, der als public deklariert wird und den Konstruktor der Basis-Klasse (LocalPrintersOnlinePluginSettings) mit dem Typ der Settings-Klasse aufruft.
Die komplette Implementierung der Plugin-Klasse:
[Export(typeof(WsapmPluginBase))] [WsapmPlugin("LocalPrintersOnline", "v1.0.0", "{YOUR-GUID-HERE}")] public class LocalPrintersOnlinePlugin : WsapmPluginAdvancedBase { // Private variable for the only instance of the settings control class. private LocalPrintersOnlinePluginSettingsControl settingsControl; public LocalPrintersOnlinePlugin() : base(typeof(LocalPrintersOnlinePluginSettings)) { } protected override object LoadDefaultSettings() { // Just return an empty instance ot the settings class as no printer should be checked by default. return new LocalPrintersOnlinePluginSettings(); } public override object SettingsControl { get { // Always return the same instance of the settings control class here. if (this.settingsControl == null) this.settingsControl = new LocalPrintersOnlinePluginSettingsControl(); return this.settingsControl; } } protected override PluginCheckSuspendResult CheckPluginPolicy() { // You can always access the current settings with the CurrentSettings property of the base class. var pluginSettings = this.CurrentSettings as LocalPrintersOnlinePluginSettings; // Don't forget to check if the settings contains data before accessing it. if (pluginSettings == null || pluginSettings.OnlinePrinters == null || pluginSettings.OnlinePrinters.Count == 0) return new PluginCheckSuspendResult(false, string.Empty); foreach (var printer in pluginSettings.OnlinePrinters) { if (PrintHelper.IsPrinterOnline(printer)) { // Just return a PluginCheckSuspendResult if the first printer was found online. return new PluginCheckSuspendResult(true, string.Format(Resources.LocalPrintersOnlinePlugin.LocalPrintersOnlinePlugin_PrinterSwitchedOn, printer)); } } // No printer was to be checked, just return a PluginCheckSuspendResult so that the standby mode should not be supressed. return new PluginCheckSuspendResult(false, string.Empty); } protected override bool Initialize() { return true; } protected override bool Prepare() { return true; } protected override bool TearDown() { return true; } }
Das Plugin-Manifest
Ein weiterer wichtiger Punkt ist das sog. Plugin-Manifest. Dies ist eine XML-Datei mit weiteren Angaben zum Plugin. Diese Informationen dienen zwei Zwecken: Zum einen werden die Informationen aus dem Manifest an der Oberfläche von WSAPM (Tabe Plugins in den Einstellungen) angezeigt. Zum anderen wird durch das Manifest eine Unterstützung von mehreren Sprachen möglich.
Das Manifest hat einen definierten Aufbau:
<?xml version="1.0" encoding="utf-8" ?> <WsapmPlugin> <DescriptionSet lang="en"> <PluginName>Local printers online</PluginName> <Description>Plugin to suppress standby when local printers are available</Description> <AuthorName>DecaTec</AuthorName> </DescriptionSet> <DescriptionSet lang="de"> <PluginName>Lokale Drucker online</PluginName> <Description>Plugin zum Unterdrücken des Standby-Modus wenn lokale Drucker eingeschaltet sind</Description> <AuthorName>DecaTec</AuthorName> </DescriptionSet> </WsapmPlugin>
In stellt eine sog. DescriptionSet die Beschreibung eines Plugins für eine bestimmte Sprache dar. Die Sprache wird dabei mit dem Attribut lang angegeben und ist ein zweistelliger Sprachcode nach ISO 639-1 (z.B. „en“ für Englisch, „de“ für Deutsch). Der hier angegebene Name des Plugins, die Beschreibung und der Name des Autors werden in der entsprechenden Sprache des Benutzers auf der Oberfläche von WSAPM angezeigt.
Weitere Informationen zum Plugin
Darüber hinaus ist es auch möglich, weitere Informationen zu einem Plugin anzugeben. Wenn das Plugin beispielsweise einige Einstellungen hat, bietet es sich an, diese Einstellungen weiter zu beschreiben. Dazu wird eine Datei ReadMe.txt dem Plugin hinzugefügt. Die Inhalte dieser Datei werden angezeigt, wenn auf der Oberfläche auf die Schaltfläche Plugin Info geklickt wird (Tabe Plugins in den Einstellungen). Es können auch weitere Dateien für andere Sprachen hinzugefügt werden: hier sollte dann wieder ein zweistelliger Sprachcode an den Dateinamen angehängt werden (z.B. ReadMe_de.txt für eine Beschreibung des Plugins in Deutsch).
Diese Dateien sind dabei optional. Wenn ein Plugin keine ReadMe-Datei beinhaltet, werden beim Klick auf die Schaltfläche Plugin Info einfach nur allgemeine Informationen zu einem Plugin angezeigt.
Das Plugin für die Verteilung vorbereiten
Nachdem die Implementierung des Plugins abgeschlossen wurde, kann das Plugin für die Verteilung vorbereitet werden. Dazu sollte die Solution zunächst einmal gebaut werden (im Release-Modus). Anschließend suchen wir den bin\Release Ordner im Projektverzeichnis. Dieser enthält die gebauten Dateien unseres Plugins. Wichtig ist in diesem Fall zunächst die Datei LocalPrintersOnlinePlugin.dll. Die anderen Dateien (v.a. die Wsapm.Extensions.dll) sollte nicht weitergegeben werden.
Falls eine Lokalisierung des Plugins vorgenommen wurde, befindet sich im Order bin\Release\<Sprache> eine Satelliten-Assembly namens LocalPrintersOnlinePlugin.resources.dll mit den lokalisierten Ressourcen (z.B. im Ordner bin\Release\de für eine Ressourcen-DLL mit deutschen Ressourcen). Ressourcen-DLLs, die Teil von WSAPM sind (z.B. Wsapm.Extensions.resources.dll) sollten auch nicht weitergegeben werden.
Alle zum Plugin gehörenden Dateien werden nun durch ein ZIP-Programm (z.B. 7zip) gepackt. Wichtig dabei ist, dass es sich um eine ZIP-Datei (Dateiendung *.zip) handelt. Nur so kann das Plugin in WSAPM installiert werden. Diese ZIP-Datei kann nun weitergeben werden.
Installation des Plugins
Das Plugin lässt sich nun in den Einstellungen von WSAPM installieren. Dazu einfach in die Tabe Plugins wechseln und auf Plugin installieren klicken. WSAPM wird im Rahmen der Installation beendet und neu gestartet. Danach muss das Plugin nur noch in den Einstellungen aktiviert werden.

Mit einem Klick auf den Button Plugin-Einstellungen gelangt man zu den Einstellungen des soeben installierten Plugins.

Weitere Möglichkeiten
Dieses erweiterte Plugin wurde von Grund auf entwickelt. Wer sich nicht mit der Erstellung des Plugin-Grundgerüsts beschäftigen will, für den gibt es ein Vorlage als Visual Studio Solution zum Download. Zu finden ist diese auf der Seite von Windows Server Advanced Power Management. Mit Hilfe dieser Vorlage ist im Grunde genommen nur noch die reine Plugin-Logik und das Zusammenspiel mit der Oberflächen-Klasse zu implementieren.
Darüber hinaus bietet es sich an, ein Plugin zu lokalisieren, wenn dieses nicht nur für den privaten Gebrauch programmiert wurde und weitergegeben werden soll. Windows Server Advanced Power Management unterstützt aktuell Deutsch und Englisch, daher wäre es wünschenswert, wenn Plugins ebenso beide Sprachen unterstützen würden.
Da die Lokalisierung von .NET Anwendungen den Rahmen dieses Artikels sprengen würde, wird an dieser Stelle nicht weiter auf dieses Thema eingegangen.
Im Sourcecode des Plugins, welcher weiter unten herunter geladen werden kann, ist eine solche Lokalisierung bereits implementiert.
Sourcecode
Der gesamte Sourcecode des entwickelten Plugins kann hier herunter geladen werden. Es wurde lediglich die Lokalisierung (Deutsch/Englisch) für das entwickelte Plugin hinzugefügt.
Damit der Code als Plugin lauffähig ist, muss nur noch eine echte GUID in das WsapmPlugin Attribut eingefügt werden, wie bereits weiter oben beschrieben.
Der Sourcecode des Plugins ist hier zu finden:
Wsapm-LocalPrintersOnline@Codeberg
Ihr habt ein eigenes Plugin entwickelt?
Falls ihr ein eigenes Plugin für WSAPM entwickelt habt und der Meinung seid, dass dieses Plugin auch für andere interessant sein könnte, dann schreibt mir doch einfach eine E-Mail. Beschreibt kurz, was euer Plugin macht und wie es funktioniert. Ich werde dann das Plugin (mir eurer Genehmigung) auf der Website veröffentlichen, so dass es auch andere herunterladen und nutzen können.
Falls ihr eine gute Idee für ein Plugin habt, es aber nicht selbst programmieren wollt, könnt ihr mir natürlich auch eine E-Mail schreiben.