Windows 8 cp auf Asus Eee Slate ep121

Ich habe mir vor ein paar Wochen die Zeit genommen, die Windows 8 consumer preview einmal genauer anzusehen.

Für meine ersten Versuche habe ich einfach die Windows Server 2008R2 Festplatten aus meinem Dell DP490 genommen und eine alte 160 GB Festplatte eingebaut. Die Installation von Windows 8 verlief problemlos und im Einsatz zeigten sich bei mir keinerlei Aussetzer.

So festigte sich mit der Zeit mein Plan, dass ein Tablet mit Windows 8 Sinn machen würde. Einige Tage habe ich im Internet recherchiert und dann stand meine Tablet Hardware fest: Asus Eee PC ep121. Das System kommt mit Windows 7 daher aber dass die Installation von Windows 8 klappen müsste zeigte sich durch diverse YouTube Videos und auch durch folgenden Blog Eintrag: http://www.wintellect.com/cs/blogs/jrobbins/archive/2011/02/04/asus-eee-slate-ep121-review.aspx.

Da man nie 100% sicher sein kann: Bestellung bei Amazon – dort ist man sehr kulant und man kann das Gerät 30 Tage lang zurück geben…

Nach der recht schnellen Lieferung ging es auch direkt los:
– Mittels Windows Backup Möglichkeiten ein Backup der Platte auf ein externes USB Laufwerk gemacht (sicher ist sicher)
– Mittels Windows DVD/USB Download Tool das ISO Image von Windows 8 CP with Apps 64Bit auf ein USB Stick geschrieben.

Die Installation selbst verlief danach sehr einfach durch. Bei der Installation habe ich nur das Tablet benutzt – ohne externer Tastatur oder Maus. Das Touchpad funktionierte von Anfang an perfekt.

Die Anmeldung am System habe ich einfach per Windows Live ID durchgeführt. Die Integration mit Windows Live ist zwar noch nicht ganz so toll (Ich kann es kaum erwarten eine neue Version in die Hände zu bekommen. Ich habe gehört, dass evtl. im Juni schon eine neue Windows 8 Beta erscheinen soll! Und dass die Integration besser geht hat Microsoft ja bereits mit dem Windows Phone 7 gezeigt!) aber die Kernfunktionalität ist auf jeden Fall da.

Wer den anderen Blogbeitrag gelesen hat incl. aller Kommentare wird sicher gespannt sein, wie meine Erfahrungen bezüglich Treiber sind:
– Blauer Zahn (Bluetooth): Direkt nach der Installation fehlten die Treiber, aber per Windows Update wurde auch hier von Microsoft bereits ein funktionierender Treiber bereit gestellt. Das mitgelieferte Keyboard funktionierte mit Windows 8 – nach der Update Aktion – einwandfrei.
– Eingebaute Kamera: Funktioniert einwandfrei
– Zusätzliche Knöpfe – die sind ohne Funktion, aber ich empfinde dies nicht als Problem. Ich wüsste nicht, wozu ich diese Knöpfe einsetzen sollte. Eine Taste zum Keyboard einblenden und sowas ist nicht notwendig.
– Lageveränderung des Displays funktioniert nicht. Aber das empfinde ich nicht als Manko. Das Display ist ein Wide Screen Display – das will ich nicht hochkant halten. Einmal den Bildschirm gedreht und die Erfahrung war nicht positiv! Das drehen macht bei 16:10 Sinn, aber bei dem 16:9 (??) empfand ich den hochkant Modus als sehr irritierend (z.B. um eine A4 Seite zu lesen – da hat man oben und unten einen Rand… ).
– Externer Monitor ist anschließbar. Da ich aber keinen Adapter habe, konnte ich es nicht ausprobieren. Der Treiber ist aber vorhanden und man findet direkt die Einstellungen unter Devices im rechten Menü.

Somit habe ich nun einen vollwertigen Rechner (i5 Prozessor, 4GB Hauptspeicher) mit dem ich alles machen kann, das ich auch sonst so mit einem Rechner mache. Ich nutze gerne an festen Arbeitsplätzen bluetooth Geräte (Tastatur und Maus) so dass ich ein vollwertigen Rechner habe und Unterwegs oder auf dem Sofa nutze ich das Gerät als Tablet was – trotz des recht hohen Gewichts von 1,6 kg) sehr gut funktioniert.

Meine Windows 8 Erfahrungen

Ich habe durchweg positive Erfahrungen mit Windows 8 gemacht. Sowohl auf dem Desktop als auch auf dem Tablet. Die Nutzung ist zwar gewöhnungsbedürftig, aber wenn man die Bedienung einmal heraus hat, dann klappt alles einwandfrei.

Auf dem Desktop mit 24″ Monitor waren die Metro Style Apps durch Ihren Vollbildmodus in dem diese laufen jedoch fast immer eine Zumutung. Hier bezweifle ich, dass die Metro Style Apps eine wirkliche Alternative zu den Desktop Applikationen werden, aber diese werden einem ja nicht aufgezwungen. Nach der Erfassung der Möglichkeiten, empfand ich die Bedienung auch auf dem Desktop besser als Windows 7, wobei ich schon mitbekommen habe, dass dies eine sehr persönliche Einstellung ist. Die Metro-Oberfläche wird von einigen Anwendern auf den Desktops kategorisch abgelehnt. Aber lassen wir uns einfach einmal überraschen, was Microsoft hier noch ändern wird – das Feedback wird von Microsoft ja ausgewertet und ich erwarte noch einige Anpassungen. Wir dürfen nicht vergessen, dass es sich erst um eine Preview handelt und um keine richtige Beta!

