Enable/disable hibernation programmatically

20 05 2009

Die Hibernate-Funktionalität lässt sich interaktiv über das „Power Options“ Control Panel Applet (powercfg.cpl) ein-/ausschalten.

Die Hibernate-Funktionalität lässt sich folgendermaßen per Kommdozeile  ein-/ausschalten:

powercfg.exe /H ON|OFF

Um die Hibernate-Funktion per Programm (API) ein-/auszuschalten, ist die Win32 API Funktion CallNtPowerInformation mit dem Parameter InformationLevel=10 (SystemReserveHiberFile) zu verwenden. Der Parameter lpInputBuffer muss dazu auf einen bool’schen Wert zeigen, für die Länge von nInputBufferSize genügt 1.

Beispiel:

bool bOnOff = true;
CallNtPowerInformation(10, &bOnOff, sizeof(bOnOff), NULL, 0);

Hinweis: Das kurzzeitige Aus- und wieder Einschalten der Hibernate-Funktion kann verwendet werden, um zu Verhindern, dass Windows nach dem Aufwecken aus dem Hibernate-Zustand nach 5 Minuten wieder schlafen geht wenn keine Benutzereingaben (Maus, Tastatur) erfolgen. Dieses „Feature“ ist hier beschrieben.





.NET Micro Framework 3.0 SP1 Flaws

13 05 2009

Ich arbeite seit einiger Zeit mit dem .NET Micro Framework 3.0 SP1 auf einem Embedded Master Development System von GHI Electronics. Dabei musste ich feststellen, dass einige der dokumentierten Methoden des .NET Micro Frameworks entweder gar nicht vorhanden sind, oder nur teilweise funktionieren weil z. B. bestimmte Parameter ignoriert werden. Teilweise gibt es auch undokumentierte Methoden. Das ist insofern besonders ärgerlich, als dass bereits das .NET Micro Framework SDK 3.0 February 2009 Documentation Update installiert ist.

Wohlgemerkt handelt es sich hier um Schwächen des .NETMF, nicht jedoch des Embedded Master Modules. Bei dieser Gelegenheit möchte ich den ausgezeichneten Support von GHI Electronics loben, den sie über ihr Forum leisten.

Folgende Fehler habe ich bisher festgestellt:

  1. Die Methode RotateBlt ist nicht (mehr) vorhanden. Es gibt keine alternative Möglichkeit, Bitmaps oder andere Objekte zu drehen.
  2. Die Füllfarbe (Brush, Gradienten) wird mit Ausnahme von Rectangles bei allen übrigen Shapes ignoriert. Auch die Methoden DrawEllipse und DrawRectangle ignorieren die Brush. In der Praxis bedeutet das, dass ausgefüllte Formen derzeit ausschließlich über Rechtecke (ohne abgerundete Ecken!) oder Bitmaps dargestellt werden können.
  3. Die Füllung mit einem linearen Gradienten funktioniert nur bei Rechtecken. Im Gegensatz zur WPF des „großen“ .NET Frameworks beziehen sich dabei die Koordinaten des Gradienten nicht relativ auf die Koordinaten des zu füllenden Rechtecks, sondern absolut auf die linke obere Ecke des Bildschirms und sind somit abhängig von der Position des zu füllenden Rechtecks. Radiale Gradienten sowie GradientStops werden derzeit nicht unterstützt.
  4. Die Methode Bitmap.GetBitmap() ist vorhanden, jedoch nicht dokumentiert. Sie wird z. B. zum Setzen eines BootImage’s beim Embedded Master benötigt.




.NET Framework Source Code unter Windows 7 RC

13 05 2009

Ich habe gerade versucht, unter Windows 7 RC (Build 7100) mit VS2008 SP1 in den .NET Framework Source Code zu debugging. Ich habe dazu alles eingerichtet wie in Shawn Burke’s Blog beschrieben. Auch den Visual Studio 2008 QFE KB944899 habe ich installiert. Die Symbole (.pdb Dateien) wurden auch herunter geladen, allerdings fehlte der Quellcode.

