BigBastis Blog About Me & my Digital Lifestyle

29Apr/100

VB.NET: Einstellungen in der Windows Registry Speichern

In so gut wie jedem Computerprogramm müssen Informationen wie Einstellungen oder Benutzerdaten gespeichert werden. Um dies zu realisieren gibt es viele Ansätze wie zB: eine Datenbank, eine XML oder INI Datei oder direkt in der Registry. In diesem kleinem Tutorial werde ich auf die letztere Methode eingehen.

Zu nächst sei gesagt, dass ich es euch nicht empfehlen würde eine Einstellungen in der Registry zu sichern. Das hat folgende Gründe:

  • Die Daten sind sehr versteckt und nicht jeder Benutzer weiß, wie man an diese herankommt
  • Man kann diese nur sehr schwer sichern oder auf ein anderen System umziehen
  • Es wird oft vergessen diese Einstellungen aus der Registry zu löschen, sodass diese als "Leichen" im System bleiben
  • Speicherort ist von der Version des Programms abhängig

Auf der anderen Seite gibts aber auch natürlich Vorteile:

  • Einstellungen sind vor ungeübten Nutzern "geschützt"
  • Sehr geringer Programmier-Aufwand
  • Einstellungen können oft auch nach einer Neuinstallation weiter verwendet werden. (Kann auch zum Nachteil werden wenn Einstellungen fehlerhaft sind)

Besonders in früheren Windows Zeiten wurden fast alle Einstellungen in der Registry gesichert. Inzwischen setzt sich die Sicherung der Daten in XML Dateien durch und das .NET Framework bietet seit der 3er Version auch den Namensraum My.Settings mit denen diese XML Dateien automatisch erzeugt werden. Aber heute scheuen wir uns mal den anderen Weg an!

Wie oben bereits erwähnt kann man mit sehr wenig Aufwand seine Einstellungen in der Registry sichern, diese werden dabei hier gesichert:

HKEY_CURRENT_USER\Software\[Unternehmen]\[Programm]\[Version]

Hier werden die Einstellungen gesichert, dabei werden die Variablen in den Klammern automatisch gefüllt.

.Dazu bietet uns der Application-Namesraum alles was wir benötigen. Was wir aber noch machen sollten ist die Überprüfung der Werte und das Exceptionhandling.

Alles was man zum speichern eines Werts angeben muss ist der Name unter dem der Wert gespeichert werden soll und der Wert selbst. Alle Werte werden dann automatisch unter dem oberen Pfad abgelegt.


	'''
	''' Speichert einen Wert unter angegebenem Namen in der Registry
	'''
	Public Sub SaveSetting(ByVal name As String, ByVal value As String)
	    Try
	      	Application.UserAppDataRegistry.SetValue(name, value)
	    Catch ex As Exception
	      	debug.WriteLine("Fehler: " & ex.Message)
	    End Try
  	End Sub

Um die Werte nun wieder auszulesen benötigt man lediglich den Namen, den man zum speichern benutzt hat und man bekommt den Wert aus der Registry geworfen!


	'''
	''' Lädt den String aus der Regitry passend zu dem angegebenem Namen
	'''
	Public Function LoadSetting(ByVal name As String) As String
		Try
			If Not Application.UserAppDataRegistry.GetValue(name) Is Nothing Then
				Return CType(Application.UserAppDataRegistry.GetValue(name), String)
		  	Else
		    	Return String.Empty
		  	End If

		Catch ex As Exception
		  	debug.WriteLine("Fehler: " & ex.Message)
		  	Return String.Empty
		End Try
	End Function

Hier muss man aber beachten, dass das Programm, das die Werte ausließt die selbe Version haben muss wie das was sie in die Registry geschrieben hat, da der Pfad (siehe oben) nicht mehr passt!

Wenn man die oberen Methoden implementeirt hat kann man nun die Einstellungen sehr bequem speichern:


	'Die Einstellung aller Controls auf der Form speichern
	Sub Button1Click(sender As Object, e As EventArgs)
		Dim rs As New RegSaver 'Diese Klasse beinhaltet die oben definierten Funktionen

		'Daten für alle Controls speichern
		For Each c As Control In Me.Controls
			If TypeOf c Is RadioButton Then
				Dim a As RadioButton = CType(c, RadioButton)
				rs.SaveSetting(a.Name,a.Checked)
			End If

			If TypeOf c Is CheckBox Then
				Dim a As CheckBox = CType(c, CheckBox)
				rs.SaveSetting(a.Name,a.Checked)
			End If

			If TypeOf c Is TextBox Then
				Dim a As TextBox = CType(c, TextBox)
				rs.SaveSetting(a.Name,a.text)
			End If
		Next
	End Sub

und auch wieder auslesen:


	'Die werte aller Controls wieder laden
	Sub MainFormLoad(sender As Object, e As EventArgs)
		Dim rs As New RegSaver
				'Daten für alle Controls laden
		For Each c As Control In Me.Controls
			Try	'beim ersten starten gibt es keinen wert der gecastet werden kann!
				If TypeOf c Is RadioButton Then
					Dim a As RadioButton = CType(c, RadioButton)
					a.Checked = cbool(rs.loadsetting(a.Name))
				End If

				If TypeOf c Is CheckBox Then
					Dim a As CheckBox = CType(c, CheckBox)
					a.Checked = cbool(rs.loadSetting(a.Name))
				End If
			Catch ex As Exception
				debug.WriteLine("Fehler: " & ex.Message)
			End Try

			If TypeOf c Is TextBox Then
				Dim a As TextBox = CType(c, TextBox)
				a.Text = rs.loadSetting(a.Name)
			End If
		Next
	End Sub

Wenn die Werte das erste Malgeladen werden, kann es zu fehlern kommen, da man NULL-Strings zurückbekommt, deswegen sollte man hier immer die Exceptions catchen! Falls es in der Registry noch keine passenden Einstellung zu dem Namen gibt, wird diese beim Laden automatisch angelegt!

Bild 1: Demoapplikation mit den geladenen Einstellungen

Wenn der Code durchgelaufen ist wird folgende Struktur in der Registry angelegt:

Bild 2: Die Einstellungen in der Registry

Natürlich bleibt es euch überlassen, wie ihr die Einstellungen benennet, ich habe hier den Namen des Controls verwendet um dessen Status zu speichern, damit ich nicht so viel Code schreiben musste.

Welche Methode ihr nutzt müsst ihr natürlich selber an Hand des Projekts entscheiden, kennen sollte man sie aber alle.

Ein Kleines Demoprojekt gibts hier:

Download: Diesmal ausnahmsweise ein SharpDevelop Projekt

18Mrz/101

VB.NET: Netzwerkadapter Einstellungen auslesen

Das .NET Framework bietet viele sehr nützliche Schnittstellen, so auch um Netzwerkinformationen auszulesen. Alles was man braucht findet man in dem System.Net Namensraum. In dieser Demo möchte ich euch zeigen, wie man alle verfügbaren Netzwerkadapter auflistet und deren Informationen ausließt.

So lässt sich sehr einfach eine Auflistung aller Netzwerkschnittstellen des Systems zusammenstellen:


    Dim adapter As NetworkInterface     'Eine Netzwerkadapter Instanz
    Dim adapters As NetworkInterface()  'Array mit allen Netzwerkadaptern

    Public Sub DisplayDnsConfiguration()
        adapters = NetworkInterface.GetAllNetworkInterfaces()

        'Alle Adapter in der Liste durchlaufen
        For Each Me.adapter In adapters
            Dim properties As IPInterfaceProperties = adapter.GetIPProperties()

            lstNetworks.Items.Add(adapter.Description)
        Next adapter
    End Sub

Hier wird die lstNetworks mit allen verfügbaren Netzwerkadaptern gefüllt. Da wir nun die Bezeichnungen der Netzwerkadapter haben können wir uns nun daran machen deren Einstellungen auszulesen.

Die Einstellungen sind hierbei genauso schnell ausgelesen, denn wenn man erst die richtige NetworkInterface-Instanz erwischt hat kann man ganz bequem auf die Einstellungen zugreifen:

bild1Bild 1: Ein paar Eigenschaften des Netzwerkadapters

Den dazugehörigen Code spare ich mir mal an dieser Stelle, da es einfach zu viel Text ist. Ihr könnt diesen aber natürlich in dem Demoprojekt nachgucken.

Wie in Bild 1 auch zu erkennen ist hat man auch Zugriff auf die übertragenen Bytes in beide Richtungen. So kann man zB. auch sehr einfach die aktuelle Netzwerkauslastung bestimmen, indem man den alten Wert speichert und mit dem neuen Wert nach einer Sekunde vergleicht. Dadurch bekommt man die übertragenen Bytes pro Sekunde. (Dies könnt ihr auch in dem Demoprojekt nachgucken)

bild2

Bild 2: Das Demoprogramm in Aktion (klicken zum Vergrößern)

In dem Demoprogramm habe ich mal versucht die am häufigsten benötigten Informationen einer Netzwerkschnittstelle zusammenzufassen, aber das meiste ist hier aber auch nur Copy’n’Paste!

Das Demoprogramm zeigt alle verfügbaren netzwerkschnittstellen, deren Eigenschaften sowie die aktuelle Adapterauslastung in KB/s.

vs2008demo

17Feb/105

VB.NET: FitzBox IP erneuern ueber uPnP

Die FritzBox ist als Router weit verbreitet und bietet einen Haufen an Konfigurationsmöglichkeiten an. Eins der nützlichsten Features ist uPnP (Universal Plug and Play). Diese Schnittstelle bietet uns eine sehr bequeme Möglichkeit mit dem Gerät zu kommunizieren ohne dass man einen Benutzernamen oder ein Kennwort benötigt!

Wer öfter mal im Internet unterwegs ist, kennst sicherlich mehrere gute Gründe seine IP zu wechseln. Und das ist sehr einfach, denn alles was dafür nötig ist, ist eine erneute Einwahl ins Internet.

Dies kann man sehr einfach über die Benutzeroberfläche der FritzBox machen oder einfach "oldschool"-mäßig das Netzwerkkabel aus der FritzBox ziehen und wieder einstecken. (Letzteres erfordert ebenfalls keinen Benutzernamen und Passwort)

Doch wenn man des öfteren auf diese Funktionalität angewiesen ist, ist es doch relativ nervig und umständlich. Deswegen werden wir hier ein einfaches kleines Programm entwickeln, dass für uns den Router zu einer Neueinwahl ins Internet "zwingt".

Wie oben bereits erwöhnt heißt die Technologie dafür UPnP (Universal Plug and Play). Über diese Technologie lassen sich sehr viele Befehle an die Fritzbox senden, vorausgesetzt, man befindet sich in dem selben Netzwerk.

Von Statusabfragen über Auslastungen und Übertragung, lassen sich sogar Telefonnummern anwählen und Anrufe steuern, im Prinzip kann man alle Funktionen darüber ansteuern. Hier gibts eine nette Übersicht mit vielen Beispielen.

Wie werden die Anfragen eigentlich an die Fritzbox übertragen? Das geschieht sehr einfach über einen POST Aufruf, denn auf der Fritzbox (und auch auf den meisten anderen Routern) läuft ein kleiner Webserver denn man ganz einfach über den Browser erreicht, wenn man die adresse "fritz.box" in die Adresszeile tippt.

Dort ist die Fritzbox genauso aufgebaut wie eine gewöhnliche Webseite. Und diese nimmt auch POST und GET aufrufe entgegen. Genau hier setzen wir an und werden unsere Befehle rüber schicken.


        Dim client As New TcpClient

        client.Connect("fritz.box", 49000)

        Dim stream As NetworkStream = client.GetStream
        Dim bytes As Byte() = New Byte((My.Resources.msg.Length)) {}

        bytes = Encoding.ASCII.GetBytes(My.Resources.msg)

        stream.Write(bytes, 0, bytes.Length)
        bytes = New Byte(1024) {}

        Dim str As String = String.Empty

        Dim count As Integer = stream.Read(bytes, 0, bytes.Length)

        str = Encoding.ASCII.GetString(bytes, 0, count)

        stream.Close()
        client.Close()

Der Code ist sehr simpel, wir erstellen uns einen WebClient und verbinden uns mit der FritzBox. Nach der Verbindung senden wir ein POST Request raus und lesen anschließend die Antwort wieder aus.

Mit dem obigen Code senden wir folgenden Befehl an die Fritzbox:


POST /upnp/control/WANIPConn1 HTTP/1.1
HOST: fritz.box:49000
SOAPACTION: "urn:schemas-upnp-org:service:WANIPConnection:1#ForceTermination"
CONTENT-TYPE: text/xml ; charset="utf-8"
Content-Length: 293

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <u:ForceTermination xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1" />
   </s:Body>
</s:Envelope>

Wie man sieht ist der Befehl ein gewöhnlicher POST Aufruf inkl. Header. Dieser übergibt einen Befehl urn:schemas-"upnp-org:service:WANIPConnection:1#ForceTermination", der der Fritzbox sagt die Verbindung ins Internet neu aufzubauen.

Man muss hierbei sehr aufpassen, besonders die Content-Length Angabe muss genaustens passen, da sonst ein Fehler als Antwort eintrudelt.

Wie immer gibts auch eine kleine Demoanwendung.

Bild 1: Das Demo Programm in action

Download hier: Download [VS 2008 Projekt]

11Dez/091

Programmfortschritt in der Windows Taskleiste anzeigen

Wenn ihr Windows 7 bereits verwendet, werden euch sicherlich einige Neuerungen aufgefallen sein. Einige von denen ist die neue Funktion der Taskleiste den Fortschritt eines Programms darzustellen.

Ihr werdet dies beobachten, wenn ihr z.B. eine Datei mit dem Internet Explorer 8 herunterladet oder eine Datei kopiert. Das Programm zeigt den eigenen Fortschritt dann unter dem Eigenen Symbol in der Taskleiste an.

Diese Funktion ist extrem praktisch, so kann man sie benutzen um den Benutzer immer aktuell zu halten, ohne dass er das eigentliche Programm offen hat. Mögliche Anwendungsfälle wären Dateidownloads, Kopiervorgänge, Bildererzeugung und allgemeine Vorgänge, die etwas Zeit in Anspruch nehmen.

Natürlich können auch wir diese Funktionen in unseren Projekten nutzen. Leider werden die Aufrufe an das Betriebssystem unmanaged gestartet, was bedeutet, dass diese Aufrufe umgewandelt (gewrapped) werden müssen. Da das ein sehr komplexer Vorgang ist hat Microsoft uns diesen gespart und schon einige vorgefertigte Klasse angefertigt auf die wir zurückgreifen können.

Diese Librarys finden wir im MSDN in der .NET Interop Sample Library. Diese Beispielsammlung enthällt viele weitere Beispiele. Wir betrachten hier nur das was mit dem Fortschrittsbalken in der Taskleiste zu tun hat.

Deswegen brauchen wir auch nicht alle Klassen, sondern nur diese:

Vista Bridge Sample Library
Windows7.DesktopIntegration
Windows7.DesktopIntegration. Registration.

Windows7.DesktopIntegration enthält hierbei die Klasse WindowsFormsExtensions, welche uns die nötigen "Extensions"-Funktionen zur Verfügung stellt um das Aussehen der Taskleiste ändern zu können.

In unserem (einfachen) Fall werden wir nur die beiden Methoden benötigen:

SetTaskbarProgress(float percent)
SetTaskbarProgressState(ThumbnailProgressState state)

Mit der Ersten Methode können wir den Fortschritt der Progressbar steuern, und mit der zweiten das Aussehen. Für die zweite Funktion müssen wir neben dem Zustands Parameters auch das Handle des Fensters angeben, für das die Aktion ausgeführt wird.

So könnten die Aufrufe folgendermaßen aussehen:


WindowsFormsExtensions.SetTaskbarProgress(Me.ProgressBar1)
WindowsFormsExtensions.SetTaskbarProgressState(Me, _
                         Windows7Taskbar.ThumbnailProgressState.Error)

Garnicht mal so kompliziert oder?

Dabei kann das Taskleistensymbol unseres Programms 5 verschiedene Werte annehmen:

progresstaskbarBild 1: Cool oder?

Wenn wir nun also ein neues Windows Forms Projekt erstellen müssen wir ersteinmal die drei oben erwähnten Class Librarys importieren. Das machen wir in dem wir über Datei->Hinzufügen->Vorhandenes Projekt wählen

progresstaskbar3Bild 2: Projekte hinzufügen

Auf diese Weise fügen wir alle drei oben erwähnten Projekte dem unseren hinzu. Wenn das geschafft ist müssen wir noch die nötigen Verweise hinzufügen. Dazu wählen wir Projekt->Verweis hinzufügen... und dann den Tab "Projekte"

progresstaskbar4Bild 3: Verweise hinzufügen

Hier wählen wir alle drei Projekte aus und klicken auf "OK"!

Wie ihr nun im Projektmappen Explorer erkennen werdet ist das Projekt gleich mal um einiges gewachsen! Ihr könnt dieses Vorgehen auch umgehen, indem ihr die importierten Class Librarys als DLL kompiliert und direkt in das Projekt einbindet. Das schafft vielleicht mehr Übersicht.

Nun haben wir schon das größte Hindernis schon überwunden! Nun gehts ans Programmieren, wofür ihr die oben vorgestellten Funktionen benutzen könnt um eurem Programm den nötigen Windows 7 Look zu verpassen!

Ich habe dazu wie immer eine kleine Demo angefertigt. Meine sieht so aus:

progresstaskbar2Bild 4: Das Demoprojekt in Aktion

Das Demoprojekt könnt ihr hier laden: Download [VS 2008]

9Dez/095

VB.NET: Saubere Klassen erstellen mit Properties

Heutzutage wird fast nurnoch Objektorientiert programmiert. Das bedeutet, dass ein größeres Projekt schnell mal auf eine Klassenanzahl von 100 und mehr wächst.

Damit man den Überblick behält und den Code, den man schon geschrieben hat möglichst wiederverwenden kann, sollte man ein paar grundlegende Regeln beachten.

In diesem Artikel möchte ich ein Beispiel zeigen, wie man eine Klasse in ein Projekt integriert und dank der benutzten Standardmethoden an ein Datagrid bindet.

Warum schreibe ich diesen Artikel überhaupt? Erst gestern habe wurde in einem Forum die Frage gestellt, warum man eine Liste (Of T) nicht als DataSource für ein DataGridView benutzen kann. Dabei ist das sehr wohl möglich, der User hat seine Klasse aber "unschön" gestaltet, weswegen sich die anderen Steuerelemente auch quer gestellt haben.

Hier ein Beispiel einer solchen "Unschönen" Klasse:


Public Class Auto
    Public marke As String
    Public motor As String
    Public geschw As Integer
End Class

        Dim al As New List(Of Auto)
        al.Add(New Auto With {.marke = "VW", .motor = "Diesel", .geschw = 0})

        al(0).geschw += 10

Wie man sieht sind hier alle Klassenvariablen öffentlich zugänglich, und die Klasse hat keinerlei Kontrolle darüber, wie sie befüllt werden. Ein weiterer Nachteil ist auch die Inkompabilität zu anderen Steuerelementen, da diese einen Standardkonformen Aufbau der Klasse erwarten. Finden sie bestimmte Elemente in der Klasse nicht so verweigern diese die Funktionalität!

Wie müsste diese Klasse also umgebaut werden, damit diese kompatibel zu anderen Objekten wird?


Public Class Auto
    Private geschw As Integer

    Public Sub New(ByVal marke As String, ByVal motor As String)
        Me.Marke = marke
        Me.Motor = motor
    End Sub

    Private _marke As String
    Public Property Marke() As String
        Get
            Return _marke
        End Get
        Set(ByVal value As String)
            _marke = value
        End Set
    End Property

    Private _motor As String
    Public Property Motor() As String
        Get
            Return _motor
        End Get
        Set(ByVal value As String)
            _motor = value
        End Set
    End Property

    Public Sub beschleunigen(ByVal a As String)
        geschw += a
    End Sub

    Public Sub bremsen(ByVal a As String)
        geschw -= a
    End Sub
End Class

Wie man sieht ist die Klasse nun natürlich um einiges länger geworden. Leider kann man das zur zeit noch nicht anders machen. Aber Abhilfe kommt in der neuen VB.NET Version, wo man die properties so wie in C# 3.0 erstellen kann.

Die Zugriffe auf die Klassenvariablen werden nun über properties gesteuert, die nun auch zB. überprüfen könnten, ob der übergebene Wert überhaupt passt und gegebenenfalls noch Änderungen daran vornehmen.

Aber was bringt uns nun dieser längere Aufbau der Klasse? Nun, wenn wir unser Objekt, das aus dieser Klasse entsteht an andere Objekte übergeben, erwarten diese, dass sie properties vorfinden, mit denen sie arbeiten können.

Hat die Klasse keine Properties so können wir auch bestimmte Funktionen auch nicht benutzen. Ich möchte das mal an einem DataGridView demonstrieren, da dieses Element von den properties sehr schön Gebrauch macht!

Fügt man folgenden Code einem Form mit einer DataGridView hinzu:


    Private Sub Form1_Load() Handles MyBase.Load
        Dim autos As New List(Of Auto)

        autos.Add(New Auto("VW", "Diesel"))
        autos.Add(New Auto("Audi", "Benziner"))
        autos.Add(New Auto("Mercedes", "Diesel"))
        autos.Add(New Auto("Renault", "Diesel"))
        autos.Add(New Auto("BMW", "Beziner"))

        Dim bs As New BindingSource
        bs.DataSource = autos

        Me.DataGridView1.DataSource = bs
    End Sub

erhält man folgendes Bild:

datagridviewBild 1: Die Datagridview stellt alle Informationen angenehm dar

