wtorek, 19 stycznia 2016

• Uchwyt okna w 64-bitowym VBA 7.

• Czy w 64-bitowym VBA 7 właściwość Me.hwnd jest liczbą typu LongLong ?

Z artykułu VBA 7.0 i MS Access 2010+ wiemy, że w 32-bitowym środowisku wszystkie uchwyty i wskaźniki są liczbą typu Long, a w środowisku 64-bitowym liczbą typu LongLong. W celu ułatwienia pisanie przenośnego kodu wprowadzono w środowisku VBA 7 nową liczbę typu LongPtr, która przyjmuje w 32-bitowym środowisku typ Long (liczba 4 bajtowa), a w 64-bitowej wersji typ LongLong (8 bajtowa liczba).
Przypisanie uchwytu lub wskaźnika w środowisku 64-bitowym do zmiennej typu Long jest niepoprawne i może prowadzić do nieprzewidzianych błędów, ponieważ 64-bitowy uchwyt lub wskaźnik może zostać „ucięty” do wartości 32-bitowej i wskazywać na nieprawidłowy obiekt. Piszę, „może zostać ucięty”, gdyż w przypadku nieprzekroczenia przez uchwyt zakresu liczby Long, nie zmieni on wartości przy przypisaniu do zmiennej typu Long.

• Jak przekazać uchwyt hwnd w 32-bitowej i 64-bitowej wersji MS Access 2010+ ?

Napiszmy prostą przykładową funkcję by pobrać i przypisać do zmiennej hWind uchwyt hwnd okna formularza MS Access i odczytać jego tytuł za pomocą funkcji API GetTextWindow(...).

#If VBA7 Then
  Private Declare PtrSafe Function GetWindowText Lib "user32" _
          Alias "GetWindowTextA" ( _
          ByVal hwnd As LongPtr, _
          ByVal lpString As String, _
          ByVal cch As Long) As Long
#Else
  Private Declare Function GetWindowText Lib "user32" _
          Alias "GetWindowTextA" ( _
          ByVal hwnd As Long, _
          ByVal lpString As String, _
          ByVal cch As Long) As Long
#End If

#If VBA7 Then
  Private Function GetFormCaption(hWind As LongPtr) As String
#Else
  Private Function GetFormCaption(hWind As Long) As String
#End If

  Dim lRet As Long
  Dim sBuffer As String
  Const conSize_Buffer = 256

  ' przygotuj buffor na przyjęcie tytułu okna
  sBuffer = String(conSize_Buffer, vbNullChar)

  ' pobierz do bufora tytuł okna i zwróć ilość pobranych znaków
  lRet = GetWindowText(hWind, sBuffer, conSize_Buffer)
  
  GetFormCaption = Left$(sBuffer, lRet)

End Function

' przykładowe wywołanie
Private Sub btnTest_Click()

  ' ustaw nowy tytuł formularza
  Me.Caption = "Nowy tytuł formularza"
  
  ' odczytaj tytuł formularza
  MsgBox GetFormCaption(Me.hwnd)
  
End Sub

Po uruchomieniu kodu pojawia się okno komunikatu:

Wszystko się zgadza. Odczytany tytuł formularza jest taki sam, jaki został ustawiony za pomocą właściwości:

Me.Caption = "Nowy tytuł formularza"

Praktycznie w tym miejscu, w poczuciu dobrze spełnionego obowiązku, powinienem zakończyć ten artykuł. Jednakże przeprowadzę jeszcze jeden malutki test odnośnie typu uchwytu hwnd. Zgodnie z wszystkimi wytycznymi Microsoftu uchwyt w 64-bitowym środowisku powinien być 8-bajtową liczbą typu LongLong, a w 32-bitowym środowisku 4-bajtową liczbą typu Long. Do sprawdzenia typu zmiennej użyję funkcji VarType( varname ), która dla zmiennej typu LongLong zwraca wartość vbLongLong = 20, a dla zmiennej typu Long zwraca wartość vbLong = 3 i tylko te dwa typy będą uwzględnione w procedurze testowej.

Private Sub GetTypeHwnd()
#If VBA7 Then
  Dim hWind As LongPtr
#Else
  Dim hWind As Long
#End If

Dim sRet As String

  'sprawdź typ zmiennej hWind
  sRet = sRet & "Zmienna  hWind  jest typu: " & _
         IIf(VarType(hWind) = vbLongLong, "LongLong", "Long")
  sRet = sRet & vbNewLine & vbNewLine
  ' sprawdź typ uchwytu okna
  sRet = sRet & "Uchwyt Me.hwnd jest typu: " & _
         IIf(VarType(Me.hwnd) = vbLongLong, "LongLong", "Long")
  
  MsgBox sRet

End Sub

Znowu ZONK ! Co widać na poniższym oknie komunikatu:

Zmienna hWind jest typu LongLong, bo tak została zadeklarowana:

Dim hWind As LongPtr

Ale dlaczego właściwość Me.hwnd jest liczbę typu Long w 64-bitowym VBA 7.0 ? Aby odpowiedzieć na to pytanie wystarczy spojrzeć na deklarację tej właściwości w oknie „Object Browser”

Property Hwnd As Long
Problem ten dotyczy także metody Application.hWndAccessApp, która ma postać:
Function hWndAccessApp() As Long
i w 64-bitowym środowisku VBA 7.0 zwróci uchwyt okna MS Access jako liczbę typu Long zamiast liczby typu LongLong.