Tytuł aplikacji MS Access
Niby prosta rzecz, jaką powinno być znalezienie okna głównego MS Access. Jest to okno klasy "OMain", którego oknem nadrzędnym (rodzicem) jest okno "Pulpit" . Wystarczy otworzyć MS Access przeczytać tytuł okna i potem spróbować znaleźć okno przekazując do funkcji wyszukującej odczytany tytuł. W celach testowych utworzyłem trzy bazy (db1,db2 i db3), otworzyłem je w różnych wersjach MS Access i odczytałem tytuły okien:
- MS Access 2007 - „Db1 : Baza danych (Access 2007) — Microsoft Access”
- MS Access 2010 - „Db2 : Baza danych (Access 2007 - 2010) — Microsoft Access”
- MS Access 2016 - „Db3 : Baza danych- C:\tmp\Db3.accdb (format pliku programu Access 2007–2016) — Access”
Tytuły okien głównych aplikacji MS Access 2007+
Po otwarciu w każdej bazie formularza, wszystkie okna główne MS Access zmieniają swój tytuł na:
- MS Access 2007 - „Db1 : Microsoft Access”
- MS Access 2010 - „Db2 : Microsoft Access”
- MS Access 2016 - „Db3 : Access”
Tytuły okien głównych aplikacji MS Access 2007+ po otwarciu formularza.
W rzeczywistości, wbrew temu co widzimy, tytuły okien głównych MS Access (pobierane przez funkcje API) nie zmieniły, cały czas były takie same, ale różniły się od widocznych na ekranie tytułów okien MS Access:
- MS Access 2007 - „Microsoft Access — Db1 : Baza danych (Access 2007)”
- MS Access 2010 - „Microsoft Access — Db2 : Baza danych (Access 2007 - 2010)”
- MS Access 2016 - „Access — Db3 : Baza danych- C:\tmp\Db3.accdb (format pliku programu Access 2007–2016)”
Jedyny rozsądnym rozwiązaniem jest zmienić „Tytuł aplikacji” na swój własny.
Wystarczy wybrać kolejno:
Menu: „Plik/Opcje programu Access/Bieżąca baza danych/Opcje aplikacji/Tytuł aplikacji”
i wpisać w okienko edycyjne wymyślona nazwę bazy danych. Dla takiego rozwiązania, po przekazaniu do funkcji wyszukującej
wpisanej nazwy bazy danych, funkcja prawidłowo odszuka okno główne MS Access.
Niestety, ale nie jest tak prosto. Możemy przecież szukać okna MS Word (klasa "OpusApp"), czy też okna
MS Excel (klasa "XLMAIN") o konkretnym tytule i w każdym z tych przypadków może być różnie.
Każda wersja MS Office rządzi się swoimi prawami. A jakimi tego niestety nie wie nikt!
W wersji aplikacji pakietu „Microsoft Office 2010 dla Użytkowników Domowych i Uczniów” oraz pakietu „Microsoft Office 2007 dla Użytkowników Domowych”
do tytule okna tekstu na końcu dopisywany był tekst „użytek niekomercyjny”, czy coś w tym stylu.
A w wersji 2019 .... (͡° ͜ʖ ͡°)
Wyszukiwanie okna po fragmencie tekstu na pasku tytułowym.
Jedynym rozwiązaniem jest wyszukiwanie okien, których tytuł zawiera poszukiwany tekst. Jeżeli wyszukiwanie ograniczymy do okien Microsoft Office, to zastosowanie opcjonalnego argumentu sWindowClass w którym przekazujemy nazwę klasy poszukiwanego okna, przyspieszy wyszukiwanie i czyni je bardziej prawdopodobnymi. Poniżej przedstawiam nazwy klas okien MS Office:
- OMain - Access
- OpusApp - Word
- XLMAIN - Excel
- PPTFrameClass - PowerPoint
- IEFrame - Internet Explorer
- Framework::CFrame - OneNote
Czy okno nadrzędne (rodzic) zawiera okno potomne (dziecko), którego tytuł zawiera określony ciąg znaków?
Chcąc sprawdzić, czy otwarte jest okno określonej klasy, którego tytuł (tekst na pasku tytułowym okna) zawiera określony fragment (konkretny ciąg znaków), napiszemy funkcję accFindWindowByTitle (...), która:
Public Function accFindWindowByTitle( _ hParent As Long, _ sWindowTitle As String, _ Optional sWindowClass As String = "") As Long
-
Za pomocą funkcji API FindWindowEx(...)
sprawdza, czy okno nadrzędne (rodzic) o uchwycie hParent zawiera okna potomne (dzieci) o określonym wzorcu
nazwy klasy i tytule zawierającym przekazany w argumencie sWindowTitle tekst. Poszukiwanie okien potomnych
(dzieci) odbywa się zgodnie z kolejnością Z-Order. Jeżeli nie jest określona nazwa klasy,
to pobierane są kolejno uchwyty okien potomnych (dzieci) okna nadrzędnego (rodzica).
Dla każdego okna, przy wykorzystaniu funkcji API GetWindowTextLength(...) pobierana jest długość tekstu okna. W przypadku okien zawierających tekst jest on pobierany przez funkcję GetWindowText(...) do bufora sChildTitle i sprawdzany jest, czy zawiera szukany ciąg znaków. - argumenty:
- hParent
- uchwyt okna nadrzędnego (rodzica), które będzie przeszukiwane
- sWindowTitle
- ciąg znaków, fragment tytułu okna jaki musi zawierać tytuł szukanego okna
- sWindowClass
- argument opcjonalny. Pełna nazwa klasy szukanego okna potomnego (dziecka). Jeżeli argument ten nie jest ustawiony, zostaje przekonwertowany na stałą vbNullString. Stałej tej nie można zastępować ciągiem zerowej długości "".
- zwraca:
-
Przy powodzeniu zwraca uchwyt pierwszego znalezionego okna, pasującego do przekazanego wzorca klasy i którego tytuł zawiera określony ciąg znaków. Jeżeli klasa okna nie została określona, funkcja zwraca uchwyt pierwszego znalezionego okna, którego tytuł zawiera określony ciąg znaków. Przy niepowodzeniu zwraca ZERO
- autor: Zbigniew Bratko
- data: 14.02.2019
Option Compare Database Option Explicit #If VBA7 Then Public Declare PtrSafe Function FindWindowEx Lib "user32" _ Alias "FindWindowExA" ( _ ByVal hWndParent As LongPtr, _ ByVal hWndChildAfter As LongPtr, _ ByVal lpszClass As String, _ ByVal lpszWindow As String) As LongPtr Public Declare PtrSafe Function GetWindowTextLength Lib "user32" _ Alias "GetWindowTextLengthA" ( _ ByVal hWnd As LongPtr) As Long Public Declare PtrSafe Function GetWindowText Lib "user32" _ Alias "GetWindowTextA" ( _ ByVal hWnd As LongPtr, _ ByVal lpString As String, _ ByVal nMaxCount As Long) As Long #Else Public Declare Function FindWindowEx Lib "user32" _ Alias "FindWindowExA" ( _ ByVal hWndParent As Long, _ ByVal hWndChildAfter As Long, _ ByVal lpszClass As String, _ ByVal lpszWindow As String) As Long Public Declare Function GetWindowTextLength Lib "user32" _ Alias "GetWindowTextLengthA" ( _ ByVal hWnd As Long) As Long Public Declare Function GetWindowText Lib "user32" _ Alias "GetWindowTextA" ( _ ByVal hWnd As Long, _ ByVal lpString As String, _ ByVal nMaxCount As Long) As Long #End If 'Wyszukiwanie okna po fragmencie tekstu na pasku tytułowym #If VBA7 Then Public Function accFindWindowByTitle( _ hParent As LongPtr, _ sWindowTitle As String, _ Optional sWindowClass As String = "") As LongPtr Dim hChild As LongPtr #Else Public Function accFindWindowByTitle( _ hParent As Long, _ sWindowTitle As String, _ Optional sWindowClass As String = "") As Long Dim hChild As Long #End If Dim sClassName As String Dim sChildTitle As String Dim lLenTitle As Long ' sWindowClass nie może być ciągiem zerowej długości ' sClassName = IIf(Len(sWindowClass) = 0, vbNullString, sWindowClass) ' UWAGA: Jeżeli korzystać ze stałej vbNullString, nie możemy korzystać z funkcji IIf ' gdyż VBA przekonwertuje wartośc stałej vbNullString na ciąg zerowej długości. If Len(sWindowClass) = 0 Then sClassName = vbNullString Else sClassName = sWindowClass End If ' pobierz uchwyt pierwszego okna potomnego (dziecka) hChild = FindWindowEx(hParent, 0&, sClassName, vbNullString) Do Until hChild = 0 ' pobierz długość tekstu okna lLenTitle = GetWindowTextLength(hChild) ' sprawdź, czy okna potomne (dziecko) zawiera tekst If lLenTitle > 0 Then ' dodaj 1, na znak końca ciągu znaków vbNullChar lLenTitle = lLenTitle + 1 ' przygotuj bufor na przyjęcie tekstu okna sChildTitle = String(lLenTitle, vbNullChar) ' pobierz tekst okna do buforu sChildTitle lLenTitle = GetWindowText(hChild, sChildTitle, lLenTitle) sChildTitle = Left$(sChildTitle, lLenTitle) ' sprawdź, czy tytuł okna potomnego zawiera szukany tytuł okna If InStr(1, sChildTitle, sWindowTitle, vbTextCompare) > 0 Then ' zwróć uchwyt znalezionego okna accFindWindowByTitle = hChild Exit Function End If End If 'pobieraj kolejno uchwyty okien potomnych, bez względu na tytuł okna hChild = FindWindowEx(hParent, hChild, sClassName, vbNullString) Loop End Function