Mittwoch, 30. Juni 2010

Mausposition ändern

Über die Klasse Cursor kann man in C# die Position des Cursors ändern. Die entsprechende Eigenschaft heißt Position, sie speichert die Position des Mauszeigers als Point.
Die Position ist global, also nicht nur relativ in der Anwendung, (0,0) beschreibt beispielsweise die obere linke Ecke des Bildschirms.
Hier die komplette Anweisung:

Cursor.Position = new Point(X, Y);

Dienstag, 29. Juni 2010

Partitionen / Laufwerke aufzählen

Mit C# kann man ganz einfach alle verfügbaren Laufwerke aufzählen lassen. Logische Partitionen auf einer physikalischen Festplatte werden allgemein wie eigenständige Laufwerke behandelt, somit erhält man gleichzeitig alle Partitionen auf der Festplatte.
Das folgende Konsolenprogramm zeigt die Verwendung der Klasse DriveInfo (System.IO muss eingebunden sein), um die Namen aller verfügbaren Laufwerke auf dem Computer auszugeben:

            DriveInfo[] Drives = DriveInfo.GetDrives();

            foreach (DriveInfo drive in Drives)
            {
                Console.WriteLine(drive.Name);
            }

Auf meinem PC produziert das Programm die Ausgabe: "C:\, D:\, E:\". Erstere Laufwerke sind Paritionen auf der Festplatte, letzteres ist das CD - Laufwerk.

Montag, 28. Juni 2010

Physikalische Adresse (MAC - Adresse) ermitteln

Im heutigen Post möchte ich euch zeigen, wie man die MAC - Adresse eines Computers, auch seine physikalische Adresse genannt, mit C# ausliest.
Die MAC - Adresse ist die physikalische Hardware - Adresse des Netzwerkadapters, mit dem dieser eindeutig auf der ganzen Welt identifiziert werden kann, sie wird nur einmal vergeben (sie kann aber gefälscht bzw. geändert werden).
Hat euer Computer also eine LAN - Karte und wählt sich mit dieser ins Internet ein, kann euer Computer über die MAC - Adresse des Netzwerkadapters im Internet eindeutig identifiziert werden.
Mit der Programmiersprache C# .Net kann man die MAC - Adresse einerseits über die Klasse System.Management.ManagementClass auslesen, doch der Code hierzu ist recht umständlich.
Es gibt einen viel einfacheren einzeiligen Aufruf, der zum gleichen Ergebnis führt.
Der folgende Code stellt eine C# - Konsolenanwendung dar, die über alle Netzwerkadapter des PCs iteriert und dessen jeweilige Beschreibungen (Namen) und zugehörige MAC - Adressen ausgibt (using System.Net.NetworkInformation; muss eingebunden sein):

            NetworkInterface[] NetworkAdapters = NetworkInterface.GetAllNetworkInterfaces();

            foreach (NetworkInterface adapter in NetworkAdapters)
            {
                Console.WriteLine(adapter.Description);
                Console.WriteLine("MAC - Adresse: " + adapter.GetPhysicalAddress().ToString());
                Console.WriteLine();
            }

Samstag, 26. Juni 2010

Konsolenanwendung nicht automatisch beenden

Startet man in der .Net - Entwicklungsumgebung eine Konsolenanwendung über den gewöhnlichen Befehl Debuggen - Debuggen starten (F5), schließt sich die Konsolenanwendung automatisch nach der Ausführung. Oftmals ist das ärgerlich, weil man so Ausgaben auf dem Bildschirm nicht mehr zu sehen bekommt, das Fenster "blitzt" nur kurz auf.
Startet man das Programm hingegen über die Tastenkombination Strg + F5 (Starten ohne Debuggen), wird die Meldung Drücken Sie eine beliebige Taste ... angezeigt, das Programm bleibt solange geöffnet, bis der Benutzer eine Taste drückt.
Alternativ kann man am Ende des Programmcodes ein Console.ReadKey(); einfügen, nun wartet das Programm am Ende auch auf einen Tastendruck.

Primzahlen ausgeben mit dem Sieb des Eratosthenes

Heute möchte ich euch eine C# - Implementierung eines Algorithmus' zeigen, der alle Primzahlen von 2 bis zu einer oberen Grenze ausgibt.
Da der Name des Algorithmus durch den griechischen Mathematiker Eratosthenes von Kyrene geprägt wurde, nennt man diesen das "Sieb des Eratosthenes".
In allgemeiner Sprechweise funktioniert das Sieb folgendermaßen wobei N die Obergrenze sei:
Man schreibt alle Zahlen von 2 - N auf. Die kleinste nicht durchgestrichene Zahl ist immer eine Primzahl. Anfangs ist das die 2. Ausgehend von der kleinsten nicht durchgestrichenen Zahl werden dann alle Vielfachen dieser durchgestrichen, wobei es genügt, wenn man mit der Quadratzahl der Zahl beginnt.
Alle am Ende übrig gebliebenen, nicht durchgestrichenen Zahlen sind Primzahlen.
Jetzt die Implementierung als C# - Konsolenanwendung:

    class SiebDesEratosthenes
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Geben Sie die obere Grenze ein:");
            int UpperBound = int.Parse(Console.ReadLine()); // Zahl, bis zu der gesucht werden soll, eingeben
            bool[] Tagged = new bool[UpperBound + 1]; // Array zur Speicherung, ob die zum Index passende Zahl gestrichen wurde

            // Array mit false initialisieren
            for (int i = 0; i < Tagged.Length; i++)
                Tagged[i] = false;

            // alle Zahlen bis zur Wurzel der oberen Zahl durchlaufen
            for (int i = 2; i < Math.Ceiling(Math.Sqrt(UpperBound)); i++)
            {
                if (!Tagged[i])
                {
                    int j = i;
                    // alle Vielfachen der Zahl i, angefangen von ihrer Quadratzahl, durchstreichen
                    while (j * i <= UpperBound)
                    {
                        Tagged[j * i] = true;
                        j++;
                    }
                }
            }

            // alle nicht durchgestrichenen Zahlen ausgeben
            for (int i = 2; i < Tagged.Length; i++)
            {
                if (!Tagged[i])
                    Console.Write(i.ToString() + " ");
            }
        }
    }

Donnerstag, 24. Juni 2010

If Abfrage mit Fragezeichen: (Bedingung) ? (falls true) : (falls false)

Hier mal schnell ein kurzer Post über eine Kurzform der if - else - Struktur in C# .Net, die ich auch im Programm aus dem vorigen Post verwendet habe.
Eine standardmäßige Verzweigung mit Wertzuweisung sieht in etwas so aus:

if (Bedingung)
{
Variable = "Bedingung erfüllt";
}
else
{
Variable = "Bedingung nicht erfüllt";
}

Diesen Code kann man auch kürzer schreiben, in dem man ein Fragezeichen (?) und einen Doppelpunkt (:) benutzt:

Variable = (Bedingung) ? "Bedingung erfüllt" : "Bedingung nicht erfüllt";

Hierbei wird Variable "Bedingung erfüllt" zugewiesen, wenn die Bedingung wahr ist, ansonsten "Bedingung nicht erfüllt".

Caesar Verschlüsselung in C#

Die Caesar Verschlüsselung war eine der ersten Verschlüsselungsmethoden der Welt, wie der Name schon sagt, benutzte sie bereits Julius Caesar um Vertrauten geheime Informationen zukommen zu lassen. Bei dieser Verschlüsselungsmethode werden alle Buchstaben zyklisch um einen Schlüsselbuchstaben verschoben, Informatiker nennen das eine monoalphabetische Substitution, d.h., es wird nur ein festes Alphabet für Klar- und Geheimtext verwendet und die Buchstaben des Klartexts werden durch andere Buchstaben ersetzt.
Ein Beispiel zur Caesarverschlüsselung: Das Klartextwort sei "zum" und der Schlüsselbuchstabe "b". Jeder Buchstabe im Klartext wird also um b (b ist der 2. Buchstabe des Alphabets, also 2) Positionen verschoben. Nach z fängt man wieder bei a an, z wird also zu b. u wird zu w und m zu o.
Das Geheimtextwort lautet dann "bwo".
Die Caesarverschlüsselung habe ich in C# implementiert (für diese Funktion gibt es in .Net ausnahmsweise mal keine vordefinierte Klasse), was ich euch nun zeigen möchte.
Der Quellcode sollte ganz gut verständlich kommentiert sein, also poste ich einfach mal den kompletten Code der Klasse (System.Text.RegularExpressions muss eingebunden sein):

class CaesarCiphering
{
bool EliminateBlanks; // true wenn Leerzeichen weggelassen werden sollen
char Offset; // der Schlüsselbuchstabe

// im Konstruktor müssen oben definierte Werte gesetzt werden
public CaesarCiphering(bool eliminateBlanks, char offset)
{
EliminateBlanks = eliminateBlanks;
Offset = offset;
}

/// <summary>
/// in dieser Funktion wird der übergebene String bereinigt, d.h. alle nicht im
/// festgelegten Verschlüsselungsalphabet (nur die Kleinbuchstaben a - z sind erlaubt)
/// vorhandenen Zeichen und eventuell Leerzeichen werden aus dem übergebenen String entfernt
/// </summary>
private string CleanString(string text)
{
text = text.ToLower(); // text in Kleinbuchstaben umwandeln

if (EliminateBlanks) // ggf. Leerzeichen löschen
text = text.Replace(" ", "");

// der folgende reguläre Ausdruck beschreibt alle Zeichenfolgen,
// die nicht aus den Buchstaben a-z und nicht aus Leerzeichen bestehen
Regex NoValidCharacters = new Regex("[^a-z\\s]*");
// alle zum regulären Ausdruck passenden Teile aus text löschen
text = NoValidCharacters.Replace(text, "");

return text;
}

/// <summary>
/// verschlüsselt den übergebenen String
/// </summary>
public string Encode(string plainText)
{
string CipherText = "";

// plainText säubern
plainText = CleanString(plainText);

// Jeden Buchstaben in plainText durch seinen passenden Geheimtextbuchstaben
// ersetzen. Die folgende Schreibweise prüft, ob der aktuelle Buchstabe c
// ein Leerzeichen ist, wenn ja, wird dieses beibehalten, andernfalls der
// Geheimtextbuchstabe berechnet.
foreach (char c in plainText)
CipherText += (c == ' ') ? ' ' : (char)(((c - 96) + (Offset - 96) + 25) % 26 + 97);

return CipherText;
}

public string Decode(string cipherText)
{
string PlainText = "";

// cipherText säubern
cipherText = CleanString(cipherText);

// Jeden Buchstaben in cipherText durch seinen passenden Klartextbuchstaben
// ersetzen. Die folgende Schreibweise prüft, ob der aktuelle Buchstabe c
// ein Leerzeichen ist, wenn ja, wird dieses beibehalten, andernfalls der
// Klartextbuchstabe berechnet
foreach (char c in cipherText)
PlainText += (c == ' ') ? ' ' : (char)(((c - 96) - (Offset - 96) + 25) % 26 + 97);

return PlainText;
}

}

Ein Aufruf der Caesar Verschlüsselung könnte beispielsweise so aussehen:

CaesarCiphering Caesar = new CaesarCiphering(false, 'b');
string CipherText = Caesar.Encode("zum");
string PlainText = Caesar.Decode(CipherText);

Dienstag, 22. Juni 2010

Online (WAN) IP-Adresse ermitteln

Im vorigen Post habe ich gezeigt, wie man die IP-Adresse des Rechners im lokalen Netzwerk, im LAN (Local Area Network), auslesen konnte.
In diesem Post geht es nun darum, wie man die globale online IP-Adresse des Rechners ermitteln kann. Im Internet hat jeder Rechner auch eine eigene IP-Adresse, mit der er identifiziert werden kann, zur Kommnunikation im Internet ist diese entscheidend und nicht die lokale. Die online IP wird auch WAN (Wide Arena Network) IP-Adresse genannt.
Zu beachten ist aber, dass die IPs pro Router vergeben werden, hängen also mehrere PCs hinter einem Router, haben alle dieselbe IP-Adresse.
Auch nach längerer Suche habe ich für diese Aufgabe keine in .Net enthaltene spezielle Funktion entdeckt - also müssen wir zum Auslesen der IP mit C# etwas tricksen.
Es gibt Webseiten, wie z.B. http://www.wieistmeineip.de/, die die online IP ermitteln.
Mit unserer C# - Anwendung lesen wir nun einfach eine solche Webseite aus.
Das folgende Beispiel benutzt einen Webclient, der den Quellcode der Seite herunterlädt.
Im Quellcode steht die IP-Adresse im folgenden Format:
<h1 class="ip">xx.xxx.xxx.xxx</h1>.
Das Programm sucht daher im Quellcode nach dem Auftreten des Strings <h1 class="ip"> und ab dieser Position nach dem Auftreten des Strings </h1>.
Der String zwischen diesen beiden Stellen ist dann die gesuchte IP-Adresse:

System.Net.WebClient IPGetter = new System.Net.WebClient();
// Quellcode der Seite herunterladen und in SourceCode speichern
string SourceCode = IPGetter.DownloadString(@"http://www.wieistmeineip.de/");

// Auftreten der Formattierung für die IP-Adresse suchen
int IPStartPosition = SourceCode.IndexOf("class=\"ip\">");
// ab dem Auftreten nach schließendem Tag suchen
int IPEndPosition = SourceCode.IndexOf(@"</h1>", IPStartPosition);
// String zwischen beiden Positionen ist die gewünschte IP-Adresse
string IP = SourceCode.Substring(IPStartPosition + ("class=\"ip\">").Length, IPEndPosition - (IPStartPosition + ("class=\"ip\">").Length));

Montag, 21. Juni 2010

Netzwerk IP-Adresse ermitteln

Um mit C# die IP-Adresse des benutzten Computers im lokalen Netzwerk zu ermitteln, müssen wir zuerst System.Net mittels using einbinden:

using System.Net;

Dann erstellen wir eine Instanz der Klasse IPHostEntry, welche Adressinformationen für Internethosts speichert, an und legen den eigenen PC als Host fest:
IPHostEntry Host = Dns.GetHostEntry(Dns.GetHostName());

In der Eigenschaft AddressList sind nun alle verfügbaren IP-Adressen des Hosts gespeichert, ganz einfach kann man so die lokale IP-Adresse auslesen über:

string IPAddress = Host.AddressList[0].ToString();

Sind im Computer aber mehrere Netzwerkadapter oder Verbindungen vorhanden, sind in der Liste mehrere IP-Adressen gespeichert (z.B. gibt es heutzutage IP6- neben den alten IP4- Adressen, eine WLAN - Verbindung benutzt eine andere IP als eine LAN - Verbindung zu einem anderen Router, usw.).
Möchte man alle IP-Adressen ermitteln, kann man durch die Liste durchiterieren.
Hier der vollständige C# - Code:

IPHostEntry Host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress IP in Host.AddressList)
    MessageBox.Show(IP.ToString());

Sonntag, 20. Juni 2010

Symbol in Taskleiste (Trayicon) anzeigen

Fügt man einer Windows Forms-Anwendung das Steuerelement NotifyIcon hinzu, wird während der Ausführung des Programms ein Symbol in dem Infobereich auf der rechten Seite der Taskleiste angezeigt, ein sogenanntes TrayIcon.
Das gewünschte Icon wird über die Eigenschaft Icon des Steuerelements ausgewählt, der anzuzeigende Tooltip über die Eigenschaft Text.
Platziert man auf dem Formular zusätzlich ein Steuerelement vom Typ ContextMenuStrip, kann dieses über die Eigenschaft ContextMenuStrip des NotifyIcons dem Icon als Menü zugeordnet werden.
Klickt der Benutzer dann während der Ausführung mit der rechten Maustaste auf das Programmicon im rechten Bereich der Taskleiste, wird das vorher erstelle Menü aus dem verknüpften Steuerelement vom Typ ContextMenuStrip angezeigt.

Samstag, 19. Juni 2010

Gruppen eines Benutzers auslesen

Mit C# .Net lassen sich die Gruppen, denen der aktuell angemeldete Benutzer angehört, über die WindowsIdentity Klasse, die schon im vorigen Post angesprochen wurde, auslesen:

foreach (System.Security.Principal.IdentityReference group in System.Security.Principal.WindowsIdentity.GetCurrent().Groups)
{
    MessageBox.Show(group.ToString());
}

Dieses Beispiel gibt aber nur kryptische Nummern, wie z.B. {S-1-1-0} aus.
Diese Nummern sind die sogennanten SIDs (Security Identifiers) der Gruppe.
Diese können glücklicherweise mittels der Funktion Translate() in einen anderen Typ umgewandelt werden, ein Typ aus der Klasse System.Security.Principal muss hierfür angegeben werden.
Nehmen wir den Typ NTAccount, werden die Gruppen in einem leserlichen Format angezeigt:

foreach (System.Security.Principal.IdentityReference group in System.Security.Principal.WindowsIdentity.GetCurrent().Groups)
{
    MessageBox.Show((group.Translate(typeof(System.Security.Principal.NTAccount))).ToString());
}

Obige Gruppe {S-1-1-0} wird so zu JEDER.

Angemeldeten Benutzer ermitteln

Den aktuell angemeldeten Benutzer ermitteln, ist mit der umfangreichen .Net Bibliothek auch kein Problem.
Am einfachsten funktioniert dies über die Klasse Environment:

string UserName = Environment.UserName;

Folgende Anweisung führt auch zum Ziel, die folgende Klasse greift etwas tiefer ins System ein und hat noch einige andere interessante Funktionen, eine davon zeige ich im nächsten Post:

string UserName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;

Donnerstag, 17. Juni 2010

Tastenkombinationen in der .Net Entwicklungsumgebung

Heute gibt's auch nur leichte Kost, die aber sehr hilfreich sein kann.
Und zwar möchte ich heute ein paar Tastenkombinationen zeigen, die in der .Net Entwicklungsumgebung benutzt werden können, wodurch die Bedienung ein wenig schneller von der Hand gehen kann.
Hier eine Übersicht über ein paar Hotkeys (die "standardmäßigen Windows - Hotkeys, wie Strg + C funktionieren natürlich auch, diese werde ich hier aber nicht auflisten):
  • Strg + Tab: Wechselt zum nächsten Reiter in der Übersicht der aktiven Dokumente (z.B zwischen Form.cs und Form.cs [Entwurf])
  • F4: Blendet die gerade angezeigte Dropdown Liste (bei der Codevervollständigung) aus
  • Strg + Umschalt + V: gleiche Funktion wie Strg + V (Einfügen), markiert das eingefügte Element aber
  • Strg + G: Springe zu Codezeile
  • Strg + Umschalt + S: Speichert die komplette Projektmappe
  • Strg + S: Speichert das aktuelle Element
  • F7: Wechselt zur Codeansicht
  • Umschalt + F7: Wechselt zur Designeransicht
  • Pfeiltasten bei markiertem Steuerelement in Entwurfsansicht: Verschiebt das ausgewählte Steuerelement in 1-Pixel-Schritten
  • Strg + Pfeiltasten bei markiertem Steuerelement in Entwurfsansicht: Verschiebt das ausgewählte Steuerelement an den jeweiligen Rand des Formulars
  • Umschalt + Pfeiltasten bei markiertem Steuerelement in Entwurfsansicht: Vergrößert bzw. verkleinert das ausgewählte Steuerelement um einen Pixel
  • F5: Führt das aktuelle Projekt aus
  • F11: Führt das aktuelle Projekt Schritt für Schritt aus, bei Druck auf F11 wird jeweils die nächste Anweisung ausgeführt
  • Umschalt + F5: Beendet die Ausführung
  • F9: Haltepunkt setzen bzw. entfernen
  • F3: Sucht das nächste Vorkommen des zuletzt gesuchten Begriffes
  • Strg + H: Dialogfeld "Suchen und Ersetzen" öffnen
Die Liste ist natürlich nicht komplett, wer noch andere nützliche Tastengriffe kennt, kann sie gerne als Kommentar hinzufügen.

Mittwoch, 16. Juni 2010

AntMe! - das interaktive Programmierspiel von Microsoft

Heute möchte ich euch ein sehr cooles Projekt von Microsoft vorstellen: Das Spiel AntMe!.
In diesem Spiel steuert der Spieler ein Ameisenvolk, welches sich gegen andere Stämme und fiese Insekten bei der Nahrungssuchen behaupten muss.
Das Besondere an diesem Spiel ist, dass der Quellcode komplett zugänglich ist und der Spieler die Ameisen nicht wie bei anderen Spielen "live" während des Spiels steuert, sondern das Verhalten seines Volkes vor Spielstart programmieren muss.
Das Spiel eignet sich für Einstieger wie Fortgeschrittene, die grundlegenden Befehle sind sehr einfach, aber natürlich können Profis auch ihre ganze Kreativität ausleben und sehr komplexe Taktiken entwickeln.
Hierbei gibt es sehr viele Möglichkeiten, es können verschiedene Ameisenkasten erzeugt werden, die Ameisen müssen Nahrung sammeln und Feinde bekämpfen können, sie können Duftmarken zur Kommunikation setzen und und und ...
Für alle, die neugierig geworden sind (es lohnt sich definitiv!), hier der Link zur AntMe! - Homepage und abschließend noch ein kleiner Screenshot aus dem Spiel:

Dienstag, 15. Juni 2010

Akkuladezustand, verbleibende Akkulaufzeit etc. auslesen

Ab .Net Framework Version 2.0 ist in diesem eine neue, sehr interessante Klasse vorhanden.
Was früher nur umständlich über API - Aufrufe ging, geht jetzt leicht mit .Net Bordmitteln.
Die Rede ist von der Klasse PowerStatus, welche Informationen über den im Laptop befindlichen Akku auslesen kann.
Das folgende Beispiel demonstriert die Möglichkeiten dieser Klasse, zum Beispiel können so die Lebenszeit des Akkus oder die Restkapazität des Akkus in Sekunden oder Prozent wirklich kinderleicht ausgelesen werden:


            /* BatteryChargeStatus beschreibt den aktuellen Akkuzustand, als Ergebnis wird eine
            Aufzählung zurückgegeben, die, als Strings betrachtet, folgende Werte beinhaltet:
            - High
            - Low
            - Critical
            - Charging
            - No SystemBattery
            - Unknown */

            string ChargeStatus = SystemInformation.PowerStatus.BatteryChargeStatus.ToString();

            // BatteryFullLifetime speichert die gemeldete Maximallebensdauer des Akkus, d.h. wie lange der Akku laut Hersteller halten kann
            float FullLifeTime = SystemInformation.PowerStatus.BatteryFullLifetime;
    
            // liest die ungefähre Restkapazität des Akkus in Prozent aus
            float RemainingPercent = SystemInformation.PowerStatus.BatteryLifePercent;

            // liest die ungefähre Restkapazität des Akkus in Sekunden aus
            float RemainingSeconds = SystemInformation.PowerStatus.BatteryLifeRemaining;

            // Gibt an, ob der Laptop an den Strom angeschlossen ist (Online) oder über Akku läuft (Offline).
            string OnBattery = System.Windows.Forms.SystemInformation.PowerStatus.PowerLineStatus.ToString();

Montag, 14. Juni 2010

Keyword - Liste für das Programm C# Syntax Highlighter

Hier der Inhalt der Datei "Keywords.txt" für das Code - Highlighting Programm:

abstract
case
const
do
explicit
float
implicit
is
null
params
ref
sizeof
this
uint
base
catch
continue
double
extern
for
in
lock
object
private
return
static
throw
ulong
bool
char
decimal
else
false
foreach
int
long
operator
protected
sbyte
string
true
unchecked
break
checked
default
enum
finally
goto
interface
namespace
out
public
sealed
struct
try
unsafe
byte
class
delegate
event
fixed
if
internal
new
override
readonly
short
switch
typeof
ushort
void
using
partial

C# Syntax Highlighter für HTML

Ohne Syntax Highlighting, das heißt ohne farbliche Hervorhebung des Codes, sähen längere Codebeispiele ziemlich unübersichtlich aus.
In diesem Blog versuche ich, den Code genau wie in der .Net Entwicklungsumgebung aussehen zu lassen.
Das Programm, welches Schlüsselwörter, Kommentare u.ä. einfärbt, habe ich selber geschrieben mit, wie sollte es auch anders sein, C#.
Für alle Interessierte poste ich hier den Quellcode.
Ich mache hierbei keinen Anspruch auf Vollständigkeit und Korrektheit des Programms, wahrscheinlich gibt es noch einige Bugs etc., für Hinweise und Verbesserungsvorschläge wäre ich dankbar!
Den Code werde ich auch nicht beschreiben, denn er ist ziemlich umfangreich.
Ich möchte einfach für alle Interessierte ein Beispiel geben, wie so ein Projekt verwirklicht werden kann und wer möchte, darf sich natürlich den Code auch einfach ziehen und selber für seine Projekte verwenden.
Nun nur noch kurz eine kleine Beschreibung des Programms:
Ihr müsst auf dem Formular einfach einen Button anlegen und das Ereignis Click() mit button1_Click_1 verknüpfen.
Das Programm liest beim Start eine Liste mit allen möglichen Keywords der .Net Entwicklungsumgebung aus der Datei "Keywords.txt", welche sich im gleichen Ordner wie die .exe - Datei befinden muss, ein. Die Datei gibt's hier.
Klickt der Benutzer auf den Button (oder ruft entsprechende Funktion anderweitig auf), wird der C# - Code aus der Zwischenablage in gültigen HTML - Code umgewandelt, so dass das Ergebnis auf einer Homepage, einem Blog o.a. (fast) so aussieht, wie in der Entwicklungsumgebung.

Und hier der Code:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        List<string> Keywords = new List<string>(); // Liste mit bekannten .Net Schlüsselwörtern
        string Code; // zu konvertierender Code

        private void button1_Click(object sender, EventArgs e)
        {
            FillKeywords(); // Schlüsselwörter einlesen

            Code = System.Windows.Forms.Clipboard.GetText(); // zu bearbeitenden Code aus Zwischenablage einfügen

            EscapeCharacters(); // Sonderzeichen escapen

            string[] Lines = SplitInLines(); // Code in Zeilen zerlegen

            Code = "";
            for (int i = 0; i < Lines.Length; i++)
            {
                Code += ColorLines(Lines[i]);
                if (i < Lines.Length - 1)
                    Code += Environment.NewLine;
            }

            // doppelte Leerzeichen auch in HTML als Leerzeichen darstellen
            Code = Code.Replace("  ", @"&nbsp;&nbsp;");

            // HTML Code in Zwischenablage schreiben
            System.Windows.Forms.Clipboard.SetText(Code);

            MessageBox.Show("Konvertierung abgeschlossen. Code ist in Zwischenablage gespeichert.");
        }

        private void FillKeywords()
        {
            // aus der Datei "Keywords.txt" im Anwendungsverzeichnis Schlüsselwörter einlesen und speichern
            StreamReader sr = new StreamReader(Application.StartupPath + "\\Keywords.txt");
            string Input = "";
            while ((Input = sr.ReadLine()) != null)
            {
                Keywords.Add(Input);
            }
        }

        private void EscapeCharacters()
        {
            // & auch escapen, da sonst HTML - Escapezeichen angezeigt werden
            Code = Code.Replace("&", "&amp;");

            // Größer / Kleiner Zeichen escapen, denn schlimmstenfalls werden diese als HTML Tags verstanden
            Code = Code.Replace("<", "&lt;");
            Code = Code.Replace(">", "&gt;");
        }

        private string[] SplitInLines()
        {
            // Code in Zeilen aufteilen

            string[] Lines = Code.Split(Environment.NewLine.ToCharArray());
            if (Environment.NewLine == "\r\n")
            {   // falls ein Zeilenumbruch durch \r\n dargestellt wird,
                // wird für jedes dieser Zeichen eine neue Zeile erzeugt,
                // also ist jede 2. Zeile überflüssing
                string[] BackupLines = new string[Lines.Length];
                // Zeilen in BackupLines sichern
                for (int i = 0; i < BackupLines.Length; i++)
                {
                    BackupLines[i] = Lines[i];
                }
                // Lines um die Hälfte verkleinern und nur jede 2. aus BackupLines übernehmen
                Lines = new string[BackupLines.Length / 2 + 1];
                for (int i = 0; i < Lines.Length; i++)
                {
                    Lines[i] = BackupLines[i * 2];
                }
            }

            // kleinste Anzahl an Leerzeichen herausfinden, die vor jeder Zeile stehen
            int Counter;
            int MinCountBlanks = int.MaxValue;
            foreach (string Line in Lines)
            {
                Counter = 0;
                // Anzahl an Leerzeichen am Anfang der aktuellen Zeile zählen
                while (Counter < Line.Length && Line.Substring(Counter, 1) == " ")
                    Counter++;
                // kleinste Anzahl speichern
                if (Counter < MinCountBlanks)
                    MinCountBlanks = Counter;
            }

            // diese Anzahl an Leerzeichen vor jeder Zeile löschen,
            // so dass Text linksbündig formattiert wird
            for (int i = 0; i < Lines.Length; i++)
            {
                Lines[i] = Lines[i].Substring(MinCountBlanks, Lines[i].Length - MinCountBlanks);
            }

            return Lines;
        }

        private string ColorLines(string line)
        {
            // übergebene Zeile farbig einfärben

            string Keyword;
            // Positionen von Schlüsselwörtern, Strings, Kommentaren suchen
            int PosKey = LookForKeyword(line, out Keyword);
            int PosQuote = LookForQuote(line);
            int PosCom = LookForComment(line);

            int PosIndex = 0;

            // solange noch Merkmale vorkommen, Schleife weiterführen
            while (PosKey != -1 || PosQuote != -1 || PosCom != -1)
            {
                if (PosKey == -1)
                    PosKey = line.Length;
                if (PosQuote == -1)
                    PosQuote = line.Length;
                if (PosCom == -1)
                    PosCom = line.Length;

                if (PosKey < PosQuote)
                {
                    if (PosKey < PosCom)
                    {
                        // Keyword kommt zuerst vor, einfärben
                        line = ColorKeyword(line, PosKey, Keyword, out PosIndex);
                    }
                    else
                    {
                        // Kommentar kommt zuerst vor, einfärben
                        line = ColorComment(line, PosCom, out PosIndex);
                    }
                }
                else
                {
                    if (PosQuote < PosCom)
                    {
                        // Anführungszeichen kommt zuerst vor, String einfärben
                        line = ColorQuote(line, PosQuote, out PosIndex);
                    }
                    else
                    {
                        if (PosCom > -1)
                        {
                            // Kommentar kommt zuerst vor, einfärben
                            line = ColorComment(line, PosCom, out PosIndex);
                        }
                        // ansonsten wurde gar nichts gefunden
                    }
                }

                // weiter nach Merkmalen suchen, aber nur ab zuletzt gefundenem Wert
                if (PosIndex <= line.Length)
                {
                    PosKey = LookForKeyword(line.Substring(PosIndex), out Keyword);
                    PosQuote = LookForQuote(line.Substring(PosIndex));
                    PosCom = LookForComment(line.Substring(PosIndex));
                }

                // wenn die Merkmale gefunden wurden, Indexe an veränderten Suchraum anpassen
                if (PosKey != -1)
                    PosKey += PosIndex;
                if (PosQuote != -1)
                    PosQuote += PosIndex;
                if (PosCom != -1)
                    PosCom += PosIndex;
            }

            return line;
        }

        private int LookForKeyword(string searchString, out string foundKeyword)
        {
            // gibt den kleinsten Index eines im übergebenen String befindlichen Schlüsselworts zurück

            int BestPos = int.MaxValue;

            Regex MatchKeyword;

            int TempIndex;

            foundKeyword = "";

            foreach (string keyword in Keywords)
            {
                // Position aller eingelesenen Keywords im übergebenen String ermitteln
                MatchKeyword = new Regex("\\b" + keyword + "\\b");

                // falls keyword früher als bisher gefundene vorkommt,
                // Index und keyword speichern
                TempIndex = (MatchKeyword.Match(searchString)).Index;
                if (MatchKeyword.Match(searchString).Success && TempIndex < BestPos)
                {
                    BestPos = TempIndex;
                    foundKeyword = keyword;
                }
            }

            if (BestPos == int.MaxValue)
                return -1;
            else
                return BestPos;
        }

        private int LookForQuote(string searchString)
        {
            // gibt die kleinste Position eines nicht escapten Anführungszeichen im übergebenen String zurück

            bool UnescapedQuoteFound = false;
            int PositionQuote = 0;

            while (!UnescapedQuoteFound && PositionQuote >= 0)
            {
                // Index des nächsten Anführungszeichens ermitteln
                PositionQuote = searchString.IndexOf("\"", PositionQuote);

                // um zu prüfen, ob Anführungszeichen escaped wurde,
                // Anzahl an Backslashes davor prüfen
                int i = 1;
                while (PositionQuote - i >= 0 && searchString.Substring(PositionQuote - i, 1) == "\\")
                    i++;

                // bei einer ungeraden Anzahl von "\" ist das Anführungszeichen escaped
                if (i % 2 == 0)
                {
                    PositionQuote++;
                    continue;
                }
                else
                    UnescapedQuoteFound = true;
            }

            if (UnescapedQuoteFound)
            {
                return PositionQuote;
            }
            else
                return -1;
        }

        private int LookForComment(string searchString)
        {
            // gibt den ersten Index eines Kommentarzeichens zurück
            return searchString.IndexOf(@"//");
            // /* Kommentar */ wird noch nicht beachtet
        }

        private string ColorKeyword(string line, int posKey, string keyword, out int posIndex)
        {
            line = line.Substring(0, posKey) + "<span style=\"color:#0000ff;\">" + keyword + "</span>" + line.Substring(posKey + keyword.Length);
            posIndex = posKey + ("<span style=\"color:#0000ff;\">" + keyword + "</span>").Length;
            return line;
        }

        private string ColorComment(string line, int posCom, out int posIndex)
        {
            // Zeilenkommentar, komplette Zeile einfärben
            if (line.Substring(posCom, 2) == @"//")
            {
                line = line.Substring(0, posCom) + "<span style=\"color:#008000;\">" + line.Substring(posCom) + "</span>";
                posIndex = line.Length;
            }
            else
                posIndex = 0; // anderer Kommentar
            return line;
        }

        private string ColorQuote(string line, int posQuote, out int posIndex)
        {
            // nächstes nicht escapted Anführungszeichen suchen
            int PosNextQuote = LookForQuote(line.Substring(posQuote + 1));

            if (PosNextQuote == -1)
            {
                if (line.Substring(posQuote - 1, 1) == "@")
                    posQuote--;
                line = line.Substring(0, posQuote) + "<span style=\"color:#A31414;\">" + line.Substring(posQuote) + "</span>";
                posIndex = line.Length;
            }
            else
            {
                PosNextQuote += posQuote + 1;
                if (line.Substring(posQuote - 1, 1) == "@")
                    posQuote--;
                line = line.Substring(0, posQuote) + "<span style=\"color:#A31414;\">" + line.Substring(posQuote, PosNextQuote - posQuote + 1) + "</span>" + line.Substring(PosNextQuote + 1);
                posIndex = posQuote + ("<span style=\"color:#A31414;\">" + line.Substring(posQuote, PosNextQuote - posQuote) + "</span>").Length;
            }

            return line;
        }
    }
}

Samstag, 12. Juni 2010

Plattformunabhängige Pfadangaben

Ja, ich muss zugeben, auch ich programmiere nicht ganz sauber. Bei Pfadangaben benutze ich aus Bequemlichkeits- und Leserlichkeitsgründen den Backslash "\" als Trennzeichen zwischen Ordnern. In C# muss dieser noch durch einen weiteren Backslash bzw. ein vorstehendes "@" escaped werden, Pfadangaben könnten bei mir also in etwa so aussehen:

string Pfad = @"C:\Dokumente und Einstellungen\Oliver";

.Net läuft aber auch auf anderen Betriebssystemen (für Linux gibt es beispielsweise das Mono - Projekt) und "\" funktioniert nicht auf allen Betriebssystemen als Trennzeichen, obige Pfadangabe könnte dort also einen Fehler produzieren.
Um eine Plattformunabhängigkeit zu gewährleisten, sollte daher die Methode Path.Combine() verwendet werden.
Diese kombiniert die übergebenen Strings zu einem Pfad mit gültigen Trennzeichen.
So könnte man obigen Beispielpfad so deklarieren:
string Pfad = System.IO.Path.Combine("C:", "Dokumente und Einstellungen", "Oliver");

Pfad wird dann auf einem Windows - Rechner folgenden Wert haben:
"C:Dokumente und Einstellungen\\Oliver".
Alternativ kann man auch direkt Path.DirectorySeparatorChar verwenden, um das plattformabhängige Trennzeichen zu erhalten (System.IO ist mittels using eingebunden)

string Pfad = "C:" + Path.DirectorySeparatorChar + "Dokumente und Einstellungen" + Path.DirectorySeparatorChar + "Oliver";

Liefert unter Windows: "C:\\Dokumente und Einstellungen\\Oliver"

Donnerstag, 10. Juni 2010

MP3 - Datei abspielen

Im Post Wave - Datei abspielen gab's den Trick, wie man Wave - Dateien mit C# abspielt. Jetzt möchte ich euch zeigen, wie man MP3 - Dateien abspielen kann.
Das ist auf Grund des Formats etwas komplizierter, es gibt keinen direkten, "einfachen" Befehl zum Abspielen einer MP3 - Datei, aber doch viele Möglichkeiten.
MP3 - Dateien kann man einerseits über die API - Funktion MCISendString() abspielen, doch der Code ist umständlich und nicht leicht verständlich.
Eine sehr naive Variante wäre System.Diagnostics.ProcessStart() die MP3 - Datei als Parameter zu übergeben, das Betriebssystem wird dann die Datei mit dem Standardplayer öffnen:

System.Diagnostics.Process.Start(@"C:\Dokumente und Einstellungen\Oliver\Eigene Dateien\Eigene Musik\101-darius_and_finlay_and_shaun_baker_-_show_me_10_(dj_gollum_edit).mp3");