Bezüglich der Hardware des Asus Eee Slate ep121 möchte ich aber nicht versäumen ein paar wichtige Punkte anzumerken:
– Die USB Anschlüsse haben eine Verschlusskappe, die nur recht schwer zu entfernen ist. Dies hätte Asus meiner Meinung nach besser lösen sollen. Aber dank stabiler Fingernägel ist dies kein wirklich großes Problem.
– Die Festplatte ist mit 60 GB relativ klein ausgelegt. Es passt zwar derzeit alles drauf (Windows 8 + Office 2010 + Visual Studio 11 Beta + SkyDrive Sync + ….), aber man merkt doch etwas, dass es an die Grenzen stößt. Eine externe USB Platte und eine 64GB SD Karte sind aber sehr sinnvoll und ich überlege derzeit, ob ich die SSD Disk nicht doch einfach austausche (YouTube zeigt Videos, wie der Austausch gehen würde und ein 120GB Platte ist auch nicht so teuer. So gibt es die OCZ Nocti 120GB mSATA SSD schon für unter 160€.
– Der Akku hält im Betrieb nicht besonders lange. meine Erfahrung ist, dass der Akku nach 2,5 – 3 Stunden (Betrieb) leer ist. Dies ist etwas unschön, aber für mich kein wirkliches Hindernis. Wichtig war für mich der vollwertige Rechner.

Überblick über die Software Entwicklung (3. Value Type vs. Reference Type)

Wenn wir uns die vorherigen Blogeinträge durch den Kopf gehen lassen, dann merken wir, dass wir Dinge teilweise unterschiedlich gespeichert haben:

Teilweise haben wir Werte direkt in eine “Kiste” geschrieben und teilweise haben wir Referenzen erhalten.

Im .Net Framework wird zwischen Value Types und Reference Types unterschieden. Man kann hier eine ganz einfache Regel aufstellen: Strukturen sind Value Types und Klassen sind Reference Types. So ist System.Int32 ein Struktur und somit ein Value Type. System.Diagnostics.Process ist jedoch eine Klasse also ein Reference Type.

Also schauen wir uns einmal etwas C# Code an und schauen wir, was passiert:

Int32 a; // Hier wird direkt der Platz für ein Int32 reserviert.

Process proc; // Hier wird nur der Platz für eine Reference auf einen Process reserviert.

Da bei dem Reference Type nur Platz für eine Reference geschaffen wurde, habe ich noch kein Objekt vom Typ Process. Aber die erste Zeile hat mir direkt ein Objekt vom Typ Int32 erstellt.

Um eine Instanz vom Typ Process zu bekommen, muss ich dies erst explizit anfordern:

proc = new Process();

Bei dem Befehl wird nun die Speicherverwaltung aufgefordert, Platz für ein Objekt vom Typ Process zu reservieren und die Reference wird in proc gespeichert.

Der große Unterschied wird auch deutlich, wenn man die Funktionsaufrufe betrachtet:

Wenn ein Value Type einer Funktion übergeben wird, wird der ganze Inhalt kopiert. Wird ein Reference Type übergeben, so wird lediglich die Referenz übergeben. Die Auswirkungen sind leicht ersichtlich:

  • Wenn eine Referenz übergeben wird, dann ist es möglich, das eigentliche Objekt zu verändern. (Ich sage Dir, wo mein Haus steht und dann kannst Du dieser Referenz folgen und dann beliebige Dinge mit meinem Haus machen z.B. klingeln, etwas verändern, …)
  • Wenn ein Wert übergeben wird, dann ist keine Anpassung möglich (Ich schreibe Dir auf einen Zettel, was ich verdiene. Den Wert auf diesem Zettel kannst Du verändern – es wird nichts an meinem Gehalt ändern!)
  • Value Types können auch recht groß sein. Bei jeder Übergabe muss alles kopiert werden. Dies kann einige Zeit mehr in Anspruch nehmen als lediglich das kopieren einer Referenz.

Übergabe von Werten an eine Funktion

Jetzt haben wir schon etwas über die Übergabe von Werten an eine Funktion geredet. Jedoch ist die Welt nicht ganz so einfach, wie sie auf den ersten Blick erscheint.

Betrachtet haben wir oben die Übergabe der Werte als “Value”. Übergabe von Werten über den eigentlichen Wert bedeutet einfach nur, dass beim Funktionsaufruf der Inhalt der Variablen kopiert wird auf den Stack (auf dem die Parameter gelegt werden). Bei einem Reference Type wird also die Reference und bei einem Value Type der Wert kopiert.

Nun kann ich aber Dinge auch als Reference übergeben. Dies wird mit dem Schlüsselwort “ref” deutlich gemacht. Das bedeutet, dass jeweils nur eine Reference auf den eigentlichen Parameter übergeben wird. Somit ist die Variable, die als Parameter angegeben wird, voll veränderbar.

Eine weitere Variante ist ein Ausgabe Parameter (”out” Schlüsselwort). Dies ist vergleichbar mit der Übergabe als Value, aber die Kopierrichtung ist nun unterschiedlich: Bei der Übergabe als Value wurde der Speicherbereich auf dem Stack reserviert und dann vor dem Aufruf der Wert auf den Stack kopiert. Bei einem Ausgabe Parameter wird der Platz reserviert aber vor dem Aufruf keine Daten kopiert. Aber nach dem Aufruf wird der Inhalt vom Stack in die angegebene Variable kopiert.

Überblick über die Software Entwicklung (2. Stack vs. Heap)

Wir haben im ersten Teil bereits erfahren, dass lokale Variablen auf dem Stack landen. Aber wir haben auch erfahren, dass die Daten verloren sind, sobald eine Routine sich beenden, da ja der Stack wieder in den Ursprungszustand gebracht werden muss.

Etwas angeklungen ist aber auch die Lagerverwaltung, bei der Platz reserviert werden kann.

Den Lagerbereich der Lagerverwaltung bezeichnet man oft als Heap. Die Daten sind dann auf dem Heap.

Was passiert denn da genau?

Es gibt eine Lagerverwaltung, die einen bestimmten Speicherbereich verwaltet und die folgende Befehle kennt:

  • Lagerplatz zuweisen: Dabei wird ein freier Lagerbereich mit der spezifizierten Größe gesucht, als belegt markiert und eine Referenz auf diesen Lagerbereich zurück gegeben.
  • Lagerplatz freigeben: Dabei wird ein belegter Lagerbereich als frei gekennzeichnet.

Das hört sich alles relativ einfach an, aber intern ist dies deutlich komplizierter als man meint. Die Kernprobleme, die auftreten können sind z.B. die Nutzung einer Referenz, die jedoch schon freigegeben wurde. Oder eben dass vergessen wird, einen Bereich freizugeben.

Zur Funktion des Heap ist hier jetzt erst einmal  nicht sehr viel mehr zu schreiben. Es bleiben mir nur ein paar Anmerkungen zu “managed” Sprachen:

Da es sich gezeigt hat, dass die Speicherverwaltung recht schnell zu Fehlern auf Seiten der Entwickler führte, wurde überlegt, wie dies optimiert werden kann. Hier sind mehrere wichtige Punkte zu nennen, die moderne Sprachen wie C# und Java umsetzen:

  1. Prüfung der Typen / Größen: Früher war es problemlos möglich, im Lager einen Quadratmeter anzufordern, aber einfach 10 Quadratmeter zu nutzen. (Und dabei Bereiche zu überschreiben, die mit anderen Daten gefüllt waren!) Weiterhin wird geprüft, ob der Inhalt wirklich das ist, was ich erwarte. Also wenn ich ein Haus im Lager erwarte aber es ist Auto, dann bekomme ich einen Fehler.
  2. Die Freigabe von Speicher hat sich teilweise als kompliziert erwiesen. Damit hier keine Fehler auftreten, wurde dies automatisiert. So wird von einem Garbage Collector in regelmäßigen Abständen geprüft, ob es noch Referenzen auf Objekte im Lager gibt. Sollte es keine Referenzen mehr geben, so wird der Speicherbereich freigegeben.

Überblick über die Software Entwicklung (1. Stack)

Eine grundlegende, wichtige Sache bei Computern ist seid frühester Zeit der sogenannte Stack. Für das generelle Verständnis für die inneren Prozesse bei einem Computer ist es aus meiner Sicht wichtig, zu verstehen, wie ein Stack funktioniert.

Um eine Vorstellung von einem Stack zu bekommen, greife ich einfach einmal auf einen kleinen Vergleich zurück:

Wir stellen uns einen Turm aus Kisten vor. Oben auf dem Turm ist ein Kletterer. Nun kann ich folgende Aktionen ausführen bzw. ausführen lassen:

  • Ich kann dem Kletterer eine neue Kiste angeben. Diese wird er dann oben auf den Turm stellen.
  • Ich kann dem Kletterer zurufen, dass er eine Kiste vom Turm herunter schmeißen soll. Die oberste Kiste kann somit jederzeit entfernt werden.
  • Ich kann dem Kletterer nach dem Inhalt einer Kiste fragen bzw. neuen Inhalt für eine Kiste geben . Da der Kletterer oben auf dem Turm sitzt, muss ich hier aber immer sagen, die wievielte Kiste von oben ich meine. Der Kletterer wird mir dann den Inhalt sagen bzw. den neuen Inhalt eintragen.

Da die Inhalte wichtig sind, ist klar: Das alles scheitert, sobald mehrere Personen dem Kletterer Kisten angeben würden. Kann man sich ja schnell überlegen anhand eines Beispiels:

  • Ich werfe dem Kletterer eine Kiste zu. In dieser Kiste vermerke ich die Anzahl meiner Freunde.
  • Ich sage dem Kletterer: Bitte schreib in die Kiste die Zahl 27
  • Nun kommt jemand und wirft dem Kletterer eine andere Kiste zu. In der Kiste steht die 1.000.000. (Dies bekomme ich aber nicht mit!)
  • Nun will ich wissen, wieviele Freunde ich habe. Und ich weiss: Das steht in der obersten Kiste! Also frage ich den Kletterer nach dem Inhalt der obersten Kiste.
  • Meine Freude ist gross … statt 27 Freunde habe ich 1.000.000 Freunde …

Diese Limitation auf eine Bedienung gibt es aber nicht. In Wirklichkeit ist der Stack entstanden, um eben Unterbrechungen handhaben zu können. Hierzu geben wir dem Kletterer noch ein paar mehr Aufgaben:

  • Der Kletterer darf bestimmen, wer ihm zuarbeiten darf.
  • Jemand, der mit seiner Zuarbeit fertig ist, sagt dem Kletterer: Ich bin fertig! Der Kletterer muss dann zurück zu der ursprünglichen Person wechseln, die dann an der Stelle weitermacht, wo er zuletzt war.
  • Man kann dem Kletterer Hinweise zu Prioritäten geben.

Damit dies funktionieren kann, muss dann noch weiterhin klar bestimmt werden: Wenn ich etwas machen will mit dem Stack, dann muss sicher gestellt sein, dass ich genau die Anzahl Kisten am Ende vom Stack herunter nehme, die ich auch drauf gepackt habe.

Wollen wir einfach einmal durchspielen, wie dies nun funktionieren kann:

  • Person A darf anfangen. Er wirft dem Kletterer 3 Kisten mit unterschiedlichem Inhalt zu und macht damit irgendwelche Dinge. Liest den Inhalt und schreibt ihn auch neu.
  • Nun kommt Person B und ruft: Ich habe etwas ganz ganz dringendes zu machen!
  • Der Kletterer tut nun selbst eine Kiste oben auf den Stack und fragt Person A, wo er gerade ist. Dies schreibt er dann in die neue Kiste (“Person A, Arbeitsschritt 27”). Und dann muss Person A warten weil nun B an der Reihe ist.
  • B kann nun seinerseits mehrere Kisten auf den Stapel legen lassen und damit arbeiten.
  • Irgendwann ist B fertig (oder wird von Person C unterbrochen – dann würde das Gleiche passieren wir schon vorher zwischen Person A und B) und weiß dann aber: Ich habe x Kisten aufgelegt: Diese entferne ich nun.
  • Nun ist der Stapel aus Sicht von B wieder unverändert. Nun kann er rufen: “Ich bin fertig!”
  • Der Kletterer schaut nun in die oberste Kiste und sieht: “Person A, Arbeitsschritt 27”. Er entfernt die Kiste und ruft Person A zu: Mach bitte bei Arbeitsschritt 27 weiter!

Der Bezug zum Computer ist recht einfach herzustellen: Wir stellen uns einen alten Computer vor ganz ohne Multitasking. Die CPU ist der Kletterer und die Spitze des Turms ist in einem Register gespeichert. Der Turm kann höher gebaut werden und kleiner gemacht werden. (Da es nur ein Zeiger auf einen Speicherbereich ist, sind das einfache Addition und Subtraktion Operationen für die CPU bzw. ein Increase oder Decrease) Ein Programm wird ausgeführt und macht irgendwas. Nun passiert etwas, auf das der Computer reagieren muss (Ein sogenannter Interrupt). Nun hält die CPU die aktuelle Ausführung an. Das Register, das den Befehl enthält, der als nächstes geschrieben werden sollte, wird auf den Stack geschrieben (“Person A – Arbeitsschritt 27”) und es wird zu einem Codeabschnitt gesprungen, der für Interrupts zuständig ist. Dieser Abschnitt wird dann weiter den Stack füllen (Die Inhalte der Register werden auf den Stack gesichert) und irgendwelche Aktionen durchführen (z.b. eine I/O Operation). Am Ende werden die Register wieder vom Stack zurück geschrieben und dann der Zeiger so modifiziert, wie er am Anfang war. Es wird der CPU signalisiert: “Bin fertig” und schon wird vom Stack die Rücksprungadresse gelesen, der Stackzeiger dabei nun endgültig auf die vorherige Stelle gesetzt und schon wird der nächste Befehl angesprungen. Und das Programm geht genau dort weiter, wo es vor dem Interrupt schon war.

Nun kommt natürlich die Frage auf, was dieses alte Interrupt Verfahren mit modernen Programmiersprachen zu tuen hat. Und wir kommen dann recht schnell zu Funktionsaufrufen und lokalen Variablen:

Lokale Variablen

Wenn ich etwas machen soll und dazu Platz brauche (temporär), dann kann ich natürlich hin gehen und bei einer Lagerverwaltung anfragen: Ich brauche so und so viel Platz um was zu lagern. (Und wenn ich fertig bin, dann muss ich natürlich mitteilen, das ich den Platz nicht mehr brauche!) Dies ist aber sehr aufwendig. Schöner wäre es, wenn ich direkt einen Platz hätte der mir zur Verfügung steht ohne irgendwelche komplizierten Verwaltungsabläufe.

Und da kommt unser Stack gerade Recht: Ich sage einfach, was für Kisten ich auf dem Stack haben möchte und schon habe ich meinen Platz.

So kann ich z.B. 10 Kisten auf den Stack legen lassen und dann auf die Inhalte zugreifen a.la. “Bitte schreib in die 3te Kiste von oben xyz”, “Was steht in der 5ten Kiste von oben?”, …

Genau dies wird von Programmiersprachen gemacht: Wenn eine Routine eine lokale Variable benötigt, so wir diese auf den Stack gelegt und schon steht sie zur Verfügung. Wenn die Routine fertig ist, werden de Kisten lediglich vom Stack genommen und gut ist es. Es wird keine komplizierte Verwaltung benötigt.

Funktionsaufrufe

Interessant wird es bei Funktionsaufrufen. Hier wird das Regelwerk noch etwas modifiziert:

Ehe die Funktion aufgerufen wird, muss ich schauen, was die Funktion zurück geben möchte. Und dann packe ich vor dem Aufruf eine Kiste mit der passenden Größe auf den Stack. (So ich der Funktion noch weitere Dinge mitgeben möchte, dann packe ich noch weitere Kisten auf den Stack die ich mit den Dingen fülle, die ich übergeben will.)

Dann erst rufe ich die Funktion auf. Und es passiert genau das, was auch schon bisher passierte: Es wird eine weitere Kiste aufgestellt, in der vermerkt wird, wo ich gerade war.

Nun kann die Funktion weitere Kisten aufstellen so diese benötigt werden. Anders als vorher ist es aber nun so, dass nun noch eine Kiste verändert wird, die nicht von der Funktion aufgestellt wurde: Die Kiste für die Rückgabe. Welche Kiste das ist, ist auch ganz klar: Wenn 5 Kisten auf dem Stapel hinzugefügt wurden für lokale Variablen, dann ist die 7te Kiste für die Rückgabe.

Am Ende passiert dann auch wieder das bekannte Schema: Die Funktion entfernt die lokalen Variablen und signalisiert das Ende. Der Kletterer schaut in die oberste Kiste und entfernt diese vom Stack. Und dank der Rücksprungadresse geht es direkt nach dem Funktionsaufruf weiter.

Nun kann der Inhalt der obersten Kiste gelesen werden (Rückgabe der Funktion). Die oberste Kiste muss dann entfernt werden, womit der Funktionsaufruf dann endlich beendet ist.

Unsere Kisten

Am Ende noch ein paar Worte zu unseren Kisten: Wenn man sich den Vergleich anschaut, dann scheint es, dass es doch nicht ganz so wenig Aufwand ist: Unterschiedlich große Kisten müssen gestapelt werden. Die Kisten müssen ja auch irgendwoher kommen und transportiert werden …

Dies war halt nur bildlich ausgedrückt. Der Vergleich wird besser passen, wenn man sich einen riesigen Stapel vorstellt mit Standard Kisten. Jede Kiste fasst eine bestimmte Menge (1 Byte, 2 Bytes (WORD), 4 Bytes, 8 Bytes, …) Das kann von System zu System unterschiedlich sein). Die oberste Kiste ist markiert. Wenn ich nun Platz brauche, sage ich genau, wieviele Kisten. Und dann wird der Marker nur eben schnell verschoben.

Das Lesen / Schreiben geht dann ggf. auch über mehrere Kisten.

Ich hoffe, dass diese Erläuterungen etwas hilfreich waren, um zu verstehen, was bei einem Funktionsaufruf in etwa passiert. Dieses Verständnis ist aus meiner Sicht relativ wichtig. (Zumindest habe ich es immer als Vorteil verstanden zu wissen, was intern passiert oder nicht passiert.)

Überblick über die Software Entwicklung (Einführung)

Ich wurde vor kurzem gebeten, doch einmal eine kleine Einführung in die Software Entwicklung zu geben. Und statt die mir wichtig erscheinenden Punkte in einer Email zu erläutern, habe ich beschlossen, statt dessen doch lieber einen Blog Beitrag zu verfassen – in der Hoffnung, dass diese Informationen evtl. doch noch für mehrere Personen interessant sind.

Dies wird keine Einführung in eine konkrete Programmiersprache. Statt dessen versuche ich (auf sehr einfache Weise) einige grundlegenden Dinge zu erläutern. (Da ich in erster Linie in C# entwickle, wird alles an C# / .Net orientiert sein. Aber die Prinzipien sind überall gleich, nur eben ändern sich die Namen …)

Ich werde versuchen, folgende Dinge etwas zu erläutern:
– Funktionsweise Stack
– Variablen auf dem Stack vs. auf dem Heap
– Value Type / Reference Type
– Objektorientierte Software Entwicklung

XPS Dokumente erstellen – Content als XML schreiben

XPS (Xml Paper Specification) ist ein Format ähnlich wie PDF. Bei XPS Dokumenten handelt es sich streng genommen um ein ZIP Archiv, in dem das Dokument in Form mehrere XML Dateien abgelegt und benötigte Ressourcen wie Bilder und Schriftarten mit eingefügt werden.

XPS Dokumente lassen sich im Code recht einfach erstellen. Eine oft benutzte Möglichkeit ist dabei, auf Visuals zurückzugreifen, d.h. der Inhalt wird einfach in XAML zusammen gestellt und dann auf einer Seite platziert. Diese Möglichkeit wird im Web an mehreren Stellen ausführlich beschrieben, so dass ich an dieser Stelle nicht weiter darauf eingehen möchte.

Statt dessen möchte ich die Dokumente von Grund auf neu erstellen und den Inhalt direkt manuell schreiben. Ein guter Ansatz wird z,B. im Blog von J. Veurink “Generating basic XPS documents” beschrieben. Jedoch geht der Autor dort nicht groß weiter als die Erstellung eines sehr einfachen Basis-Dokumentes und der Benutzer wird recht schnell diverse Probleme bekommen. Ich möchte an dieser Stelle zumindest ein paar weiterer Klippen umschiffen.

Unter http://neitzel.de/XPS.ZIP habe ich ein Archiv mit lauffähigem Code zusammen gestellt. So kann ich im Folgenden lediglich auf einzelne Codeabschnitte eingehen und muss keine weiteren Abhängigkeiten beachten. (Der Code wird mit der Zeit ggf. noch erweitert. Ich habe noch einige Ideen, und vielleicht finde ich die Zeit, diese noch weiter umzusetzen!)

Erstellung des XPS Dokuments

Ein Dokument kann relativ einfach erstellt werden. Der Code findet sich bereits im Blog von J. Veurink.

string fileName = @"MyFile.XPS";

// The file is created from scratch so if it already exists it will be removed.
if (File.Exists(fileName))
    File.Delete(fileName);

// Create the new package with all internal things that are required
Package _package = Package.Open(fileName);
XpsDocument _document = new XpsDocument(_package);
IXpsFixedDocumentSequenceWriter _xpsDocSeqWriter = _document.AddFixedDocumentSequence();
IXpsFixedDocumentWriter _xpsDocWriter = _xpsDocSeqWriter.AddFixedDocument();
IXpsFixedPageWriter _xpsPageWriter = _xpsDocWriter.AddFixedPage();
XmlWriter _xmlWriter = _xpsPageWriter.XmlWriter;

Wichtig ist, dass nach der Durchführung von Änderungen diese am Ende immer Commited werden müssen. Hierzu implementieren die einzelnen Klassen eine Commit() Funktion. Desweiteren ist zu beachten, dass Package und XpsDocument IDisposable implementiert und somit darauf zu achten ist, dass hier ein Dispose durchgeführt wird. (Entweder über eine Implementation von IDisposable in der eigenen Klasse oder über die Verwendung eines using Befehls wenn eine Instanz nur kurzfristig gebraucht wird.)

Einfügen von Ressourcen

Alle Ressourcen, die innerhalb des Dokumentes verwendet werden, müssen mit in das XPS Dokument eingebunden werden. Jede Seite, die eine Ressource verwendet, muss einen Link auf die Uri der eingebundenen Ressource enthalten.

Einfügen von Fonts (Mit verfügbarer Font-Datei)

Eine Font kann Problemlos über eine Schriftarten Datei (z.B. .TTF Datei) in ein XPD Dokument eingebunden werden. Dies kann relativ einfach genau so erfolgen, wie es J. Veurink in seinem Blog erläutert hat:

public void EmbedFont(string fontFamily, string fontFile)
{
    // Add the font to the document
    var font = _xpsPageWriter.AddFont();
    WriteObfuscatedStream(font.Uri.ToString(), font.GetStream(), fontFile);
    font.Commit();
}

Einfügen von Fonts (Installiert im System)

Ebenso ist es möglich, eine Font direkt aus dem System zu lesen. Leider scheint es hierzu keine managed API zu geben, so dass ich auf unmanaged Funktionen zurückgegriffen habe.

Die Funktion meiner Wahl ist GetFontData aus der gdi32.dll. Ein Einbinden einer Font kann über folgende Funktion erfolgen:

public bool EmbedFont(string fontFamily)
{
    byte[] data = null;
    var font = new Font(new FontFamily(fontFamily), 8);
    var hfont = font.ToHfont();
    var hdc = NativeMethods.GetDC(IntPtr.Zero);
    var error = Marshal.GetLastWin32Error();
    var oldFont = NativeMethods.SelectObject(hdc, hfont);
    error = Marshal.GetLastWin32Error();
    // Get the size required to store the font
    var size = NativeMethods.GetFontData(hdc, 0, 0, null, 0);
    error = Marshal.GetLastWin32Error();
    if (size > 0)
    {
        data = new byte[size];
        int effectiveSize = NativeMethods.GetFontData(hdc, 0, 0, data, data.Length);
        if (effectiveSize != size)
        {
            // Read data is not valid!
            data = null;
        }
        NativeMethods.SelectObject(hdc, oldFont);
        NativeMethods.ReleaseDC(IntPtr.Zero, hdc);
        error.GetType();
    }

    // Only continue if we was able to load the data.
    if (data == null)
        return false;

    // Add the font to the document
    var xpsFont = _xpsPageWriter.AddFont();
    WriteObfuscatedStream(xpsFont.Uri.ToString(), xpsFont.GetStream(), data);
    xpsFont.Commit();

    return true;
}

Verlinkung auf bereits eingefügte Fonts

Wenn auf einer weiteren Seite erneut eine bereits eingebundene Font referenziert werden soll, so kann dies zwar theoretisch über die bereits dargestellten Funktionen erfolgen, jedoch erstellt man somit mehrere Kopien der Ressource innerhalb des Dokuments, was zu einem unnötig großen Dokument führt. Somit ist es sinnvoll statt dessen nur einen Hinweis auf die bereits eingefügte Ressource hinzu zu fügen.

Wichtig ist, dass man sich z.B. beim Einfügen die Uri der Ressource/Font merkt. Dann kann eine Verbindung zu dieser Resosurce auf jeder beliebigen Seite eingefügt werden:

// Uri to include
Uri uri;

// Relationship to required ressource
const string RequiredResourceRelationship = @"http://schemas.microsoft.com/xps/2005/06/required-resource";

// get the part in which we have to add the Relationship
var part = _package.GetPart(_xpsPageWriter.Uri);

// Add Relationship
part.CreateRelationship(uri, TargetMode.Internal, RequiredResourceRelationship);

Füllen der Seite

Nun kommen wir zu dem Schreiben des eigentlichen Inhaltes. Bei der Erstellung des XPS Dokuments haben wir auch schon eine erste Seite erstellt und einen XmlWriter erzeugt. Über diesen können wir nun den XML Inhalt schreiben. Was für XML Inhalte möglich sind, entnimmt man am besten den XPS Standards von Microsoft. Dort ist alles recht gut erläutert.

FixedPage

Schreiben wir einmal eine FixedPage:

_xmlWriter.WriteStartElement("FixedPage");
_xmlWriter.WriteAttributeString("Width", format.Width.ToString("0.00", CultureInfo.CreateSpecificCulture("en-US")));
_xmlWriter.WriteAttributeString("Height", format.Height.ToString("0.00", CultureInfo.CreateSpecificCulture("en-US")));
_xmlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/xps/2005/06");
_xmlWriter.WriteAttributeString("xml:lang", "en-US");

In diesem Beispiel verwende ich eine Variable format die von einem eigenen Typ PageFormat ist und lediglich Height und Width implementiert. Die Werte müssen in 1/96” angegeben werden. Bei A4 ist dies 1122.56 und 793.76. In meinem Code habe ich mich komplett auf en-US eingestellt, um Probleme bei dem parsen der Zahlen zu vermeiden.

Dies schreibt dann z.B.:

<FixedPage Width="1122.56" Height="793.76" xmlns="http://schemas.microsoft.com/xps/2005/06" xml:lang="en-US">

Wenn wir die Seite zuende beschrieben haben, dann dürfen wir nicht vergessen, dieses Xml Element zu schließen:

_xmlWriter.WriteEndElement();

Text auf die Seite schreiben

Beim Schreiben von Text gibt es viele Möglichkeiten, aber nicht alles, was so bekannt ist, wird unterstützt.

/// <summary>
/// Write some text to the document
/// </summary>
/// <param name="x">x position of bottom left point (in 1/96" units).</param>
/// <param name="y">y position of bottom left point (in 1/96" units).</param>
/// <param name="size">Size of font to use (in 1/96" units).</param>
/// <param name="text">Text to write</param>
/// <param name="fontFamily">Name of the embedded font.</param>
/// <param name="format">Format to use.</param>
/// <param name="rightAlign">Write text alligned to right side.</param>
/// <param name="color">Color of the text</param>
public void WriteText(int x, int y, int size, string text, string fontFamily, FontStyle format, bool rightAlign, string color)
{
    // Get the font
    var fontRessource = _embeddedFonts[fontFamily];

    // Filter out all non supported FontStyle flags:
    var supportedFlags = format & (FontStyle.Regular | FontStyle.Bold | FontStyle.Italic);
    var formatText = "None";
    switch (supportedFlags)
    {
        case FontStyle.Regular:
            formatText = "None";
            break;

        case FontStyle.Bold:
            formatText = "BoldSimulation";
            break;

        case FontStyle.Italic:
            formatText = "ItalicSimulation";
            break;

        case FontStyle.Bold | FontStyle.Italic:
            formatText = "BoldItalicSimulation";
            break;
    }

    // Revert text so it is displayed correctly
    if (rightAlign)
    {
        var sb = new StringBuilder();
        for (var i = text.Length - 1; i >= 0; i--)
            sb.Append(text[i]);
        text = sb.ToString();
    }

    // Write XML for text
    _xmlWriter.WriteStartElement("Glyphs");
    _xmlWriter.WriteAttributeString("Fill", color);
    _xmlWriter.WriteAttributeString("FontUri", fontRessource.ToString());
    _xmlWriter.WriteAttributeString("FontRenderingEmSize", size.ToString(CultureInfo.InvariantCulture));
    _xmlWriter.WriteAttributeString("OriginX", x.ToString(CultureInfo.InvariantCulture));
    _xmlWriter.WriteAttributeString("OriginY", y.ToString(CultureInfo.InvariantCulture));
    _xmlWriter.WriteAttributeString("StyleSimulations", formatText);
    _xmlWriter.WriteAttributeString("UnicodeString", text);
    _xmlWriter.WriteAttributeString("BidiLevel", rightAlign ? "1" : "0");
    _xmlWriter.WriteEndElement();
}

Wie man erkennen kann, ist man selbst verantwortlich bezüglich der FontStyles und der Plazierung des Textes. Bezüglich FontStyles habe ich bei diesem Beispiel einfach einmal die Styles unterstützt, die von XPS simuliert werden können.

Die Plazierung bezüglich Links / Mittig / Rechts ist hier ebenfalls “ermogelt”. Die Zentrierung ist alles andere als genau und es ist hier nicht zu erwarten, dass die Ergebnisse wirklich überzeugend sind. Aber ich denke, dass dieses Beispiel vielleicht einen Einblick gibt, was möglich ist.

Was aber sehr schnell deutlich wird: Wir schreiben direkt die XML Inhalte, so wie diese von Microsoft für das XPS Dokument beschrieben sind, in die Seite.

Neue Seite erstellen

Wenn eine Neue Seite erstellt werden soll, so ist die bisherige Seite zu schließen und eine neue zu öffnen. Wichtig ist, dass Ressourcen, die verwendet werden sollen, auch auf der neuen Seite vermerkt werden.

_xmlWriter.WriteEndElement(); // XML Tag schließen
_xpsPageWriter.Commit(); // Commit nicht vergessen!

// Create new page (These do not implement IDisposable!)
_xpsPageWriter = _xpsDocWriter.AddFixedPage();
_xmlWriter = _xpsPageWriter.XmlWriter;

Links

Blue Dragon – Ein Alptraum wird wahr …

Ich bin ein absoluter Fan von allen RPGs. Und die Zeitschrift XBG hat Blue Dragon in der Ausgabe 6/07 getestet. Und das gar nicht mal soo schlecht.

Somit stand für mich fest: Auch dieses Spiel muss her. Und damit begann mein Alptraum …

Die Entwickler haben scheinbar das Problem erkannt. So haben sie mich in Ihrer Sprache scheinbar angefleht, doch endlich das Spiel aus der XBox zu nehmen und irgendwo in einem Schrank zu verstecken … Leider habe ich diese Zeichen nicht sofort verstanden …

Aber beginnen wir am Anfang:

Man spielt am Anfang einen kleinen Jungen. Man wird da richtig reingesteckt und steht nun da in einem Dorf und hat ein riesiges ? in seinem Kopf. Glücklicherweise startet nach etwas herum laufen eine Videosequenz und man erhofft sich ein paar Informationen … Leider kommen nicht viele Informationen rüber und die Entwickler sind so nett, und lassen einen nun erst einmal den Opa des Jungen spielen. Wobei spielen kann man das nicht nennen, denn man kann sich nur sehr begrenzt bewegen und mit den anderen Dorfbewohnern reden. (Wer sich hier wertvolle Informationen erhofft: Ich war doch sehr enttäuscht!). Nach kurzer Zeit wechselte es aber wieder und nun spielte man wirklich den Jungen …

Den Kampf mit einem fiesen Gegner hat man dann auch irgendwann hin bekommen und dann bewegte man sich mehr oder weniger langsam durch die Geschichte. Es schlossen sich mehr und mehr Leute dem Jungen an, so dass man in den Kämpfen schön mehrere Figuren gleichzeitig steuern konnte.

Die Kämpfe laufen nicht nicht in Echtzeit ab. Dies hat den Vorteil, dass man in Ruhe Befehle an seine Figuren geben kann, wenn diese an der Reihe sind. Dies macht es gerade am Anfang sehr leicht für Anfänger. Da es aber bei der Bewegung über die Landkarte zu sehr vielen Kämpfen kommt, werden diese Kämpfe aber mit der Zeit extrem nervig. Spätestens wenn man dann einen Gegner trifft, der dann im Kampf einen Erstschlag hat und mal eben den Tank aus den Latschen haut. Solche Gegner sind aber sehr selten und ich habe sie eigentlich nur an einer Stelle im Spiel richtig schlecht erleben dürfen (Da habe ich mir aber doch gedacht, dass die Entwickler nicht wirklich bedacht haben, was sie da auf mich los geschickt haben. Ich habe mehr oder weniger abwechselnd Ratten und Tiger bekommen,wobei die Ratten  freie Erfahrung für meine Gruppe waren und die Tiger durchaus meine zwei Melee Spitzen mal eben so plattgemacht haben…

Aber ok, ich wollte da ja durch. Ich will den Bösewicht jagen. Und siehe da: Ich bin auf dem 2ten Medium (von 3) und darf den Bösewicht mal wieder entgegen treten. Das Geschehen ist aber relativ seltsam: Meiner Gruppe wird die Magiekunst geklaut, die Gruppe liegt flach. Shu ist ganz alleine im Kampf gegen den Bösewicht. Der will mit seinem Roboter mich kleinen Jungen zertrampeln.

Gut, dass ich dies verhindern kann: Einfach nur wild auf eine vorgegebene Taste des XBox Controllers rumhämmern. (Solche Situationen gibt es von Zeit zu Zeit. Wobei man da auf der Hut sein muss, denn es kommt dann halt die Aufforderung sowas zu tun und man nur begrenzt Zeit. sonst passiert etwas. In diesem Fall zertrampelt der Roboter den armen Shu und fügt ihm damit 180 Schaden oder so zu.)

Also ich versuche den X-Knopf zu zertrümmern und es gelingt mir, so einen Balken zu füllen. Super. Nun kommt die Kampfauswahl. Was will ich? Angreifen? Verteidigen? Einen Gegenstand nutzen? Leider kommt dann nur eine Anzeige, dass dies derzeit nicht möglich ist, da Shu ja gerade zertrampelt wird. Dies kann man sich auch Minutenlang ansehen. Es passiert halt minutenlang (!!!!) nichts. Ach nein. Stimmt natürlich nicht: Die Entwickler versuchen einen zu Überreden, die Medien im Schrank zu verstauen. Dies machen sie mit Ihrer “Seekrankheit”-Taktik: Die Kamera bewegt sich schön leicht etwas von rechts nach links und so. Nach einiger Zeit kommt dann die Einblendung: X Taste oft drücken. Bis man damit loslegt, ist es aber meist schon zu spät und man bekommt einmal kräftig Schaden. Dann darf man etwas auswählen, das aber nicht geht … Natürlich immer mit schöner Wartezeit …

Und dann habe ich es endlich geschafft: Der Held ist tot! Und nein – kein “Lang lebe der Held”. Der Held wird jetzt zu Grabe getragen und sollte er noch einmal auferstehen, so kann sich der Junge Shu sicher sein, das ich das “Hausrecht” nutze und ihm den Zugang zu meiner XBox für alle Zukunft verwehre.

Eine Frage stellt sich mir nun aber dennoch: Im Spiel ist man immer wieder an Kisten vorbei gekommen, die durch ein farbliches Schutzfeld geschützt waren. Als man diese Schutzfelder wegmachen konnte: Ist da wirklich nochmal jemand all die Gebiete abgelaufen um die Kisten zu leeren? Dazu konnte ich mich wirklich nicht durchringen, ehe ich dem Bösewicht entgegen treten wollte ….

Also mein Fazit: Finger Weg! Es sei denn, das Schmerzensgeld, das Sie bekommen, rechtfertigt es 🙂