Dienstag, 19. Juli 2011

Windows Mobile - .Net Compact Framework Exceptions anzeigen

Bei einem aktuellen Projekt arbeite ich an einer Anwendung für ein Windows CE 5.0 Gerät. Um genau zu sein handelt es sich um einen Scanner vom Typ Casio DT-X7.

Bei der Entwicklung habe ich festgestellt, dass meine Fehlerprotokolle (Logging der Exceptions in eine Textdatei) nicht korrekt geschrieben werden können. Und zwar wurde immer folgende Meldung protokolliert, egal welcher Fehler aufgetreten ist:

"An error message is available for this exception but cannot be displayed because these messages are optional and are not currently installed on this device. Please install ‘NETCFv35.Messages.EN.wm.cab’ for Windows Mobile 5.0 and above or  ‘NETCFv35.Messages.EN.cab’ for other platforms. Restart the application to see the message."

Folglich habe ich mich, ganz wie in der Meldung beschrieben, daran gemacht die angegeben CAB zu installieren. Hierzu habe ich die folgende Datei von meinem PC (Windows Mobile SDK muss installiert sein) auf das mobile Gerät kopiert:

C:\Programme\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE\Diagnostics\NETCFv35.Messages.EN.cab

Nach dem Kopieren habe ich die Datei auf dem mobilen Gerät gestartet und erfolgreich installiert.

Hier gilt es zu beachten, dass es im genannten Verzeichnis auch eine Datei "NETCFv35.Messages.EN.wm.cab" gibt. Diese Datei ist ausschließlich für Geräte mit Windows Mobile. Die Datei ohne ".wm" im Namen ist für alle weiteren Systeme wie z. B. Windows CE.

Verwendet man versehentlich die falsche Datei, so kommt eine Fehlermeldung vor der Installation wie z. B. folgende:

The file "NETCFv35.Messages.EN.wm.cab" is not a valid Windows CE Setup file.

In diesem Fall empfehle ich einfach noch einmal genau hinzuschauen und ggf. auch einmal das Bestriebssystem auf dem mobilen Gerät zu überprüfen. Manchmal hat man vielleicht doch etwas verwechselt oder übersehen.

Wurde aber alles korrekt installiert, so sollten auch die Fehlermeldungen wieder korrekt angezeigt/protokolliert werden.

Bei mir war dieses aber nicht der Fall. So musste ich weitersuchen.
Hierfür habe ich mir die kostenfreien Windows Mobile Power Toys von Microsoft installiert und mit dem MobileLogger geschaut, ob es noch irgendwelche Probleme gibt. Und ich bin direkt fündig geworden. Der Logger zeigt mir die Meldung:

"Failed to load [System.SR, Version=3.5.0.0, Culture=neutral, PublicKeyToken=969DB8053D3322AC]"

Das Problem hier ist, dass eine im CAB enthaltene Datei unter anderem Namen vorliegt, als sie eigentlich referenziert ist. Und zwar handelt es sich hierbei um die Datei "SYCCFA~1.001". Als Referenz wird der Name "System.SR" erwartet. Helfen kann man sich hier ganz einfach, in dem man die CAB-Datei entpackt, die Datei in "System.SR.dll" umbenennt und diese anschließend im sein Windows Mobile Projekt als Referenz einbindet.

Jetzt wurden sogar bei mir alle Fehlermeldungen korrekt ausgegeben.

Donnerstag, 14. Juli 2011

MonoTouch: UITextView mit zentriertem Inhalt bei der Bearbeitung

In einer IPhone-App hatte ich gerade das Problem, dass ich eine UITextView (Textbox) implementiert habe, die im Bearbeitungsmodus (nach Klick) immer den beinhalteten Text außerhalb des sichtbaren Bereichs verschoben hat. Das heißt, man musste jedes Mal scrollen um den Text zu sehen.

So sieht das ganze dann im Einsatz aus:

Dieses liegt daran, dass die UITextView standardmäßig einen unteren "Rahmen" von 32 Pixel einfügt. Da auch standardmäßig das Autoscrolling aktiv ist, wird immer die letzte Zeile fokussiert. Somit rutsch der eigentlich Inhalt/Text soweit nach oben, dass er nicht mehr sichtbar ist.

Abhilfe schafft hier die kleine Property "ContentInset".

Codebespiel:
UITextView textView = new UITextView(new RectangleF(10, 10, 280, 30));
textView.ContentInset = new UIEdgeInsets(0, 0, 0, 0);

Und so sieht das Ganze dann mit der gesetzten Property aus:

Freitag, 8. Juli 2011

SQLite BulkInsert-Alternative - Kleiner Code, große Wirkung


Heute bin ich auf ein Performance-Problem beim Füllen einer SQLite Datenbank gestoßen. Und zwar nutze ich für eine mobile Anwendung eine Datenbank, welche sich täglich komplett  inhaltlich verändern kann. Diese Datenbank wird dann per REST Webservice aus einem ASP.Net Web heruntergeladen.

Die Datenbank beinhaltet zwei Tabellen mit jeweils zehn Spalten. Da es bei SQLite kein Bulk-Insert wie z.B. beim SQL-Server gibt, habe ich zuerst versucht die ca. 40.000 Datensätze nacheinander ganz einfach per INSERT einzufügen. Dieses war mir aber deutlich zu langsam, so dass ich nach optimaleren Alternativen gesucht habe. Und ich bin auch recht schnell fündig geworden. Hierfür konnte ich sogar meine bestehende Programmierung fast komplett behalten. Ich musste lediglich einen kleinen Teil umstellen und um ein paar Zeilen Code erweitern.

Hier ist erst einmal zur Übersicht der Aufbau der ersten Tabelle.

CREATE TABLE Artikel(Artikelnummer VARCHAR(255) PRIMARY KEY ,EAN 
VARCHAR(255),Artikelbezeichnung1 VARCHAR(255),Artikelbezeichnung2 
VARCHAR(255),Hersteller VARCHAR(255),EmpfVK FLOAT,Verfuegbarkeit INTEGER, 
Gewicht FLOAT, Status_NichtVerfuegbar BOOLEAN,Artikelinfo 
TEXT);


Das Befüllen der Tabelle sieht dann in meine Programmierung wie folgt aus.


   1:  
   2: SQLiteConnection connection = new 
   3: SQLiteConnection();
   4:      
   5: SQLiteCommand command = new 
   6: SQLiteCommand(connection);
   7: command.CommandText = "INSERT INTO Artikel 
   8: VALUES 
   9: (@Artikelnummer,@EAN,@Artikelbezeichnung1,@Artikelbezeichnung2,@Hersteller,@EmpfVK,@Verfuegbarkeit,@Gewicht,@Status_NichtVerfuegbar,@Artikelinfo);";
  10: command.Prepare();
  11:  
  12:  
  13: SQLiteTransaction tr = connection.BeginTransaction();
  14: command.Transaction = tr;
  15:  
  16:  
  17: for (int 
  18: i = 0; i < listArtikel.Count; i++)
  19: {
  20:   command.Parameters.Add(new SQLiteParameter("@Artikelnummer", listArtikel[i].Artikelnummer));
  21:   command.Parameters.Add(new SQLiteParameter("@EAN", 
  22: listArtikel[i].EAN));
  23:   command.Parameters.Add(new SQLiteParameter("@Artikelbezeichnung1", 
  24: listArtikel[i].Artikelbezeichnung1));
  25:   command.Parameters.Add(new SQLiteParameter("@Artikelbezeichnung2", 
  26: listArtikel[i].Artikelbezeichnung2));
  27:   command.Parameters.Add(new SQLiteParameter("@Hersteller", 
  28: listArtikel[i].Hersteller));
  29:   command.Parameters.Add(new SQLiteParameter("@EmpfVK", 
  30: listArtikel[i].EmpfVK));
  31:   command.Parameters.Add(new SQLiteParameter("@Verfuegbarkeit", listArtikel[i].Verfuegbarkeit));
  32:   command.Parameters.Add(new SQLiteParameter("@Gewicht", 
  33: listArtikel[i].Gewicht));
  34:   command.Parameters.Add(new SQLiteParameter("@Status_NichtVerfuegbar", 
  35: listArtikel[i].Status_NichtVerfuegbar));
  36:   command.Parameters.Add(new SQLiteParameter("@Artikelinfo", 
  37: listArtikel[i].Artikelinfo));
  38:  
  39:  
  40:   command.ExecuteNonQuery();
  41:  
  42:  
  43:   command.Parameters.Clear();
  44: }
  45:  
  46:  
  47: tr.Commit();
  48: command.Dispose();

Das Wichtigste an dieser Programmierung ist, dass sich die Parameter-Zuweisung sowie die Ausführung (ExecuteNonQuery) innerhalb der gestarteten Transaktion liegt. Außerdem sollte das eigentliche SQL-Statement (Command.Text) außerhalb der Schleife und nicht bei jedem Durchlauf zugewiesen werden.

Ein weiterer wichtiger Punkt ist das Aufbereiten des SQL-Statements mit command.Prepare().

Mir hat die Implementierung der Transaktion eine Zeitersparnis von fast 90% gebracht. Ich denke das ist ein sehr erstaunliches Ergebnis.

Samstag, 2. Juli 2011

Base64-Strings encodieren und decodieren

Gerade wenn man Web-Applikationen entwickelt und mit Datenübertragungen zu tun hat, wird man regelmäßig mit sogenannten Base64-Strings konfrontiert. Wenn man dieses zum ersten Mal hört, fragt man sich wahrscheinlich, was das eigentlich ist.
Dabei ist das alles eigentlich ganz einfach. Base64 ist eine Kodierung für 8-Bit-Binärdaten wie zum Beispiel Bilder, PDF-Dokumente oder Zip-Archive. Bei einer Base64-Kodierung werden die Binärdaten zum Beispiel eines Bildes in einen lesbaren ACII-String umgewandelt. Daher ist auch oft die Rede von einem Base64-String. Erkennen kann man eine solche Zeichenkette daran, dass keine Sonderzeichen außer "+", "/" und "=" vorhanden sind und der String immer mit einem "="-Zeichen endet.

Setzt man das ganze nun in der Programmierung (C# / .Net) um, könnte es wie folgt aussehen.

Daten encodieren / String in Base64-String umwandeln

public string base64Encode(string dataToEncode)
{
     // Übergebenen String in ein ByteArray umwandeln
     byte[] bytesToEncode = System.Text.Encoding.UTF8.GetBytes(dataToEncode);

     // Erstelltes ByteArray in einen Base64 kodierten String umwandeln und zurückgeben
     return Convert.ToBase64String(bytesToEncode);
}  

Daten decodieren / Base64-String in String umwandeln

public string base64Decode(string dataToDecode)
{
     // Ein Objekt der Klasse ASCIIEncoding instanziieren
     System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

     // Übergebenen String in ein ByteArray umwandeln
     byte[] bytesToDecode = Convert.FromBase64String(dataToDecode);

     // Erstelltes ByteArray aus dem Base64-String in einen decodierten (nicht kodierten) String umwandeln und zurückgeben
     return encoding.GetString(bytesToDecode);
}