Elegant ist diese Methode aber nicht.
Deswegen möchte ich euch heute das Einbinden des Steuerelements Windows Media Player zeigen, womit u.a. MP3s abgespielt werden können.
Das Steuerelement kann man in die Toolbox hinzufügen, in dem man rechts auf diese klickt und dann Element auswählen ... anklickt. Im sich öffnenden Fenster zu COM - Steuerelementen wechseln und Windows Media Player auswählen.
Dieser kann nun wie zum Beispiel ein Button auf dem Formular platziert werden.
Wenn der Player nur zum Abspielen für MP3s gebraucht wird, kann die Eigenschaft Visible auf false gestellt werden, so ist er für den Benutzer nicht sichtbar.
Über die Eigenschaft URL kann die abzuspielende MP3 - Datei ausgewählt werden. Normalerweise startet der Player dann direkt mit dem Abspielen der Datei.
Mittels der Eigenschaft Ctlcontrols kann sozusagen auf den geladenen Inhalt des Players zugegriffen werden, die Funktionen start() und stop() dienen zum manuellen Starten und Stoppen der Datei.
Über settings sind einige Einstellungen des Players, wie z.B. die Lautstärke änderbar.
Im folgenden Beispiel wird eine MP3 - Datei geladen, das Abspielen gestoppt und die Anwendung wartet 2 Sekunden.
Danach wird der Player auf volle Lautstärke gestellt und die Datei abgespielt:

axWindowsMediaPlayer1.URL = @"C:\Dokumente und Einstellungen\Oliver\Eigene Dateien\Eigene Musik\101-darius_and_finlay_and_shaun_baker_-_show_me_10_(dj_gollum_edit).mp3";
axWindowsMediaPlayer1.Ctlcontrols.stop();
System.Threading.Thread.Sleep(2000);
axWindowsMediaPlayer1.settings.volume = 100; // 0 = kein Ton, 100 = volle Lautstärke
axWindowsMediaPlayer1.Ctlcontrols.play();

Mittwoch, 9. Juni 2010

Installieren und Einbinden des DirectX SDK

Bei aufwendigeren Multimediaanwendungen, wie zum Beispiel bei Spielen, ist DirectX nicht mehr wegzudenken.
Glücklicherweise hat .Net auch für dieses Thema umfangreiche Bibliotheken parat.
Für C++ und .Net Sprachen gibt es das DirectX SDK.
Hiermit lassen sich auf jeden Fall viele coole Anwendungen erstellen, ich werde in Zukunft einige Posts dazu schreiben.
Für heute aber erstmal, wie der Titel es schon sagt, die Installation und Einbindung des DirectX SDK, die
ein paar Probleme bereiten kann (zumindest bei mir lief es nicht so flüssig).
Die neueste Version des DirectX SDK gibt es hier zum Download. Mit Windows XP und .Net Framework 4.0 lies sich diese Datei bei mir aber nicht installieren, ich habe die Version von März genommen.
Nach der Installation steht das SDK zur Verfügung, in jedem Projekt aber, in dem DirectX Funktionen benutzt werden sollen, müssen Referenzen auf die richtigen DLL - Dateien eingebunden werden.
Verweise können über den Menübefehl Projekt - Verweis hinzufügen eingefügt werden. Laut Literatur sollten nach Installation die entsprechenden Dateien (z.B. Microsoft.DirectX) im Fenster Verweis hinzufügen auf dem Reiter .Net vorhanden sein. Bei mir war dies jedoch nicht der Fall, weswegen ich sie manuell einbinden musste.
Um manuell einzubindende Verweise zu suchen, wechselt ihr zum Reiter Durchsuchen und wählt dann die entsprechenden Dateien aus.
Bei mir liegen die DLLs unter C:\WINDOWS\Microsoft.NET\DirectX for Managed Code. Falls ihr sie nicht findet, einfach mal die Datei "Microsoft.DirectX.dll" suchen.
So, nun solltet ihr in der Lage sein, das SDK richtig vorzubereiten, im Internet gibt's coole DirectX Tutorials und ich werde bald auch einige Posts erstellen, wann welche Verweise benötigt werden etc.

Dienstag, 8. Juni 2010

Using mal anders

Die meisten Benutzer kennen das using Schlüsselwort sicherlich zum Einbinden von Namespaces und Klassen aus Anweisungen wie:
using System;

Using kann man aber auch so verwenden, dass man darüber eine Ressource für einen Block einbindet. Der Vorteil hierbei ist, dass die Ressource direkt danach wieder aus dem Speicher gelöscht wird.
Diesen Vorgang, die Freigabe von nicht mehr benötigtem Speicherplatz, erledigt unter .Net der sogenannte Garbage Collector, welcher asynchron aufgerufen wird.
Die Speicherverwaltung in .Net ist eigentlich super, doch über die hier beschriebene Methode wird der Speicher eben noch schneller freigegeben. Übrigens gilt dieses Prinzip nicht nur bei der reinen Speicherverwaltung, auch bei anderen Ressourcen, z.B. Dateizugriffen etc. ist eine schnelle Freigabe erwünscht.
Im folgenden Beispiel wird mit using eine Instanz einer Klasse benutzt. Ressourcen müssen, damit sie mit using angesprochen werden können, die Schnittstelle IDisposable implementieren.
Streamreader etc. machen das von Haus schon, nur bei selber geschriebenen Klassen muss man diese Schnittstelle manuell implementieren. Die Implementation der Methode IDisposable.Dispose() ist dabei erforderlich. Genannte Methode wird beim "Zerstören" einer Klasseninstanz aufgerufen:

     public partial class Form1 : Form
    {
         public Form1()
        {
            InitializeComponent();
        }

         private void Form1_Load( object sender, EventArgs e)
        {
             using (UsingTest InstanceUsingTest = new UsingTest())
            {   // in diesem Using - Block wird InstanceUsingTest genutzt
                InstanceUsingTest.ExampleFunction();
            }
            // außerhalb des Blocks wird der Speicher sofort freigegeben
        }

         public class UsingTest : IDisposable
        {  
            // diese Methode muss bei Implementierung von IDisposable vorhanden sein
             void IDisposable.Dispose()
            {
                MessageBox.Show("Instanz gelöscht, Speicher wieder freigegeben.");
            }

             public void ExampleFunction()
            {
                MessageBox.Show("Klasse in Benutzung.");
            }
        }
    }