Wie man sieht konnte man mit nur 3 Zeilen Code (Abgesehen vom Erzeugen der Werte) alle Daten der Liste visualisieren. Man beachte desweiteren, dass sogar die Namen der Properties (Marke und Motor) ausgelesen werden und der richtigen Tabelle zugeordnet werden!

Dies könnte man natürlich auch ohne Properties hinbekommen, doch würde man viel mehr Aufwand betreiben müssen und man denke an die Zukunft, in der unser Modell sehr einfach zu warten wäre.

Hier gibts ein kleines Demoprojekt: Download [VS 2008]

8Dez/094

Windows Aero Glas in eigenen Projekten nurzen Teil 3

Es ist etwas Zeit seit dem zweiten Teil vergangen, und wie ich in den Kommentaren sehe, sind da noch ein paar Fragen offen!

Das Hauptproblem mit den Glasoberflächen unter Windows sind die GDI und GDI+ Komponenten, diese unterstützen keine Transparenz. Das macht es so schwierig diese auf Glas ordentlich zu rendern.

Wenn ihr meine persönliche Meinung hören wollt, lasst es ganz bleiben! Denn seit knapp 4 Jahren stellt Microsoft uns ein sehr mächtiges grafisches Werkzeug zur Verfügung, nämlich WPF.

WPF soll die neue Präsentationsschicht unter Windows werden. Das heißt WPF regelt wie die UI-Elemente, die wir sehen, zb. Knöpfe und Fenster aussehen und sich verhalten!

Aber ich will nicht weiter auf den Aufbau von WPF eingehen, für uns ist wichtig zu wissen, dass in WPF alle Elemente die Transparenz beherrschen. Das macht sie für uns perfekt um sie auf Glas einzusetzen!

aerowpfBild 1: Ein in WPF gerendertes Fenster mit Steuerelementen

Wie man auf dem Bild 1 gut erkennen kann, sieht man keine verpixelten Texte mehr und der Hintergrund ist wunderbar Transparent, auch bei dem Button!

Alle WPF Steuerelemente beherrschen viel mehr Optische Funktionen, so sind die beiden ProgressBars im Bild 1 ebenfalls Transparent. Das geht nicht mit GDI+!

Für alle die jetzt denken, oh nein, ich will nicht schon wieder eine neue Sprache erlernen, kann ich für Entwarnung sorgen. Auch unter WPF wird mit VB.NET und C# programmiert. Ihr könnt euren Code in 80% der Fälle komplett übernehmen!

Wie gesagt liegt der Schwerpunkt von WPF auf der Präsentation, deswegen werdet ihr die alten Grafik-Klassen nicht mehr wiederfinden. Desweiteren gibts auch keine Windows.Forms mehr, sondern nurnoch Windows (Fenster). Dies sind ein paar der Stolperfallen, durch die man sich kämpfen muss und an die man sich gewöhnen muss.

Wie ihr an dem folgenden Beispielcode sehen werdet, werdet ihr viel wiedererkennen:


Imports System.Runtime.InteropServices
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Windows.Media
Imports System.Windows
Imports System.Windows.Interop

Public Class GlassForm
 _
        Private Shared Sub DwmExtendFrameIntoClientArea _
                (ByVal hwnd As IntPtr, ByRef margin As MARGINS)
    End Sub
 _
    Public Shared Function DwmIsCompositionEnabled() As Boolean
    End Function

    Public Shared Function ExtendGlassFrame(ByVal window As Window, _
                            ByVal margin As MARGINS) As Boolean
        If Not DwmIsCompositionEnabled() Then
            Return False
        End If

        Dim hwnd As IntPtr = New WindowInteropHelper(window).Handle

        ' Hintergrundfarbe auf Tranzparent setzen, für WPF und die Win32 Ansicht
        window.Background = Brushes.Transparent
        HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = _
                                        Colors.Transparent

        Dim margins As New MARGINS(margin.left, margin.right, _
                                   margin.top, margin.bottom)
        DwmExtendFrameIntoClientArea(hwnd, margins)
        Return True
    End Function
End Class

Public Structure MARGINS
    Public left As Integer
    Public right As Integer
    Public top As Integer
    Public bottom As Integer

    Public Sub New(ByVal l As Integer, ByVal r As Integer, _
                   ByVal t As Integer, ByVal b As Integer)
        left = l
        right = r
        top = t
        bottom = b
    End Sub
End Structure

Wie man sieht ähnelt der Code sogar sehr dem aus Teil 2. Nur die entsprechenden (in WPF nicht mehr vorhandenen)  Elemente wurden angepasst.

Das tolle hierran ist, dass wir nun alle Steuerelemente benutzen können die wir wollen, ohne uns Sorgen machen zu müssen wie diese gerendert werden!

Das Demoprojekt gibts es hier: Download [VS 2008]

<< Teil 2 des Tutorials Teil 4 >>

Eventuell schiebe ich noch einen vierten Teil nach, warten wir ersteinmal eure Reaktionen ab :)

1Dez/090

Kostenloses E-Book von Microsoft zum Thema VB.NET

Es ist wieder soweit. Microsoft bietet wiedermal ein Buch komplett gratis zum Download an. Dieses Mal ist Visual Basic.NET das Thema.

Visual-Basic-2008-Das-Entwicklerbuch-1259659733

Das Buch ist von dem Hauseigenen Verlag MicrosoftPress, welches einen sehr guten Ruf genießt. Ich selber habe mehrere Bücher von denen.

Dieses Buch kostet normalerweise 59 € und ist somit ein sehr gutes "Schnäppchen". Dabei kann es sich vom Inhalt sehen lassen. Dieses Buch vermittelt alles von den Grundlagen, bishin zur fortgeschrittenen Programmierung mit Datenbanken.

