W poprzednim artykule
• Funkcje API - Mutex, jedna instancja bazy MS Access • zasygnalizowałem problem zbyt lakonicznego komunikatu w przypadku niepowodzenia
podczas wywołania funkcji API CreateMutex(...)
.
Komunikat typu:
MsgBox "Nieprzewidziany błąd wywołania biblioteki DLL." & vbNewLine & _
Numer błędu: " & Err.LastDllError
wydaje się być dość zdawkowy i nie daje końcowemu użytkownikowi zbyt wiele informacji o przyczynie błędu.
Spróbuję przedstawić rozwiązania pokazujące, jak w przypadku wystąpienia błędu zewnętrznej biblioteki DLL,
przekonwertować „suchy numer” błędu Err.LastDllError na opisowy komunikat tekstowy,
odnoszący się do zaistniałego błędu.
Zrobię to na przykładzie wywołania funkcji API • GetFileAttributes(...)
, która przy powodzeniu
zwraca atrybuty pliku lub katalogu, a przy niepowodzeniu zwraca wartość INVALID_FILE_ATTRIBUTES = -1.
Bardziej szczegółowe informacje o przyczynach niepowodzenia wywołania funkcji, możemy uzyskać z opisu kodu błędu systemowego wygenerowanego
przy wywołaniu biblioteki dołączanej dynamicznie (DLL). Kod błędu systemowego możemy pobrać dzięki
właściwości Err.LastDllError.
• Opis błędu o kodzie odpowiadającym właściwości Err.LastDllError
Najpierw musimy zadeklarować funkcję API • GetFileAttributes(...)
, a następnie spróbować
wywołać ją z nieprawidłowym argumentem, tak by wywołanie funkcji • GetFileAttributes(...)
zakończyło się
niepowodzeniem, w wyniku czego funkcja zwróci wartość INVALID_FILE_ATTRIBUTES = -1), a właściwość
Err.LastDllError zwróci nam systemowy kod błędu wywołania biblioteki DLL.
Option Compare Database Option Explicit #If VBA7 Then Private Declare PtrSafe Function GetFileAttributes Lib "kernel32" _ Alias "GetFileAttributesA" _ (ByVal lpFileName As String) As Long #Else Private Declare Function GetFileAttributes Lib "kernel32" _ Alias "GetFileAttributesA" _ (ByVal lpFileName As String) As Long #End If ' przykładowe wywołania Public Function MojTest() Dim lAttrib As Long ' próba pobrania atrybutów pliku, który nie istnieje lAttrib = GetFileAttributes("C:\NotExistingFile") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "A - Err = "; Err.LastDllError ' próba pobrania atrybutów pliku w nieistniejącej lokalizacji (brak stacji dysków B:\) lAttrib = GetFileAttributes("B:\MojPlik.txt") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "B - Err = "; Err.LastDllError ' próba pobrania atrybutów pliku z błędnie wpisanej lokalizacji lAttrib = GetFileAttributes(":MojPlik.txt") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "C - Err = "; Err.LastDllError ' próba pobrania atrybutów pliku z pustego dysku DVD (CD) lAttrib = GetFileAttributes("Z:\NotExistingFile") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "D - Err = "; Err.LastDllError ' próba pobrania atrybutów pliku z pustej stacji DVD (CD) lAttrib = GetFileAttributes("Z:\MojPlik.txt") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "E - Err = "; Err.LastDllError DoCmd.RunCommand acCmdDebugWindow End Function
Po wywołaniu przykładowej funkcji MojTest()
w oknie „Immediate” możemy zobaczyć wyniki:
Nie wiem jak innym, ale mi cyferki (liczby) typu: 2, 3, 123, 1, 21 nic nie mówią. Tym bardziej użytkownikowi, który zobaczy poniższy komunikat:
Aby dowiedzieć się czegoś więcej o przyczynie błędu wywołania funkcji DLL, wykorzystamy funkcję API:
• FormatMessage(...)
, która formatuje tekst komunikatu związanego z kodem błędu wywołania biblioteki DLL
i zapisuje sformatowany komunikat do bufora wyjściowego. Zwraca ilość znaków zapisanych w buforze wyjściowym,
(nie uwzględnienia znaku końca ciągu vbNullChar).
Option Compare Database Option Explicit ' • Function LastDllErrorDescr(lNoLastError As Long,[sFunctionName As String = ""]) As String ' -------------------------------------------------------------------------------------- ' autor: Zbigniew Bratko - 03.2016 ' Zwraca sformatowany tekst komunikatu związany z kodem błędu wywołania biblioteki DLL ' [lNoLastError] - numer błędu wywołania funkcji DLL, odpowiadający właściwości Err.LastDllError ' [sFunctionName] - opcjonalny argument odnoszący się do funkcji w której wystąpił błąd ' [OUT] - Przy powodzeniu zwraca tekst komunikatu związany z kodem błędu wywołania biblioteki DLL, ' przy niepowodzeniu (braku opisu błędu) zwraca informację: "Nie znaleziono opisu błędu." ' #If VBA7 Then Private Declare PtrSafe Function FormatMessage Lib "kernel32" _ Alias "FormatMessageA" _ (ByVal dwFlags As Long, _ lpSource As Any, _ ByVal dwMessageId As Long, _ ByVal dwLanguageId As Long, _ ByVal lpBuffer As String, _ ByVal nSize As Long, _ Arguments As LongPtr) As Long #Else Private Declare Function FormatMessage Lib "kernel32" _ Alias "FormatMessageA" _ (ByVal dwFlags As Long, _ lpSource As Any, _ ByVal dwMessageId As Long, _ ByVal dwLanguageId As Long, _ ByVal lpBuffer As String, _ ByVal nSize As Long, _ Arguments As Long) As Long #End If Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000 Private Const INVALID_FILE_ATTRIBUTES = -1 Private Const cDescrNotExist As String = "Nie znaleziono opisu błędu." ' Funkcja własna LastDllErrorDescr Public Function LastDllErrorDescr(lNoLastError As Long, _ Optional sFunctionName As String = "") As String Dim sBufferMessage As String Dim lLenMessage As Long Const cBufferSize As Long = 256 ' przygotuj bufor na przyjęcie komunikatu o błędzie sBufferMessage = String(cBufferSize, vbNullChar) ' zapisz tekst komunikatu do bufora sBufferMessage lLenMessage = FormatMessage(ByVal FORMAT_MESSAGE_FROM_SYSTEM, _ 0&, _ ByVal lNoLastError, _ 0&, _ ByVal sBufferMessage, _ ByVal cBufferSize, _ 0) ' nie znaleziono opisu błędu If lLenMessage = 0 Then ' przygotuj własny komunikat sBufferMessage = cDescrNotExist lLenMessage = Len(sBufferMessage) End If ' pobierz tekst komunikatu z bufora If Len(sFunctionName) = 0 Then LastDllErrorDescr = Left$(sBufferMessage, lLenMessage) Else LastDllErrorDescr = "Błąd nr " & lNoLastError & vbNewLine & _ "Funkcja " & sFunctionName & vbNewLine & _ "Opis błędu: " & vbNewLine & _ Left$(sBufferMessage, lLenMessage) End If End Function ' przykładowe wywołania Public Function MojTestBis() Dim lAttrib As Long ' próba pobrania atrybutów pliku, który nie istnieje lAttrib = GetFileAttributes("C:\NotExistingFile") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "A - Err = "; Err.LastDllError & " " & LastDllErrorDescr(Err.LastDllError) End If ' próba pobrania atrybutów pliku w nieistniejącej lokalizacji (brak stacji dysków B:\) lAttrib = GetFileAttributes("B:\MojPlik.txt") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "B - Err = "; Err.LastDllError & " " & LastDllErrorDescr(Err.LastDllError) End If ' próba pobrania atrybutów pliku z błędnie wpisanej lokalizacji lAttrib = GetFileAttributes(":MojPlik.txt") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "C - Err = "; Err.LastDllError & " " & LastDllErrorDescr(Err.LastDllError) End If ' próba pobrania atrybutów pliku z pustego dysku DVD (CD) lAttrib = GetFileAttributes("Z:\NotExistingFile") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "D - Err = "; Err.LastDllError & " " & LastDllErrorDescr(Err.LastDllError) End If ' próba pobrania atrybutów pliku z pustej stacji DVD (CD) lAttrib = GetFileAttributes("Z:\MojPlik.txt") If lAttrib = INVALID_FILE_ATTRIBUTES Then Debug.Print "E - Err = "; Err.LastDllError & " " & LastDllErrorDescr(Err.LastDllError) End If DoCmd.RunCommand acCmdDebugWindow End Function
Po wywołaniu przykładowej funkcji MojTestBis()
w oknie „Immediate” możemy zobaczyć wyniki:
Uwaga: Właściwość Err.LastDllError odnosi się tylko do wywołania funkcji z zewnętrznych bibliotek DLL z kodu programu pisanego w języku VBA. Tak wywoływana funkcja zwykle zwraca kod oznaczający, czy wywoływana funkcja zakończyła się sukcesem, czy też niepowodzeniem i ustawiana jest właściwość Err.LastDllError. Właściwość Err.LastDllError powinna być sprawdzana po każdym niepowodzeniu wywoływanej funkcji biblioteki DLL. Jeśli właściwość Err.LastDllError została ustawiona, nie jest zgłaszany żaden wyjątek .
Brak komentarzy:
Prześlij komentarz