Sonntag, 6. Juni 2010

Pfad zur Anwendung herausfinden

Oft macht es Sinn, anwendungsbezogene Dateien im Verzeichnis der Anwendung zu speichern. Doch wie fragt man den Pfad zur eigenen Anwendung ab?
Folgender Befehl gibt den Pfad zur Anwendung, ohne den Namen der .exe Datei, zurück:
Application.StartupPath

Wave - Datei abspielen

Um Wave - Dateien abzuspielen, stellt .Net die Klasse System.Media.SoundPlayer zur Verfügung.
Die Klasse kann entweder mit dem Pfad zu einer Wave - Datei im Konstruktor initialisiert werden, oder der Pfad wird über die Eigenschaft SoundLocation gesetzt.
Folgender Code spielt eine Beispielsounddatei ab:

System.Media.SoundPlayer ExamplePlayer = new System.Media.SoundPlayer();
ExamplePlayer.SoundLocation = "C:\\Dokumente und Einstellungen\\Oliver\\Desktop\\wav\\MM_Lachen.wav";
ExamplePlayer.Play();

In dem Post MP3 - Datei abspielen zeige ich die Verwendung des Windows Media Player Steuerelements, welches sich nicht ganz zum Titel passend zum Abspielen eigentlich aller Audioformate eignet. Wer also mehr als die hier beschriebenen Wave - Dateien nutzen möchte, der sei auf den genannten Post verwiesen.

Samstag, 5. Juni 2010

Systemzeit auslesen

Im vorigen Post habe ich die Grundlagen von P/Invoke beschrieben und wie man damit die Systemzeit ändert.
Analog dazu kann man die Systemzeit natürlich auch mit einer API - Funktion auslesen:

    public class ZeitLesen
    {
        struct str_Zeit
        {
            public ushort Jahr;
            public ushort Monat;
            public ushort TagInDerWoche;
            public ushort Tag;
            public ushort Stunde;
            public ushort Minute;
            public ushort Sekunde;
            public ushort Millisekunde;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern void GetSystemTime(out str_Zeit AktuelleZeit);

        public void LeseSystemzeit()
        {
            str_Zeit Zeit = new str_Zeit();
            GetSystemTime(out Zeit);
            DateTime AktuelleZeit = new DateTime(Zeit.Jahr, Zeit.Monat, Zeit.Tag, Zeit.Stunde, Zeit.Minute, Zeit.Sekunde, Zeit.Millisekunde);
        }
    }


Will man allerdings einfach nur das aktuelle Datum und die aktuelle Zeit erfahren, geht das mit .Net Mitteln viel einfacher über die Klasse DateTime:

DateTime CurrentDate = DateTime.Now;

Systemzeit ändern

Um die Uhrzeit des PCs zu lesen bzw. zu ändern, gibt es im .Net Framework keine hauseigene Version. Mit allen .Net Sprachen kann man jedoch auf die Funktionen der Win32-API zugreifen.
Die Win32-API ist eine sehr umfangreiche Programmierschnittstelle, welche Funktionen für die Windowsprogrammierung bereitstellt.
Die oft systemnahen Funktionen sind alle in C oder Assembler geschrieben und sind in dll - Dateien gespeichert.
Glücklicherweise gibt es auch eine API - Funktion zur Verwaltung der Systemzeit, welche wir in unsere C# - Anwendung einbinden können. Das einbinden von externen Funktionen bezeichnet man in der Fachsprache als Platform Invoke (P/Invoke).
Nun zum Code:
Um DLLs einbinden zu können, ist folgende using - Direktive notwendig:
using System.Runtime.InteropServices;

Die externe Funktion zur Systemzeitsetzung wird dem Programm schließlich folgenderweise bekannt gemacht:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetSystemTime(ref str_Zeit neueZeit);

Die Funktion SetSystemTime() lässt sich dann wie eine gewöhnliche Funktion aufrufen.
Als Parameter erwartet sie ein Objekt vom Typ der Struktur str_Zeit, die wir selber definieren:

struct str_Zeit
        {
            public ushort Jahr;
            public ushort Monat;
            public ushort TagInDerWoche;
            public ushort Tag;
            public ushort Stunde;
            public ushort Minute;
            public ushort Sekunde;
            public ushort Millisekunde;
        }

Diese Struktur ist in der API so nicht gespeichert, unsere Struktur ist nur eine Nachbildung des Typs, den die Funktion erwartet - jeder Typ mit 8 vorzeichenlosen 16-Bit-Ganzzahlen erfüllt die Anforderungen.
Der komplette Code könnte schließlich so aussehen (Aufruf der Funktion SetzeSystemzeit() mit einem Datumsobjekt als Parameter ändert die Systemzeit auf den übergebenen Wert):

public class ZeitSetzen
    {
        struct str_Zeit
        {
            public ushort Jahr;
            public ushort Monat;
            public ushort TagInDerWoche;
            public ushort Tag;
            public ushort Stunde;
            public ushort Minute;
            public ushort Sekunde;
            public ushort Millisekunde;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool SetSystemTime(ref str_Zeit neueZeit);

        public void SetzeSystemzeit(DateTime neueZeit)
        {
            str_Zeit Zeit = new str_Zeit();
            Zeit.Jahr = (ushort)neueZeit.Year;
            Zeit.Monat = (ushort)neueZeit.Month;
            Zeit.TagInDerWoche = (ushort)neueZeit.DayOfWeek;
            Zeit.Tag = (ushort)neueZeit.Day;
            Zeit.Stunde = (ushort)neueZeit.Hour;
            Zeit.Minute = (ushort)neueZeit.Minute;
            Zeit.Sekunde = (ushort)neueZeit.Second;
            Zeit.Millisekunde = (ushort)neueZeit.Millisecond;
            
            SetSystemTime(ref Zeit);
        }
    }

Aufpassen bei Windows Vista und 7: Zum Ändern der Systemzeit braucht das Programm Administratorrechte. Wie die Anwendung diese erhält, steht in einem anderen Post.