Anhand der Datei PESymbolList.xml konnte ich feststellen, dass unter Windows 7 RC eine neuere Version des .NET Frameworks installiert ist, z. B. System.dll, Version 2.0.50727.4918 (NetFXspW7.050727-4900). Für diese Version steht offensichtlich (noch) keine Quellcode zur Verfügung.





.NET Remoting TcpChannel, multi-homed Server

8 05 2009

In einem aktuellen Projekt bestand das Problem, dass wir einen .NET Remoting Server implementiert hatten, der den TcpChannel als Transport verwendet. Das Problem entstand dadurch, dass der Server mit mehreren Netzwerkadaptern (LAN und WLAN) ausgestattet war und die IP-Adresse des WLAN-Adapters sich dynamisch ändert weil der Server auf einem mobilen Gerät läuft, das herum getragen wird („roaming users“) und sich mit verschiedenen WLAN-(Sub)netzen verbindet. All das wäre noch kein Problem wenn es im Netzwerk des Kunden einen DNS-Server gäbe. In diesem Fall könnte man den TcpChannel so konfigurieren, dass er den Rechnernamen des mobilen Servers für die Bindung der Proxies verwendet. Da aber kein DNS zur Verfügung steht, mussten wir auf die Option useIpAddress=“true“ ausweichen. Dadurch entstand eine Reihe von Problemen:

  1. Wenn beim Start des Systems (Windows XP Embedded) kein Netzwerkkabel angesteckt ist und kein WLAN verfügbar ist, wird der Maschine keine IP-Adresse zugewiesen. Eine Kommunikation mit dem Gerät über den .NET Remoting TcpChannel ist dann auch nach dem Herstellen einer Netzwerkverbindung nicht möglich (der .NET Remoting Server läuft als Win32 Service und wird beim Systemstart automatisch gestartet).
  2. Wenn sich die IP-Adresse des WLAN-Adapters zur Laufzeit des .NET Remoting Service ändert, dann war keine Kommunikation mit dem .NET Remoting Service möglich. Selbst dann nicht, wenn der Client neu gestartet wurde. Der Client kennt zwar die neue IP-Adresse und kann den Server anpingen und sogar eine TCP Verbindung zum .NET Remoting Interface herstellen, eine Kommunikation über das .NET Remoting Protokoll war jedoch nicht möglich.

Ursächlich hierfür ist die Implementierung des TcpChannels im .NET Framework (2.0 SP1, neuere Versionen sind aber auch nicht besser). Bereits beim Registrieren des TcpChannels (z. B. via RemotingConfiguration.Configure) werden die IP-Adressen des Hosts ermittelt und der TcpChannel wird an die erstbeste(!) IP-Adresse (und zwar nur an diese!) gebunden. Diese Bindung bleibt bis zum Ende des Prozesses bestehen. Sie passt sich nicht dynamisch an. Ein nachträgliches Umkonfigurieren ist nicht möglich da die Klasse TcpChannel hierfür keine Properties/Methoden zur Verfügung stellt. Selbst ein explizites Deregistrieren (ChannelServices.UnregisterChannel) mit anschließender Neuinstanziierung und Neuregistrierung (ChannelServices.RegisterChannel) des TcpChannels ist nicht möglich weil das .NET Framework die Registrierung eines TcpChannels nur ein einziges Mal zulässt (warum?).