Auch wenn ihr kein VB.NET Programmierer Seit kann ich es euch trotzdem empfehlen dieses Buch zu laden, den man weiß ja nie!

Download: Hier

9Nov/094

Windows Aero Glass in eigenen Projekten nutzen Teil 2

dotnetIn dem ersten Teil dieses Beitrags, der nun schon einige Monate her ist habe ich gezeigt, wie man die Aero Glas Effekte auch in eigene Projekte einbauen kann. Doch leider waren da noch einige Probleme, so konnten wir nur das ganze Formular in Glas verwandeln und die Darstellung der Steuerelemente war falsch.

Genau hier möchte ich den zweiten Teil ansetzen und diese letzten Probleme aus der Welt schaffen!

Wir beginnen genauso wie im ersten Teil nur nennen wir unsere Klasse diesmal GlassForm um nicht durcheinander zu kommen.

Diesmal importieren wir eine weitere Methode aus der Windows API, nämlich DwmIsCompositionEnabled() die es uns ermöglicht zu prüfen, ob diese Effekte, die wir nutzen möchten auch verfügbar sind.

Die zweite Funktion DwmExtendFrameIntoClientArea müsste aus Teil 1 schon bekannt sein, diese hilft uns die Formränder zu vergrößern und somit die Glasoberfläche ins Formularinnere zu holen.

Da wir dieses Mal die eine Option für alle Ränder haben möchten, sodass wir zB. nur den oberen Rand in Glas verwandeln, benötigen wir ein Structure, in dem wir die Werte für jeden Rand speichern können und den wir dann später der Funktion DwmExtendFrameIntoClientArea übergeben.

So sieht unsere Klasse aus:


Imports System.Runtime.InteropServices

Public Class GlassForm
 _
        Private Shared Sub DwmExtendFrameIntoClientArea _
                (ByVal hwnd As IntPtr, ByRef margin As AeroDemo2.MARGINS)
    End Sub
 _
    Public Shared Function DwmIsCompositionEnabled() As Boolean
    End Function

    Public Shared Function ExtendGlassFrame _
        (ByVal hwnd As IntPtr, ByVal margin As AeroDemo2.MARGINS) As Boolean

        GlassForm.DwmExtendFrameIntoClientArea(hwnd, margin)

        Return True

    End Function

End Class

Public Structure MARGINS
    Public left As Integer
    Public right As Integer
    Public top As Integer
    Public bottom As Integer

    Public Sub New(ByVal l As Integer, ByVal r As Integer, _
                   ByVal t As Integer, ByVal b As Integer)
        left = l
        right = r
        top = t
        bottom = b
    End Sub
End Structure

Wie man sieht enthält die Structure MARGINS je eine Variable für jede Seite des Formulars, wobei der Wert den sie beinhaltet den Abstand zum eigentlichen Rahmen angibt!

Wechseln wir nun zur Klasse Form1, da wir hier fertig sind. In der Form_Load Methode können wir uns nun einen neuen Structure erstellen und diesem unsere gewünschten Rahmenabstände übergeben. Daraufhin rufen wir unsere Funktion auf die für sns das Glas herzaubern soll.

Doch wenn wir das Programm nun starten werden wir kein Glas vorfinden! Das liegt daran, dass Windows die Farbe Schwarz für Transparente Darstellung auf Glas verwendet. Das bedeutet, dass alle Steuerelemente, die Schwarze Farbe enthalten transparent und somit zu Glas werden! Das konntet ihr aber auch im ersten Teil schon beobachten!

Dieses Problem nehmen wir aber später nocheinmal in Angriff! Kommen wir ersteinmal zu unserem Glasfenster!

Damit wir auch wirklich Glas sehen, müssen wir ersteinmal das Fenster mit schwarzer Farbe füllen. Dazu schreiben wir folgendes in die Form_Paint Methode:


    Dim margins As AeroDemo2.MARGINS

    Private Sub Form1_Load() Handles MyBase.Load
        'Den "normalen" Bereich festlegen
        margins = New AeroDemo2.MARGINS(0, 0, 35, 50)
        'Die Fensterränder erweitern
        GlassForm.ExtendGlassFrame(Me.Handle, margins)
    End Sub

Wenn man das Programm startet, müsste das ca so aussehen:

aerodemo1Bild 1: Alles ist schwarz, bis auf den Glasteil

Bitte nicht erschrecken, denn eigentlich ist es keine Überraschung, dass wir nur schwarz sehen, denn wir haben im letzten Schritt alles Schwarz gefärbt. Der Aero Teil ist dadurch nun transparent geworden!

