<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xscDevBlog - LastSharp &#38; Co. &#187; C#</title>
	<atom:link href="http://dev.xscheme.de/tag/c/feed/" rel="self" type="application/rss+xml" />
	<link>http://dev.xscheme.de</link>
	<description>Der xscheme-DevelopmentBlog</description>
	<lastBuildDate>Sun, 23 May 2010 11:40:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Vom Gedankenspiel zum Versuch: Universal Version Description (UVD)</title>
		<link>http://dev.xscheme.de/2009/05/universal-version-description-uvd/</link>
		<comments>http://dev.xscheme.de/2009/05/universal-version-description-uvd/#comments</comments>
		<pubDate>Fri, 01 May 2009 22:24:43 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Theorie]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[pad]]></category>
		<category><![CDATA[portable application description]]></category>
		<category><![CDATA[universal version description]]></category>
		<category><![CDATA[update check]]></category>
		<category><![CDATA[uvd]]></category>
		<category><![CDATA[version check]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=713</guid>
		<description><![CDATA[Das Problem der Updates
Ich bin in meinem vorletzten Artikel zum Thema Update-Check bereits darauf eingegangen: Versionsänderungen sind nicht immer leicht zu verfolgen und die Überprüfung auf Updates erfordert meist einen nicht zu unterschätzenden Programmier- und Verwaltungsaufwand. Weiterhin bedeutet es für den Entwickler selbst auch, dass er auf mehreren Hochzeiten tanzen muss: er stellt meist eine [...]]]></description>
			<content:encoded><![CDATA[<h2>Das Problem der Updates</h2>
<p>Ich bin in meinem <a href="http://dev.xscheme.de/2009/04/gedankenspiel-update-notification-server/">vorletzten Artikel zum Thema Update-Check</a> bereits darauf eingegangen: Versionsänderungen sind nicht immer leicht zu verfolgen und die Überprüfung auf Updates erfordert meist einen nicht zu unterschätzenden Programmier- und Verwaltungsaufwand. Weiterhin bedeutet es für den Entwickler selbst auch, dass er auf mehreren Hochzeiten tanzen muss: er stellt meist eine eigene Downloadseite bereit, die Links zu verschiedenen Versionen enthält, er aktualisiert gleichzeitig die Dateien, die für die Versionsüberprüfung zuständig sind, er kümmert sich um die Daten, die in irgendwelchen Softwareverzeichnissen (heise, chip.de, etc&#8230;) stehen, usw., usw&#8230; Als verantwortungsvoller Entwickler mit der Ambition, sein Programm unter die Leute zu bringen, hat man richtiggehend die Pflicht, diese Schritte durchzuführen &#8211; und das regelmäßig und mit äußerster Genauigkeit.</p>
<p>Hier nun also das (absolut logische und vernünftige) Konzept, das die meisten Beispiele, die man im Web zu dem Thema &#8220;Update-Check&#8221; findet, implementieren:</p>
<p style="text-align: center;"><img class="alignnone size-full wp-image-715" title="Allgemeines Konzept zum Update-Check" src="http://dev.xscheme.de/wp-content/uploads/2009/05/unserver.png" alt="Allgemeines Konzept zum Update-Check" width="600" height="300" /></p>
<p>Augenscheinliche Probleme hierbei:</p>
<ul>
<li> Die Verwaltung der Programmdaten läuft (beim Otto-Normal-Programmierer) meist von Hand ab, d.h. man bearbeitet die Update-Datei (die nicht, wie im Beispiel, im XML-Format vorliegen kann), lädt sie von Hand auf den <em>eigenen</em> Webserver und ist aus Kompatibilitätsgründen meist an ein (demnach schwer erweiterbares) Format und an einen bestimmten Dateinamen gebunden.</li>
<li>Je nachdem, wie detailliert die Update-Informationen sind (nur Versionsnummer vs. Versionsnummer, Änderungsdatum, Changelog, &#8230;), kann das Bearbeiten der Versionsdatei eine Heidenarbeit bedeuten.</li>
<li>Der Entwickler muss das Programm erst einmal dazu bringen, einen funktionierenden Versions-Check durchzuführen.</li>
<li>Und was passiert bei Programmen, die mehrsprachig funktionieren und beworben werden?</li>
</ul>
<p>Die Idee im erwähnten Artikel war es also, den &#8220;Update Notification Server&#8221; nicht mehr als Eigentum des Entwicklers zu sehen, sondern als über ein API zugängliche Plattform im Internet, die Verwaltungsfunktionen komfortabel bereitstellt. Gleichzeitig wären verschiedene Bibliotheken für das API nötig, um dem Programmierer die Implementierung der Versionsprüfung fast gänzlich abzunehmen.</p>
<p>Was folgte, war ein Einspruch.</p>
<p><span id="more-713"></span></p>
<p>Es sei nicht garantierbar, dass der verantwortliche zentrale Server auch in zwei Jahren noch erreichbar sein würde, wurde argumentiert. Man könne das bei einem eigenen Server viel eher kontrollieren &#8211; und außerdem habe heutzutage ohnehin jeder Entwickler zumindest ein bisschen Webspace.</p>
<p>Dem kann man nicht widersprechen. Zwar würde eine zentrale Plattform den Komfort steigern, allerdings unvermeidbar auch das Risiko. Was also tun?</p>
<h2>Unvereinbarkeit</h2>
<p>Ein großes Problem bei den vielen Versionsprüfungsimplementierungen (was für ein Wort!) im Web ist die Unvereinbarkeit. Die meisten basieren auf verschiedenen Dateiformaten, die wiederum unterschiedlichen Informationsgehalt bieten. Eine reine Textdatei mit der Versionsnummer mag dem <a href="http://www.aeroxp.org/board/index.php?showtopic=11508">einen</a> reichen, der <a href="http://themech.net/2008/05/adding-check-for-update-option-in-csharp/">andere</a> benötigt aber vllt. auch schon den Downloadlink für die aktuelle Version dazu. Oder gar den Namen des Programms und Zusatzmeldungen. <a href="http://beta.unclassified.de/code/dotnet/updatecheck/">Kann passieren</a>.</p>
<p>Wenn einem schließlich keine der Lösungen des World Wide Webs zusagt, dann bleibt nichts anderes zu tun, als eine bestehende Lösung anzupassen. Oder <em>from scratch</em>, also komplett neu zu beginnen. Das wiederum kostet Zeit und Energie, Schweiß und Kaffee, von den Nerven gar nicht erst zu sprechen. Was also tun? (Zweimal haben wir uns das jetzt schon gefragt!)</p>
<p>Eine Lösung, die es allen recht macht, wird es vermutlich nie geben, aber das heißt nicht, dass man nicht versuchen darf, in ihre Nähe zu kommen. Was benötigt wird, ist ein <strong>standardisiertes Dateiformat für Softwarebeschreibungen</strong>.</p>
<h2>Standards</h2>
<p>Es gibt so etwas bereits unter dem Namen <strong>Portable Application Description (PAD)</strong>, nachzulesen beispielsweise in <a href="http://de.wikipedia.org/wiki/Portable_Application_Description">Wikipedia</a> oder der <a href="http://www.asp-shareware.org/pad/">Association of Shareware  Professionals</a>, anzusehen u.a. <a href="http://download.agilita.de/pad_file.xml">hier</a>. Der Vorteil: das <em>ist</em> in der Tat ein Standard; der Nachteil: er wurde für den Vertrieb von <a href="http://de.wikipedia.org/wiki/Shareware">Shareware</a> entworfen. Außerdem wird ein alternatives XML-Namensraumkonzept verwendet, was sich für einen solchen Standard meiner Meinung nach nicht gehört.</p>
<p><img class="alignright size-full wp-image-717" style="margin-left: 1em; margin-bottom: 0.5em;" title="Portable Application Description" src="http://dev.xscheme.de/wp-content/uploads/2009/05/pad.png" alt="Portable Application Description" width="300" height="310" />Aber ganz allgemein hat das Format in meinen Augen signifikante Schwächen:</p>
<ul>
<li>Erweiterbarkeit: Man darf genau <em>einen</em> Screenshot angeben, <em>eine</em> Informations-URL, usw&#8230;</li>
<li>Es findet keine Unterscheidung nach unterstützen Betriebsystemen statt. Angenommen, es gäbe einen Installer, der das Programm auf den neueren Windows-Versionen einrichtet, sowie einen, der es sogar auf Windows 95 zum Laufen bringt: PAD bietet keine Möglichkeit, diese Dateien zu unterscheiden bzw. überhaupt erst vernünftig gemeinsam anzubieten.</li>
<li>Wir leben in einer multilingualen Welt. Die meisten Programme sind für mehrere Sprachen ausgelegt, doch würde man versuchen, Informations/Download-URLs nach Sprache anzubieten, würde man an PAD absolut <em>insane</em> werden.</li>
<li>Überhaupt fehlt so etwas wie Hinweise auf Support im Web in PAD. Man kann Telefonnummern angeben, aber das Leben und die Lebenshilfe findet heutzutage nun einmal zu größeren Teilen im Internet statt als noch früher. Und E-Mail-Adressen bedeuten bloß Wartezeit.</li>
<li>Man kann in PAD genau einen Entwickler angeben. Was macht man bei heutzutage weit verbreiteten Entwickler-Teams?</li>
<li>Das Format hat eine Struktur, die das Verständnis manchmal erschwert. Aber es wird ja von Computern verarbeitet, also ist das akzeptabel. Andererseits: auch Suchmaschinencrawler sind Computerprogramme, und PAD (ein Standard!) wird von denen so gut wie ignoriert!</li>
<li>Was Nutzer oftmals interessiert, ist die Entwicklung eines Programms: Was gibt es neues? Was wurde entfernt, was verbessert?</li>
</ul>
<p>Es ist vielleicht etwas hochgegriffen, aber für die Softwarebeschreibung der heutigen Zeit muss ein neuer Standard erarbeitet werden. Und hier wird aus dem Gedankenspiel der Versuch.</p>
<h2>Was macht Software aus?</h2>
<p>Um welche Punkte müsste sich ein neues Format kümmern, was sind seine Prämissen und Ziele? Und was genau will ich eigentlich? Ein Brainstorming:</p>
<ul>
<li>Sprachenunterstützung</li>
<li>Änderungsverfolgung</li>
<li>Autorenliste</li>
<li>Firmenkontaktdaten</li>
<li>Lizenzinformationen</li>
<li>Suchmaschinen-Metadaten</li>
<li>Installationsanweisungen</li>
<li>Abhängigkeiten: Was brauche ich, damit das Programm läuft?</li>
<li>Einfacher Zugang zu Support-Quellen (Foren, Anleitungen, FAQ)</li>
</ul>
<p>Es entwickelt sich vor meinem inneren Auge also langsam eine XML-Struktur, die das alles mit sich bringt: <strong>XML Application Description (XAD)</strong>, veranschaulicht durch ein <a href="http://dev.xscheme.de/xad/lastsharp.xad.xml">Beispiel</a> (Rechtsklick &gt;&gt; Quelltext), das verwendete <a href="http://dev.xscheme.de/xad/xad.xsl">XSLT-Stylesheet</a> und die <a href="http://dev.xscheme.de/xad/xad.dtd">DTD</a> (das Dokument ist <a href="http://www.validome.org/grammar/validate/?lang=ge&amp;viewSourceCode=1&amp;grammarTyp=DTD&amp;url=http://dev.xscheme.de/xad/xad.dtd">valide</a>, ich weiß nicht, warum Firefox und der IE da rumspinnen).</p>
<h2>Ein Gedanke</h2>
<p>Dann kam mir ein Gedanke: <strong>nicht nur Programme</strong> unterliegen Änderungen, auch Dokumente, Filme, Serien lassen sich einem Versionskonzept unterordnen. Zwei Beispiele: die FAZ vom 30.04.2009 könnte ebensogut die Versionsnummer 2009.119 (der 30.04. ist der 119. Tag des Jahres 2009) tragen, und die Special Extended Edition von &#8220;Herr der Ringe: Die zwei Türme&#8221; könnte auch &#8220;Herr der Ringe 2.1&#8243; heißen (wenn man keinen Wert auf Gewinn legen würde). Mit ein wenig Phantasie überträgt man das ganze auf Autos, CPUs, kurz gesagt: alles, was sich irgendwie als bildlicher <strong>Entwicklungsstrom</strong> darstellen lässt.</p>
<p style="text-align: center;"><img class="size-full wp-image-718 aligncenter" title="strom" src="http://dev.xscheme.de/wp-content/uploads/2009/05/strom.png" alt="strom" width="550" height="230" /></p>
<p>Wichtig hierbei ist die Möglichkeit, stets auf die früheren Versionen eines Objektes zugreifen zu können, weil im Idealfall Verweise auf diese vorhanden sind.</p>
<p>Wenn man also das oben erwähnte Format weiterentwickelt, verallgemeinert und anpasst, dann hat man ein mächtiges Werkzeug für weit mehr als bloß Software. Und hier wären wir.</p>
<h2>Universal Version Description (UVD)</h2>
<p>Da die Verwendung nicht mehr nur auf Programme beschränkt ist, ist es wichtig, anzugeben, was genau in einem Dokument beschrieben wird. Die Nomenklatur hierfür wäre idealerweise etwas wie &#8220;Kategories:Unterkategorie&#8221; oder &#8220;Kategorie&#8221; allein. Beispiele:</p>
<ul>
<li>media:tv-episode</li>
<li>software:utility</li>
<li>paper:specification</li>
<li>paper:mathematics</li>
<li>&#8230;</li>
</ul>
<p>Folgendes wäre ein Beispiel für das kleinstmögliche UVD-Dokument:</p>
<pre>&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE uvd SYSTEM "http://dev.xscheme.de/uvd/uvd-1.dtd"&gt;
&lt;uvd version="1.0.0" type="software:audio"&gt;
  &lt;general&gt;
    &lt;name&gt;WinAmp&lt;/name&gt;
  &lt;/general&gt;
&lt;/uvd&gt;</pre>
<p>Nicht sehr aussagekräftig, aber jetzt bauen wir noch Informations-Links (in verschiedenen Sprachen), sowie Beschreibungstexte (in verschiedenen Sprachen und Längen; die unterstützen Längenangaben sind 100, 255, 2000 und &#8220;any&#8221;) ein:</p>
<pre>&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE uvd SYSTEM "http://dev.xscheme.de/uvd/uvd-1.dtd"&gt;
&lt;uvd version="1.0.0" type="software:audio"&gt;
  &lt;general&gt;
    &lt;name&gt;WinAmp&lt;/name&gt;
    &lt;urls&gt;
      &lt;url lang="en"&gt;http://www.winamp.com/&lt;/url&gt;
      &lt;url lang="de"&gt;http://de.winamp.com/&lt;/url&gt;
    &lt;/urls&gt;
    &lt;descriptions&gt;
      &lt;description lang="en" maxlength="255"&gt;
        WinAmp is a multimedia player, supporting (...)
      &lt;/description&gt;
      ...
    &lt;/descriptions&gt;
  &lt;/general&gt;
&lt;/uvd&gt;</pre>
<p>Deutsch und Englisch vorhanden, alles andere beliebig nachtragbar. Weitere Informationen wären: Lizenz, Keywords, Screenshots, etc&#8230;Anschließend könnte in einem neuen Abschnitt &#8220;release&#8221; die aktuelle Version (inkl. Downloadlinks nach Sprache und OS) beschrieben, in &#8220;contributors&#8221; alle Beteiligten aufgelistet und in &#8220;company&#8221; Informationen zum Firmenkontakt bereitgestellt werden.</p>
<p>Eine komplette Dokumentation des Formats würde den Rahmen aber noch mehr sprengen, deswegen wird hier auf die folgenden Dokumente verwiesen:</p>
<ul>
<li>&#8220;Spezifikation&#8221; zu UVD: <a href="http://dev.xscheme.de/uvd/Universal%20Version%20Description.txt">http://dev.xscheme.de/uvd/Universal%20Version%20Description.txt</a></li>
<li>DTD: <a href="http://dev.xscheme.de/uvd/uvd-1.dtd">http://dev.xscheme.de/uvd/uvd-1.dtd</a> (<a href="http://www.validome.org/grammar/validate/?lang=ge&amp;viewSourceCode=1&amp;grammarTyp=DTD&amp;url=http://dev.xscheme.de/uvd/uvd-1.dtd">Validate</a>)</li>
<li>Beispiele:
<ul>
<li><a href="http://dev.xscheme.de/uvd/release-example.uvd.xml">Programm-Release</a></li>
<li><a href="http://dev.xscheme.de/uvd/writer-example.uvd.xml">Dokument</a></li>
<li><a href="http://dev.xscheme.de/uvd/episode-example.uvd.xml">TV-Episode</a></li>
</ul>
</li>
</ul>
<h2>Fazit</h2>
<p>Meistens ist der eigene Blick von Selbstherrlichkeit getrübt, wenn man etwas in den eigenen Augen neues und tolles geschaffen hat. Deswegen brauche ich Meinungen hierzu.</p>
<p>Um das anfangs erwähnte Argument noch einmal aufzugreifen, man könne seinen eigenen Webspace besser kontrollieren, will ich hier erwähnen, dass ein UVD-Dokument natürlich überall liegen kann. Was bisher fehlt, sind Programme zur Verwaltung und Erstellung, sowie Bibliotheken zur Verarbeitung.</p>
<p>Vielleicht überschätze ich auch den Nutzen des ganzen. Kein Grund allerdings, den Stein nicht ins Rollen zu bringen.</p>
<h2>Update (02.05.2009)</h2>
<p>Ich will an dieser Stelle noch demonstrieren, wie sich das Format für einfache Versionsprüfung, sowie Update-Verarbeitung eignet.</p>
<p>Für einen normalen Versionscheck würde schon so etwas reichen:</p>
<pre class="xml:collapse" name="code">&lt;uvd version="1.0.0" type="software:ripper"&gt;
  &lt;general&gt;
    &lt;name&gt;LastSharp&lt;/name&gt;
  &lt;/general&gt;
  &lt;release&gt;
    &lt;date&gt;
      &lt;day&gt;20&lt;/day&gt;
      &lt;month&gt;4&lt;/month&gt;
      &lt;year&gt;2009&lt;/year&gt;
    &lt;/date&gt;
    &lt;version&gt;
      &lt;major&gt;0&lt;/major&gt;
      &lt;minor&gt;4&lt;/minor&gt;
      &lt;build&gt;1&lt;/build&gt;
    &lt;/version&gt;
  &lt;/release&gt;
&lt;/uvd&gt;</pre>
<p>Und folgende Datei könnte von einem Updater automatisch verarbeitet werden:</p>
<pre class="xml:collapse" name="code">&lt;uvd version="1.0.0" type="software:ripper"&gt;
  &lt;general&gt;
    &lt;name&gt;LastSharp&lt;/name&gt;
  &lt;/general&gt;
  &lt;release&gt;
    &lt;date&gt;
      &lt;day&gt;20&lt;/day&gt;
      &lt;month&gt;4&lt;/month&gt;
      &lt;year&gt;2009&lt;/year&gt;
    &lt;/date&gt;
    &lt;version&gt;
      &lt;major&gt;0&lt;/major&gt;
      &lt;minor&gt;4&lt;/minor&gt;
      &lt;build&gt;1&lt;/build&gt;
    &lt;/version&gt;
    &lt;files&gt;
      &lt;file name="LastUtility" os="any" type="lib" filename="LastUtility.dll"
            update-action="update" update-path=".inc" direct-download="no"&gt;
        &lt;urls&gt;
          &lt;url lang="all"&gt;http://download.server.tld/LastUtility.dll&lt;/url&gt;
        &lt;/urls&gt;
      &lt;/file&gt;
      &lt;file name="LastSharpExe" os="any" type="exe" filename="LastSharp.exe"
            update-action="update" direct-download="no"&gt;
        &lt;urls&gt;
          &lt;url lang="all"&gt;http://download.server.tld/LastSharp.exe&lt;/url&gt;
        &lt;/urls&gt;
      &lt;/file&gt;
      &lt;file name="SettingsFile" os="any" type="xml" filename="settings.xml"
            update-action="remove" /&gt;
      &lt;file name="LastSharpFull" os="any" type="package" filename="LastSharp041.zip"
            update-action="ignore"&gt;
        &lt;urls&gt;
          &lt;url lang="all"&gt;http://download.server.tld/LastSharp041.zip&lt;/url&gt;
        &lt;/urls&gt;
        &lt;descriptions&gt;
          &lt;description lang="de" maxlength="any"&gt;
            Das vollständige Paket mit allen Dateien von LastSharp 0.4.1.
          &lt;/description&gt;
        &lt;/descriptions&gt;
      &lt;/file&gt;
    &lt;/files&gt;
  &lt;/release&gt;
&lt;/uvd&gt;</pre>
<p>Wenn man genau hinsieht, erkennt man die Anweisungen an den Updater:</p>
<ol>
<li>Lade die Datei &#8220;LastUtility.dll&#8221; von der angegebenen URL herunter und überschreibe ihre Entsprechung im Unterverzeichnis &#8220;.inc&#8221;.</li>
<li>Tue dasselbe mit der Datei &#8220;LastSharp.exe&#8221;, allerdings diesmal im Hauptverzeichnis. (&#8220;update-path&#8221; fehlt)</li>
<li>Lösche die Datei &#8220;settings.xml&#8221; im Hauptverzeichnis.</li>
</ol>
<p>Der letzte &#8220;file&#8221;-Eintrag enthält das Gesamtpaket, das beim automatischen Update ignoriert wird. (&#8220;update-action=&#8217;ignore&#8217;&#8221;) Gleichzeitig wird es aber dem User im Gegensatz zu den Dateien zuvor direkt zugänglich gemacht. (&#8220;direct-download=&#8217;no&#8217;&#8221; soll verhindern, dass evtl. Clients die so markierten Links dem User zeigen)</p>
<p>Wenn man jetzt bedenkt, dass ein wichtiger Punkt von UVD die Verlinkung mit den früheren Versionen ist (Der &#8220;release&#8221;-Tag bietet das Attribut &#8220;previous&#8221;, das auf das entsprechende UVD-Dokument verlinkt!), sieht man schnell, dass man mit diesem Format <strong>jede beliebige</strong> Version aktualisieren kann, wenn man sich in der Versionsgeschichte &#8220;zurückhangelt&#8221;.</p>
<p>Ich hoffe, diese kleine Demonstration hat noch einmal verdeutlicht, worin ich u.a. den Nutzen des Formats sehe.</p>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2009/05/universal-version-description-uvd/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Proxy-Server verwenden (C#)</title>
		<link>http://dev.xscheme.de/2008/10/proxy-server-verwenden-c/</link>
		<comments>http://dev.xscheme.de/2008/10/proxy-server-verwenden-c/#comments</comments>
		<pubDate>Mon, 06 Oct 2008 22:14:59 +0000</pubDate>
		<dc:creator>xsc</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[download]]></category>
		<category><![CDATA[proxy verwenden]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=384</guid>
		<description><![CDATA[Einen Proxy kann man sich als Zwischenstation vorstellen: man schickt ihm eine Anfrage (z.B.: &#8220;Hole die Website XY&#8221;), er speichert das entsprechende Ergebnis (z.B. die Seite XY) und überträgt es an den Anfragenden (Client).
Ein ganz klarer Vorteil hiervon ist der Schutz des Clients: ein Proxy-Server bietet aufgrund seines einfacheren Aufbaus viel weniger Angriffsfläche als der [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://dev.xscheme.de/wp-content/uploads/2008/10/proxy.png"><img class="alignleft size-medium wp-image-407" style="margin-right: 1em; margin-bottom: 0.5em; border-style: none;" title="proxy" src="http://dev.xscheme.de/wp-content/uploads/2008/10/proxy-300x118.png" alt="" width="300" height="118" /></a>Einen Proxy kann man sich als Zwischenstation vorstellen: man schickt ihm eine Anfrage (z.B.: &#8220;Hole die Website XY&#8221;), er speichert das entsprechende Ergebnis (z.B. die Seite XY) und überträgt es an den Anfragenden (Client).</p>
<p>Ein ganz klarer Vorteil hiervon ist der Schutz des Clients: ein Proxy-Server bietet aufgrund seines einfacheren Aufbaus viel weniger Angriffsfläche als der eigentlich anfragende Rechner &#8211; man denke nur an die regelmäßig auftretenden Sicherheitslücken in populären Betriebssystemen &#8211; und garantiert außerdem ein gewisses Maß an Anonymität, da nur die IP des Proxies nach außen hin sichtbar ist.</p>
<p>Nachteile wiederum sind, dass man sich nicht sicher sein kann, was der Server protokolliert und speichert, und ob die Daten, die bei einem selbst ankommen, nicht auf dem Weg (vom Proxy) modifiziert wurden. Des weiteren werden viele Proxies von einer großen Anzahl Anwendern genutzt, was einen Geschwindigkeitsverlust im Vergleich zu &#8220;normalen&#8221; Verbindungen bedeuten kann. Ein ebenso großes Problem kann es sein, wenn Funktionen fehlen (z.B. die Möglichkeit, Daten zu übertragen oder Dateien herunterzuladen) oder wenn der Server eine Inkompatibilität zum verwendeten Programm aufweist. (z.B. Server des <a href="http://dev.xscheme.de/2008/10/proxy-host-header-und-codeen-network/">CoDeeN-Networks</a> zu .NET-Programmen)</p>
<p>Trotzdem, genug des Geplänkels, die Verwendung eines Proxies im eigenen Programm ist nicht sonderlich schwer.</p>
<p><span id="more-384"></span></p>
<p><strong>Das WebProxy-Objekt</strong></p>
<p>Erster Schritt hierbei ist die Erstellung eines Objektes der Klasse System.Net.WebProxy. Hierzu brauchen wir die Adresse des Servers (ggf. seine IP) und den Port, über den er erreicht werden kann:</p>
<pre>System.Net.WebProxy proxy =
    new System.Net.WebProxy("http://&lt;Adresse&gt;:&lt;Port&gt;", true);</pre>
<p>Der zweite Parameter des Konstruktors gibt an, ob der Proxy für lokale Verbindungen (z.B. zu Testzwecken auf &#8220;http://localhost/&#8221;) umgangen werden soll.</p>
<p><strong>Authentifizierung</strong></p>
<p>Für die Authentifizierung, also für alle nicht-anonymen Proxies, muss ein System.Net.NetworkCredential-Objekt mit den entsprechenden Zugangsdaten erstellt und dem Proxy-Objekt zugewiesen werden:</p>
<pre>System.Net.NetworkCredential auth =
    new System.Net.NetworkCredential("&lt;Benutzername&gt;", "&lt;Passwort&gt;");
proxy.Credentials = auth;</pre>
<p>Mithilfe eines so konfigurierten Proxy-Objektes kann man nun entweder mithilfe der HttpWebRequest-Klasse HTTP-Anfragen senden, oder mithilfe der WebClient-Klasse Dateien herunterladen &#8211; beides über die von uns gewählte Zwischenstation.</p>
<p><strong>HTTP-Requests über einen Proxy</strong></p>
<pre>System.Net.HttpWebRequest request =
    (System.Net.HttpWebRequest) System.Net.WebRequest.Create("&lt;URI&gt;");
request.Proxy = proxy;<span style="color: #008000;">
</span>System.Net.HttpWebResponse response =
    (System.Net.HttpWebResponse) request.GetResponse();
...</pre>
<p><strong>Datei-Download mit Proxy</strong></p>
<pre>System.Net.WebClient client = new System.Net.WebClient();
client.Proxy = proxy;
client.DownloadFile("&lt;URI&gt;", "&lt;lokaler Pfad&gt;");
...</pre>
<p><strong>Aus.</strong></p>
<p>Ich hoffe, ich habe mit diesem kurzen Beitrag all jenen geholfen, die ihre Programme um Proxy-Funktionalität erweitern wollen.</p>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2008/10/proxy-server-verwenden-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Audiostreaming mit irrKlang (C#)</title>
		<link>http://dev.xscheme.de/2008/08/audiostreaming-mit-irrklang/</link>
		<comments>http://dev.xscheme.de/2008/08/audiostreaming-mit-irrklang/#comments</comments>
		<pubDate>Thu, 14 Aug 2008 05:59:27 +0000</pubDate>
		<dc:creator>xsc</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[irrklang]]></category>
		<category><![CDATA[part of lastsharp]]></category>
		<category><![CDATA[streaming]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=147</guid>
		<description><![CDATA[
Auf der Suche nach einem Ersatz für die DirectX-Wiedergabe in LastSharp bin ich gestern über irrKlang gestolpert. Soweit so gut: plattformunabhängig, einfach zu bedienen, eigentlich alles in Ordnung. Aber dann gab es da doch noch ein größeres Manko, das ich nicht ignorieren konnte: das Streaming von Dateien, die im Internet liegen, ist nicht möglich &#8211; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.ambiera.com/irrklang/"><img src="http://dev.xscheme.de/wp-content/uploads/2008/08/irrklang_link_big.png" style="margin-bottom: 1em" /></a><br/><br />
Auf der Suche nach einem Ersatz für die DirectX-Wiedergabe in LastSharp bin ich gestern über <a href="http://www.ambiera.com/irrklang/">irrKlang</a> gestolpert. Soweit so gut: plattformunabhängig, einfach zu bedienen, eigentlich alles in Ordnung. Aber dann gab es da doch noch ein größeres Manko, das ich nicht ignorieren konnte: das Streaming von Dateien, die im Internet liegen, ist nicht möglich &#8211; und genau das bräuchte ich aber für LastSharp. Was also machen?</p>
<p><span id="more-147"></span></p>
<p>Über <a href="http://www.ambiera.com/irrklang/docu/index.html#fileOverriding">dieses Tutorial</a> bin ich der Sache näher gekommen: Man muss eine eigene Implementierung des Interfaces <code>IFileFactory</code> erstellen, und &#8211; falls nötig &#8211; eine eigene <code>Stream</code>-Klasse. Und letzteres war durchaus nötig, da mir keine bereitgestellte <code>Stream</code>-Klasse die entsprechenden Möglichkeit der Pufferung eines Audiostreams geboten hat. So ist die Klasse <code>InternetStream</code> geboren worden, die eine Verbindung zu einer Datei im Internet herstellt (mittels HttpWebRequest), und die empfangenen Daten in einer Datei auf der Festplatte speichern kann, auf die ebenso die Möglichkeit gleichzeitigen Zugriffs besteht:</p>
<pre class="c#" name="code">
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;
using System.Collections;
using IrrKlang;

public class InternetStream : Stream
{
    // =======================================================
    // Buffer-Daten
    private Stream resp = null;
    private string BufferFile = "streaming.mp3";
    private FileStream OutputStream = null;
    private FileStream BufferStream = null;
    private Thread BufferThread = null;
    // =======================================================
    // Konstruktor
    public InternetStream(string uri, string bfile)
    {
        // Dateiname des lokalen Files
        this.BufferFile = bfile;
        // FileStream zum Schreiben öffnen
        if (File.Exists(this.BufferFile)) { try { File.Delete(this.BufferFile); } catch { } }
        this.BufferStream = new FileStream(this.BufferFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
        // ResponseStream zum File im Internet
        this.resp = ((HttpWebRequest)WebRequest.Create(uri)).GetResponse().GetResponseStream();
        // Pufferung starten
        this.BufferThread = new Thread(new ThreadStart(this.FillBuffer));
        this.BufferThread.Start();
    }
    // =======================================================
    // Read: wenn zehn mal 0 zurückgegeben wird, werden alle
    // Streams geschlossen und der Thread beendet
    private int ReadTries = 0;
    public override int Read(byte[] array, int offset, int count)
    {
        // Ausgabe-Stream öffnen
        if (this.OutputStream == null) this.OutputStream = new FileStream(this.BufferFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        try
        {
            // Dem Puffer Zeit geben, Daten zu holen
            while (this.OutputStream.Length == 0) Thread.Sleep(100);
            // Daten lesen
            int s = this.OutputStream.Read(array, offset, count);
            // Wenn 0: ein erfolgloser Versuch mehr
            if (s == 0) this.ReadTries++;
            if (this.ReadTries &gt; 10)
            {
                this.Close();
            }
            //
            return s;
        }
        catch { return 0; }
    }
    // =======================================================
    // Seek
    public override long Seek(long offset, SeekOrigin loc)
    {
        return this.OutputStream.Seek(offset, loc);
    }
    // =======================================================
    // Puffer-Thread
    private void FillBuffer()
    {
        byte[] buffer = new byte[20 * 1024];
        int rd = 0;
        while ((rd = this.resp.Read(buffer, 0, buffer.Length)) &gt; 0)
        {
            this.BufferStream.Write(buffer, 0, rd);
        }
    }
    // =======================================================
    // Schließen
    public override void Close()
    {
        this.OutputStream.Close();
        this.BufferStream.Close();
        this.BufferThread.Abort();
        if (File.Exists(this.BufferFile))
            try { File.Delete(this.BufferFile); }
            catch { }
        base.Close();
    }
    // =======================================================
    // Length
    public override long Length
    {
        get
        {
            if (this.OutputStream == null) return 0;
            else return this.OutputStream.Length;
        }
    }
    // =======================================================
    // Properties: CanRead, CanSeek, CanWrite
    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return true; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }
    // =======================================================
    // restliche Methoden, damit die Stream-Implementierung
    // komplett ist
    public override void Flush()
    {
        this.OutputStream.Flush();
    }

    public override long Position
    {
        get
        {
            return this.OutputStream.Position;
        }
        set
        {
            this.OutputStream.Position = value;
        }
    }

    public override void SetLength(long value)
    {
        this.OutputStream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        return;
    }
}</pre>
<p>Die entsprechende <code>IFileFactory</code>-Implementation:</p>
<pre class="c#" name="code">
public class StreamFileFactory : IFileFactory
{
    // =====================================
    // Öffne ein File (von Irrklang verwendete Methode)
    public Stream openFile(string filename)
    {
        if (filename.StartsWith("http://"))
        {
            // neuen Starten
            string fn = Application.StartupPath + "/streaming.mp3";
            try
            {
                InternetStream istream = new InternetStream(filename, fn);
                return istream;
            }
            catch { return null; }
        }
        else
        {
            // lokale Datei
            return new FileStream(filename, FileMode.Open, FileAccess.Read);
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2008/08/audiostreaming-mit-irrklang/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>mp3Gain-Wrapper (C#)</title>
		<link>http://dev.xscheme.de/2008/08/mp3gain-codesnippet-c-batch/</link>
		<comments>http://dev.xscheme.de/2008/08/mp3gain-codesnippet-c-batch/#comments</comments>
		<pubDate>Tue, 12 Aug 2008 15:48:32 +0000</pubDate>
		<dc:creator>xsc</dc:creator>
				<category><![CDATA[Batch]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[part of lastsharp]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=101</guid>
		<description><![CDATA[mp3Gain ist ein Programm zur (reversiblen!) Normalisierung der Lautstärke in MP3-Dateien. Die folgende Klasse kann mithilfe der Kommandozeilenversion von mp3Gain einen MP3-Titel normalisieren.

using System;
/*
 * [class] mp3Gain: Klasse zur Normalisierung von MP3-Dateien mithilfe von mp3Gain
 *
 * Copyright (C) 2008 Yannick Scherer
 * This program is free software; you can redistribute it and/or modify it [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://dev.xscheme.de/wp-content/uploads/2008/08/mp3gainlogosmall.gif" align="left" style="margin-right: 1em;" /><a href="http://mp3gain.sourceforge.net">mp3Gain</a> ist ein Programm zur (reversiblen!) Normalisierung der Lautstärke in MP3-Dateien. Die folgende Klasse kann mithilfe der <a href="http://prdownloads.sourceforge.net/mp3gain/mp3gain-dos-1_4_6.zip?download">Kommandozeilenversion von mp3Gain</a> einen MP3-Titel normalisieren.<br />
<span id="more-101"></span></p>
<pre class="c#" name="code">using System;
/*
 * [class] mp3Gain: Klasse zur Normalisierung von MP3-Dateien mithilfe von mp3Gain
 *
 * Copyright (C) 2008 Yannick Scherer
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see http://www.gnu.org/licenses/.
 * */

using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.IO;

class mp3Gain
    {
        // mp3Gain-Daten
        private static Process          mp3GainProcess = null;
        private static ProcessStartInfo mp3GainProcessInfo = new ProcessStartInfo(mp3GainPath);
        private static string           mp3GainCurrentFile = "";
        public static bool              mp3GainIsRunning = false;
        public static string            mp3GainPath    { get { return System.Windows.Forms.Application.StartupPath+"/mp3gain/mp3gain.exe"; } }
        public static string            mp3GainParams { get { return "/r /d 20 /k /c /q"; } }
        // Lautstärke normalisieren
        public static void NormalizeVolume(string mp3File) {
            if (!mp3GainIsRunning &amp;&amp; File.Exists(mp3GainPath) &amp;&amp; File.Exists(mp3File)) {
                // Prozess läuft
                mp3GainIsRunning   = true;
                mp3GainCurrentFile = mp3File;
                // Datei, an der gearbeitet wird
                string mp3Work = mp3File+"_work.mp3";
                File.Copy(mp3File, mp3Work);
                // mp3GainProcessInfo initialisieren
                mp3GainProcessInfo.Arguments = mp3GainParams+" \""+mp3Work+"\"";
                mp3GainProcessInfo.WindowStyle = ProcessWindowStyle.Hidden;
                // mp3Gain aufrufen
                mp3GainProcess = new Process();
                mp3GainProcess.StartInfo = mp3GainProcessInfo;
                mp3GainProcess.EnableRaisingEvents = true;
                mp3GainProcess.Exited += new EventHandler(mp3GainExited);
                mp3GainProcess.Start();
            }
        }
        public static void mp3GainExited(object sender, EventArgs e)
        {
            // Daten rückkopieren
            string mp3Work = mp3GainCurrentFile + "_work.mp3";
            if (new FileInfo(mp3Work).Length &gt; 0)
            {
                File.Copy(mp3Work, mp3GainCurrentFile, true);
                File.Delete(mp3Work);
            }
            // Reset
            mp3GainIsRunning = false;
            mp3GainCurrentFile = "";
            return;
        }

    }</pre>
<p>Eine MP3-Datei wird normalisiert, durch den Aufruf <code>mp3Gain.NormalizeVolume(&lt;MP3-Datei&gt;)</code>.</p>
<p>Innerhalb der Klasse müssen noch der Pfad zu mp3gain.exe (<em>mp3GainPath</em>) angepasst werden, sowie die gewünschten Parameter (<em>mp3GainParams</em>). Eine Übersicht über die Parameter von mp3Gain gibt es <a href="http://wiki.ubuntuusers.de/MP3Gain?rev=66766">hier</a>.</p>
<p><strong>Batch</strong></p>
<p>Ein BatchFile, das mp3Gain verwendet, könnte folgendermaßen aussehen (vorrausgesetzt, es befindet sich im selben Verzeichnis wie mp3gain.exe):</p>
<pre>@rem // Ziehen Sie eine MP3-Datei auf dieses Batchfile, um sie zu normalisieren.

@rem // In aktuelles Verzeichnis wechseln
@cd /D %~d0%~p0

@echo --------------------------------------------------------------------
@echo Normalisieren von %1 ...
@echo --------------------------------------------------------------------
@ "%cd%/mp3gain.exe" &lt;Parameter&gt; %1
@echo --------------------------------------------------------------------
@echo Prozess abgeschlossen...
@echo --------------------------------------------------------------------
@pause</pre>
<p>Dieser Code lässt sich natürlich erweitern, sodass ganze Ordner bearbeitet werden. Das überlasse ich aber jedem, der fleißig genug dafür ist&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2008/08/mp3gain-codesnippet-c-batch/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>LastFM.XMLProcessor</title>
		<link>http://dev.xscheme.de/2008/08/lastxml/</link>
		<comments>http://dev.xscheme.de/2008/08/lastxml/#comments</comments>
		<pubDate>Sat, 09 Aug 2008 20:00:52 +0000</pubDate>
		<dc:creator>xsc</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[part of lastsharp]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=15</guid>
		<description><![CDATA[Die Klasse LastFM.XMLProcessor kann auf Basis des Last.FM-Protokolls 1.2 eine Verbindung zum Server herstellen, sowie Playlists herunterladen. (Scrobbling unterstütz sie nicht.) Eine leicht modifizierte Version dieser Klasse kommt auch in LastSharp zum Einsatz.

using System;
using System.Xml;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;

namespace LastFM
{
    // ====================================================
    // Klasse mit Hilfsfunktionen
    [...]]]></description>
			<content:encoded><![CDATA[<p>Die Klasse LastFM.XMLProcessor kann auf Basis des Last.FM-Protokolls 1.2 eine Verbindung zum Server herstellen, sowie Playlists herunterladen. (Scrobbling unterstütz sie nicht.) Eine leicht modifizierte Version dieser Klasse kommt auch in LastSharp zum Einsatz.<br />
<span id="more-15"></span></p>
<pre class="c#" name="code">using System;
using System.Xml;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;

namespace LastFM
{
    // ====================================================
    // Klasse mit Hilfsfunktionen
    // ====================================================
    class Utilities
    {
        // MD5 erstellen
        public static string MD5(string password)
        {
            byte[] textBytes = System.Text.Encoding.Default.GetBytes(password);
            try
            {
                System.Security.Cryptography.MD5CryptoServiceProvider cryptHandler;
                cryptHandler = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] hash = cryptHandler.ComputeHash(textBytes);
                string ret = "";
                foreach (byte a in hash)
                {
                    if (a &lt; 16)
                        ret += "0" + a.ToString("x");
                    else
                        ret += a.ToString("x");
                }
                return ret;
            }
            catch
            {
                throw;
            }
        }
        // Ruft eine URI auf und liefert sie als String zurück
        public static string ExecuteURI(string uri)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            StreamReader sr = new StreamReader(resp.GetResponseStream());
            string result = sr.ReadToEnd();
            sr.Close();
            return result;
        }
    }

    // ====================================================
    // Datenkapselung für Playlist und Tracks
    // ====================================================
    public class Playlist : ArrayList
    {
        public string PlaylistName = "";
        public DateTime Timestamp = new DateTime();
    }
    public class Track
    {
        public string Title="";
        public string Artist = "";
        public string Location = "";
        public string Album = "";
        public string Image = "";
        public int Duration = 0;
        // Gibt die Dauer des Tracks im Format MM:SS aus
        public string DurationString()
        {
            int sec = (this.Duration / 1000) % 60;
            int min = ((this.Duration / 1000) - sec) / 60;
            return ((min &lt; 10) ? "0" : "") + min + ":" + ((sec &lt; 10) ? "0" : "") + sec;
        }
    }

    // ====================================================
    // Handshake-Klasse für Bereitstellung der Handshake-Daten
    // ====================================================
    public class Handshake : Hashtable
    {
        /// Verarbeitet die Handshake-Rückgabe
        public Handshake(string HandshakeString)
        {
            string[] lines = HandshakeString.Split('\n');
            foreach (string line in lines)
            {
                string[] p = line.Split('=');
                this.Add(p[0], p[1]);
            }
            if (!this.ContainsKey("session") || !this.ContainsKey("base_path") ||
                !this.ContainsKey("base_url"))
            {
                throw new Exception("Ungültiger Handshake! (Passwort falsch?)");
            }
        }
        // Daten (nur die wichtigsten)
        public string SessionID  { get { return (string) this["session"]; } }
        public string BasePath   { get { return (string) this["base_path"]; } }
        public string BaseURL    { get { return (string) this["base_url"]; } }
        public string RequestURL { get { return "http://" + this.BaseURL + this.BasePath + "/"; } }
        // Der Hashtable ist nicht veränderbar
        public override bool IsReadOnly
        {
            get
            {
                return true;
            }
        }
    }

    // ====================================================
    // XMLProcessor-Klasse
    // ====================================================
    public class XMLProcessor
    {
        // ========================================
        // based on: http://code.google.com/p/thelastripper/wiki/LastFM12UnofficialDocumentation
        // ========================================
        // Login-Daten
        private string user;
        private string password;
        //aktuelle Radiostationsdaten
        private bool tuned_in = false;
        private string to_station = null;
        //
        public bool TunedIn { get { return tuned_in; } }
        public string Station { get { return to_station; } }
        public string Username { get { return user; } }
        // ========================================
        public XMLProcessor(string username, string password)
        {
            this.user        = username;
            this.password = Utilities.MD5(password);
        }
        ///
        /// Führt einen Handshake mit LastFM aus.
        ///
        public Handshake DoHandshake()
        {
            // Request ausführen
            string uri =
                "http://ws.audioscrobbler.com/radio/handshake.php?version=1.3.1.1" +
                "&amp;platform=win32&amp;username=" + this.username + "&amp;passwordmd5="
                + this.password + "&amp;language=de&amp;player=lastsharp";
            string resp = Utilities.ExecuteURI(uri);
            // Daten verarbeiten (in Handshake-Klasse)
            return new Handshake(resp);
        }

        ///
        /// Setzt die Radiostation
        ///
        public bool TuneIn(Handshake hs, string station)
        {
            // Operation ausführen
            string uri = hs.RequestURL + "adjust.php?session=" + hs.SessionID + "&amp;url=" + station + "〈=de";
            string resp = Utilities.ExecuteURI(uri);
            // Überprüfen + Status setzen
            if (resp.StartsWith("response=OK"))
            {
                this.tuned_in = true;
                this.to_station = station;
                return true;
            }
            else
            {
                this.tuned_in = false;
                this.to_station = null;
                return false;
            }
        }

        ///
        /// Ruft die Playlist ab
        ///
        public Playlist GetPlaylist(Handshake hs)
        {
            // Radiostation ausgewählt?
            if (!this.tuned_in) throw new Exception("Keine Radiostation angewählt.");
            // Playlist abrufen
            string uri =
                  hs.RequestURL + "xspf.php?sk=" + hs.SessionID +
                  "&amp;discovery=0&amp;desktop=1.3.1.1";
            string resp = Utilities.ExecuteURI(uri);
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(resp);
            // =========================================
            // Verarbeiten
            // =========================================
            Playlist pl = new Playlist();
            XmlNode root = doc.FirstChild;
            if (root.Name != "playlist")
                throw new Exception("Ungültiges XML-Format der Playlist");
            // Titel der Playlist + Timestamp
            XmlNode pl_title = root.SelectSingleNode("title");
            if (pl_title != null) pl.PlaylistName = pl_title.InnerText;
            pl.Timestamp = DateTime.Now;
            // Tracklist
            XmlNode pl_tracks = root.SelectSingleNode("trackList");
            if (pl_tracks != null) for (int i = 0; i &lt; pl_tracks.ChildNodes.Count; i++)
                {
                    {
                        XmlNode trn = pl_tracks.ChildNodes[i];
                        Track tr = new Track();
                        tr.Artist     = trn.SelectSingleNode("creator").InnerText;
                        tr.Album    = trn.SelectSingleNode("album").InnerText;
                        tr.Title      = trn.SelectSingleNode("title").InnerText;
                        tr.Location = trn.SelectSingleNode("location").InnerText;
                        tr.Image    = trn.SelectSingleNode("image").InnerText;
                        tr.Duration = Convert.ToInt32(trn.SelectSingleNode("duration").InnerText);
                        pl.Add(tr);
                    }
                }
            return pl;
        }
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2008/08/lastxml/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
