Montag, 22. Oktober 2012

MonoTouch/Mono for Android: Windows-1252 kodierte Texte lesen und schreiben

Eines der am nervigsten Themen mit denen man sich beim schreiben von Software rumschlagen muss ist aus meiner Sicht das Thema Encoding. Ich weiß nicht mehr wann ich das erste mal so richtig mit den Problemen konfrontiert wurde, aber es ist gefühlt eine halbe Ewigkeit. Dank Unicode und Co. werden die Probleme bei neuer Software zwar weniger, aber es kommt immer wieder die Situation in denen man Daten einlesen muss welche nicht entsprechend kodiert sind.

Als .NET Entwickler hat man durch die Klasse System.Text.Encoding allerdings eine ganz gute Waffe in der Hand um mit den verschiedenen Kodierungen umzugehen. Die Sache wird aber dann wieder “interessanter” wenn verschiedene Plattformen wie z.B. MonoTouch oder Mono for Android  im Spiel sind.

In meinem Fall muss ich Daten verarbeiten die mit einem Delphi Programm erzeugt werden. Da der Quellcode der Software vorhanden ist konnte ich sehen, dass die Daten mit der Standard Windows Codepage geschrieben werden (und da die Software nur im deutschsprachigen Raum verwendet wird kann man Windows-1252 Westeuropäisch als gegeben nehmen). Für den neuen .NET Code bin ich also ganz naiv davon ausgegangen das ich mit System.Text.Encoding.Default zum Ziel komme. Das war anfangs auch der Fall. Da die Delphi Software auch immer das Default Encoding verwendet kam es nie zu Problemen.

Das Problem

Jetzt soll der .NET Code aber auch auf Plattformen wie iOS und Android laufen. Ist ja danke MonoTouch und Mono for Android kein Problem. Nur leider unterscheidet sich das Default-Encoding dieser Plattformen von dem von Windows. Auf beiden wird UTF-8 verwendet:

MonoTouch

UTF-8 Encoding von MonoTouch

Mono for Android

mono4android

Werden die Daten nun mit dem Default-Encoding verarbeitet, dann klappt das bei einigen Zeichen noch ganz gut, aber spätestens wenn Steuerzeichen ins Spiel kommen geht das ganze schief.

Die Lösung

Die Lösung für dieses Problem ist eigentlich ganz einfach: Man besorgt sich das richtige Encoding. In meinem Fall Windows-1252 Westeuropäisch. Das geht unter .NET/Mono ganz einfach mit folgendem Einzeiler:

var encoding = Encoding.GetEncoding(1252);

Wenn man dieses Encoding jetzt an alle Lese und Schreib Methoden übergibt, dann klappt es auch wieder mit den Steuerzeichen.

   1: var encoding = Encoding.GetEncoding(1252);
   2:  
   3: File.ReadAllText(@"C:\Temp\text.txt", encoding);
   4:  
   5: File.WriteAllText(@"C:\Temp\text.txt", "content",encoding);
   6:  
   7: var sr = new StreamReader(@"C:\Temp\text.txt", encoding);
   8:  
   9: var sw = new StreamWriter(new MemoryStream(), encoding);

Ein weiteres Problem


Das Ganze hat dann unter Mono for Android auch gleich funktioniert (zumindest sah es so aus) und ich war mir sicher das es unter MonoTouch auch so sein wird. Hier wurde ich aber gleich eines besseren belehrt:


Exception MT



Die Codepage wurde nicht gefunden Trauriges Smiley


Nach ein bisschen suchen in den Projekteinstellungen und bei Google bin ich auf folgende Settings-Page gestoßen:


monotouch_settings




Hier kann man angeben welche Encodings mit in den Build integriert werden sollen. Diese werden nur bei explizit integriert um die Anwendung kleiner zu halten. Eine Liste mit den Encodings gibt es bei Xamarin: http://docs.xamarin.com/ios/advanced_topics/internationalization


Mono for Android schlägt zurück


Wie oben schon geschrieben sah es bei Mono for Android leider nur so aus, als ob es ohne Probleme funktioniert. Ich habe das ganze leider nur im Debug Build getestet. Und hier schlägt eine Eigenart von Mono for Android zu. Bei Debug Builds wird die Shared Runtime von Mono for Android verwendet. In dieser sind alle Encodings integriert. Bei Release Builds hingegen wird die Mono Runtime mit der App in das Package gelinkt. Und hier gilt nun das selbe wie bei MonoTouch: Die verfügbaren Encodings müssen explizit hinzugefügt werden.


