Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Achtung: Dies ist nicht der vollständige Artikel, sondern nur ein paar Seiten davon. Wenn Sie hier nicht erfahren, was Sie wissen möchten, finden Sie am Ende Informationen darüber, wie Sie den ganzen Artikel lesen können.

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 3/2002.

Diesen Artikel jetzt als PDF plus Beispieldatenbank herunterladen?

Wenn Sie sich jetzt für den Newsletter anmelden, erhalten Sie in Kürze eine E-Mail mit dem Artikel im PDF-Format plus Beispieldatenbank.

Hier anmelden:973525

E-Mail:

Anrede:

Vorname:

Nachname:

Tipps und Tricks zur API

Autor: Christoph Spielmann, Düsseldorf

Bei der Windows-API (Application Programmers Interface) handelt es sich um eine mächtige Bibliothek von Funktionen, die Ihnen den Zugang zu Windows-internen Ressourcen ermöglichen. Unser Autor Christoph Spielmann hat für den vorliegenden Beitrag in seinen umfangreichen Erfahrungsschatz gegriffen, um Ihnen anhand einiger Beispiele die Möglichkeiten der API zu eröffnen.

Temporäre Dateien

Temporäre Dateien werden von Anwendungen vorübergehend angelegt, um verschiedene Daten, Zustände oder gerade in Bearbeitung befindliche Dokumente zwischenzuspeichern.

Es ist zwar kein Muss, aber als allgemeine
Übereinkunft gilt, einen im System als speziell für diesen Zweck deklarierten Ordner zur Ablage von temporären Dateien zu verwenden - den TEMP-Ordner.

Windows stellt dazu sogar eine eigene API-Funktion bereit, über die Sie den Pfad dieses Ordners ermitteln können (Gesucht - Systemordner). Und wie der Name es sagt: Temporäre Dateien sind ihrer Natur nach vergänglich - Ihre Anwendungen sollten daher auch dafür sorgen, dass sie von Ihnen angelegte temporäre Dateien spätestens beim Beenden wieder löschen.

Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA" (ByVal _
    pszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, _

    ByVal lpTempFileName As String) As Long

Quellcode 1

Wenn Sie für Ihr Programm eine temporäre Datei erzeugen wollen, brauchen Sie einen eindeutigen Dateinamen. Denn schließlich legen ja auch andere Anwendungen im TEMP-Ordner ihre Dateien ab, sodass es leicht zu Namenskonflikten kommen könnte.

Sie könnten natürlich einfach einen beliebigen Dateinamen wählen und für den Fall, dass eine Datei dieses Namens bereits existieren sollte, in einer Schleife einen neuen Dateinamen kreieren, bis Sie einen freien gefunden haben.

Doch besser ist wie immer der Weg, die speziell dafür bereitgestellte API-Funktion GetTempFileName (s. Quellcode 1) zu verwenden. Der Funktion werden vier Parameter übergeben.

Im ersten Parameter, lpszPath übergeben Sie den Pfad, in dem der temporäre Dateiname angelegt werden soll, vorzugsweise den des TEMP-Ordners.

In lpPrefixString übergeben Sie einen aus bis zu drei Zeichen bestehenden Präfix, den Sie dem Dateinamen voranstellen möchten. Damit können Sie gewissermaßen eine Anwendung individuell hervorheben. Sie können natürlich auch ganz normal ein "tmp" übergeben.

Private Declare Function GetTempFileName _
    Lib "kernel32" Alias "GetTempFileNameA" _
    (ByVal lpszPath As String, ByVal lpPrefixString As _
    String, ByVal wUnique As Long, _
    ByVal lpTempFileName As String) As Long 

Private Declare Function GetTempPath Lib "kernel32" _
    Alias "GetTempPathA" (ByVal BufferLength As Long, _
    ByVal lpBuffer As String) As Long

Private Const kLength = 255

Public Function CreateTempFile(Optional Prefix As _
    String = "tmp", Optional TempPath As String) _
    As String

    Dim nBuffer, nTempPath As String

    Dim nReturn, nPosEnd As Long

    If Len(TempPath) = 0 Then

        nBuffer = Space(kLength)

        nReturn = GetTempPath(kLength, nBuffer)

        If nReturn > 0 Then

            nTempPath = Left(nBuffer, nReturn)

        Else

            nTempPath = CurDir$

            If Right$(nTempPath, 1) <> "\" Then

                nTempPath = nTempPath & "\"

            End If

        End If

    Else

        nTempPath = TempPath

    End If

    nBuffer = Space(kLength)

    nReturn = GetTempFileName(nTempPath, Left$(Prefix, _
        3), 0&, nBuffer)

    If nReturn <> 0 Then

        nPosEnd = InStr(nBuffer, Chr$(0))

        If nPosEnd = 0 Then

            CreateTempFile = nBuffer

        Else

            CreateTempFile = Left(nBuffer, nPosEnd - 1)

        End If

    End If

End Function

Quellcode 2

Um den Parameter wUnique brauchen Sie sich nicht unbedingt zu kümmern. Hier könnten Sie Ihrer temporären Datei eine von Ihrer Anwendung selbst generierte und verwaltete Nummer geben. Übergeben Sie 0, erzeugt Windows selbst eine eindeutige Zahl anhand der Systemzeit.

Im Puffer lpTempFileName liefert Ihnen die Funktion den erzeugten Dateinamen zurück.

Die Funktion CreateTempFile (s. Quellcode 3) verpackt den Aufruf auf VBA-gerechte und einfach zu handhabende Weise.

Beide Parameter sind optional. Verzichten Sie auf den individuellen Präfix, wird "tmp" als Präfix verwendet. Lassen Sie die Pfadangabe weg, wird die Datei automatisch im
TEMP-Ordner des Systems angelegt. Dazu wird hier die API-Funktion GetTempPath (s. Quellcode 2) direkt aufgerufen (Gesucht Systemordner). Konnte aus irgendeinem Grund keine temporäre Datei angelegt werden, wird ein leerer String zurückgegeben.

Beachten Sie, dass der Aufruf der API-Funktion GetTempFileName (s. Quellcode 2) auf jeden Fall eine Datei anlegt, die Sie nach Gebrauch oder Nichtgebrauch wieder ordnungsgemäß löschen sollten. Dieses Funktionalität der API-Funktion ist sinnvoll, da nach der Ermittlung eines unverwendeten Dateinamens dieser unter Umständen doch schon nicht mehr frei sein könnte, wenn Sie selbst nur Sekunden später eine Datei dieses Namens anzulegen versuchten.

Function CreatePathDeep(NewPath As String) As Boolean

    Dim nNewPath As String

    Dim nStart As Integer

    nNewPath = Trim$(NewPath)

    If Left$(nNewPath, 2) = "\\" Then

        If Len(nNewPath) > 6 Then

            nStart = 7

        Else

            Exit Function

        End If

    ElseIf Mid$(nNewPath, 2, 2) = ":\" Then

        nStart = 4

    Else

        Exit Function

    End If

    If nStart Then

        Do

            nPos = Instr(nStart, nNewPath, "\")

            If nPos Then

                On Error Resume Next

                MkDir Left$(nNewPath, nPos - 1)

                On Error GoTo 0

            Else

                On Error Resume Next

                MkDir nNewPath

                If Err.Number = 0 Then

                    CreatePathDeep = True ' Erfolg

                End If

                On Error GoTo 0

            End If

        Loop While nPos

    End If

End Function

Quellcode 3

Der Pfad ins Ungewisse

Ein neuer Ordner in einem tief verschachtelten Pfad kann mit der VBA-Funktion MkDir nur dann angelegt werden, wenn der Pfad selbst bereits vorhanden ist. Anderenfalls ist es in VBA eine recht mühselige Angelegenheit.

Wollten Sie beispielsweise den Pfad c:\abc\def\ghi anlegen, wenn selbst c:\abc noch nicht existiert, sähe das etwa so aus:

On Error Resume Next

MkDir "c:\abc"

MkDir "c:\abc\def"

MkDir "c:\abc\def\ghi"

On Error GoTo 0

Direkt an Ort und Stelle im Code platziert ist das schon recht aufwändig. Es würde allerdings noch aufwendiger, wenn Sie diese Codezeilen als allgemein verwendbare Funktion implementieren wollten (s. Quellcode 3).

Für Windows-Versionen vor Windows 98 werden Sie nicht darum herum kommen, eine solche oder ähnliche Funktion auf diese Weise zu implementieren. Ab Windows 98 und unter Windows NT können Sie jedoch eine API-Funktion aus einer der System-DLLs (imagehlp.dll) verwenden.

Die Funktion MakeSureDirectoryPathExists (s. Quellcode 4) hält, was ihr Name verspricht. Sie stellt sicher, dass der als Parameter übergebene Pfad nach ihrem Aufruf tatsächlich existiert - selbst wenn sie ihn erst anlegen muss. Das heißt - sie versucht zumindest, das Versprechen zu halten.

Natürlich kann auch sie nichts gegen nicht bereite Laufwerke oder Netzwerkpfade ausrichten. Und ohne die entsprechenden Berechtigungen wird auch sie keine Pfade anlegen können. Den Erfolg meldet sie mit dem Rückgabewert 1 - bei Misserfolg ist dieser 0.

Private Declare Function MakeSureDirectoryPathExists _

    Lib "imagehlp.dll" (ByVal lpPath As String) As Long

Quellcode 5

Public Function CreatePath(NewPath As String) As Boolean

    Dim nNewPath As String

        nNewPath = Trim$(NewPath)

    If Right$(nNewPath, 1) <> "\" Then

        nNewPath = nNewPath & "\"

    Else

        nNewPath = nNewPath

    End If

    CreatePath = _
        CBool(MakeSureDirectoryPathExists(nNewPath))

End Function

Quellcode 6

Sie erwartet allerdings einen abschließenden "\" bei der Übergabe des anzulegenden Pfades. Anderenfalls nimmt die Funktion an, dass der letzte Teil ein Dateiname sein könnte und legt diesen Teil daher nicht als Pfadbestandteil an.

Und so rufen Sie diese Funktion beispielsweise auf und prüfen den Erfolg:

Private Declare Function _
  MakeSureDirectoryPathExists _

  Lib "imagehlp.dll" (ByVal lpPath As _
  String) As Long

  ' ...

  If Not (Cbool _
    (MakeSureDirectoryPathExists _
    ("c:\abc\def\ghi\") Then

      MsgBox "Pfad konnte nicht " _
      & "angelegt werden."

  End If

Wenn Sie die Funktion benötigen, um beispielsweise eine Datei, deren Zielpfad und Name in einem String zusammen vorliegen, in genau diesen Zielpfad zu kopieren, können Sie sich die Sicherheitsmaßnahmen dieser Funktion zunutze machen: Sie können den Pfadstring (eben einschließlich des Dateinamens) an sie übergeben, ohne ihn zuvor erst in Pfad und Dateinamen zerlegen zu müssen.

Wenn Sie sich jedoch nicht bei jedem Aufruf um den abschließenden "\" kümmern wollen, verpacken Sie die API-Funktion in eine eigene Funktion, CreatePath, die automatisch den abschließenden "\" hinzufügt, falls er fehlen sollte (s. Quellcode 6).

CD-ROM
auswerfen

Es ist kaum verwunderlich, dass eine Funktion zum Ein- und Ausfahren eines CD-Laufwerks im Multimedia-API zu finden ist - immerhin wird ein CD-Laufwerk gelegentlich zu den Multimedia-Geräten gezählt.

Allerdings mutet der Umgang mit der API-Funktion mciExecute ein wenig seltsam an. So muss der auszuführende Befehl im Klartext als String übermittelt werden. Und daher muss dieser String mit der Angabe des entsprechenden CD-Laufwerks erst einmal zusammengebaut werden.

Ein Schwachpunkt der Funktion ist, dass sie keine vernünftige Fehlerbehandlung ermöglicht, falls etwa eine Laufwerksbezeichnung für ein Nicht-CD-Laufwerk angegeben worden ist oder falls das Laufwerk aus irgendeinem Grund nicht bereit ist.

Es werden dem Anwender lediglich nichtssagende Fehlermeldungen angezeigt, die sich offensichtlich nicht per Code unterdrücken lassen. Eine falsche Laufwerksbezeichnung können Sie jedoch schon vorab verhindern, indem Sie mittels der API-Funktion GetDriveType prüfen, ob es sich um ein CD-Laufwerk handelt. Mittels dieser Funktion können sie zudem auch ein CD-Laufwerk ermitteln.

Option Compare Text

Private Declare Function GetDriveType Lib "kernel32" _
    Alias "GetDriveTypeA" (ByVal nDrive As String) As _
    Long

Private Declare Function mciExecute Lib "winmm.dll" _
    (ByVal lpstrCommand As String) As Long

Public Function OpenCloseCD(Optional Drive As String, _

    Optional ByVal OpenClose As Boolean = True, _

    Optional FindFirstCDDrive As Boolean) As Boolean

    Dim nDrive, nExec, nAlias, nResult As Long

    Const DRIVE_CDROM = 5

    If FindFirstCDDrive Then

        nDrive = GetFirstCDDrive()

    Else

        nDrive = Trim$(Drive)

        If Len(nDrive) Then

            nDrive = Left$(nDrive, 1)

            If nDrive Like "[a-z]" Then

                If GetDriveType(nDrive & ":\") <> _
                    DRIVE_CDROM Then

                    Exit Function

                End If

            End If

        End If

    End If

    If Len(nDrive) Then

    nAlias = "CDDrive" & nDrive

    nExec = "Open " & nDrive _
        & ": Type CDAudio Alias "  & nAlias

    nResult = mciExecute(nExec)

        If nResult Then

            If OpenClose Then

                nExec = "Set " & nAlias & " Door Open"

            Else

                nExec = "Set " & nAlias _
                    & " Door Closed"

            End If

            nResult = mciExecute(nExec)

            If nResult Then

                nExec = "Close " & nAlias

                nResult = mciExecute(nExec)

                OpenCloseCD = True

            End If

        End If

    End If

End Function

Quellcode 7

Die folgende Funktion OpenCloseCD (s. Quellcode 7) übernimmt diese Prüfungen und verwendet, falls kein Laufwerk angegeben und im optionalen Parameter FindFirstCDDrive True übergeben worden ist, das erste verfügbare CD-Laufwerk des Systems.

Als Laufwerksbezeichnung können Sie den Laufwerksbuchstaben alleine (Groß- oder Kleinbuchstabe), eine übliche Laufwerksbezeichnung mit Doppelpunkt oder einen beliebigen Pfad auf der CD angeben.

Beachten Sie, dass zur Prüfung des Laufwerksbuchstabens unabhängig von der Groß-/Kleinschreibung der VBA-Operator Like verwendet wird und dazu die Vergleichsmethode des Moduls, in dem sich die Funktion befindet, auf Textvergleich (Option Compare Text) eingestellt wird.

Die weiter unten stehende Hilfsfunktion GetFirstCDDrive zur Ermittlung des ersten vorhandenen CD-Laufwerks können Sie natürlich auch für andere Zwecke verwenden.

Nervenschoner

Stellen Sie sich nur einmal den Lärm vor, wenn in einem Großraumbüro mehrere Leute mit einer Anwendung arbeiten, die unablässig jede Fehleingabe, jede MessageBox, jedes Öffnen eines Dialogs und dergleichen mit einem aufgeregten Piepser oder gar einer Klang-Fanfare quittiert.

Public Function GetFirstCDDrive() As String

  Dim i As Integer

  Dim nCDDrive As String

  Const DRIVE_CDROM = 5

  For i = 65 To 90

    nCDDrive = Chr$(i) & ":\"

    If GetDriveType(nCDDrive) = DRIVE_CDROM Then

      GetFirstCDDrive = nCDDrive

      Exit For

    End If

  Next 'i

End Function

Quellcode 8

Public LocalBeep As Boolean

Public Sub Beep()

    If LocalBeep Then

        VBA.Beep

    End If

End Sub

Quellcode 9

Private Declare Sub SystemParametersInfoGet Lib _
    "user32" Alias "SystemParametersInfoA" (ByVal _
    uAction As Long, ByVal uParam As Long, lpvParam As _
    Any, ByVal fuWinIni As Long)

Private Declare Sub SystemParametersInfoSet Lib _
    "user32" Alias "SystemParametersInfoA" (ByVal _
    uAction As Long, ByVal uParam As Long, ByVal _
    lpvParam As Long, ByVal fuWinIni As Long)

Private Const SPI_GETBEEP = 1

Private Const SPI_SETBEEP = 2

Private Const SPIF_SENDWININICHANGE = &H2

Private Const SPIF_UPDATEINIFILE = &H1

Public Property Get SysBeep() As Boolean

    Dim nSysBeep As Boolean

        SystemParametersInfoGet SPI_GETBEEP, 0, _
        nSysBeep, 0

    SysBeep = nSysBeep

End Property

Public Property Let SysBeep(ByVal New_SysBeep As _
    Boolean)

    SystemParametersInfoSet SPI_SETBEEP, _
        CLng(New_SysBeep), 0&, _
        SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE

End Property

Quellcode 10

Da macht es durchaus Sinn, dem Anwender die Möglichkeit zu geben, die akustische Signalisierung abzuschalten.

Mithilfe des hier vorgestellten Moduls können Sie zum einen festlegen, natürlich vom Anwender einstellbar, ob die Beep-Anweisungen in Ihrer Anwendung hörbar werden. Und zum anderen können Sie auch systemweit das Piepen ein- und ausschalten.

Aber ganz abgesehen davon: Sie sollten die Beep-Anweisung so sparsam wie möglich einsetzen. Denn ein Alarmsignal verliert seine
alarmierende Wirkung, wenn es ständig und andauernd ausgelöst wird. Denken Sie nur einmal an die Alarm-Anlage in Ihrer Nachbarschaft, die wegen Übersensibilität oder wegen einer Fehlfunktion so oft anspringt, dass letztlich niemand mehr darauf achtet - auch dann nicht, wenn tatsächlich einmal Einbrecher die Ursache des Alarms sein sollten.

Das Modul modBeep.bas bietet Ihnen zum einen die Möglichkeit, die Aufrufe der Beep-Anweisung aus Ihrer Anwendung abzufangen und gegebenenfalls zu unterdrücken. Das Prinzip ist einfach: Die öffentliche Prozedur Beep (s. Quellcode 9) überschreibt die originale VBA-Anweisung, die in dieser Prozedur nur dann aufgerufen wird, wenn die globale Variable LocalBeep auf True gesetzt ist. Zum anderen können Sie in diesem Modul das Beep-Signal systemweit ein- und ausschalten.

Dazu wird die API-Funktion SystemParametersInfo (s. Quellcode 10) mit dem Index SPI_SETBEEP aufgerufen, der im zweiten Parameter die gewünschte Einstellung übergeben wird. Sie brauchen im Code Ihrer Anwendung lediglich die Eigenschaft SysBeep auf True oder False zu setzen. Umgekehrt liefert Ihnen diese Eigenschaft die aktuelle Einstellung im System.

Da der dritte Parameter der API-Funktion zum Setzen als As Long mit Wertübergabe (ByVal), zum Auslesen jedoch als As Any deklariert sein muss, damit (für eine API-Funktion sehr ungewöhnlich) eine Variable vom Typ Boolean übergeben werden kann, werden hier zwei separate Alias-Deklarationen verwendet.

Windows-Version detailliert ermitteln

Ziemlich undurchsichtig erscheinen die Informationen, die Ihnen die API-Funktion GetVersionEx über das installierte Betriebssystem liefert. Womöglich kann bei Microsoft selbst niemand mehr so richtig nachvollziehen, warum sich sowohl alle Windows 9x-Versionen als auch NT 4.0 als Hauptversion 4 zu erkennen geben.

Wahrscheinlich beruht das auf dem ursprünglich für einen viel früheren Zeitpunkt geplanten Zusammenwachsen der Betriebssysteme auf der Basis eines gemeinsamen Kerns.

Grundsätzlich ergibt sich die tatsächliche Betriebssystem-Version aus der Kombination der Hauptversion (Major) und der Unterversion (Minor) mit der Plattform-Kennung (Plat-formID - 32s, 9x oder NT).

Im Prinzip reicht es, von der API-Funktion GetVersionEx die benutzerdefinierte Variable OSVERSIONINFO füllen zu lassen. Sie umfasst bereits alle zur Bestimmung des Betriebssystems notwendigen Informationen. Ein wenig mehr können allerdings die neueren Windows-Versionen über sich erzählen: Windows 2000 und Windows XP - und auch schon Windows NT 4.0 ab Service Pack 6.

Die um die erweiterten Informationen größere benutzerdefinierte Variable OSVERSIONINFOEX kann auch erst unter diesen Versionen an GetVersionEx übergeben werden. Und gerade sie bietet erst eine systematischere Unterscheidung der Service-Pack-Stufe, sogar wiederum mit einer getrennten Kennung Haupt- und Unterversion, wie Sie sie eigentlich schon unter NT 4.0 brauchen könnten, um das Vorhandensein des Service Pack 6 feststellen zu können.

Eine etwas vage anmutende Information können Sie dazu jedoch auch schon über OSVERSIONINFO in Erfahrung bringen. Im Element szCSDVersion (einem String der festen Länge 128) werden nämlich Zusatzinformationen geliefert - unter Windows 9x eine Kennung für die Release-Version (OSR, Second Edition usw.) und unter NT/2000 die ServicePack-Stufe im Klartext. Zumindest unter NT 4.0 können sich Sie sich wohl darauf verlassen, dass die Nummer des Service Packs mit dem 14. Zeichen beginnt.

Wir haben für Sie die Auswertung der Einzelinformationen in ein Standard-Modul gepackt. Sie initialisieren es einmal mit dem Aufruf der Prozedur ReadVersionInfo, in der mittels GetVersionEx die Informationen einmal eingelesen werden. Über eine ganze Reihe von Eigenschaften können Sie nun die Details auslesen.

Zum einen entspricht jeweils eine Eigenschaft einem jeden Element von OSVERSIONINFOEX. Für den Fall, dass GetVersionEx jedoch nicht damit aufgerufen werden konnte, sondern lediglich mit OSVERSIONINFO, wird deren Inhalt einer modulweit gültigen Variablen des Typs OSVERSIONINFOEX per LSet zugewiesen.

Zum anderen bietet das Modul eine direkte Möglichkeit, die Windows-Version als enumerierten Wert aus der Eigenschaft WinVersion auszulesen, als auch als Klartext aus der Eigenschaft WinVersionText. Schließlich können Sie noch über separate Eigenschaften für jede Betriebssystem-Version erfragen, ob die jeweilige Version installiert ist.

Sie haben das Ende des frei verfügbaren Teils des Artikels erreicht. Lesen Sie weiter, um zu erfahren, wie Sie den vollständigen Artikel lesen und auf viele hundert weitere Artikel zugreifen können.

Sind Sie Abonnent?Jetzt einloggen ...
 

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Diesen Artikel jetzt als PDF plus Beispieldatenbank herunterladen?

Wenn Sie sich jetzt für den Newsletter anmelden, erhalten Sie in Kürze eine E-Mail mit dem Artikel im PDF-Format plus Beispieldatenbank.

Hier anmelden:

E-Mail:

Anrede:

Vorname:

Nachname:

© 2003-2015 André Minhorst Alle Rechte vorbehalten.