Da das Schwarze aber ziemlich blöd aussieht möchten wir das natürlich wieder auf unsere Standardeinstellung ändern. Dazu müssen wir diesen schwarzen Rechteck da oben nun wieder mit der Ausgangsfarbe füllen! Das machen wir so:


    Private Sub Form1_Paint(ByVal sender As Object, _
                ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        If GlassForm.DwmIsCompositionEnabled() = True Then
            'Hintergrund Schwarz füllen, um den Glass effenkt zu erhalten
            e.Graphics.Clear(Color.Black)
            'Nun das Stück, dass nicht aus Glas sein soll wieder "zurückmalen"
            Dim clientArea As New Rectangle(margins.left, margins.top, _
                    Me.ClientRectangle.Width - margins.left - margins.right, _
                    Me.ClientRectangle.Height - margins.top - margins.bottom)
            Dim b As Brush = New SolidBrush(Me.BackColor)
            e.Graphics.FillRectangle(b, clientArea)
        End If
    End Sub

Nachdem wir das Rechteck wieder mit der Ausgangsfarbe bemalt haben sieht das schon viel angenehmer aus:

aerodemo2Bild 2: Das Fenster hat nun wieder die "Normale" Farbe

Was natürlich nun auffällt ist das Komplett schwarze Label (ja, es ist wirklich eins) und der nicht lesbare Button.

Dies sind die oben besagten Probleme von GDI Objekten. Diese werden Standardmäßig genutzt um Abwärtskompabilität zu gewährleisten. Schaltet man aber das TextRendering auf GDI+ so werden auch die Schriften korrekt dargestellt.

Um das zu ändern muss die Option SetCompatibleTextRenderingDefault auf true gesetzt werden. Dies muss aber geschehen bevor das Programm gestartet wird. Also klicken wir mit der Rechten Maustaste auf unser Projekt -> Hinzufügen -> Modul

In das Modul schreibt ihr nun folgendes rein:


    Sub Main()
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(True)
        Application.Run(New Form1) 'Name eurer Form
    End Sub

Nun müssen wir nurnoch dafür sorgen, dass unser Projekt durch das Modul gestartet wird. Das machen wir in dem wir auf "Projekt" oben im Menü klicken und dann auf Eigenschaften. Hier muss nun der Haken bei "Anwendungsframework aktivieren" herausnehmen und dann bei "Startobjekt" "Sub Main" wählen:

aerodemo2_1Bild 3: Das Projekt über das Modul starten

Wenn wir das Programm nun starten sollte man die Texte und auch den Button lesen können!

Nun sind wir eigentlich schon durch. Was noch zu sagen bleibt ist, dass man es möglichst vermeiden sollte Steuerelemente auf dem Glas zu platzieren, da diese sehr oft falsch dargestellt werden.

Wenn ihr unbedingt Schrift auf Glas haben wollt solltet ihr diese Schrift auf das Formular Zeichnen und nicht einfach über ein Label dort platzieren. Das selbe gilt auch für Grafiken. Leider kann man nicht einfach mit der Graphics.DrawString() Methode Zeichnen sondern muss den Text erst in ein GraphicsPath Objekt "zeichnen" bevor man es dem Glas übergibt. So stellt man sicher, dass die Textverläufe richtig dargestellt werden!

Hier ein Beispiel:


            'Text zeichnen
            Dim txt = Me.CreateGraphics()
            Dim path = New GraphicsPath()

            path.AddString("Ich bin ein gezeichneter Text", _
                           New FontFamily("Tahoma"), CInt(FontStyle.Regular), _
                           20, New Point(60, 0), StringFormat.GenericDefault)

            Dim brush = New PathGradientBrush(path)
            Dim clr As Color() = {Color.Transparent}

            txt.SmoothingMode = SmoothingMode.HighQuality
            brush.CenterColor = Color.White
            brush.SurroundColors = clr
            txt.FillPath(Brushes.Black, path)

            brush.Dispose()
            path.Dispose()
            txt.Dispose()

Und wie es aussehen könnte:

aerodemo3Bild 4: So könnte ein Aero Formular aussehen

Weitere Informationen auch mit Beispielen findet ihr hier: (englisch & C#)

Microsoft
CodeProject
CodeProject

Wie üblich habe ich das Ganze in ein kleines Demoprojekt verpackt: Download

Wie hats euch gefallen, möchtet ihr einen dritten Teil? Freue mich auf euer Feedback!

Teil 1 des Tutorials gibts hier
Zum dritten Teil gehts hier lang

6Nov/0910

Windows Lizenzschlüssel aus der Registry auslesen

Da der Beitrag über das Auslesen des Office Schlüssels sehr beliebt ist, habe ich nun auch das Lesen des Windows Schlüssels als Beispiel verfasst.

Das Vorgehen hierbei ist sogar noch einfacher als beim Office Schlüssel, da es keine Aufteilungen in Versionen gibt! In allen Windows NT Versionen (also alle ab XP) befindet sich der Schlüssel in der Registry unter diesem Pfad:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

winkeyBild 1: Der Registryordner mit dem Windows Schlüssel

In diesem Ordner finden wir den Binärwert "DigitalProductId", der viele Hexadezimalzeichen enthält!

Der Code ist hierbei dem aus dem Officebeispiel sehr ähnlich, da er ja auch nichts anderes macht, als den im HEX-Code vorhandenen Key in lesbare Schrift umzuwandeln! Die einzige Schwierigkeit hierbei besteht darin, den Key richtig zusammen zusetzen, denn es wird nicht das komplette Alphabet verwendet um einen Windows Key zu generieren sondern nur diese Zeichen:

B C D F G H J K M P Q R T V W X Y 2 3 4 6 7 8 9

Wenn man das beachtet steht einem nichts mehr im Wege! Der Code aus diesem Beispiel stammt ausnahmsweise nicht von mir sondern von vcware.de - danke dafür an dieser Stelle!

Ich habe diesbezüglich wie immer ein kleines Demoprojekt angefertigt, das ihr euch herunterladenkönnt! Download

Bitte berichtet ob es bei euch gut geklappt hat.
Hier könnt ihr euch das komplette kostenlose Programm zum auslesen von Windows & Office key herunterladen. (English & Deutsch)

5Nov/092

Windows 7 und seine Schönheitsfehler Teil 1: Ordnerpfade

win7Seit einiger Zeit ist nun Microsofts neues Betriebssystem Windows 7 auf dem Markt und schlägt sich bisher sehr gut! Ich nutze es selber jeden Tag und bin begeistert von der Geschwindigkeit und den neuen Features die es bietet. Doch leider gibt es auch hier ein paar (wenn auch wenige) Schattenseiten, über die ich in dieser Reihe berichten möchte!

In diesem Ersten Teil geht es um ein kleines "Feature", dass einem nicht gleich ins Auge springt und teilweise auch schon in Windows Vista vorzufinden war. Der "normale" Nutzer würde es wahrscheinlich garnicht bemerken, doch wir als Programmierer müssen desöfteren mit konkreten Pfaden arbeiten, und das ist genau die Sache!

Blicken wir kurz zurück auf Windows XP und seine NT-Vorgänger. Jede Windows XP Version, egal in welcher Sprache sie vorliegt hat den selben Aufbau, zB. wird man immer den Ordner C:\Programme vorfinden, in dem die ganzen installierten Programme liegen.

Wenn wir nun also diesen Pfad fest in unser Programm einbauen, da wir ja davon ausgehen, dass dieser Pfad konstant ist müsste es ja "immer" laufen! Doch das ist leider nicht so, denn wenn man unser Programm auf einem Rechner mit einer Englischen Kopie von Windows (XP) startet, wird das Programm abstürzen, da es den Ordner C:\Programme nicht finden wird!
Warum? Ganz einfach, denn im Englischen gibts das Wort "Programme" nicht! Dort heißt der Ordner "Program Files" und müsste dementsprehend über "C:\Program Files" angesprochen werden! - Ja, ich weiß, ein normaler Mensch würde hier die Umgebungsvariable "ProgramFiles" nutzen, die immer auf den richtigen Ordnerpfad zeigt, aber das würde das Problem nicht verdeutlichen! (dazu kommen wir noch)

Also zurück zu Windows 7. Microsoft dachte sich auch, dass das wohl blöd ist und entschied sich dazu alles einheitlich zu machen! - Gute Idee, doch etwas komisch umgesetzt!

Schauen wir uns doch mal einen Typischen Windows 7 Ordnerpfad an:

win71Bild 1: Der Ordner "Eigene Bilder" ausgewählt im WIndows Explorer

Wenn ich Sie nun nach dem Pfad zu dem ausgewählten Ordner frage, würden Sie wohl wie folgt antworten:

C:\Benutzer\Basti\Eigene Bilder

Ich kann Sie beruhigen, diese Angabe ist völlig korrekt, so scheint es. Denn an dieser Stelle täuscht uns Windows 7 bzw. der Windows Explorer.

Denn würden Sie den o.g. Pfad aufrufen wollen, würde Windows eine Fehlermeldung ausgeben, die besagen würde, dass der Ordner nicht existiert! - Wie kann das sein?

Das liegt daran, dass Windows uns die deutsche Sprache nur vortäuscht! Denn der echte Pfad sieht so aus:

win72Bild 2: Der wahre Pfad steht oben

Nach einem Klick in die Adresszeile wird man feststellen, dass der Ordnerpfad komplett nur aus Englischen Wörtern besteht!

Das macht es manchmal umständlich eine Datei zu finden, auch wenn es für den eigentlichen Nutzer angenehmer erscheint!

Wie kann man sich schützen?

ALs Programmierer sollte man seinen Code immer so schreiben, dass es auf allen Windows Versionen lauffähig ist, doch wie macht man das wenn die Ordner immer anders heißen?

Die Antwort ist relativ leicht und lautet "Umgebungsvariable"! Denn Windows übersetzt nicht alle Ordner, sondern nur die so genannten "Special Folders". Das sind Ordner, die für das System von Bedeutung sind!

Dazu gehören der Windows Ordner Selbst, der Systemordner, der Programmordner oder der "Eigene Dateien" Ordner. Aber woher weis ich, ob ein Ordner ein "Special Folder" ist oder nicht?

Das kann man ganz einfach herausfinden, indem man in die Liste der Umgebungsvariablen (Environmentvariables in English) schaut. Diese findet ihr zb so: [Start]+[R] Tippt nun "cmd" ein und drückt auf [ENTER] um in die Eingabeaufforderung von Windows zu gelangen. Nun tippt "set" ein und drückt erneut auf [ENTER].

win73Bild 3: Die Liste mit den Umgebungsvariablen unter Windows

Hier sehr ihr eine komplette Liste mit allen dem Benutzer zugänglichen Umgebungsvariablen. Alternativ könnt ihr auch so vorgehen: [Start] und nun tippt ihr in das Suchfeld von Windows Vista oder 7 "erweitert" ein und lasst Windows ein paar Sekunden suchen, dann klickt ihr auf den Eintrag "Erweiterte Systemeinstellungen Anzeigen", wählt nun oben den Tab "Erweitert" auf und klickt unten auf "Umgebungsvariablen" ihr bekommt folgendes zu sehen:

win74Bild 4: Die Grafische Oberfläche der Umgebungsvariablen

Unter Windows XP Erreicht ihr dieses Fenster mit einem Rechtsklick auf Arbeitsplatz und dann Erweitert!

Auf diese Variablen kann man auch sehr bequem vom Code aus zugreifen, und da diese auf jedem System stimmen hat man keine Probleme zu erwarten!


        '
        '
        'Pfad zum Programmordner
        Dim ProgrammPfad As String
        ProgrammPfad = Environment.GetEnvironmentVariable("ProgramFiles")

Wie man sieht kann man so sehr einfach den Programmordner auslesen.

Windows 7 bietet noch weitere kleine Überraschungen über die ich im Nächsten Teil schreiben werde!

Hat euch diese Beitrag gefallen? Schreibt mir einen Kommentar!

Get Adobe Flash player