Zur Lösung des Problems haben wir eine eigene Implementierung des TcpChannels erstellt. Als Vorlage diente der Sourcecode von mono 2.4. Damit war es möglich, den TcpChannel zu de- und anschließend wieder zu registrieren. Im Gegensatz zum Standard-TcpChannel aus dem .NET Framework liefert unser TcpChannel nicht nur eine einzige URI für ein Objekt, sondern eine Liste von URIs, die einen Eintrag für jede dem System zugewiesene IP-Adresse (gefiltert auf IPv4) liefert. Parallel dazu haben wir einen „TcpAddressMonitor“ implementiert, der die dem System aktuell zugewiesenen IP-Adressen überwacht (mittels Polling im 5 s Takt) und bei einer Änderung den TcpChannel deregistriert, eine neue Instanz erstellt und diese wieder registriert. Damit waren sowohl das Problem mit nicht vorhandenen IP-Adressen beim Systemstart als auch das Roaming-Problem gelöst.

Allerdings wurden nun Exceptions, die beim Aufruf von Server-Methoden auftraten, nicht mehr an den Client durchgereicht. Die Option RemotingConfiguration.CustomErrorsMode=CustomErrorsMode.Off wurde ignoriert. Clientseitig kam lediglich eine RemotingException mit der Message „Server encountered an internal error“ an. Die Lösung für dieses Problem fand sich hier: Es muss im TcpChannel der RequestHeader __CustomErrorsEnabled=false gesetzt werden. Auf diese Lösung bin ich nur dadurch gekommen, dass ich mir den gesamten verfügbaren .NET Sourcecode über den NetMassDownloader heruntergeladen und nach „customErrors“ durchsucht habe.

Update, 13.05.2009: So ganz hat der oben beschriebene Weg doch nicht funktioniert. Wie sich gezeigt hat, gibt es Probleme mit bereits gemarshalten Objekt-Referenzen (ObjRef) wenn der TcpChannel neu registriert wird. Die Referenzen werden dann ungültig. Da hilft nicht einmal ein Neustart der Clients. Anhand eines Analyse des Quellcodes habe ich folgendes gelernt:

1. Jeder Channel enthält eine Liste von URIs (beim TcpChannel wird die Liste von der internen Klasse TcpServerChannel verwaltet).

2. Jede Objekt-Referenz enthält eine Liste von URIs (z. B. „tcp://hostname:port/xxxxxx.rem“).

Wird eine Referenz zum ersten Mal gemarshalt, wird ein ObjRef Objekt erzeugt. Dabei wird die Liste aller URIs aller in .NET Remoting registrierten Channels in das ObjRef Objekt kopiert.

Um unser Problem der sich ändernden IP-Adressen zu lösen, war nun folgendes nötig sobald sich die IP-Adressliste des Hosts geändert hat:

1. Die URI-Liste jedes TcpChannels muss aktualisiert werden.

2. Die URI-Liste sämtlicher Objekt-Referenzen muss aktualisiert werden.

Das ließ sich folgendermaßen implementieren:

1. Die Liste aller registrierten TcpChannels erhält man über ChannelServices.RegisteredChannels. Mit Hilfe von „… is TcpChannel“ wird überprüft, ob es sich um einen Channel vom Typ TcpChannel handelt. Um den TcpChannel aktualisieren zu können, wurde eine public Methode UpdateChannelData() hinzugefügt. Diese delegiert einfach an eine ebenfalls neue Methode UpdateChannelData() des internen TcpServerChannels. Damit waren die URIs des TcpChannels aktualisiert, was allerdings nur Auswirkungen auf zukünftige gemarshalte Objekt-Referenzen hat.

2. Die bereits gemarshalten Objekt-Referenzen können aktualisiert werden, indem die Schnittstelle ITrackingHandler von einer Klasse TrackingHandler implementiert wurde, und eine entsprechede Instanz beim TrackingService registriert wurde. ITrackingHandler wird benachrichtigt, wann immer eine Objekt-Referenz gemarshalt, unmarshalt oder disconnected wird. Beim Marshaling vermerkt unsere Klasse die erzeugte Objekt-Referenz in einer Liste, beim Disconnect wird die Objekt-Referenz aus der Liste entfernt. Anhand dieser Liste lässt sich leicht über alle bereits gemarshalten Objekt-Referenzen iterieren und deren URIs aktualisieren.