Donnerstag, 30. Dezember 2010

Youtube durchsuchen

In diesem Post möchte ich euch zeigen, wie man mit C# Youtube durchsuchen, also auf der Plattform nach bestimmten Videos suchen kann.
Hierfür benötigen wie die Google Data API, insbesondere das Youtube SDK. Mit diesen Programmierbibliotheken funktioniert der Zugriff auf Youtube & Co ganz einfach.
Zum Herunterladen gibt's die Google Data API auf dieser Seite, zum Zugriff auf Youtube brauchen wir aber nur diese Datei, das Youtube SDK.
Damit wir es später in das Projekt einbinden können, muss es installiert werden.
Das Durchsuchen von Youtube läuft über sogenannte Feeds.
Feeds sind so etwas wie Inhalte einer Website in Kurzform, meistens in reinem Textformat, so können sich Leser schnell über neueste Themen informieren, außerdem lesen viele Programme diese Textdateien (Suchmaschinen benutzen sie beispielsweise zur Indizierung).
Auch dieser Blog hat einen Feed, den man sogar abonnieren kann ;-)
Mit dem Youtube SDK können wir Feeds mit einem C# Programm auslesen, zum Durchsuchen von Youtube benutzen wir den Feed unter http://gdata.youtube.com/feeds/videos?q=Suchbegriff.
Dieser Feed wird von Youtube bereitgestellt und liefert die zu Suchbegriff passenden Ergebnisse in textueller Form, der Feed lässt sich auch im Browser betrachten.

Im Folgenden werde ich den Code erläutern, über den dieser Feed in C# ausgewertet werden kann.
Zuerst müssen die heruntergeladenen Bibliotheken Google.GData.Client.dll und Google.GData.Extensions.dll eingebunden werden.
Diese befinden sich im Redist Verzeichnis des Installationsordners (bei mir C:\Program Files (x86)\Google\Google YouTube SDK for .NET\Redist).
Das Einbinden lässt sich über Projekt - Verweis hinzufügen - Durchsuchen erledigen.
Zum benutzen im Projekt sind dann die Codezeilen

using Google.GData.Client;
using Google.GData.Extensions;

nötig.
Wichtig ist auch, dass unter Projekteigenschaften - Anwendung das Zielframework auf .Net Framework 4 und nicht etwa .Net Framework 4 Client Profile gestellt wird, denn für ersteres wurden die dlls kompilliert und nur hiermit funktioniert der Code.

Ich denke, der kommentierte Code ist eigentlich selbsterklärend, wesentlich hierbei ist, dass die Suchergebnisse letztendlich in Form eines Feeds (beim Programmieren als Objekt vom Typ AtomFeed) vorliegen, wessen Einträge dann ausgelesen werden können.

Das folgende Konsolenprogramm sucht auf Youtube nach dem Begriff "VLC einbinden" und gibt die ersten 10 Ergebnisse auf der Konsole aus:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.GData.Client;
using Google.GData.Extensions;

namespace YoutubeToolkit
{
    class YoutubeQuery
    {
        static void Main(string[] args)
        {
            FeedQuery Abfrage = new FeedQuery(); // Objekt zur Handhabung von Feeds
            Abfrage.Uri = new Uri("http://gdata.youtube.com/feeds/videos?q=VLC+einbinden"); // Feed URL
            Abfrage.StartIndex = 0; // Index, ab welchem die Suchergebnisse angezeigt werden sollen
            Abfrage.NumberToRetrieve = 10; // maximale Anzahl an anzuzeigenden Suchergebnissen

            Service Service = new Service(); // Objekt zur Durchführung der Suchabfrage über das FeedQuery Objekt
            AtomFeed Feed = Service.Query(Abfrage); // Ergebnis der Suchabfrage als Feed

            // für jedes Ergebnis den Titel des Videos ausgeben
            foreach (AtomEntry Ergebnis in Feed.Entries)
            {
                Console.WriteLine(Ergebnis.Title.Text);
            }
        }
    }
}

Montag, 27. Dezember 2010

Ton ausschalten (Windows XP / Vista / 7)

Im Post Ton ausschalten / Lautstärke einstellen stellte ich vor, wie man den PC stumm stellen und die Lautstärke anpassen kann. Die vorgestellte Methode funktioniert allerdings nur mit Windows XP.
Deswegen zeige ich in diesem Post kurz, wie man unter Windows Vista und 7 (aber auch XP kompatibel) den PC stummschalten (muten) kann.
Die Lösung führt uns wieder über die WinAPI,
weswegen folgende using - Direktive nötig ist: using System.Runtime.InteropServices;.
Die benötigte P/Invoke Methode heißt SendMessageW und hat die Signatur IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam).
Diese Funktion schickt eine Nachricht an ein bestimmtes Programm oder Fenster.
Um den Ton am PC auszuschalten, schicken wir den richtigen Befehl an einen Systemprozess.
Und so sieht der Code im Ganzen aus:
Der Deklarationsteil:

        [DllImport("user32.dll")]
        public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
        private const int WM_APPCOMMAND = 0x319;

Und die Funktion:

private void Mute()
{
    SendMessageW(this.Handle, WM_APPCOMMAND, this.Handle, (IntPtr)APPCOMMAND_VOLUME_MUTE);
}

Freitag, 24. Dezember 2010

Weihnachtspost

using System;
using System.Text;

namespace Weihnachtsprogramm
{
    class Frosty
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}{16}{17}", (char)70, (char)114, (char)111, (char)104, (char)101, (char)32, (char)87, (char)101, (char)105, (char)104, (char)110, (char)97, (char)99, (char)104, (char)116, (char)101, (char)110, (char)33);
        }
    }
}

Dienstag, 7. Dezember 2010

Menüs und Untermenüs dynamisch hinzufügen

In diesem Post widme ich mich den Menüsteuerelementen in C# und zeige, wie man Menüs und Untermenüs zur Laufzeit hinzufügen kann.
Wie ich feststellen musste, ist diese Funktion nämlich nicht ganz klar ersichtlich.
Ich zeige hier meine Methode die über ein paar Umwege läuft, wer also direkteren Code kennt bitte Bescheid sagen.
Ziel des Beispielprogramms soll es sein, folgende Menüstruktur zu erzeugen:

Wir ziehen nun erstmal mit dem Designer ein Steuerelement vom Typ menuStrip auf das Formular (kann natürlich auch dynamisch gemacht werden, siehe Steuerelemente zur Laufzeit hinzufügen).
Nun wird lediglich oben im Formular eine noch unsichtbare Menüleiste bereitgestellt, die aber noch leer ist.
Im Designer kann man jetzt in diese schreiben, um Menüs zu erstellen, wir beschränken uns jedoch auf den dynamischen Teil.
Obermenüs, also Menüs auf höchster Ebene in der Menüleiste (im Beispiel "Obermenü 1" und "Obermenü 2") können über das Attribut Items der Klasse menuStrip bearbeitet werden.
Folgende Codezeilen legen die 2 Obermenüs an:

menuStrip1.Items.Add("Obermenü 1");
menuStrip1.Items.Add("Obermenü 2");

Über einen Index (z.B. über menuStrip1.Items[0]) können wir nun auf die Menüeinträge zugreifen, in diesen aber keine Untermenüs anlegen.
Dafür müssen wir das Objekt explizit in ein Objekt vom Typ ToolStripMenuItem konvertieren. Dieses hat nun die Eigenschaft DropDownItems, über die wir Untermenüs anlegen können.
Um beispielsweise die Untermenüs "Untermenü 1" und "Untermenü 2" zu erzeugen, führen wir folgenden Code aus:

((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 1");        
((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 2");

Diese Untermenüs sind nun so ersteinmal nicht mehr greifbar. Wir brauchen sie jedoch, wenn wir weitere Untermenüs anlegen wollen, die nach rechts aufklappen (im Beispiel "rechtes Untermenü 1" und "rechtes Untermenü 2").
Deswegen speichern wir einfach beim Anlegen des Untermenü dieses in einem Objekt vom Typ ToolStripMenuItem, z.B. so:

ToolStripMenuItem Untermenue1 = (ToolStripMenuItem)((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 1");

(Vor der Zuweisung muss wieder eine explizite Typumwandlung stehen.)
Nun haben wir über die Variable dauerhaft Zugriff auf das gewünschte Untermenü, und können diesem über DropDownItems.Add() eigene Untermenüs hinzufügen.
Hier zum Abschluss der komplette Beispielcode:

            menuStrip1.Items.Add("Obermenü 1");
            menuStrip1.Items.Add("Obermenü 2");

            ToolStripMenuItem Untermenue1 = (ToolStripMenuItem)((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 1");
            ((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 2");

            Untermenue1.DropDownItems.Add("rechtes Untermenü 1");
            Untermenue1.DropDownItems.Add("rechtes Untermenü 2");

Sonntag, 5. Dezember 2010

Tastendruck von mehreren Tasten gleichzeitig prüfen

In den Methoden KeyDown() und KeyPress(), die nahezu jedes Steuerelement implementiert, können Tastaturereignisse untersucht werden.
Der Parameter e vom Typ KeyEventArgs stellt die hierfür benötigten Methoden und Eigenschaften bereit.
So kann z.B. über e.KeyCode geprüft werden, welche Taste gedrückt wurde.
Werden mehrere Tasten (gleichzeitig) gedrückt, wird für jede Taste ein Ereignis aufgerufen, was aber nicht hilft, wenn man den Druck von Tastenkombinationen abfragen möchte.
In diesem Post beschreibe ich, wie man diese Fälle mittels der Eigenschaft KeyData bzw. Flags abdeckt, wobei diese Methoden nur funktionieren, wenn die Kombinationen nur aus Alt und / oder Shift und / oder Strg und / oder maximal einer anderen Taste besteht.
Zuerst über KeyData:
Diese Eigenschaft wird in 4 Bytes gespeichert, die unteren beiden geben die gedrückte Taste wieder, die oberen beiden die gedrückten Funktionstasten (Alt, Shift, Strg (Control)).
Werden mehrere Tasten drückt, werden die Byte - Werte Oder verknüpft.
Drückt man nun nur die Zustandstasten und maximal eine andere Taste, erhält man in KeyData eine eindeutige Bitkombination. Es sollte allerdings klar sein, dass die Eindeutigkeit beim Drücken von mehreren "normalen" Taste nicht mehr gewährleistet ist, da die Bits sich dann überschneiden können.
Zum Prüfen der gedrückten Kombination wird der Wert aus e.KeyData mit einer bitweisen Oder - Verknüpfung des gewünschten Wertes verglichen.
Folgendes Beispiel beendet das Programm, wenn die Tastenkombination Strg + Shift + Alt + "E" gedrückt wurde.

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyData == (Keys.Shift | Keys.Alt | Keys.Control | Keys.E))
        this.Close();
}

Das gleiche Ergebnis lässt sich aber auch erreichen, wenn man über e.KeyCode die gedrückte Taste abfragt und zusätzlich noch die Eigenschaften e.Shift, e.Alt und e.Control überprüft (diese Eigenschaften sind sogenannte Flags), die angeben, ob die entsprechenden Funktionstasten gedrückt sind:

if (e.KeyCode == Keys.E && e.Shift && e.Alt && e.Control)
    this.Close();

Freitag, 3. Dezember 2010

Status eines Netzwerkadapters überprüfen

Im vorigen Post wurde gezeigt, wie man prüfen kann, ob der Computer mit einem Netzwerk verbunden ist, ob also ein beliebiger Netzwerkadapter eine Verbindung hat.
In diesem Post untersuchen wir den Status von nur einer Netzwerkschnittstelle, und das etwas genauer. Wir möchten nun nicht mehr nur zwischen "verbunden" und "nicht verbunden" unterscheiden, sondern mehr Informationen erhalten.
Wir benutzen wieder die Klasse System.Net.NetworkInformation, fragen aber dieses Mal die Eigenschaft OperationalStatus ab.
Diese kann folgende Werte annehmen:
  • Dormant - Die Schnittstelle wartet auf ein externes Ereignis, sie kann keine Daten übertragen.
  • Down - Keine Verbindung.
  • LowerLayerDown - Die Schnittstelle kann keine Daten übertragen, da sie auf "tieferliegende" Schnittstellen zugreift und mindestens eine dieser nicht in Betrieb ist.
  • NotPresent - Nicht funktionsfähig, meistens wegen einem Hardwaredefekt.
  • Testing - Die Schnittstelle führt Tests aus.
  • Unknown - Unbekannter Status.
  • Up - Die Schnittstelle ist in Betrieb.

Ein kleines Beispiel, es listet alle Netzwerkverbindungen auf, die in Betrieb sind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (System.Net.NetworkInformation.NetworkInterface n in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
            {
                if (n.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up)
                    Console.WriteLine(n.Name + " ist Up.");
            }      
        }
    }
}

Verfügbare Netzwerkadapter auflisten und weitere Informationen anzeigen

Wie im vorigen Post benutzen wir wieder die Klasse System.Net.NetworkInformation. Die Methode NetworkInterface.GetAllNetworkInterfaces() liefert ein Array vom Typ System.Net.NetworkInformation.NetworkInterface zurück, wobei jeder Eintrag eine Netzwerkschnittstelle bzw. einen Netzwerkadapter repräsentiert.
Über dieses Array können wir nun iterieren und so alle verfügbaren Netzwerkadapter auflisten.
Das folgende Beispiel gibt den Namen sowie den Status (wird im nächsten Post näher erläutert) jedes einzelnen aktivierten Netzwerkadapters aus:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (System.Net.NetworkInformation.NetworkInterface n in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
            {
                Console.WriteLine("Name: " + n.Name + " Status: " + n.OperationalStatus.ToString());
            }
        }
    }
}

Sonntag, 28. November 2010

Prüfen ob Netzwerkverbindung besteht

Wir benutzen die Klasse System.Net.NetworkInformation.NetworkInterface um zu prüfen, ob der Computer eine Verbindung zu einem Netzwerk hat.
Die hierzu benötigte Methode heißt GetIsNetworkAvailable(). Sie prüft nur, ob der Computer sich über einen beliebigen Adapter in ein Netzwerk eingewählt halt, nicht, ob dieses auch funktioniert oder eine Verbindung zum Internet besteht.
Folgendes Codebeispiel prüft die Konnektivität:

bool NetworkAvailable = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();

Donnerstag, 25. November 2010

Assertions

Assertions sind eine Methode zur Kontrolle der Programmkorrektheit.
Sie stellen eine Bedindung dar, die bei Verletzung eine Fehlermeldung ausgibt.
Assertions stehen meistens vor Programmbereichen, die ohne Erfülltsein der Bedingung nicht funktionieren würden. Wird eine Assertion geworfen, weiß der Programmierer, dass er in diesem Bereich den Fehler abzufangen hat.
Der Ausdruck System.Diagnostics.Debug.Assert() kann in der Debug - Konfiguration verwendet werden, System.Diagnostics.Trace.Assert() in der Release - Version.
Als Beispiel eine Methode zur Division, die beim Versuch, durch 0 zu teilen, mittels Assert() dazwischengeht:

public double divide(double divident, double divisor)
{
    System.Diagnostics.Debug.Assert(divisor != 0);
    return (divident / divisor);
}

Dienstag, 23. November 2010

String - Repräsentation einer Klasse durch Überladen von ToString()

Alle Klassen und Strukturen in C# erben implizit von der Überklasse Object.
Diese stellt einige grundlegende Methoden wie Equals() (prüft auf Gleichheit) und ToString() (stellt die Klasse als Zeichenkette dar) zur Verfügung.
Alle Klassen und Strukturen implementieren somit standardmäßig diese Methoden.
Jedem wird die Schreibweise Console.WriteLine(x.ToString()); bekannt sein, wobei x beispielsweise eine Integer - Variable ist. Auch diese Klasse besitzt die Methode ToString(), sie gibt den Wert der Ganzzahl als String aus.
Vom Programmierer angelegte Klassen erben auch von Object, haben also somit auch die Methode ToString() vordefiniert, hier liefert diese aber wahrscheinlich nicht das gewünschte Ergebnis.
Der Compiler druckt einfach den Typnamen der Klasse als Zeichenkette aus, woher sollte er auch wissen, wie der Programmierer eine Instanz der Klasse als Zeichenkette darstellen möchte.
Deswegen bietet es sich an, bei eigenen Klassen die Methode ToString() zu überschreiben. Beim Überschreiben (engl. overriding) von Methoden wird in der Kindsklasse eine Methode mit der gleichen Signatur (d.h. Name und Parameterliste gleich) wie eine Methode aus der Vaterklasse angelegt.
Wird nun die Methode der Kindsklasse aufgerufen, wird der Aufruf nicht mehr auf die Methode der Basisklasse weitergeleitet, sondern direkt in der Kindsklasse verarbeitet.
Das folgende Konsolenprogramm implementiert eine kleine Klasse Employee, in welcher die Methode ToString() überladen wird.
In der modifizierten Version werden Name und Gehalt des Mitarbeiters ausgegeben:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        Employee TestEmployee = new Employee("Hans Meier", 1000);
        Console.WriteLine(TestEmployee.ToString());
    }
}

class Employee
{
    public string Name;
    public int Salary;

    public Employee(string name, int salary)
    {
        Name = name;
        Salary = salary;
    }

    public override string ToString()
    {
        return "Name: " + Name + ", Gehalt: " + Salary + ".";
    }
}

Sonntag, 21. November 2010

Ein Quine mit C#

Ein Quine ist ein Programm, welches seinen kompletten eigenen Quellcode als Ausgabe ausgibt.
Lautet der Quellcode also beispielsweise

class Quine
{
    static void Main()
    {
        System.Console.WriteLine("Dies ist (noch) kein Quine.");
    }
}

müsste das Programm genau diesen Text ausgeben.
Je nachdem, wie erfahren man mit Problemen aus diesem Bereich ist, wird einem die Aufgabe, ein Quine zu schreiben, vielleicht als einfach, schwer oder sogar unmöglich vorkommen.
(Für theoretisch interessierte: In allen "höheren" Programmiersprachen, also C, C#, Java etc. ist sogar garantiert, dass Quines existieren müssen. Denn all diese Sprachen sind Turing - vollständig, und in Turing - vollständigen Sprachen lässt sich ein Quine realisieren.)
Geht man ganz naiv an die Sache ran, könnte man vielleicht vermuten, das Problem wäre ganz schnell gelöst.
Man druckt zuerst den Code bis zur Zeile mit WriteLine(), dann diese Zeile und schließlich den Rest bis zur abschließenden Klammer.
Aber Moment! Die Aufgabenstellung besagt, dass der komplette Quellcode gedruckt werden soll, es müssen also auch alle WriteLine() Anweisungen gedruckt werden.
Schreibt man beispielsweise
string test = "abc";
und gibt diese Zeile dann mittels WriteLine() aus
WriteLine("string test = \"abc\";");
, muss im nächsten Schritt dann auch diese Zeile ausgedruckt werden.
Damit so keine Endlosrekursion entsteht, besteht der Trick darin, den auszugebenden Code in einem String zu speichern und dann nur diesen String auszugeben.
Weiterhin wird noch ausgenutzt, dass in C# in der WriteLine() - Methode Platzhalter benutzt werden können, welche dann später ersetzt werden.
Ich zeige jetzt einfach den Code eines von mir geschriebenen funktionierenden Quines, die abstrakten Forderungen von oben werden so vielleicht am besten deutlich:

class Quine
{
    static void Main(string[] args)
    {
        string s = "class Quine {3} {0} {3} static void Main(string[] args) {3} {0} {3} string s = {2}{1}{2}; {3} System.Console.WriteLine(s, System.Convert.ToChar(123), s, System.Convert.ToChar(34), System.Environment.NewLine, System.Convert.ToChar(125)); {3} {4} {3} {4}";
        System.Console.WriteLine(s, System.Convert.ToChar(123), s, System.Convert.ToChar(34), System.Environment.NewLine, System.Convert.ToChar(125));
    }
}

Samstag, 13. November 2010

In das Windows Ereignisprotokoll schreiben

Im Windows Ereignisprotokoll zeichnet Windows viele systemrelevante Informationen (z.B. das Starten / Beenden von Diensten, um nur eins von unzähligen Anwendungsgebieten zu nennen) auf.
Mit C# können wir in dieses Protokoll ganz leicht eigene Einträge über die Klasse System.Diagnostics.EventLog schreiben, um beispielsweise den Programmfluss zu überwachen.
Zuerst müssen wir hierfür (sofern noch nicht vorhanden) eine neue Quelle im Ereignisprotokoll anlegen.
Die Quelle gibt an, durch welche Anwendung etc. der Eintrag geschrieben wurde.
Gibt es noch keine Einträge von der Anwendung, wird die Quelle also neu angelegt, müssen wir spezifizieren, zu welcher Kategorie das Programm zählt. Im Ereignisprotokoll gibt es beispielsweise die Kategorien "Anwendung", "Sicherheit", "Installation" etc.
Das letztendliche Schreiben des Ereignisses führt die Methode WriteEntry() durch. Diese erwartet als 1. Parameter die Quelle, als 2. die Kategorie und optional als 3. den Typ des Ereignisses (z.B. Warnung, Information, Fehler ...).
Zum Schreiben in das Ereignisprotokoll wird das Programm wahrscheinlich (unter Windows Vista und 7) Administratorrechte benötigen, wie man die eigene Anwendung damit ausstattet könnt ihr hier lesen.
Das folgende Codebeispiel schreibt einen kurzen Informationstext in die Windows Ereignisanzeige (using System.Diagnostics; wird vorrausgesetzt):

        private void Form1_Load(object sender, EventArgs e)
        {
            string Source;
            string LogType;
            string LogEvent;

            Source = "Ereignisprotokoll C# Demo";
            LogType = "Application";
            LogEvent = "Diesen C# Blog finde ich super.";

            if (!EventLog.SourceExists(Source))
                EventLog.CreateEventSource(Source, LogType);

            EventLog.WriteEntry(Source, LogEvent, EventLogEntryType.Information);
        }

Montag, 8. November 2010

Herunterfahren von Windows verhindern / verzögern

Im vorigen Post wurde gezeigt, wie man erkennen kann, ob der PC heruntergefahren wird.
In diesem Post geht es darum, wie man das Herunterfahren abbricht bzw. verzögert um z.B. vorher Daten zu sichern.
Im vorigen Post wurde die Methode FormClosing() benutzt, in welcher beim Schließen des Formulars der Beendigungsgrund abgefragt wurde.
In dieser Methode können wir nun ebenfalls über den Parameter e das Herunterfahren einfach unterbinden, in dem wir e.Cancel = true setzen.
Das Herunterfahren des Rechners wird damit nicht vollständig abgebrochen, sondern nur solange verzögert, wie das Programm läuft - nun hat die Anwendung beispielsweise Zeit, ungesicherte Daten zu speichern.
In Windows Vista und 7 erscheint dabei ein Wartungsfenster mit dem Hinweis: "Folgendes Programm verhindert das Herunterfahren des Computers."

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.WindowsShutDown)
        e.Cancel = true;
}


Eine andere Möglichkeit, das Herunterfahren von Windows zu verzögern, besteht in der Verwendung des Microsoft.Win32.SystemEvents.SessionEnding Ereignisses, welches bei Herunterfahren des Systems eintritt.
Diesem Ereignis kann man wie anderen Ereignissen (z.B. Klick auf einen Button) eine Methode zuweisen, die aufgerufen wird, wenn es eintritt.
Folgender Codeausschnitt zeigt ein Beispiel unter Verwendung des Systemereignisses:

        private void Form1_Load(object sender, EventArgs e)
        {
            Microsoft.Win32.SystemEvents.SessionEnding += new Microsoft.Win32.SessionEndingEventHandler(this.WinShutdown);
        }

        private void WinShutdown(object sender, Microsoft.Win32.SessionEndingEventArgs e)
        {
            e.Cancel = true;
        }

Samstag, 6. November 2010

C# erkennen ob System heruntergefahren wird

Eine häufig durchgeführte Suchanfrage, bei welcher Surfer auf diesem Blog landeten, war die Frage, wie man feststellen kann, ob der PC gerade heruntergefahren wird.
Deswegen gibt es die Lösung jetzt hier als Post (warum siehe hier).
Wird das System heruntergefahren, werden logischerweise alle laufenden Prorgamme beendet. Das können wir ausnutzen, in dem wir in der Methode FormClosing() des Hauptformulars, welche aufgerufen wird, wenn das Formular geschlossen wird, den Grund des Schließens abfragen.
Dieser kann über die Eigenschaft CloseReason des Parameters e ermittelt werden.
Im Falle des Herunterfahrens lautet dieser Grund CloseReason.WindowsShutDown.
Der folgende Codeausschnitt aus einer Windows Forms-Anwendung implementiert diese Möglichkeit und gibt eine Meldung aus, wenn der PC heruntergefahren wird:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.WindowsShutDown)
        MessageBox.Show("PC wird heruntergefahren.");
}

Die Verwendung des Ereignisses Microsoft.Win32.SystemEvents.SessionEnding, welches nur eintritt wenn der PC heruntergefahren wird, wird im nächsten Post kurz erwähnt.

Freitag, 5. November 2010

C# .txt Datei bereits vorhanden

In einem vorigen Post wurde allgemein beschrieben, wie man prüfen kann, ob eine Datei / ein Ordner bereits vorhanden ist.
Im Rahmen der "Posts für häufige Suchbegriffe" zeige ich in diesem Post explizit den Code zum Prüfen, ob eine Textdatei bereits vorhanden ist.
Wer den Code nicht versteht sei auf den allgemeineren Post verwiesen, ich poste hier nur kurz die Lösung:

        private void Form1_Load(object sender, EventArgs e)
        {
            bool test = TxtFileExisting(@"C:\Users\User\Desktop\c#");
        }

        private bool TxtFileExisting(string path)
        {
            bool TxtDateiExistiert = System.IO.File.Exists(path + ".txt");
            return TxtDateiExistiert;
        }

Aus der Methode Form_Load() wird die Methode TxtFileExisting() mit dem Pfad zur prüfenden Datei (ohne Endung) aufgerufen.
In dieser Methode wird an den Pfad ".txt" angehängt und mittels System.IO.File.Exists() geprüft, ob die entsprechende Textdatei existiert. Falls ja, wird true zurückgegeben, andernfalls false.

Dienstag, 2. November 2010

C# Textdatei zufällige Zeile auslesen

In diesem Post möchte ich zeigen, wie man mit C# eine zufällige Zeile aus einer Textdatei liest.
(Diesen Post schreibe ich im Rahmen der Posts für häufige Suchbegriffe.)
Leider gibt es in C# keine direkte Möglichkeit, auf eine bestimmte Zeile einer Textdatei zuzugreifen.
Denn die Inhalte von Dateien werden als Folgen von Bits gespeichert, verschiedene Zeilen sind erst in einer höheren Abstraktionsebene sichtbar.
Um nun eine zufällige Zeile aus einer Textdatei auszulesen, lesen wir zuerst alle Zeilen ein und geben dann eine zufällige Zeile aus.
Das Auslesen der Textdatei erfolgt über die Klasse StreamReader, die Methode ReadToEnd() liest den kompletten Inhalt der Textdatei aus und gibt diesen als String zurück.
Diesen String kann man mit der Methode Split() aufteilen, als Trennzeichen übergeben wir Environment.NewLine.
Diese Konstante gibt das auf dem aktuellen System verwendete Trennsymbol für Zeilenumbrüche zurück.
Unter Windows ist dieses Symbol "\r\n", da dieses quasi aus 2 verschiedenen Symbolen besteht wird der String zuoft gesplittet, jede 2. Zeile ist leer und wir setzen die Option StringSplitOptions.RemoveEmptyEntries
um diese leeren Zeilen zu löschen.

Zur Erzeugung von Zufallszahlen, was zur Rückgabe einer zufälligen Zeile benötigt wird, habe ich diesen Post geschrieben.

Und nun der Code für eine fertige Funktion, welche den Pfad zu der zu lesenden Textdatei als Parameter entgegen nimmt und als Ergebnis eine zufällige Zeile der Textdatei als String zurückgibt (oben im Projekt muss die Anweisung using System.IO; eingebunden sein):

private string ReadRandomTextLine(string path)
{
    StreamReader sr = new StreamReader(new FileStream(path, FileMode.Open)); // StreamReader zum Lesen der Datei im Pfad path
    string[] Lines = sr.ReadToEnd().Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries); // kompletten Inhalt der Datei auslesen und nach Zeilen splitten
    Random rnd = new Random(); // den Zufallsgenerator initialisieren
    return Lines[rnd.Next(Lines.Length)]; // eine zufällige Zeile zurückgeben
}

Posts für häufige Suchbegriffe

Ich benutze Google Analytics zur Analyse des Besucherverhaltens dieses Blogs. Mit Analytics kann man u.a. sehen, wie oft die eigene Seite für welche Suchbegriffe in den Ergebnisseiten von Suchmaschinen auftaucht und wie oft sie angeklickt wird.
In diesen Statistiken finden sich einige interessante Begriffe betreffend C# Fragen, die häufig gesucht werden. In den nächsten Tagen werde ich Posts zu genau den gesuchten Themen veröffentlichen. Erfahrenen C# Nutzern werden sie vielleicht komisch oder überflüssig vorkommen, aber ich hoffe, einige Programmierer finden genau die Lösung ihres Problems und ich erhalte nebenbei noch den ein oder anderen Besucher ;-)

Liste der Posts aus dieser Reihe:
C# Textdatei zufällige Zeile auslesen
CSharp txt Datei bereits vorhanden

Samstag, 30. Oktober 2010

Allgemeiner Fehler in GDI+

Die Fehlermeldung "Allgemeiner Fehler in GDI+." ist ganz schön fies in C# - sie tritt sporadisch auf und die Ursache ist oft auf den ersten Blick nicht ersichtlich. Deswegen werde ich diesen Post der Fehlermeldung und ihren Ursachen widmen und die meiner Meinung nach 2 häufigsten Quellen des Grafikfehlers beschreiben.
Der oben genannte Fehler kann auftreten wenn ein Bild (Image) über die Methode Save() in einen nicht existierenden Pfad gespeichert werden soll.
Folgender Code versucht, den Inhalt der pictureBox als Bild zu speichern und wirft dabei die Fehlermeldung, sofern der angegebene Pfad nicht vorhanden ist:

pictureBox1.Image.Save("C:\\NichtExistierenderPfad\\test.jpg");

Beim Laden eines Bildes führt ein falscher Pfad dagegen zu einer FileNotFoundException.
Eine weitere sehr häufige Ursache des leidigen Fehlers "Allgemeiner Fehler in GDI+." ist die Nichtfreigebung eines Bildobjekts.
Lädt man ein Bild z.B. über die Methode Image.FromFile(), bleibt dieses solange im Speicher geöffet, bis es nicht freigegeben wird. Insbesondere ist vor dem Freigeben kein Speichern über Save() möglich.
Das folgende Codebeispiel lädt ein Bild in die pictureBox und versucht dann, dieses zu speichern - doch es tritt der "Allgemeiner Fehler in GDI+." Fehler auf, da das Bild nicht freigegeben wurde.

pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
pictureBox1.Image.Save("C:\\test\\test2.jpg");

Das Bild muss zur korrekten Funktionsweise des Codes intern "umkopiert" werden:

pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
Image Copy = pictureBox1.Image;
Copy.Save("C:\\test\\test2.jpg");

Ich hoffe, der Post hat die grundlegende Problematik dieses Fehlers deutlich gemacht, über weitere Ursachen, Besonderheiten etc. bin ich jederzeit dankbar!

Donnerstag, 28. Oktober 2010

Bild in byte - Array speichern

Um mit C# ein Bild (Image) in ein byte - Array zu konvertieren, speichern wir das Bild zuerst in einen Stream und kopieren den Inhalt dieses Streams dann in ein byte - Array.
Prinzipiell kann jeder Streamtyp dazu verwendet werden, am einfachsten ist jedoch die Verwendung eines MemoryStreams, welcher lediglich temporär Daten in den Arbeitsspeicher schreibt bzw. daraus liest.
Ein Bild vom Typ Image kann über die Methode Save() in den als 1. Parameter spezifizierten Stream geschrieben werden, als 2. Parameter kann das Format angegeben werden, in welchem das Bild gespeichert werden soll (also z.B. Gif oder Bmp).
Der folgende Beispielcode speichert den Inhalt der pictureBox auf dem Formular in einem byte - Array:

            MemoryStream TempStream = new MemoryStream();
            byte[] ImageInBytes;

            pictureBox1.Image.Save(TempStream, System.Drawing.Imaging.ImageFormat.Gif);
            ImageInBytes = TempStream.ToArray();

Dienstag, 26. Oktober 2010

Code mit #region gliedern

Bei größeren Codeprojekten kann die Codeansicht schnell unübersichtlich werden, sind alle Methoden etc. erweitert ist ganz schön Scrollarbeit nötig, um von oben im Quellcode bis nach unten zu kommen.
Doch Codeblöcke lassen sich mit C# auch ganz einfach gliedern, und zwar mit dem Schlüsselwort #region.
Dieses Schlüsselwort beginnt einen Block welcher durch #endregion beendet werden muss.
Die definierten Regionen können dann z.B. wie Methoden ein- oder ausgeklappt werden, die folgenden 2 Bilder zeigen dies:

"Normal" (ausgeklappt):










Eingeklappt:

Mittwoch, 20. Oktober 2010

Komprimierung mit C#, Teil 3 - Archive erstellen und wieder entpacken

Im 3. und (vorerst) letzten Teil zur Reihe "Komprimierung in C#" möchte ich euch eine Möglichkeit zeigen, mehrere Dateien auf einmal in einem Archiv zu komprimieren und dieses wieder zu entpacken.
Um diesen Post hier zu verstehen, ist ein Verständnis der Techniken aus Teil 1 und 2 hilfreich.
Mehrere Dateien in ein Archiv zu verpacken, geht in C# nur über einen kleinen Umweg, denn das verwendete gzip - Format unterstützt standardmäßig nur die Komprimierung von einzelnen Dateien.
Sammlungen von mehreren Dateien werden so meistens zuerst mit tar und dann mit gzip komprimiert, sie erhalten dann die Endung .tar.gz.
Obwohl uns das tar - Format in .Net nicht zur Verfügung steht, lässt sich eine Komprimierung von mehreren Dateien doch umsetzen: Wir fassen die einzelnen Dateien einfach zu einer großen Datei zusammen, in welcher die Dateien durch besondere Zeichen voneinander getrennt sind. Diese große Datei wird dann komprimiert und beim Dekomprimieren wieder anhand der Sonderzeichen in die einzelnen Dateien aufgeteilt.

Die Technik:
Als Kennzeichnung zwischen den Dateien schrieb ich folgenden Header vor jede Datei in den Stream:
|*START*OF*HEADER*|*||GRÖSSE_DER_DATEI||NAME_DER_DATEI*|*END*OF*HEADER*|
Probleme könnte es also in dem unwahrscheinlichen Fall geben, wenn der Inhalt einer Datei der Struktur gleichen würde.

Die Komprimierung / Erstellung eines Archivs:
Der Methode zur Komprimierung werden die zu komprimierenden Dateien als String - Array übergeben sowie der Name und Pfad des Archivs.
Die Dateien werden nun durchlaufen und der jeweilige Header sowie anschließend der Inhalt der Datei wird zuerst in einen MemoryStream geschrieben.
Die Daten aus diesem werden anschließend in ein byte - Array kopiert und dieses wird dann mit einem GZipStream in die Archivdatei geschrieben.
Dadurch, dass die Dateien erst zusammengefasst und dann mit dem GZipStream geschrieben werden und nicht jede Datei nacheinander in das Archiv geschrieben wird, kann das Archiv viel stärker komprimiert werden, da auch Redundanzen zwischen den einzelnen Dateien ausgenutzt werden können.
Der Code:

        private void CreateArchive(string[] files, string archiv)
        {
            GZipStream CompressStream = new GZipStream(new FileStream(archiv, FileMode.Create), CompressionMode.Compress);
            FileStream NormalFileStream;
            byte[] Content;

            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] HeaderStart = encoder.GetBytes("|*START*OF*HEADER*|*");
            byte[] HeaderEnd = encoder.GetBytes("*|*END*OF*HEADER*|");
            byte[] FileSize; // Größe der aktuellen Datei
            byte[] Separator = encoder.GetBytes("||");
            byte[] FileName; // Name der aktuellen Datei

            MemoryStream TempStream = new MemoryStream();

            foreach (string file in files)
            {
                NormalFileStream = new FileStream(file, FileMode.Open);
                FileSize = encoder.GetBytes(NormalFileStream.Length.ToString());
                FileName = encoder.GetBytes(file.Substring(file.LastIndexOf('\\') + 1));

                TempStream.Write(HeaderStart, 0, HeaderStart.Length);
                TempStream.Write(Separator, 0, Separator.Length);
                TempStream.Write(FileSize, 0, FileSize.Length);
                TempStream.Write(Separator, 0, Separator.Length);
                TempStream.Write(FileName, 0, FileName.Length);
                TempStream.Write(HeaderEnd, 0, HeaderEnd.Length);

                Content = new byte[NormalFileStream.Length];
                NormalFileStream.Read(Content, 0, Content.Length);
                NormalFileStream.Close();
                TempStream.Write(Content, 0, Content.Length);
            }

            byte[] BigFileContent = new byte[TempStream.Length];
            TempStream.Position = 0;
            TempStream.Read(BigFileContent, 0, BigFileContent.Length);
            CompressStream.Write(BigFileContent, 0, BigFileContent.Length);
            CompressStream.Close();
        }