Visual Studio


M4A Win


MonoDevelop


M4A Mac


Fazit


Nach der ganzen Aktion weiß ich wieder warum ich dieses T-Shirt besitze.
Grundsätzlich muss ich sagen, dass der Umgang mit verschiedenen Zeichenkodierungen für mich noch nie so komfortabel wie mit .NET war. Das ganze aber mal wieder “interessant” geworden ist, als MonoTouch bzw. Mono for Android mit gespielt haben. Das soll keine Kritik an Xamarin sein, da ihr vorgehen hier vollkommen legitim ist um die Größe der Anwendung zu reduzieren.


Ansonsten empfehle ich noch jedem Entwickler diesen Blogeintrag von Joel Spolsky zu lesen:


The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

Samstag, 3. März 2012

Monotouch - Eigene Views im Interface Builder verwenden

Auch wenn man, so wie ich, kein großer Freund von Apples Interface Builder ist, so hat es gelegentlich doch einen gewissen Charme User Interfaces für iOS Geräte "mal kurz zusammen zu klicken".
Wenn man nur die Standard Elemente, wie Button, WebView usw. verwendet, dann ist das alles auch ganz schnell gemacht.

Interessanter wird das ganze, wenn man eine eigene View verwenden möchte.

Erstellen einer Custom View

Wichtig bei einer Custom View sind die zwei folgende Eigenschaften:
  • Das Register Attribut auf der Klasse
  • Ein Constructor mit folgender Signatur: .ctor(IntrPtr handle) : base(handle)
   1: [Register("MyView")]
   2: public class MyView : UIView
   3: {
   4:     public MyView(IntPtr handle)
   5:         :base(handle)
   6:     {
   7:         this.Initialize();
   8:     }
   9:  
  10:     private void Initialize()
  11:     {
  12:         // Initialization Code
  13:     }
  14: }

Verwendung im Interface Builder

Wenn die eigene View erfolgreich compiliert ist es Zeit den Interface Builder zu starten.

Hier zieht man dann ein View Element aus der Object Library auf seine View und positioniert dieses an der richtigen Stelle.
SelectView

Damit die eigene View verwendet wird muss man nur noch den Namen der Klasse im Identity Inspector setzen.
IdentityInspector

Abschließend legt man noch ein Outlet für die View an und kann die eigene View wie alle anderen Views im Code verwenden.

Outlet


Freitag, 20. Januar 2012

Visual Studio durch eine RAM Disk beschleunigen


Heute hat mich mein Visual Studio mal wieder durch eine quälende Langsamkeit genervt. Oft gibt sich das nach ein paar Minuten und man kann normal damit arbeiten. Also habe ich mich dann mal auf Ursachen begeben. Die ersten verdächtigen sind dabei immer die Add-Ins und Extension die so installiert sein. Und nachdem mich Visual Studio Achievments als “Extensions Junkie Deluxe” bezeichnet hat, sollte da eigentlich auch Potential da sein und 1-2 Extensions können wahrscheinlich fliegen. Das hat leider auch nicht den gewünschten Erfolg gebracht und ich musste weiter suchen. Nach ein paar Minuten fiel mein Verdacht auf NCrunch und Resharper. Beides auf jeden Fall Tools auf die ich nicht verzichten möchte.
NCrunch selbst hat mich dann auf eine gute Idee gebracht: Der Einsatz einer RAM Disk.

image

Ram Disk konfigurieren

Nach ein bisschen suchen im Internet bin ich auf DataRam RAMDisk. Die ist für Ram Disks bis zu 4GB kostenlos und läuft auch unter 64-Bit Systemen. Des weiteren lässt sich die Ram Disk so konfigurieren dass der Inhalt beim herunterfahren gesichert und bei nächsten Start wieder geladen wird.
Die Ram Disk lässt sich nach der Installation über das mitgelieferte Configuration Utility einstellen. Neben der Größe kann hier eingestellt werden ob die Disk sich direkt als FAT16 oder FAT32 Partition melden soll.

image

Da ich NTFS auf der Disk verwenden möchte habe ich mich für Unformated entschieden und die Disk direkt unter Windows partitioniert und mit NTFS formatiert. Damit ich das nicht nach jedem Neustart durchführen muss lass ich die Disk beim herunterfahren sichern:

image

NCrunch konfigurieren

Damit NCrunch jetzt auch auf der Ram Disk arbeitet muss es noch konfiguriert werden. Dafür setzt man unter NCrunch –> Configuration den WorkspacePath auf ein Verzeichnis auf der Ram Disk.

image

Resharper konfigurieren

Beim Resharper gestaltet sich die Sache leider etwas schwieriger. Hier hat man nur die Möglichkeiten das dieser seine Meta-Daten entweder direkt bei der Solution abspeichert oder aber dem TEMP Verzeichnis.

image

Da ich meinen Source Code aber nicht in der Ram Disk halten möchte bleibt mir nur die Möglichkeit mein TEMP Verzeichnis zu verlegen.

TEMP Verzeichnis verlegen

Jetzt muss nur noch das TEMP Verzeichnis auf die Ram Disk verlegt werden. Das wird im Bereich Umgebungsvariablen… in den Erweiterten Systemeinstellungen gemacht

image

Dort legt man die beiden Variable TEMP und TMP auf einen Pfad auf der Ram Disk. Hier sollte man aufpassen das man nicht direkt das Wurzelverzeichnis nimmt, da wohl einige Programme damit nicht klar kommen

image

 


Fazit

Nach ein paar Stunden muss ich sagen, dass ich echt glücklich mit der Lösung bin. Visual Studio lahmt zwar immer noch hin und wieder, aber alles im allem rennt das ganze sehr gut. Anfangs war ich zwar etwas skeptisch, da dass TEMP Verzeichnis ursprünglich auf einer SSD lag und ich mir nicht all zu viel von dem verschieben erhofft hab, aber ich wurde eines besseren belehrt.

Donnerstag, 22. September 2011

Darstellung von WPF Anwendungen

WPF Anwendungen haftet immer noch der Ruf an, dass diese "irgendwie komisch" aussehen. Dies liegt vor allem an der Art und Weise wie WPF Text darstellt (vgl. Die Geschichte vom unscharfen Text oder Fonts in WPF seem blurry) Wenn man ein Windows ab Vista verwendet bessert sich das ganze ein bisschen, aber auch hier sehen die Schriften alle nicht wirklich scharf aus (vgl. Alles wird unscharf).

Das ein WPF Anwendung nicht zwangsläufig "unscharf" und "komisch" aussehen muss, sieht man an Visual Studio 2010. Das hatte zwar zu Beta Zeiten auch das Problem, aber nach genug Kritik aus Entwicklerkreisen hat Microsoft es dann doch geschaft eine "scharfe" Version von Visual Studio 2010 auf den Markt zu bekommen.

Dafür hat Microsoft ein paar Anpassungen im WPF Text Stack (vgl. WPF 4.0 Text Stack Improvements) vorgenommen. Leider sind diese Anpassungen nicht per Default für WPF Anwendungen verfügbar. Auch wenn man einen neue Anwendung entwickelt sind diese nicht von Haus aus eingestellt.

Nach ein bisschen herumspielen haben sich für mich die folgenden Einstellungen als gut herraus gestellt:

<Window TextOptions.TextFormattingMode="Display" 
SnapsToDevicePixels="True" 
UseLayoutRounding="True" />


Die folgenden Bilder zeigen den Unterschied. Auf der linken Seite werden die Controls mit den Standardeinstellungen angezeigt. Auf der rechten Seite mit den obigen Einstellungen:

Windows XP mit Classic Theme
Windows XP mit Luna Theme

Windows 7 mit Aero Theme
Das Demo Programm selbst kann hier herunter geladen werden.

Dienstag, 9. August 2011

Erste Schritte beim Entwickeln eines Outlook 2010 Add-ins

Aktuell schreibe ich gerade mein erstes Add-in für Outlook 2010.
Der Einstieg ist dank der guten Projektvorlagen von Visual Studio 2010 auch nicht weiter schwer. Einfach ein neues "Outlook 2010 Add-in" anlegen und man kann eigentlich loslegen.

Visual Studio 2010 - New Project
Nach dem Anlegen erhält man ein Projekt bei dem eigentlich alles schon konfiguriert ist. Einfach "Start Debugging" wählen und Outlook wird gestartet und das Add-in geladen.

Seinen eigenen Code kann man in die, von Visual Studio angelegte, Klasse ThisAddIn einfügen.
Nach dem Anlegen des Projekts
Hier hat man jetzt z.B. die Möglichkeit auf verschiedene Events von Outlook zu reagieren. Neben denen, im Screenshot gezeigten, StartUp und Shutdown von ThisAddIn gibt es über this.Application noch einige weitere. Genauere Informationen darüber sind in der MSDN (ApplicationEvents_11_Event Events) zu finden.



Mittwoch, 25. Mai 2011

NUnit Cheatsheet

Frei nach "Ey Mann, wo ist mein Auto?" kann man sagen, dass NUnit ein sehr großes und mächtiges Unittest Framework ist. Und seine Größe wird nur durch seine Mächtigkeit übertroffen.


NUnit ist aber auch sehr gut darin seine Größe und Mächtigkeit zu verbergen. Wenn ich mir die Unittests in meiner Firma gelegentlich so anschaue, dann könnte man meinen NUnit hätte nur die Assert.AreEqual(...) Methode. Eigentlich alle Tests werden über diese Methode realisiert. 
Man könnte jetzt doch sagen "Hey, ist doch super. Mann muss sich nur diese eine Methode merken und kann Unittests schreiben".
Aus Erfahrung habe ich damit aber zwei Probleme:

  1. Man schreibt keine Unittests, da man die Bedingung nicht oder nur umständlich mit einer Prüfung auf Gleichheit "erschlagen" kann.
  2. Unittests werden groß und kryptisch wenn man versucht alles mit Assert.AreEuqal(...) zu machen.
Hier passt ein Zitat von Paul Watzlawick ziemlich gut: 

"Wer als Werkzeug nur einen Hammer hat, sieht in jedem Problem einen Nagel."

Und damit man nicht immer mit dem AreEqual-Hammer arbeitet habe ich hier jetzt mal einen kleinen Spickzettel für NUnit zusammen geschrieben.

Prinzipiell hat NUnit zwei API Modelle. Da ist zum einen das bekannte "Classic Model" mit den Assert.Are... Methoden. Zum anderen das "Constraint Model" das mit sogenannten Constraints (deutsch Bedingungen) arbeitet.
Ich werde hier nur das "Constraint Model" zeigen, da dies meiner Meinung nach besser lesbar und mächtiger ist.


Cheatsheet

            // Vergleich von 2 Objekten
            Assert.That("lorem ipsum", Is.EqualTo("lorem ipsum"));

            // Vergleich von 2 Zahlen mit absoluter Toleranz
            Assert.That(3.14, Is.EqualTo(Math.PI).Within(0.01));

            // Vergleich von 2 Zahlen mit relativer Toleranz
            Assert.That(3.14, Is.EqualTo(Math.PI).Within(1).Percent);

            // Vergleich auf identische Referenz
            object o = new object();
            object o2 = o;
            Assert.That(o, Is.SameAs(o2));

            // Vergleich auf Null
            object x = null;
            Assert.That(x, Is.Null);

            // Vergleich auf nicht Null
            Assert.That(o, Is.Not.Null);

            // Zahl größer als
            Assert.That(23, Is.GreaterThan(10));

            // Zahl kleiner als
            Assert.That(23, Is.LessThan(100));

            // Innerhalb eines Bereichs
            Assert.That(42, Is.InRange(1, 100));

            // ******************************************************
            // Strings
            // ******************************************************
            // Auf konkreten Type Prüfen
            Assert.That("lorem", Is.TypeOf());

            // Auf substring prüfen
            Assert.That("lorem", Contains.Substring("ore"));

            // Nicht case sensitiv auf Substring prüfen
            Assert.That("lorem", Is.StringContaining("ORE").IgnoreCase);

            // Text mit einem regulären Ausdrück überprüfen
            Assert.That("lorem ipsum", Is.StringMatching("^lorem\\s"));
            
            // ******************************************************
            // Collections
            // ******************************************************
            string[] values = new string[] { "lorem", "ipsum", "dolor", "sit" };

            // Enhält
            Assert.That(values, Has.Member("dolor"));
            Assert.That(values, Has.Some.EqualTo("dolor"));

            // Enhält nicht
            Assert.That(values, Has.No.Member("amet"));