vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
sevDataGrid - G?nnen Sie Ihrem SQL-Kommando diesen kr?nenden Abschlu?!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Newsletter  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2017
 
zurück
Rubrik: .NET   |   VB-Versionen: VB200801.08.08
Kontrollierte Abfrage numerischer Werte durch Extension Methods

Modul zur Überwachung der Ausprägung von Doublewerten während der Durchführung umfangreicher Berechnungen durch kontrollierte und protokollierte Abfrage der Werte

Autor:  Manfred BohnBewertung:     [ Jetzt bewerten ]Views:  7.424 

Summer-Special bei Tools & Components!
Gute Laune Sommer bei Tools & Components
Top Summer-Special - Sparen Sie teilweise bis zu 120,- EUR
Alle sev-Entwicklerkomponenten und Komplettpakete jetzt bis zu 25% reduziert!
zum Beispiel:
  • Developer CD nur 479,20 EUR statt 599,- EUR
  • sevDTA 3.0 nur 224,30 EUR statt 299,- EUR
  •  
  • vb@rchiv   Vol.6 nur 20,00 EUR statt 24,95 EUR
  • sevCoolbar 3.0 nur 55,20 EUR statt 69,- EUR
  • - Werbung -Und viele weitere Angebote           Aktionspreise nur für kurze Zeit gültig

    Die Compiler-Direktive 'Extension' (in System.Runtime.CompilerServices) ermöglicht es, 'Subs' und 'Functions', die in einem Modul vereinbart sind, im VB-Code wie die Methoden einer Klasse aufzurufen.

    Zweckmäßig ist die Verwendung der 'Extension'-Direktive insbesondere bei Klassen, die nicht 'geerbt' werden können.

    Ein Modul, das solche Extensions enthält, sollte im 'NameSpace' einer Klassenbibliothek enthalten sein, damit es möglich wird, durch entsprechende Imports-Anweisungen zu steuern, welche Extension-Module in einer bestimmten Code-Datei verfügbar sind. Das reduziert die Verwechslungsgefahr.

    Das Modul modDoubleEx demonstriert einige Routinen, die als 'Extension Methods' aufgerufen werden können - zur Werte-Abfrage (bei Variablen des IEEE-Datentyps 'Double'). Diese Routinen lösen bei ungeeigneten Argumenten Ausnahmen aus und sollten deshalb nur innerhalb von Try-Catch-Blöcken gerufen werden. Das Modul dient in erster Linie zur Unterstützung bei der kontrollierten Berechnung komplexer numerischer Algorithmen.

    Beim Datentyp 'Double' von Interesse:

    • die Kontrolle und das Filtern der Werte;
    • die Prüfung der Konvertierbarkeit;
    • kontrollierte Rundungsoperationen;
    • die Registrierung von Limit-Verstößen.

    Routinen im Modul 'modDoubleEx':
    Die Routine GetValue löst eine Ausnahme aus, wenn bei der Abfrage ein IEEE-Sonderwert in der Variable enthalten ist.

    Die Routine SetRange erlaubt die Definition einer zulässigen Spannweite für Werte, die 'ausnahmefrei' aus einer Double-Variable abgefragt werden können. Nach dem Setzen eines 'Range'-Bereichs werfen die Routinen eine Exception, falls der enthaltene Wert außerhalb der gültigen Grenzen liegt. Die Routine 'GetValue_SetToRange' löst in solchen Fällen keine Ausnahme aus, sondern gibt bei Ausreißer-Argumenten den zulässigen Grenzwert zurück - der Variablen-Inhalt bleibt dabei unverändert. Die Routine 'ClearRange' gibt den gesamten Double-Bereich für die Abfrage-Extensions wieder frei.

    Die Routine SetThreshold setzt eine kritische Schranke. Ist diese Ausprägung bei einer Abfrage überschritten, erfolgt die Registrierung in der zugehörigen Counter-Variable, die durch 'GetThreshold_Counter' abgefragt werden kann ('stiller Alarm').

    Die Routine GetValue_NaN setzt nach der Wertabfrage den Inhalt der Variable auf 'Double.NaN' ( = Not a Number). Der Versuch einen 'NaN'-Wert abzufragen, führt zu einer Ausnahme. Auf diese Weise wird bewirkt, dass ein zugewiesener Wert nur einmal abgefragt werden kann - dann ist er verloren. (In diesem Fall wird der Variablen-Inhalt durch die Extension geändert: ByRef-Parameter). Bei darauf folgenden direkten Zuweisungen des Typs x = y wird der 'NaN'-Wert an die Ziel-Variable weitergegeben!

    Die Routine IsLong dient der Überprüfung, ob der in einer Double-Variable aktuell enthaltene Wert ohne nennenswerten Genauigkeitsverlust auf eine Variable des Typs 'Long' zugewiesen werden kann. 'IsSingle' überprüft ob der Wert im Geltungsbereich des Datentyps 'Single' liegt.

    Die Funktion Round4 rundet den zurückgegebenen Wert auf vier Nachkommastellen und überwacht dabei die Wertausprägung. Es müssen im Argument mindestens fünf Nachkommastellen hinreichend korrekt darstellbar sein, sonst ist eine Rundung auf vier Stellen eher witzlos. Falls erforderlich, wird deshalb eine Ausnahme ausgelöst.

    Die Prozedur Round rundet den IN DER VARIABLE ENTHALTENEN Wert auf die geforderte Nachkomma-Stellenzahl.

    Die Routine GetIn fragt den Wert der Variable ab, wobei die Rückgabe stets in den gegebenen Grenzen liegt. Bei dieser Routine lösen Grenzüberschreitungen weder eine Ausnahme aus, noch werden sie durch eine der globalen Variablen registriert.

    Die Routine GetUnSafe gibt den Wert der Variable völlig unkontrolliert zurück. y = x.GetUnSafe entspricht y = x

    Die Funktion NearBy prüft, ob zwei Double-Werte so nahe beieinander liegen, dass sie als gleich gelten können.

    Modulglobale Variablen in 'modDoubleEx':
    Modulglobale Variable können auf verschiedene Weise bei Programmierung von 'Extension Methods' einbezogen werden. Zu beachten ist, dass sie sich stets auf alle Variablen beziehen, die die 'Extensions' aufrufen können.

    Im Beispiel-Modul ist ein 'Access_Counter' enthalten, der alle über die Extensions ausgeführten Wert-Abfragen aller Double-Variablen zählt. Die Variable 'gTime_LastAccess' enthält den Zeitpunkt der letzten Abfrage eines Wertes. (In der Praxis würde man einen 'TickCounter' verwenden, um eine sinnvolle Genauigkeit zu erreichen.)

    Bei jeder gültigen Abfrage eines Wertes wird geprüft, ob es sich dabei um den bisher größten oder kleinsten Wert handelt. Die Extreme werden registriert und können durch die Routine 'GetMinMax' abgefragt werden. Die Re-Initialisierung geschieht durch 'ResetMinMax'. 'GetMinMax' wirft eine Ausnahme, falls vor dem Aufruf noch keine Werte abgefragt worden sind.

    Wenn man 'Extension Methods' für einen allgemein einsetzbaren Datentyp wie 'Double' erstellt, sollte man darauf achten, dass diese Routine auch tatsächlich für alle Argumente und Anwendungsfälle sinnvoll einsetzbar ist. Die Demo-Methode 'Sphere' berechnet aus einem Radius die Oberfläche einer Kugel. Sie ist nur verwendbar, wenn das Argument auch tatsächlich einen Kugelradius darstellt und deshalb als Extension für den Datentyp 'Double' ungeeignet.

    Die Routine Swap vertauscht den Inhalt von zwei Double-Variablen. Sie ist zwar allgemein verwendbar, aber der ''reguläre' Aufruf Swap(x, y) ist im Code klarer lesbar als der Extension-Aufruf x.Swap(y). Auch in solchen Fällen sollte die Compiler-Direktive nicht benutzt werden.

    Demo-Code

    ' Deklaration von zwei Double-Variablen 
    Dim a, b As Double
     
    ' Zuweisung eines IEEE-Sonderwerts
    a = Double.PositiveInfinity
     
    Try
      ' Dieser Versuch löst eine Ausnahme aus
      ' Kontroll-Extensions mögen keine Sonderwerte
      b = a.GetValue
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try
     
    ' ...die hier aber schon 
    b = a.GetUnSafe ' entspricht: b = a
     
    ' Zuweisung eines Double-Wertes
    a = 100012.7456
     
    ' Jetzt gibt es bei der Abfrage kein Problem
    b = a.GetValue
     
    ' Einen zulässigen Wertebereich setzen:
    SetRange(101, 200)
     
    Try
      ' Jetzt wird die Ausnahme ausgelöst, weil der
      ' Wert in 'a' nicht im Range-Bereich liegt
      b = a.GetValue
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try
     
    ' Die Range-Überwachung wird gelöscht ....
    ClearRange()
     
    ' ... dafür wird ein stiller Alarm eingerichtet
    SetThreshold(200)
     
    ' Jetzt löst diese Zuweisung keine Ausnahme aus.
    ' Die Schrankenüberschreitung wird im Modul notiert
    b = a.GetValue
     
    ' Verwendung der Extension in einem Ausdruck
    b = (a.GetValue ^ 2) / a.GetValue
     
    ' a und b sollten jetzt 'nahezu' gleich sein
    ' --> die Extension prüft das: 
    ' Stop wird nicht ausgelöst
    If Not b.NearBy(a) Then Stop
     
    ' Der Wert in 'a' geht als Single durch, 
    ' aber kann nicht als 'Long' gelten
    ' --> Die Bedingungen werden nicht ausgelöst
    If Not a.IsSingle Then Stop
    If a.IsLong Then Stop
     
    ' Diese Zuweisung auf 'b' löscht 
    ' den Wert in 'a'
    b = a.GetValue_NaN
     
    ' Der Wert in 'b' wird ganzahlig gerundet
    b.Round(0)
    ' Diese Bedingung wird wg. Runden nicht ausgelöst 
    If Not b.IsLong Then Stop
     
    Try
      ' Da läuft jetzt nichts mehr
      b = a.GetValue
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try
     
    ' Die Abfrage-Vorgänge überprüfen .....
    ' ======================================
    If GetException_Counter() > 0 Then
      MsgBox("Bei Wert-Abfragen sind " + _
        CStr(GetException_Counter()) + _
        " mal Ausnahmen ausgelöst worden")
    End If
     
    If GetThreshold_Counter() > 0 Then
      MsgBox("Die kritische Schranke ist " + _
        "insgesamt " + CStr(GetThreshold_Counter()) + _
        " mal überschritten worden !")
    End If
     
    MsgBox("Letzter Double-Zugriff per Extension: " + vbCrLf + GetTime_LastAccess())
     
    GetMinMax(a, b)
    MsgBox("Spannweite der per Extension " + "abgefragten Werte: " + vbCrLf + _
      CStr(a) + " bis " + CStr(b))

    Das Modul emDoubleEx

    Option Strict On
    Option Explicit On
    Option Infer Off
     
    ' Namespace, der die Direktive 'Extension' enthält 
    Imports System.Runtime.CompilerServices
    Namespace emDoubleEx
    ''' <summary>
    ''' Demo-Modul für 'Extension Methods' für die
    ''' kontrollierte Abfrage des Werts in einer 
    ''' Double-Variable
    ''' </summary>
    <Extension()> _
    Public Module modDoubleEx
     
      ' zulässige Wertespannweite
      ' für die Abfrage-Routinen (optional)
      ' (incl. Initialisierung)
      Private gMinValue As Double = Double.MinValue
      Private gMaxValue As Double = Double.MaxValue
      Private gLimits As Boolean = False
     
      ' kritische Schranke
      Private gThreshold As Double = Double.MaxValue
      Private gThreshold_Counter As Long = 0
     
      ' Register der Extreme der abgefragten Werte
      Private gMaximum As Double = Double.MinValue
      Private gMinimum As Double = Double.MaxValue
     
      ' Zeitpunkt der Letzten Abfrage
      Private gTime_LastAccess As String = ""
     
      ' Zugriffszähler
      Private gAccess_Counter As Long = 0
     
      Private gException_Counter As Long = 0
      '=================================================
      ' Extensions für die kontrollierte Abfrage
      '=================================================
      ''' <summary>
      ''' Die Abfrage löst eine Ausnahme aus, falls
      ''' ein IEEE-Sonderwert enthalten ist oder
      ''' ein (optionaler) Limit-Verstoß vorliegt
      ''' </summary>
      <Extension()> _
      Public Function GetValue(ByVal x As Double) As Double
        If Double.IsInfinity(x) Or Double.IsNaN(x) Then
          gException_Counter += 1
          Throw New NotFiniteNumberException
        End If
        If gLimits Then
          ' zulässigen Bereich prüfen
          If x > gMaxValue Or x < gMinValue Then
            gException_Counter += 1
            Throw New ArgumentOutOfRangeException
          End If
        End If
        ' Abfrage registrieren
        Adjust_Globals(x)
        ' abgefragten Wert zurückgeben
        Return x
      End Function
      ''' <summary>
      ''' Die Abfrage setzt Ausreißerwerte auf
      ''' die Grenzwerte (falls welche gesetzt)
      ''' </summary>
      <Extension()> _
      Public Function GetValue_SetToRange(ByVal x As Double) As Double
        If Not gLimits Then
          ' Keine Limits gesetzt
          gException_Counter += 1
          Throw New InvalidOperationException
        End If
        Dim ret As Double = x
        If ret > gMaxValue Then ret = gMaxValue
        If ret < gMinValue Then ret = gMinValue
        Adjust_Globals(ret)
        Return ret
      End Function
      ''' <summary>
      ''' Die Abfrage setzt den Variablen-Wert 
      ''' auf 'NaN' und löst eine Ausnahme aus, 
      ''' falls 'NaN' bereits enthalten ist
      ''' </summary>
      <Extension()> _
      Public Function GetValue_NaN(ByRef x As Double) As Double
        ' Wert abfragen
        Dim arg As Double = x.GetValue
        ' x nach der Abfrage auf NaN setzen
        x = Double.NaN
        Return arg
      End Function
      ''' <summary>
      ''' Läßt sich ein Double-Wert mit hoher Genauigkeit 
      ''' in einen Long-Wert transformieren? 
      ''' </summary>
      ''' <returns>true: Long ist möglich, 
      ''' sonst false</returns>
      <Extension()> _
      Public Function IsLong(ByVal x As Double) As Boolean
        Dim y As Double = x.GetValue
        If y > Long.MaxValue Then Return False
        If y < Long.MinValue Then Return False
        Return iNearBy(y, CLng(y))
      End Function
      ''' <summary>
      ''' Läßt sich ein Double-Wert 
      ''' in einen Single-Wert transformieren? 
      ''' (nur Range-Kontrolle)
      ''' </summary>
      ''' <returns>true: Single ist möglich, 
      ''' sonst false</returns>
      <Extension()> _
      Public Function IsSingle(ByVal x As Double) As Boolean
        Dim y As Double = x.GetValue
        If y > Single.MaxValue Then Return False
        If y < Single.MinValue Then Return False
        Return True
      End Function
      ''' <summary>
      ''' Der Wert wird kontrolliert gerundet auf vier 
      ''' Nachkommastellen zurückgegeben
      ''' (MidPointRounding.AwayFromZero)
      ''' </summary>
      <Extension()> _
      Public Function Round4(ByVal x As Double) As Double
        Dim y As Double = x.GetValue
        If Math.Abs(x) > 999999999 Then
          gException_Counter += 1
          Throw New ArgumentOutOfRangeException
        End If
        Return Math.Round(y, 4, _
        System.MidpointRounding.AwayFromZero)
      End Function
      ''' <summary>
      ''' Die Inhalt der Variable wird gerundet
      ''' (MidPointRounding.AwayFromZero)
      ''' </summary>
      ''' <param name="x"></param>
      ''' <param name="digits">Zahl der Nachkommastellen
      ''' (0-10)</param>
      <Extension()> _
      Public Sub Round(ByRef x As Double, _
        Optional ByVal digits As Integer = 2)
     
        If digits < 0 Or digits > 10 Then
          gException_Counter += 1
          Throw New ArgumentOutOfRangeException
        End If
        Dim y As Double = x.GetValue
        x = Math.Round(y, digits, _
        MidpointRounding.AwayFromZero)
      End Sub
      ''' <summary>
      ''' Abfrage eines Wertes
      ''' falls erforderlich: Rückgabe des Grenzwertes 
      ''' </summary>
      ''' <param name="x">abgefragte Variable</param>
      ''' <param name="ug">Untergrenze der Abfrage</param>
      ''' <param name="og">Obergrenze der Abfrage</param>
      ''' <returns>kontrolliert abgefragter Wert</returns>
      <Extension()> _
      Public Function GetIn(ByVal x As Double, _
        ByVal ug As Double, ByVal og As Double) As Double
     
        ' Diese Routine akzeptiert Infinity-Werte in 'x'
        ' und setzt sie wie gewöhnliche Ausreißer 
        ' auf ug bzw. og
     
        ' Prüfung der Grenzen (ohne Registrierung)
        If Double.IsNaN(ug) Or Double.IsNaN(og) Then
          Throw New NotFiniteNumberException
        End If
        If ug > og Then
          Throw New ArgumentException
        End If
     
        If Double.IsNaN(x) Then
          gException_Counter += 1
          Throw New NotFiniteNumberException
        End If
     
        Dim ret As Double = x
        If ret > og Then ret = og
        If ret < ug Then ret = ug
        Return ret
      End Function
      ''' <summary>
      ''' völlig unkontrolliertes Auswerfen des enthaltenen 
      ''' Wertes - leider aktueller VB-Standard
      ''' </summary>
      ''' <param name="x">abgefragte Variable</param>
      <Extension()> _
      Public Function GetUnSafe(ByVal x As Double) As Double
        Return x
      End Function
      ''' <summary>
      ''' Können zwei kontrolliert abgefragte 
      ''' Double-Werte als gleich gelten?
      ''' </summary>
      ''' <param name="x"></param>
      ''' <param name="y"></param>
      ''' <returns>true: Die Werte können als gleich gelten
      ''' </returns>
      <Extension()> _
      Public Function NearBy(ByVal x As Double, ByVal y As Double) As Boolean
        Dim xi As Double = x.GetValue
        Dim yi As Double = y.GetValue
        Return iNearBy(xi, yi)
      End Function
     
      Private Function iNearBy(ByVal x As Double, ByVal y As Double) As Boolean
        ' Interner Vergleich bereits aus der 
        ' rufenden Variable abgefragter Werte
        Dim eps As Double = 0.0000000001
        Dim mx As Double = Math.Max(Math.Abs(x), Math.Abs(y))
        ' geeignetes Epsilon ermitteln
        If mx > 1 Then eps *= mx
        Return Math.Abs(x - y) < eps
      End Function
      '==========================================================
      ' Beispiele für ungeeignete Extensions
      '==========================================================
      ''' <summary>
      ''' Kugel-Oberfläche für einen Radius 
      ''' </summary>
      ''' <param name="radius"></param>
      ''' <returns></returns>
      ''' <remarks></remarks>
      Public Function Sphere(ByVal radius As Double) As Double
        If radius < 0 Then
          Throw New ArgumentException
        End If
        Return Math.PI * 4 * radius * radius
      End Function
      ''' <summary>
      ''' Austausch des Inhalts von x und y
      ''' </summary>
      <Extension()> _
      Public Sub Swap(ByRef x As Double, ByRef y As Double)
        ' Argumente zunächst checken
        x.GetValue() : y.GetValue()
        ' Inhalt austauschen
        Dim z As Double = x
        x = y : y = z
      End Sub
      '=========================================================
      'Routinen zur Verwaltung der modulglobalen Variablen
      '=========================================================
      ''' <summary>
      ''' Routine setzt die Werte-Spannweite
      ''' für die Funktion 'GetValue_Range'
      ''' </summary>
      ''' <param name="MinValue"></param>
      ''' <param name="MaxValue"></param>
      Public Sub SetRange(ByVal MinValue As Double, ByVal MaxValue As Double)
        If MinValue > MaxValue Then
          Throw New ArgumentException
        End If
     
        gMinValue = MinValue
        gMaxValue = MaxValue
        gLimits = True
      End Sub
      ''' <summary>
      ''' Ein gesetzter Bereich zulässiger Werte
      ''' wird gelöscht
      ''' </summary>
      Public Sub ClearRange()
        gLimits = False
        gMinValue = Double.MinValue
        gMaxValue = Double.MaxValue
      End Sub
      ''' <summary>
      ''' Zeitpunkt der letzten Abfrage einer
      ''' Double-Variable über die Extensions
      ''' </summary>
      Public Function GetTime_LastAccess() As String
        Return gTime_LastAccess
      End Function
      ''' <summary>
      ''' Zahl der Abfragen von Double-Variablen
      ''' über die Extensions
      ''' </summary>
      Public Function GetAccess_Counter() As Long
        Return gAccess_Counter
      End Function
      ''' <summary>
      ''' Reinitialisierung des Zugriffs-Zählers
      ''' </summary>
      Public Sub Reset_AccessCounter()
        gAccess_Counter = 0
      End Sub
      ''' <summary>
      ''' Größter und kleinster der bisher abgefragten
      ''' Double-Werte
      ''' </summary>
      ''' <param name="Minimum">Kleinster Wert</param>
      ''' <param name="Maximum">Größter Wert</param>
      Public Sub GetMinMax(Optional ByRef Minimum As Double = 0, _
        Optional ByRef Maximum As Double = 0)
     
        If gMinimum > gMaximum Then
          ' falls noch keine Werte abgefragt 
          ' worden sind
          Throw New InvalidOperationException
        End If
        Minimum = gMinimum
        Maximum = gMaximum
      End Sub
      ''' <summary>
      ''' Zurücksetzen des Ausprägungsregisters
      ''' für die Extreme aller abgefragten Werte
      ''' </summary>
      Public Sub ResetMinMax()
        gMinimum = Double.MaxValue
        gMaximum = Double.MinValue
      End Sub
      ''' <summary>
      ''' Kritische Schranke für die Ausprägung 
      ''' der abgefragten Werte
      ''' </summary>
      ''' <param name="Threshold">Kritische Schranke</param>
      ''' <remarks></remarks>
      Public Sub SetThreshold(ByVal Threshold As Double)
        gThreshold = Threshold
        gThreshold_Counter = 0
      End Sub
      ''' <summary>
      ''' Zahl der Abfragen von Double-Variablen
      ''' über die Extensions, bei denen die 
      ''' kritische Schranke überschritten war
      ''' </summary>
      Public Function GetThreshold_Counter() As Long
        Return gThreshold_Counter
      End Function
      ''' <summary>
      ''' Zahl der Abfragen von Double-Variablen
      ''' über die Extensions, bei denen eine 
      ''' Ausnahme ausgelöst werden mußte
      ''' </summary>
      Public Function GetException_Counter() As Long
        Return gException_Counter
      End Function
      Private Sub Adjust_Globals(ByVal arg As Double)
        ' Anpassung der modul-globalen
        ' Registriervariablen auf die aktuelle Abfrage 
        gTime_LastAccess = Now.ToString
        gAccess_Counter += 1
        If gMaximum < arg Then gMaximum = arg
        If gMinimum > arg Then gMinimum = arg
        If arg > gThreshold Then
          gThreshold_Counter += 1
        End If
      End Sub
    End Module
    End Namespace

    Dieser Workshop wurde bereits 7.424 mal aufgerufen.

    Über diesen Workshop im Forum diskutieren
    Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

    Neue Diskussion eröffnen

    nach obenzurück


    Anzeige

    Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
    (einschl. Beispielprojekt!)

    Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
    - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
    - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
    Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
     
       

    Druckansicht Druckansicht Copyright ©2000-2017 vb@rchiv Dieter Otter
    Alle Rechte vorbehalten.
    Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

    Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel