Category: Visual Basic

Benutzerdefinierte Collections

By , 1. März 2014

Collections, auf Deutsch Auflistungen, begegnen uns beim Entwickeln in VBA eigentlich ständig, sei es die Auflistung aller Formulare (Forms), oder aller Steuerelemente in einem Formular (Controls). Neben diesen bereits vorhandenen Collections, ist es möglich auch eigene Collections zu erstellen. Dies geschieht mit der simplen Anweisung:
Dim CollectionName As New Collection.

Im Unterschied zu einem Array kann eine benutzerdefinierte Collection Werte und Objekte jeglichen Typs aufnehmen, eine Collection ist also nicht Typ gebunden. Ein weiterer, nicht zu unterschätzender Vorteil ist dieser, dass im Gegensatz zu einem Array – wo die Items nur durch ihre Ordnungszahl definiert sind – bei einer Collection als optionalen Parameter einen eindeutigen Bezeichner angegeben werden kann, über den auf das entsprechende Item zugegriffen werden kann.

Eine Benutzerdefinierte Collection verfügt über die Methoden „ADD“, „REMOVE“, „COUNT“ und „ITEM“, d.h. es gibt keine Edit-Methode. Will man ein Item ändern muss man es aus der Collection entfernen und geändert neu einfügen. Eine weitere Besonderheit benutzerdefinierter Collections ist, dass sie immer 1-Basiert sind, also das erste Element in einer Collection hat die Ordnungszahl 1, und nicht 0 wie bei einem Array.

Ein Beispiel:

Sub CollectionTest()
    Dim cBuch As New Collection
    Dim Ausgabe As String
    
    With cBuch
        .Add "Ken Getz", "Autor"
        .Add "VBA Developers Handbook", "Titel"
        .Add 922, "Seiten"
    End With
    
    Ausgabe = cBuch("Autor") & vbCrLf
    Ausgabe = Ausgabe & cBuch(2) & vbCrLf
    Ausgabe = Ausgabe & cBuch("Seiten") & " Seiten"
    MsgBox Ausgabe
End Sub

Wie man an diesem kleinen Beispiel sieht ist es egal ob ich cBuch(2) oder cBuch(„Titel“) schreibe. Der große Vorteil des Bezeichners ist, dass ich mir keine Gedanken machen muss an welcher Stelle ein Item steht, mit dem Bezeichner wird immer der richtige Wert verwendet.

Bis dahin
© 2014 Andreas Vogt

Arbeiten mit Interface-Klassen

By , 28. Februar 2014

Dass man mit Access auch objektorientiert entwickeln kann – wenn auch nicht völlig – dürfte hinlänglich bekannt sein. Klassenmodul erstellen, Methoden und Eigenschaften in als Prozeduren und Properties definieren, alles längst bekannt. Weniger bekannt aber ist die Verwendung von Interfaces bzw. Interface-Klassen.

Man stelle sich vor, man hat eine Klasse Auto, eine Klasse Motorrad und eine Klasse Fahrrad. 3 verschiedene Klassen, und doch werden die Methoden und Eigenschaften dieser Klassen in vielen Punkten identisch sein. Die Objekte aller 3 Klassen haben sicherlich die gemeinsamen Methoden „Fahren“, „Bremsen“ und „Lenken“. Außerdem die gemeinsamen Eigenschaften „Geschwindigkeit“, „Richtung“ und „Farbe“, um ein paar Beispiele zu nennen.

Diese Zusammenhänge lassen sich durch ein Interface abbilden, und das geschieht einfacher als man denkt. Ein Interface ist zuerst einmal nichts anderes als ein Klassenmodul, das man speziell benennt. Eingebürgert hat sich, dass man vor den Interface-Namen ein „i“ setzt. Wir erstellen also ein Klassenmodul, und benennen es „iFahrzeug“. Um jetzt die Struktur für Fahrzeuge in diesem Interface abzubilden erstellen wir darin alle Properties und Prozeduren wie oben benannt, aber ohne weiteren Code. Unser Interface sieht jetzt wie folgt aus:

Option Explicit

Property Get Geschwindigkeit() As Long
End Property
Property Let Geschwindigkeit(ByVal lSpeed As Long)
End Property

Property Get Richtung() As String
End Property
Property Let Richtung(ByVal cRichtung As String)
End Property

Property Get Farbe() As Long
End Property
Property Let Farbe(ByVal lColor As Long)
End Property

Sub Fahren()
End Sub

Sub Bremsen()
End Sub

Sub Lenken()
End Sub

Read more »

Array definieren mal anderst

By , 27. Februar 2014

Arrays werden entweder per Array() Anweisung definiert, oder auch durch den Split() Befehl.
Auf eine andere bzw. erweiterte Möglichkeit bin ich kürzlich gestoßen beim Entwickeln eines Benutzersteuerelementes mit VB6.
Dort ging es u.a. darum, Steuerelemente beim Klick auf einen Button zu plazieren und sichtbar zu machen.

Die Größe und Position der Steuerelemente habe ich dabei wie folgt definiert:

Private Sub Form_Open(Cancel As Integer)
    Dim sizeArr(8) As Variant
    Dim i As Long
    sizeArr(0) = Array("cmdClose", 2350, 23, 306, 975)
    sizeArr(1) = Array("Linie1", 47, 2412, 2066, 2066)
    sizeArr(2) = Array("Linie2", 47, 2412, 697, 697)
    sizeArr(3) = Array("lblMonat", 23, 47, 454, 2381)
    sizeArr(4) = Array("zurueck", 142, 120, 227, 227)
    sizeArr(5) = Array("vor", 142, 2160, 227, 227)
    sizeArr(6) = Array("MonatJahr", 120, 480, 255, 1575)
    sizeArr(7) = Array("Heute", 2066, 47, 255, 2381)
    sizeArr(8) = Array("Rechteck1", 23, 47, 2280, 2381)

Das bedeutet also, jedem einzelnen Array-Element wurde ein weiteres Array zugewiesen.
Der Zugriff auf einen bestimmten Wert, z.B. auf „lblMonat“ lautet dann: sizeArr(3)(0)

Und um wieder zum Beispiel aus der Praxis zurückzukommen, hier der restliche Code, der die Steuerelemente dimensioniert, positioniert und sichtbar schaltet:

    For i = LBound(sizeArr) To UBound(sizeArr)
        With Controls(sizeArr(i)(0))
            .Top = sizeArr(i)(1)
            .Left = sizeArr(i)(2)
            .Height = sizeArr(i)(3)
            .Width = sizeArr(i)(4)
            .Visible = True
        End With
    Next i
End Sub

Bis dahin
© 2014 Andreas Vogt

Clouden Sie noch oder wie wichtig sind Ihnen Ihre Daten?

By , 15. Februar 2014

Die Zeit der Goldgräberstimmung in der Cloud Server Branche außerhalb der EU ist längst vorbei, zu tief sind doch die emotionalen Gräben die uns die NSA beschert hat. Für mich war es immer schon klar dass in die Cloud keine betriebswichtigen Daten gehören. Meine Denkweise war aber eher die dass in einem Rechtsstreit ServerHardware und entsprechende auch die Daten beschlagnamt werden könnten – dass aber die NSA die Cloud direkt anzapfte, Hersteller von Internetdiensten dazu verpflichten Hintertürchen einzubauen etc. etc. – diese Dimension haben sich wohl die wenigsten ausgemalt, ich auch nicht.

Die ganze Spionagetätigkeit läuft unter dem Aspekt der Terrorabwehr, und verschleiert vollkommen, dass es auch um handfeste Wirtschaftsspionage geht.

Hintertuerchen Gut, dann eben wieder lokale Lösungen mit eigenem und teuren Rechenzentrum. Das wäre auch bislang eine sichere Bank gewesen, bis zu dem entsprechenden Spiegel-Beitrag vor etwa 6 Wochen.
Die NSA hat ganze Lieferungen von PC Hardware abgefangen, aufgeschraubt und Spionage-Tools verbaut als Hintertürchen, um so an Daten und Kontrolle über den PC zu kommen. Das ganze klingt nach einem übertriebenen Science-Fiction Roman – doch die Wirklichkeit hat diese Lektüre bereits überholt.

Viele „flüchten“ jetzt zu Cloud-Anbietern innerhalb Europa vorallem auch innerhalb Deutschland. Was bleibt ist die Frage ob auch hier es so weit kommt bzw. ob es nicht längst auch so weit ist.
Das digitale Wettrüsten hat längst begonnen und ist im vollen Gange.

Also schauen Sie ruhig mal nach Ihrem PC unter dem Schreibtisch, nicht dass dort ein Hintertürchen aufsteht.

Meint der Autor.

Quelle: http://www.spiegel.de/netzwelt/netzpolitik/…
© 2014 Andreas Vogt

Abfrage oder Tabelle ‚Zeichengetrennt‘ in Textdatei speichern

By , 13. Februar 2014

Die landläufige Methode eine Abfrage oder eine Tabelle als Txt-Datei zu speichern ist die Docmd.OutputTo() Methode.
Informationen dazu findet ihr hier: http://msdn.microsoft.com/en-us/library/office/…

Um aber Zeichengetrennte Textdateien zu erstellen, d.h. jede Zeile ein Datensatz, jedes Feld in der Zeile durch einen Separator getrennt, muss man die Methode Docmd.TransferText() incl. Exportspezifikation erstellen. http://msdn.microsoft.com/en-us/library/office/… Das wäre sicherlich der einfachste Weg.

Falls aber die Exportspezifikation aber nicht mehr genügt, kann man mittels Output-Stream eine Textdatei zu erstellen, eine Abfrage oder Tabelle in ein Recordset packen und zeilenweise durchlaufen und in die Textdatei zu schreiben. Und mit den Worten von Jean Pütz sag ich: „ich hab da mal was vorbereitet“:

Sub Table2Textfile(ByVal Outputfile As String, ByVal TableName As String, _
                   Optional Separator As String = ";", _
                   Optional titlebar As Boolean = False)
    
    Dim rs As DAO.Recordset
    Dim strTableRow As String
    Dim filenum As Long
    Dim i As Long
    filenum = FreeFile

    Set rs = CurrentDb.OpenRecordset(TableName)
    Open Outputfile For Output As #filenum

    'Spaltenköpfe
    If titlebar Then
        For i = 0 To rs.Fields.Count - 1
            strTableRow = strTableRow & rs(i).Name & Separator
        Next i
        strTableRow = Left(strTableRow, Len(strTableRow) - Len(Separator))
        Write #filenum, strTableRow
        strTableRow = vbNullString
    End If

    Do While Not rs.EOF
        For i = 0 To rs.Fields.Count - 1
            strTableRow = strTableRow & rs(i) & Separator
        Next i
        strTableRow = Left(strTableRow, Len(strTableRow) - Len(Separator))
        Write #filenum, strTableRow
        strTableRow = vbNullString
        rs.MoveNext
    Loop
    Close #filenum
    rs.Close
    Set rs = Nothing
End Sub

Dies ist eine universelle Prozedur mit dem man Abfragen bzw. Tabellen schnell in eine separierte Liste bringen kann.
In einer Schleife über alle Recordset-Felder wird der String zusammengestellt, welcher in die Textdatei gespeichert wird.
Ebenso die Spaltenköpfe welche mit rs(Zeilennummer).Name auszulesen sind.

Die Prozedur welche obige Prozedur aufruft sieht dann z.B. so aus:

Sub Tester()
    Table2Textfile "c:\users\Public\test4711.txt", "qryTeilnehmer", ";", True
End Sub

Anstatt eines Separators als String kann man natürlich auch VBA-Konstanten verwenden, z.B. vbTab bietet sich hier an, Verwendung ohne Anfürungszeichen!

Ein Wermutstropfen bleibt aber übrig. In der erstellten Textdatei sind die einzelnen Textzeilen in Anführungszeichen. D.h. vor dem Weiterverarbeiten müsste man eventuell mittels FileSystemObject ran gehen und die einzelnen Zeilen bearbeiten – oder falls per VBA die Weiterverarbeitung stattfindet kann man die Bereinigung auch im VBA-Code machen.

Bis dahin
© 2014 Andreas Vogt

Statistikfunktionen in Abfragen verwenden

By , 18. Januar 2014

Zur statistischen Auswertung von Datenreihen können in Abfragen Funktionen eingesetzt werden wie z.B. AVG() für den aritmetischen Mittelwert oder StDev() für die Standardabweichung.
Eine nicht ganz triviale Aufgabe wäre es z.B., wenn man das gespeicherte Bestelldatum von Bestellungen in einer Tabelle hat, und jetzt den durchschnittlichen Bestellabstand in Tagen ermitteln möchte.

Manuell ist das leicht zu lösen indem man die Abstände zur nächsten Bestellung in Tagen aufsummiert und durch die Anzahl Bestellungen -1 dividiert. Beispiel:

  1. Bestellung: 12.10.2013
  2. Bestellung: 18.10.2013
  3. Bestellung: 23.10.2013
  4. Bestellung: 02.11.2013
  5. Bestellung: 14.11.2013

Die Bestellabstände sind: 6 Tage, 5 Tage, 10 Tage und 12 Tage.
In Summe 33 Tage geteilt durch 5-1 ergibt einen durchschnittlichen Bestellabstand von 8,25 Tagen.

Möchte man das nun in einer Abfrage berechnen lassen, brauchen wir also immer das Datum vom aktuellen Daten und das Datum vom folgenden Datensatz. Daher müssen wir die Bestell-Tabelle 2x in die Abfrage einbinden einmal mit Alias T1 und einmal mit Alias T2, und der Bedingung dass das Bestelldatum von T2 kleiner dem Bestelldatum von T1 sein muss. Fangen wir also erst mal damit an die Bestellabstände aufzulisten:
Read more »

Steuerung von Fußschalter oder Joystick

By , 16. Januar 2014

Um Ereignisse zu erfassen die von einem Fußschalter oder einem Joystick ausgelöst werden, kann man sich der Methoden von DirectX bedienen. Im vorliegenden Beispiel wird die Version DirectX8 verwendet, d.H. Sie benötigen einen gültigen Verweis auf die „DirectX 8 für Visual Basic Type Library“. Dazu benötigen Sie die registrierte Datei dx8vb.dll auf Ihrem Computer.
Nach dem nun der Verweis gesetzt wurde kann mit der Deklaration der Objekte begonnen werden. Dazu verwende ich die Early-Binding Variante um die DirectX-Konstanten verwenden zu können:
Im Deklarationsteil des Formulars:

Private Const BufferSize As Long = 10
Private DX As DirectX8
Private DI As DirectInput8
Private DIEnum As DirectInputEnumDevices8
Private DIDevice As DirectInputDevice8
Private hEvent As Long
Implements DirectXEvent8

Im Load-Ereignis werden die Objekte dann Instanziert:

    Set DX = New DirectX8
    Set DI = DX.DirectInputCreate
    Set DIEnum = DI.GetDIDevices(DI8DEVCLASS_GAMECTRL, DIEDFL_ATTACHEDONLY)
    ' kein Gerät gefunden dann Abbruch
    If DIEnum.GetCount = 0 Then Exit Sub

Die Verwendeten Konstanten beim Zugriff auf die Geräte können hier nachgelesen werden:
DirectInput8::EnumDevices
Read more »

Umrechnung Minuten/Sekunden in Zeitangabe

By , 24. Mai 2012

Konvertierungsfunktionen wie Minuten oder Sekunden in Zeitangabe zu konvertieren. sollte an und für sich kein Problem sein. Wenn die Rechenarithmetik von Access uns keinen Strich durch die Rechnung machen würde. Angenommen man hat 400 Minuten und teilt diese durch 60 und weisst das Ergebnis einer Long-Variablen zu, um die Stunden zu erhalten: 400/60 = 6.66666667. Dieses einem Long-Wert zugeordnet ergibt aber 7!
Ein Trick ist die Teilung mit einem Backslash, also 400\60. Diese Division gibt immer die Zahl vor dem Komma zurück, Nachkommastellen werden einfach abgeschnitten.

Fangen wir mal mit den Funktionen an. Diese am besten in einem Modul plazieren:

Public Function ConvertMintoTime(varM As Long)
    Dim hours As Long
    Dim minutes As Long

    hours = varM \ 60
    minutes = varM - hours * 60
    
    ConvertMintoTime = Format(hours, "00") & Format(minutes, "\:00") & ":00"
End Function

Public Function ConvertSectoTime(varS As Long)
    Dim hours As Long
    Dim minutes As Long
    Dim seconds As Long

    hours = varS \ 3600
    minutes = (varS - hours * 3600) \ 60
    seconds = varS - hours * 3600 - minutes * 60
    
    ConvertSectoTime = Format(hours, "00") & Format(minutes, "\:00") & Format(seconds, "\:00")
End Function

In der ersten Funktion werden die Minuten durch 60 geteilt um die Stunden zu erhalten. Im zweiten Schritt wird die Differenz zwischen den Minuten und den Stunden x 60 ermittelt, um die Minuten zuerhalten, was letztendlich nichts anderes als der Rest der ersten Division ist.

In der zweiten Funktion haben wir Sekunden, daher teilen wir durch 3600 für die Stunden. der Rest dieser Division wird dann durch 60 geteilt für die Minuten, und der letzte Rest bildet dann die Sekunden ab.
Per Format() Funktion wird die Ausgabe der Funktion aufbereitet.

Der Aufruf kann z.B. aus einem Formular oder anderen Modul erfolgen, da die Funktionen den Gültigkeitsbereich Public besitzen. Dieser könnte z.B. so aussehen:

Sub test()
    Dim t As Long
    t = 4000

    MsgBox t & " Sekunden sind Umgerechnet: " & ConvertSectoTime(t)
    MsgBox t & " Minuten sind Umgerechnet: " & ConvertMintoTime(t)
End Sub

Das wars schon, also eigentlich bis auf den Fallstrick mit dem Teil-Strich nichts besonderes.

Bis dahin
Andreas Vogt © 2012

BCD-Code erleichtert die Auswahl von Entitäten

By , 15. April 2012

BCD-Code ist vielen sicherlich noch von früher aus dem Informatikuntericht bekannt. Ich kam damit das erste mal während meiner Ausbildung duch BCD-Codierschalter in Berührung. Beim BCD-Code wird jede Ziffer einer Dezimalzahl einzeln dualkodiert. BCD-Code wird auch 8-4-2-1 Code genannt, weil die zweierpotenzen diese Ziffern ergeben. Damit lässt sich jede Ganzzahl darstellen.

Beispiel: 14: 8+4+2 oder 22: 16+4+2 usw.

Wie nützt uns jetzt dies bei Access? Der Aufbau des Codes hat auch einen zusätzlichen Effekt. Und zwar ist jede Zahl im Code (1, 2, 4, 8, 16….) größer als die Summe aller darunter liegender Zahlen, und zwar immer genau um 1 größer. Beispiel: 64 > 32+16+8+4+2+1.

Wenn man jetzt einen Enum Definiert dann nicht mehr mit 1, 2, 3…. sondern mit 1, 2, 4…

Ich glaub ich mach jetzt am besten erst mal ein Beispiel:

Option Explicit

Private Enum eAuswahl
    vbArbeiter = 1
    vbAngestellte = 2
    vbTechniker = 4
    vbführungskräfte = 8
    vbBerater = 16
    vbAlle = 32
End Enum

Read more »

Kodieren und Dekodieren von Texten

By , 8. April 2012

Kürzlich bin ich auf eine relativ alte Methode wieder gestoßen um Texte zu kodieren. Dabei verschiebt man einfach die Buchstaben im Wort um eine definierte Anzahl stellen. Ein bekannter Vertreter dieser Art ist Rot13. (Rotate by 13). Solche Kodierer haben den Vorteil dass sie erstens wieder dekodiert werden können – was gleichzeitig aber auch ihr Nachteil in Bezug auf Sicherheit ist – und zweitens dass es bekannte Algorithmen in verschiedenen Programmiersprachen gibt. Teilweise findet man auch im internet Online-Tools die Texte kodieren bzw. dekodieren können.
Dies sollte man wissen wenn man Text-Daten in einer Tabelle verschlüsseln möchte. Jeder der die Kodierungsmethode herausfindet kann es auch wieder lesbar machen.

Um hier ein wenig Sicherheit hineinzubringen sollte man den Kodier-Algorithmus nach eigenen Vorgaben anpassen. Über Ostern hatte ich wohl ein bisschen Zeit, und hab da mal etwas gebastel.
Meine Idee war kein fester Verschiebefaktor vorzugeben, sondern in Ahhängigkeit der Stelle eines Buchstabens im Wort vergrößerten Faktur. Also der erste Buchstabe um z.B. 15 verschieben, den zweiten um 15 usw., und beim nächsten Wort wieder bei 15 anfangen. Ich nenne diesen Algorithmus mal Rot15plus.
Read more »

OfficeFolders theme by Themocracy