Die Dekomprimierung / Entpackung eines Archivs:
Die Komprimierung war der einfache Part, die Dekomprimierung gestaltet sich etwas schwieriger.
Die Methode zur Dekomprimierung erhält den Pfad und Namen des Archivs sowie den Pfad, in den die Dateien anhand ihrer ursprünglichen Namen entpackt werden sollen.
Zuerst müssen die Daten aus der Archivdatei dekomprimiert werden, hierzu wird diese mit einem GZipStream ausgelesen.
Der Inhalt dieses wird anschließend in einen MemoryStream kopiert und dieser schreibt seinen Inhalt in ein byte - Array (ist leichter über einen MemoryStream, deswegen der Umweg).
Das byte - Array wird mit einer Instanz der Klasse ASCIIEncoder in einen String umgewandelt.
Das Auswerten des Inhalts erfolgt in einer Endlosschleife, in jedem Durchlauf wird eine Datei behandelt.
Es gibt 2 Zeiger, die auf eine Position im String zeigen.
Der erste speichert die aktuelle Position, der zweite die aktuelle Suchposition.
Der erste steht immer auf der Position, an der der Header der aktuellen Datei anfängt, der zweite auf eine um 22 höhere Position (da wo die Dateigröße anfängt).
Da die Struktur des Headers bekannt ist (z.B. die Größe der ersten Datei beginnt ab Position 22 und geht bis zum ersten Vorkommen von "||" - deshalb steht die Suchposition auf einer um 22 höheren Position als die aktuelle Position, nach der Suchposition markiert das erste Vorkommen von "||" das Ende der Dateigröße, davor taucht noch ein "||" vor der Dateigröße aus.), können Dateigröße und Name ausgelesen werden.
Die aktuelle Position wird nun um die Länge des Headers inkrementiert.
Mit einem FileStream wird nun das byte - Array, welches den Inhalt der großen Datei speichert, von der aktuellen Position bis zur Position aktuelle Position zuzüglich aktueller Dateilänge, in eine neue Datei geschrieben, diese wird im übergebenen Verzeichnis angelegt und trägt den Namen der ursprünglichen Datei.
Die Positionszeiger werden anschließend um die Größe der aktuellen Datei erhöht.
Der Code:
        private void OpenArchive(string archiv, string decompressPath)
        {
            GZipStream DecompressStream = new GZipStream(new FileStream(archiv, FileMode.Open), CompressionMode.Decompress);
            FileStream NormalFileStream;
            MemoryStream TempStream = new MemoryStream();

            ASCIIEncoding decoder = new ASCIIEncoding();
            ASCIIEncoding Encoder = new ASCIIEncoding();

            string StringFromBytes; // String - Darstellung der eingelesenen Bytes
            int EndSize; // Position im Header, an welcher die Bezeichnung der Dateigröße zu Ende ist
            long FileLength; // Größe der aktuellen Datei
            int StartFileName; // Position im Header, an welcher die Bezeichnung des Dateinamens anfängt
            int EndFileName; // Position im Header, an welcher die Bezeichnung des Dateinamens aufhört
            string FileName; // Name der aktuellen Datei
            string EmptyHeader = "|*START*OF*HEADER*|*||||*|*END*OF*HEADER*|"; // "Prototyp" des Headers
            byte[] EmptyHeaderBytes = Encoder.GetBytes(EmptyHeader); // Prototyp als Bytes

            long CurrentPosition = 0; // aktuelle Position im Inhalt der Datei
            long CurrentSearchPosition = 22; // aktuelle Suchposition im Inhalt der Datei

            DecompressStream.CopyTo(TempStream);
            byte[] BigFileContent = new byte[TempStream.Length];
            TempStream.Position = 0;
            TempStream.Read(BigFileContent, 0, BigFileContent.Length);
        
            StringFromBytes = decoder.GetString(BigFileContent);

            while (true)
            {
                EndSize = StringFromBytes.IndexOf("||", (int)CurrentSearchPosition);
                FileLength = long.Parse(StringFromBytes.Substring((int)CurrentSearchPosition, EndSize - (int)CurrentSearchPosition)); // die Bezeichnung der Dateigröße geht von Position 22 im Header bis Position EndSize

                StartFileName = EndSize + 2;
                EndFileName = StringFromBytes.IndexOf("*|*", StartFileName);
                FileName = StringFromBytes.Substring(StartFileName, EndFileName - StartFileName); // Dateinamen auslesen
              
                CurrentPosition += EmptyHeaderBytes.Length + Encoder.GetBytes(FileLength.ToString()).Length + Encoder.GetBytes(FileName).Length;
                
                NormalFileStream = new FileStream(decompressPath + "\\" + FileName, FileMode.Create);
                NormalFileStream.Write(BigFileContent, (int)CurrentPosition, (int)FileLength);

                CurrentPosition += FileLength;
                CurrentSearchPosition = CurrentPosition + 22;
                NormalFileStream.Close();

                if (CurrentSearchPosition > BigFileContent.Length)
                    break;
            }

            DecompressStream.Close();
        }

Montag, 18. Oktober 2010

Komprimierung mit C#, Teil 2 - Dateien komprimieren

Heute setzte ich das Tutorial zu Komprimierungsmethoden in C# aus dem vorigen Post fort.
Während es im selbigen um das Grundprinzip und die Komprimierung / Dekomprimierung von einfachen Zeichenketten ging, zeige ich jetzt eine etwas fortgeschrittenere Anwendung dieser Prinzipien zum Komprimieren / Dekomprimieren von ganzen Dateien.

Die Kompression:
Wie bei der Komprimierung von Zeichenketten legen wir eine neue Instanz der Klasse GZipStream an und übergeben dieser im Konstruktor einen FileStream, der auf die Zieldatei zeigt.
Wir legen weiterhin einen anderen FileStream an, der auf die Quelldatei zeigt.
Mit diesem lesen wir nun den Inhalt der alten Datei in ein byte - Array.
Schreiben wir dieses nun mit der Methode Write() des GZipStreams, schreibt dieser den komprimierten Inhalt der Quelldatei in die Zieldatei - wir haben eine Datei komprimiert, fast so, als hätten wir sie per WinZip oder einem ähnlichen Programm zu einem Archiv hinzugefügt.
Öffnet man die komprimierte Datei mit einem Entpackungsprorgamm (z.B. WinZip), kann man das ursprüngliche Bild extrahieren und öffnen.
Der entsprechende Code dazu:

        private void CompressFile(string normalFile, string compressedFile)
        {
            GZipStream CompressStream = new GZipStream(new FileStream(compressedFile, FileMode.Create), CompressionMode.Compress);
            
            FileStream NormalFileStream = new FileStream(normalFile, FileMode.Open);
            byte[] Content = new byte[NormalFileStream.Length];
            NormalFileStream.Read(Content, 0, Content.Length);
            NormalFileStream.Close();

            CompressStream.Write(Content, 0, Content.Length);
            CompressStream.Close();
        }

Nun die Dekompression:
Hierfür wird im Konstruktor des GZipStreams ein FileStream, der auf die auszulesende, komprimierte Datei zeigt, übergeben.
Außerdem wird ein neuer FileStream angelegt, welcher auf die zu schreibende, dekomprimierte Datei zeigt.
Wie im vorigen Post auch zur Dekomprimierung benutzt, wird nun eine Endlosschleife eingesetzt, um die komprimierte Datei auszulesen.
Hierfür wird die Methode Read() des GZipStreams verwendet, welche die übergebene Anzahl an Bytes einliest und dabei dekomprimiert. Das Ergebnis wird in einem Buffer gespeichert, welcher dann über den FileStream in die neue Datei geschrieben wird.
Wurde das Ende der Datei erreicht, liest die Methode Read() weniger Bytes als in den Buffer passen würden, woran das Programm das Ende erkennt.
Die komprimierte Datei wurde nun entpackt und wieder als lesbare Datei angelegt.
Der Code dazu lautet:

        private void DecompressFile(string compressedFile, string normalFile)
        {
            GZipStream DecompressStream = new GZipStream(new FileStream(compressedFile, FileMode.Open), CompressionMode.Decompress);

            FileStream NormalFileStream = new FileStream(normalFile, FileMode.Create);

            int BytesReadCount = 0;
            byte[] Buffer = new byte[4096];

            while (true)
            {
                BytesReadCount = DecompressStream.Read(Buffer, 0, Buffer.Length);
                if (BytesReadCount != 0)
                {
                    NormalFileStream.Write(Buffer, 0, BytesReadCount);
                }
                if (BytesReadCount < Buffer.Length)
                    break;
            }

            NormalFileStream.Close();
            DecompressStream.Close();

        }

Sonntag, 17. Oktober 2010

Webcam Chat

Auf Anfrage eines Lesers habe ich mich kürzlich der Realisierung eines etwas aufwendigeren Projekts gewidmet, der Entwicklung eines in C# implementierten Webcam Chats.
Diesen möchte ich in diesem Post vorstellen, zum Verständnis sind einige Vorkenntnisse hilfreich:
- Den Code zur Einbindung einer Webcam habe ich mit Erlaubnis vom net-blog übernommen.
- Die Verbindung der Partner wird über TCP / IP hergestellt, hierzu gibt es eine Einführung auf diesem Blog (hier).
- Viele möchten sich sicherlich über das Internet mit Freunden vernetzen, wie die Verbindung darüber läuft und insbesondere welche IPs zu verwenden sind, findet ihr hier (und auch noch allgemeine Infos zur Vernetzung im LAN).
- Wie in Bild "gestreamt" werden kann wird in diesem Post erklärt.
- Und schließlich gibt's hier allgemeine Infos zum Fehler "Allgemeiner Fehler in GDI+", welcher sporadisch bei Grafikanwendungen auftritt.

Hier die Oberfläche des Programms:

Freitag, 15. Oktober 2010

Komprimierung mit C#, Teil 1 - einfache Strings

Das .Net Studio bringt standardmäßig eine Klasse zur Komprimierung und Dekomprimierung von Daten im gzip Format mit sich, System.IO.Compression.GZipStream.
gzip ist ein gutes, offen verfügbares Format, welches, für technisch Interessierte, zur Komprimierung, ähnlich wie das ZIP - Format, den Deflate Algorithmus verwendet.
Die Klasse GZipStream ist ein Stream, wie herkömmliche Streams können mit ihr Daten geschrieben und gelesen werden, nur werden diese dabei direkt komprimiert bzw. dekomprimiert.
Als erstes Anwendungsbeispiel werde ich eine einfache Möglichkeit zur (De-)Komprimierung von Zeichenketten in (bzw. aus) einer Datei zeigen.
Für alle folgenden Codebeispiele werden die Anweisungen
using System.IO.Compression;
using System.IO;

benötigt.

Zuerst zur Komprimierung: Die Klasse GZipStream erwartet einen Stream im Konstruktor, in den die Daten hinein komprimiert werden sollen sowie den Komprimiermodus (hier CompressionMode.Compress).
Als Stream geben wir einen FileStream an, der auf die Datei zeigt, in welche der komprimierte String geschrieben werden soll.
Die Klasse GZipStream kann nur Bytes lesen und schreiben, daher muss der zu komprimierende String in ein byte - Array umgewandelt werden, was die Klasse ASCIIEncoding erledigt.
Das erhaltene Array wird dann mittels Write() geschrieben, der GZipStream komprimiert die Byte - Folge und schreibt sie über den FileStream in eine Datei.
Und so sieht der dazu passende Code aus:

private void CompressString(string uncompressedString)
{
    GZipStream CompressStream = new GZipStream(new FileStream(Application.StartupPath + "\\CompressedString.gz", FileMode.Create), CompressionMode.Compress);
    ASCIIEncoding Encoder = new ASCIIEncoding();
    byte[] UncompressedStringInBytes = Encoder.GetBytes(uncompressedString);
    CompressStream.Write(UncompressedStringInBytes, 0, UncompressedStringInBytes.Length);
    CompressStream.Close();
}

Der der Methode CompressString übergebene String wird in die Datei "CompressedString.gz" im Anwendungsverzeichnis geschrieben, .gz ist die Dateiendung für gzip - Dateien.
Die Datei kann mit herkömmlichen Entpackungsprogrammen (z.B. WinZip) entpackt werden, öffnet man die entpackte Datei mit einem Texteditor findet man den ursprünglichen String wieder.

Nun zum umgekehrten Fall, der Dekomprimierung des Strings aus der Datei:
Die Klasse GZipStream erwartet im Konstruktor wieder einen Stream, dieses Mal den Stream, aus welchem die komprimierten Daten gelesen werden sollen, und den Komprimiermodus (hier CompressionMode.Decompress).
Wir übergeben als Stream wieder einen FileStream.
Das Auslesen der Datei erfolgt nun durch eine Endlosschleife, in dieser wird jedes Mal über die Methode Read() des GZipStreams ein byte - Array in einen Buffer gelesen.
Die gelesenen Bytes werden über die Klasse ASCIIEncoding in einen String umgewandelt, wurden in einem Durchlauf weniger Bytes gelesen als der Buffer groß ist, ist die Datei offensichtlich zu Ende und die Schleife wird beendet.
Der Code:

        private string DecompressString(string compressedFile)
        {
            GZipStream DecompressStream = new GZipStream(new FileStream(Application.StartupPath + "\\CompressedString.gz", FileMode.Open), CompressionMode.Decompress);
            byte[] Buffer = new byte[4096];
            ASCIIEncoding Decoder = new ASCIIEncoding();
            int BytesReadCount = 0;
            string DecompressedString = "";

            while (true)
            {
                BytesReadCount = DecompressStream.Read(Buffer, 0, Buffer.Length);
                if (BytesReadCount != 0)
                {
                    DecompressedString += Decoder.GetString(Buffer, 0, BytesReadCount);
                }
                if (BytesReadCount < Buffer.Length)
                    break;
            }

            DecompressStream.Close();
            return (DecompressedString);
        }

Der Aufruf der beiden Methoden könnte zum Beispiel so aussehen:

CompressString("Dies ist ein Test 123456.");
string DecompressedString = DecompressString(Application.StartupPath + "\\CompressedString.gz");

Mittwoch, 13. Oktober 2010

Startseite des Internet Explorers auslesen / ändern

Die Startseite des Internet Explorers wird in der Registry gespeichert, und zwar im Schlüssel HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Start Page.
Diese Information kann nun einfach mit C# ausgelesen und beliebig verändert werden.
Ein kleines Tutorial zur Benutzung der Registry mit C# gab es hier.
Da so die benutzten Methoden bekannt sein sollten, poste ich hier nur kurz den Programmcode.
Zuerst eine Methode, die die Startseite des Internet Explorers ausliest und als String zurückgibt:

private string GetIEStartPage()
{
    RegistryKey StartPageKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Internet Explorer\Main"); // entsprechenden Pfad in der Registry öffnen
    return StartPageKey.GetValue("Start Page").ToString(); // den Wert aus dem Schlüssel "Start Page" auslesen und zurückgeben
}

Nun noch die Methode zum Ändern der Startseite, diese erwartet die neue Startseite als String - Parameter:

private void SetIEStartPage(string newStartPage)
{
    RegistryKey StartPageKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Internet Explorer\Main", true); // entsprechenden Pfad in der Registry mit Schreibrechten öffnen
    StartPageKey.SetValue("Start Page", newStartPage); // den neuen Wert in den Schlüssel "Start Page" schreiben
}

Montag, 11. Oktober 2010

Computername herausfinden

Um mit C# den Namen des eigenen Computers herauszufinden, gibt es 2 einfache Möglichkeiten:
Über die Klasse Dns (using System.Net wird benötigt):

string ComputerName = Dns.GetHostName();

Und über die Klasse SystenInformation:

string ComputerName = System.Windows.Forms.SystemInformation.ComputerName.ToString();

Freitag, 8. Oktober 2010

HTML - Seiten mit dem Webbrowser Steuerlement aufrufen

In C# gibt es ein fertiges Steuerelement, mit dem sich Internetseiten wie im Browser aufrufen lassen.
Das Steuerelement heißt WebBrowser und befindet sich standardmäßig schon in der Toolbox.
Zieht man es auf ein Formular, erstreckt es sich wahrscheinlich standardmäßig über dessen ganze Breite, ist dieses nicht gewünscht, kann man die Eigenschaft Dock ändern.
So sieht ein Teil dieses Blogs im WebBrowser aus:



Nun möchte ich ein paar Methoden dieses Steuerlements beschreiben:

Navigate(): Dieses ist wohl die wichtigste Methode, mit ihr kann man Seiten im WebBrowser Steuerelement aufrufen. Sie erwartet in einer Überladung einen String als Parameter, welcher dann als Zieladresse interpretiert wird.

GoHome(): Navigiert zur Startseite des aktuellen Benutzers.

GoSearch(): Navigiert zur Seite der Standardsuchmaschine des aktuellen Benutzers.

Print(): Druckt die aktuelle Seite des Browsersteuerelements mit den aktuellen Druckeinstellungen des Systems.

ShowPrintDialog(): Ruft den "Drucken" Dialog des Internet Explorers auf, um die aktuelle Seite zu drucken.

ShowSaveAsDialog(): Öffnet den Dialog "Seite speichern", um die aktuelle Seite als HTML - Dokument zu speichern.

Zum Schluss noch ein Link zu einer meiner Meinung nach sehr informativen Seite zum Thema Webbrowser, auf der auch ein kostenlos E-Book zu diesem Thema verfügbar ist:
readup.de

Dienstag, 5. Oktober 2010

Video zum Post "VLC Player einbinden" online

Auf mein Youtube Konto habe ich ein neues Video hochgeladen, es zeigt die Einbindung des VLC Players wie in diesem Post beschrieben.
Das Video findet ihr unter der Adresse http://www.youtube.com/watch?v=Bh4qsHrk910 .

Montag, 4. Oktober 2010

IP Adresse anhand des Hostnamens herausfinden

Internetseiten kann man durch Eingabe von einprägsamen Adressen im Browser aufrufen. Dass wir uns hierbei keine kryptischen IP - Adressen der Server, auf denen die Seiten gehostet werden, merken müssen, verdanken wir dem DNS (Domain Name System) Dienst.
Dieser löst bei Anfragen die übergebene URL in eine IP - Adresse auf, welche dann in Wirklichkeit aufgerufen wird.
Um mit C# abzufragen, welche IP - Adressen denn nun hinter den Hostnamen stehen, können wir die Klasse Dns aus System.Net verwenden.
Die Methode GetHostByName() gibt ein Array von IP - Adressen zurück, welche für den übergebenen Host hinterlegt sind.
Der folgende Code gibt die IP - Adresse des Servers, auf dem dieser Blog läuft, in einer Konsole aus (using System.Net wird vorrausgesetzt):

foreach (IPAddress IP in Dns.GetHostByName("csharp-tricks.blogspot.com").AddressList)
{
    Console.WriteLine(IP.ToString());
}

Samstag, 2. Oktober 2010

Wahl der IP - Adressen zur Kommunikation über TCP / IP

Im vorigen Post habe ich beschrieben, wie man mit C# Daten über das TCP / IP Protokoll austauschen kann.
Die beschriebene Methode funktioniert sowohl über das Internet als auch über das Netzwerk.
In diesem Post noch kurz zur Wahl der IP - Adressen.
Befinden sich Server und Client im selben lokalen Netzwerk, kann der Client beim Verbinden auf den Server einfach dessen Netzwerk IP eingeben.
In einem lokalen Netzwerk hat jeder Computer eine eindeutige IP, diese haben meistens die Form 192.168.xxx.xxx.
Soll die Verbindung über das Internet erfolgen, muss die sogenannte WAN IP verwendet werden. Diese kann zum Beispiel über die Seite http://www.wieistmeineip.de/ abgefragt werden (um diese mit C# auszulesen siehe dieser Post).
Das Problem ist allerdings, dass diese Adresse nur pro "Zugangspunkt" zum Internet und nicht pro Rechner vergeben wird. Heutzutage haben die meisten Haushalte eine Internet Verbindung über einen Router, an dem mehrere PCs hängen.
Folglich kriegt nur der Router eine WAN IP, alle angeschlossenen PCs erhalten dieselbe.
Wenn die Verbindung also über das Internet nicht klappt, kann das daran liegen, dass der Router zwar die Verbindungsanfrage vom Client an seine IP erhält, aber nicht weiß, an welchen PC im lokalen Netzwerk er diese Anfrage weiterleiten soll.
Dann muss im Router eine neue Regel zum Port Forwarding angelegt werden.
Port Forwarding bedeutet Port Weiterleitung und sagt dem Router folglichermaßen, welche Anfragen auf welchen Ports er an welchen PC weiterleiten soll.
Legt man nun eine neue Regel an für den Port, auf dem der Server "lauscht" und leitet diesen Port an die IP Adresse des eigenen PCs im lokalen Netzwerk weiter, klappt auch die TCP / IP Verbindung über das Internet.

Freitag, 1. Oktober 2010

Datenaustausch im Internet und Netzwerk über TCP / IP

Um 2 PCs mit C# entweder über das Internet oder das lokale Netzwerk zu verbinden, gibt es unzählige Möglichkeiten.
Die meisten Möglichkeiten werden jedoch über die Verwendung von Sockets und über das TCP - Protokoll führen. Heute möchte ich euch eine davon zeigen.
Es gibt im Netz sicherlich sehr viele gute Tutorials, die das gleiche Thema behandeln wie dieser Post und sehr detailliert auf die damit verbundene Technik eingehen.
Im generellen Stil dieses Blogs werde ich daher nur relativ knapp auf den Code eingehen und die Theorie anderen überlassen.
Einleitend aber doch ein paar Satz dazu: Zur Kommunikation über ein Netzwerk (das Internet einbezogen) brauchen die beteiligten Computer ein gemeinsames Protokoll zur Datenübertragung, dass alle verstehen. Ein solches Protokoll ist zum Beispiel das hier verwendete TCP/IP Protokoll. Diese Abkürzung bedeutet Transmission Control Protocol / Internet Protocoll und ist eigentlich eine Sammlung von Protokollen zur Kommunikation über das Internet.
Das TCP/IP Protokoll nutzt zur Kommunikation Sockets. Diese sind Endpunkte, zwischen denen die Datenübertragung abläuft, man kann sie als äußere Schnittstellen zwischen den Kommunikationspartnern ansehen. Die Kommunikation läuft weiterhin über einen festgelegten Port. Ports sind eine Methode, die eingehenden Daten auf einem Rechner zu sortieren. Jede Anwendung, die Daten über das Internet / lokale Netzwerk sendet und empfängt, tut dies über einen bestimmten Port. So weiß der Computer, wohin er welche Daten leiten soll.

So, nun aber wie versprochen zur Praxis: