Access

  MS Access 2010+ i Visual Basic for Applications VBA 7.0

• Microsoft Office 2010+ - wersja 64 i 32 bitowa.

Microsoft Office 2010+ jest obecnie dostępny w 32 i 64 bitowej wersji. Wraz z wprowadzeniem 64 bitowej wersji pakietu Microsoft Office 2010+, wprowadzona została nowa wersja Microsoft Visual Basic for Applications 7.0 (VBA 7). Wersja 32 bitowa pakietu Office 2010+ pozwala nadal używać istniejących 32 bitowych dodatków dla pakietu Office oraz korzystanie ze „starego” kodu bez konieczności jakichkolwiek modyfikacji w kodzie VBA.

Jeżeli chcemy korzystać w 64 bitowej wersji Microsoft Office z rozwiązań opartych na funkcjach API (ang.) Application Programming Interface, to musimy zaktualizować wszystkie istniejące 32 bitowe odwołania do wskaźników (address pointers) i uchwytów (handlers) odwołujących się do określonych miejsc w pamięci fizycznej, do 64 bitowych odwołań tak, by pracowały prawidłowo w wersji 64 bitowej.

Dla wersji 32 lub 64 bitowej wersji rozmiar wskaźnika lub uchwytu (w bajtach) będzie różny. W aplikacjach 64 bitowych do przechowywania wskaźników i uchwytów używana jest 64 bitowa liczba całkowita typu LongLong lub specjalny typ LongPtr. Typ ten w zależności od wersji jest liczbą typu LongLong w 64 bitowej wersji pakietu Office (VBA7) i 4 bajtową liczbą typu Long w wersji 32 bitowej pakietu Office (VBA7).

• Visual Basic for Applications 7.0

Wraz z wprowadzeniem 64 bitowej wersji pakietu MS Office, wprowadzona została nowa wersja Visual Basic for Applications 7.0 (VBA 7). Podstawowe zmiany to 64-bitowe uchwyty (handlers) i wskaźniki (address pointers). W związku z tym prowadzono nowe typy danych, nowy znak deklaracji zmiennych (^), nowe funkcje konwersji danych, nowe, uniwersalne funkcje zwracające wskaźniki VarPtr, ObjPtr, StrPtr, stałą vbLongLong = 20 określającą typ zmiennej LongLong oraz dwie stałe kompilacji warunkowej VBA7 i Win64.

Poniżej tabelka opisująca dokładniej nowości w VBA 7

Typ Pozycja Opis
Kwalifikator PtrSafe Kwalifikator ten określa, że deklaracja jest zgodna z 64 bitowymi środowiskami programistycznymi. Kwalifikator ten jest obowiązkowy w systemach 64 bitowych.
Typ danych LongPtr 8 bajtowa liczba (LongLong) w 64 bitowej wersji MS Office i 4 bajtowa (Long) w wersji 32 bitowej wersji VBA 7 pakietu MS Office. Typ ten jest zalecany przy deklarowaniu wskaźników i uchwytów w obu środowiskach (zarówno w 32 bitowym jak i 64 bitowym). Typ ten jest obsługiwany tylko w VBA 7 (zarówno w 32 bitowym jak i 64 bitowym środowisku). Korzystanie z typu LongPtr umożliwia pisanie przenośnego kodu, który będzie dział zarówno 32 jak i 64-bitowym środowisku VBA7.
Typ danych LongLong 8 bajtowy typ danych. Przechowuje liczby w przedziale wartości od -9.223.372.036.854.775.808 do 9.223.372.036.854.775.807. Typ ten jest dostępny tylko w 64 bitowym środowisku (w 64 bitowej wersji pakietu MS Office.)
Znak deklaracji ^ Znak deklaracji zmiennej typu LongLong.
Operator konwersji CLngPtr Konwertuje przekazywane wyrażenie do typu LongPtr.
Operator konwersji CLngLng Konwertuje przekazywane wyrażenie do typu LongLong.
Funkcja VarPtr Zwraca wskaźnik typu LongPtr w wersji 64 bitowych do przekazanej zmiennej, a typu Long w 32 bitowym środowisku.
Funkcja ObjPtr Zwraca wskaźnik typu LongPtr w wersji 64 bitowych do przekazanego obiektu, a typu Long w 32 bitowym środowisku.
Funkcja StrPtr Zwraca wskaźnik typu LongPtr w wersji 64 bitowych do przekazanego ciągu znaków, a typu Long w 32 bitowym środowisku.
Stała vbLongLong = 20 stała określająca typ zmiennej LongLong (tylko w 64 bitowym środowisku).
Pakiet Microsoft Office w wersji 2010+ lub nowszej zawiera dwie nowe stałe kompilacji warunkowej:
Stała kompilacji VBA7 Umożliwia sprawdzenie, czy uruchomiona aplikacja używa nowego VBA 7 (64 bitowego lub 32 bitowego), czy poprzedniej 32 bitowej wersji VBA (6.0).
Zwracana wartość VBA7 = TRUE informuje, że uruchomiona została aplikacja pakietu Office w wersji 2010+ lub nowszej.
Stała kompilacji Win64 Umożliwia sprawdzenie, czy uruchomiona została 32 bitowa, czy 64 bitowa wersja pakietu Microsoft Office.

Stałe kompilacji warunkowej VBA7 i Win64.

 
 
' Sprawdź wersję VBA
#If VBA7 Then     
	' 64-bitowe środowisko VBA7.
	#If Win64 Then
		MsgBox "Kod VBA 7 uruchomiony w 64-bitowej wersji MS Office +2010"        
	' 32-bitowe środowisko VBA7.
	#Else
		MsgBox "Kod VBA 7 uruchomiony w 32-bitowej wersji MS Office +2010"
	#End If
' To nie jest środowisko VBA 7.
#Else
	MsgBox "Kod VBA uruchomiony w wersji VBA 6 lub niższej."
#End If
  

Windows API i 64 bitowe środowisko VBA 7.

Atrybut PtrSafe - deklarowania procedur i funkcji z zewnętrznych bibliotek DLL w 64-bitowym VBA 7.

W 64 bitowym środowisku VBA7 za każdą instrukcją Declare tworzącą odwołanie do zewnętrznej procedury w bibliotece dołączanej dynamicznie (DLL) należy umieścić atrybut (kwalifikator, słowo kluczowe) PtrSafe i zmienić 32 bitowe odwołania do wskaźników i uchwytów na zgodne z typem LongPtr, a 64-bitowym argumentom przypisać nowy typ danych LongLong. Atrybut PtrSafe nie jest wymagany w 32 bitowym środowisku VBA7, co zapewnia wsteczną kompatybilność z VBA w wersji 6.
Brak atrybutu PtrSafe w 64 bitowym środowisku spowoduje błąd kompilacji.

 
 
' Sprawdź wersję VBA
#If VBA7 Then
	' Deklaracja dla 32 bitowej lub 64-bitowej wersji MS Office 2010 lub wyższej
	Declare PtrSafe Sub...
#Else 
	' Deklaracja dla 32 bitowej wersji MS Office 2007 lub niższej
	Declare Sub...
#End If 
 

Popatrzmy jak zaleca deklarowanie funkcji API w środowisku VBA7 Microsoft. W oparciu o przykład stronie Working with Different Versions of VBA utworzyy funkcję zwracającą położenie i wymiary okna aplikacji Excel.

 
' Struktura przechowująca dane o położeniu okna 
Type RECT
	Left As Long
	Top As Long
	Right As Long
	Bottom As Long
End Type

' Sprawdź, która wersja VBA jest używana
#If VBA7 Then
 ' Funkcja API szukająca okno (po nazwie klasy, lub tytule okna.
Declare PtrSafe Function FindWindow Lib "user32" _
				Alias "FindWindowA" ( _
				ByVal lpClassName As String, _
				ByVal lpWindowName As String) As LongPtr

' Funkcja API zwracające w strukturze lpRect dane o położeniu okna.
Declare PtrSafe Function GetWindowRect Lib "user32" ( _
				ByVal hwnd As LongPtr, _
				lpRect As RECT) As Long
#Else
' Funkcja API szukająca okno (po nazwie klasy, lub tytule okna).
Declare Function FindWindow Lib "user32" _
				Alias "FindWindowA" ( _
				ByVal lpClassName As String, _
				ByVal lpWindowName As String) As Long

' Funkcja API zwracające w strukturze lpRect dane o położeniu okna.
Declare Function GetWindowRect Lib "user32" ( _
				ByVal hwnd As Long, _
				lpRect As RECT) As Long
#End If
 

Jak na razie wszystko jest zrozumiałe. Struktura RECT i funkcje API zostały zadeklarowane. Poniżej krótkie wyjaśnienie dotyczące zadeklarowanych funkcji:

  • funkcja FindWindow (lpClassName, lpWindowName) w środowisku VBA 7 zwraca wartość liczbową  LongPtr (typu LongLong w 64 bitowym środowisku i typu Long w środowisku 32 bitowym. W 32 bitowym środowisku VBA 6 zwraca wartość typu Long.
  • funkcja GetWindowRect (hwnd, lpRect) w środowisku VBA 7 pobiera uchwyt hwnd jako wartość liczbową  LongPtr (typu LongLong w 64 bitowym środowisku i typu Long w środowisku 32 bitowym. W 32 bitowym środowisku VBA 6 pobiera uchwyt hwnd typu Long.
    W obu środowiskach VBA funkcja zwraca ByRef w argumencie lpRect strukturę RECT oraz informacje o powodzeniu typu Long
 
Sub DisplayExcelWindowSize()
Dim hwnd As Long, uRect As RECT

' Pobierz uchwyt głównego okna Excel.
hwnd = FindWindow("XLMAIN", Application.Caption)

' Pobierz dane o położeniu okna do struktury UDT.
GetWindowRect hwnd, uRect

' Pokaż położenie i wymiary okna.
MsgBox "The Excel window has these dimensions:" & _
				vbCrLf & " Left: " & uRect.Left & _
				vbCrLf & " Right: " & uRect.Right & _
				vbCrLf & " Top: " & uRect.Top & _
				vbCrLf & " Bottom: " & uRect.Bottom & _
				vbCrLf & " Width: " & (uRect.Right - uRect.Left) & _
				vbCrLf & " Height: " & (uRect.Bottom - uRect.Top)
End Sub

Po przeglądnięciu kodu funkcji DisplayExcelWindowSize mam pewne wątpliwości, czy dobrze wszystko zrozumiałem ? Załóżmy, że uruchamiamy funkcję w 64 bitowym środowisku Excel 2010, w którym stała VBA7 = TRUE. Moim zdaniem deklaracja uchwytu hwnd w omawianej funkcji jest niepoprawna i powoduje kolejne błędy w dalszej części kodu :

  1. Dim hwnd As Long - z opisów powyżej wynika, że uchwyty i wskaźniki w 64 bitowym środowisku VBA 7 są  typu LongLong. I zostały zadeklarowane jako liczba typu LongPtr, która w tym środowisku jest właśnie typu LongLong.
  2. hwnd = FindWindow(...) - to samo, uchwyty i wskaźniki w 64 bitowym środowisku VBA 7 są typu LongLong.
    I tak została zadeklarowana zwracana wartość: Declare PtrSafe Function FindWindow(...) As LongPtr
  3. GetWindowRect hwnd, uRect - i tutaj podobnie, przekazywany uchwyt hwnd powinien być typu LongLong
    I tak została zadeklarowana funkcja:
    Declare PtrSafe Function GetWindowRect (ByVal hwnd As LongPtr, ...) As Long

Wklejenie kodu do modułu aplikacji Excel i próba jego skompilowania, kończy się komunikatem o błędzie kompilacji.

Błąd kompilacji

Podświetlona zostaje linijka w której występuje przypisanie do błędnie zadeklarowanego uchwytu hwnd, który powinien być w środowisku VBA7 zadeklarowany jako LongPtr, a w środowisku VBA6 jako Long.

 
Sub DisplayExcelWindowSize()
' poprawiona deklaracja uchwytu okna
#If VBA7 Then
	' w 64 bitowym środowisku VBA 7 uchwyt hwnd będzie typu LongLong.
	' w 32 bitowym środowisku VBA 7 uchwyt hwnd będzie typu Long.
	Dim hwnd As LongPtr  
#Else
	' w 32 bitowym środowisku VBA 6 uchwyt hwnd jest zawsze typu Long.
	Dim hwnd As Long
#End If
Dim uRect As RECT

' Pobierz uchwyt głównego okna Excel.
hwnd = FindWindow("XLMAIN", Application.Caption)

' Pobierz dane o położeniu okna do struktury uRect.
GetWindowRect hwnd, uRect

' Pokaż położenie i wymiary okna.
MsgBox "The Excel window has these dimensions:" & _
				vbCrLf & " Left: " & uRect.Left & _
				vbCrLf & " Right: " & uRect.Right & _
				vbCrLf & " Top: " & uRect.Top & _
				vbCrLf & " Bottom: " & uRect.Bottom & _
				vbCrLf & " Width: " & (uRect.Right - uRect.Left) & _
				vbCrLf & " Height: " & (uRect.Bottom - uRect.Top)
End Sub

Teraz kod się kompiluje i działa prawidłowo.

Wymiary okna Excel

Położenie i wymiary okna Excel.

W przypadku tak prostej funkcji oraz gdy uchwyt hwnd nie jest potrzebny w dalszej procedury możemy, (kosztem czytelności i wbrew niektórym purystom), można znacznie uprościć kod poprzez bezpośrednie przekazanie zwracanego uchwytu przez funkcję FindWindow(...) do funkcji GetWindowRect(...)

 
Sub DisplayExcelWindowSize()
Dim uRect As RECT
' Pobierz dane o położeniu okna do struktury uRect.
GetWindowRect FindWindow("XLMAIN", Application.Caption), uRect

' Pokaż położenie i wymiary okna.
MsgBox "The Excel window has these dimensions:" & _
				vbCrLf & " Left: " & uRect.Left & _
				vbCrLf & " Right: " & uRect.Right & _
				vbCrLf & " Top: " & uRect.Top & _
				vbCrLf & " Bottom: " & uRect.Bottom & _
				vbCrLf & " Width: " & (uRect.Right - uRect.Left) & _
				vbCrLf & " Height: " & (uRect.Bottom - uRect.Top)
End Sub