%REM Library class:CEnumerateWindowsHandles Created Apr 30, 2012 by Robert Ibsen Voith Description: Class to enumerate Window handles and manipulate window handles. Also contains CSearchBarManager which allows interaction with Full Text Search Bar. Uses Win32 API Example: Dim searchBarMgr As New CSearchBarManager() Dim hWndSearchBar As Long hWndSearchBar = searchBarMgr.FindSearchBar() If hWndSearchBar > 0 Then Call searchBarMgr.SetSearchBarText("Hey there!") MessageBox searchBarMgr.GetSearchBarText() Call searchBarMgr.ClickSearchButton() Else MessageBox |Click View -> "Search This View" to show the search bar first| End If See Notes Database Design Collector (NDDC) for a sample on how to use %END REM Option Public Option Explicit %REM Type CEW_TYPE_HANDLE Description: Defines one Window Entry %END REM Type CEW_TYPE_HANDLE m_hWnd As Long m_hWndParent As Long m_lDepth As Long m_strHex As String m_strHexParent As String m_strTitle As String m_strClass As String End Type %REM Type CEW_TYPE_RECT Description: The same as Win32 Def for TYPE %END REM Type CEW_TYPE_RECT Left As Long Top As Long Right As Long Bottom As Long End Type Declare Function CEW_W32_GetActiveWindow Lib "user32" Alias "GetActiveWindow" () As Long Declare Function CEW_W32_GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String,ByVal chMax As Long) As Long Declare Function CEW_W32_GetClassName Lib "User32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpString As String,ByVal chMax As Long) As Long Declare Sub CEW_W32_SetWindowText Lib "User32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString$) Declare Function CEW_W32_GetWindow Lib "User32" Alias "GetWindow" (ByVal hwnd As Long, ByVal uFlag As Long) As Long Declare Function CEW_W32_GetParent Lib "User32" Alias "GetParent" (ByVal hwnd As Long) As Long Declare Function CEW_W32_IsWindowVisible Lib "User32" Alias "IsWindowVisibleA" (ByVal hwnd As Long) As Boolean Declare Function CEW_W32_GetClipBox Lib "GDI32" Alias "GetClipBox" (ByVal hWnd As Long, Rect As CEW_TYPE_RECT) As Long Declare Function CEW_W32_GetDC Lib "User32" Alias "GetDC" (ByVal hWnd As Long) As Long Declare Function CEW_W32_ReleaseDC Lib "User32" Alias "ReleaseDC" (ByVal hWnd As Long, ByVal hDC As Long) As Long Declare Function CEW_W32_SendMessage Lib "User32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal Msg As Long, wParam As Long, lParam As Long) As Long Declare Function CEW_W32_PostMessage Lib "User32" Alias "PostMessageA" (ByVal hWnd As Long, ByVal Msg As Long, wParam As Long, lParam As Long) As Long Declare Sub CEW_W32_Keybd_Event Lib "user32.dll" Alias "keybd_event" (ByVal bVk As Integer, ByVal bScan As Integer, ByVal dwFlags As Integer,ByVal dwExtraInfo As Integer) Const CEW_BM_CLICK = &HF5 Const CEW_WM_ACTIVATE = &H6 Const CEW_WA_ACTIVE = 1 Const CEW_WM_KEYDOWN = &H100 Const CEW_WM_KEYUP = &H101 Const CEW_NULLREGION = 1, CEW_SIMPLEREGION = 2, CEW_COMPLEXREGION = 3 Const CEW_GW_HWNDNEXT = 2 Const CEW_GW_CHILD = 5 Const CEW_VK_RETURN = &h0D' ENTER key Const CEW_KEYEVENTF_KEYUP = &h2 %REM Class CEWHandleList Description: List of Windows entries. %END REM Class CEWHandleList Public m_listHandles List As CEW_TYPE_HANDLE Private m_lCount As Long %REM Sub New Description: Construction %END REM Sub New() End Sub %REM Sub AddHandle Description: Add a handle to the list %END REM Sub AddHandle(hWnd As Long, lDepth As Long) Dim handle As CEW_TYPE_HANDLE Dim strWindowTitle As String Dim lWindowTitleLen As Long Dim strClassName As String Dim lClassNameLen As Long On Error Resume Next strClassName = String(255, 0) strWindowTitle = String(255, 0) handle.m_hWnd = hWnd handle.m_strHex = Right("00000000" & Hex(hWnd), 8) handle.m_hWndParent = CEW_W32_GetParent(hWnd) handle.m_strHexParent = Right("00000000" & Hex(handle.m_hWndParent), 8) handle.m_lDepth = lDepth lWindowTitleLen = CEW_W32_GetWindowText(hWnd, strWindowTitle, 255) If lWindowTitleLen > 0 Then handle.m_strTitle = Left(strWindowTitle, lWindowTitleLen) End If lClassNameLen = CEW_W32_GetClassName(hWnd, strClassName, 255) If lClassNameLen > 0 Then handle.m_strClass = Left(strClassName, lClassNameLen) End If ' Only add element if not there to start with If IsElement(m_listHandles(CStr(hWnd))) = False Then m_listHandles(CStr(hWnd)) = handle m_lCount = m_lCount + 1 End If End Sub %REM Function GetCount Description: Return the count of entries in this list %END REM Function GetCount() As Long GetCount = m_lCount + 1 End Function End Class %REM Class CEnumerateWindows Description: Core class for enumerating window handles. Call EnumerateActiveWindow() to enumerate all child window handles for active window. The result is stored in a list for fast and easy enumeration. Please see CSearchBarManager for a sample on how to derive this class. %END REM Class CEnumerateWindows Private m_hWndParent As Long Public m_listHandles As CEWHandleList Private m_strWindowTitleParent As String %REM Sub New Description: Comments for Sub %END REM Sub New m_hWndParent = 0 Set m_listHandles = New CEWHandleList() End Sub %REM Function EnumerateActiveWindow Description: Comments for Function %END REM Function EnumerateActiveWindow() As Boolean Dim hWndCur As Long If m_hWndParent = 0 Then m_hWndParent = CEW_W32_GetActiveWindow() m_strWindowTitleParent = GetWindowTitle(m_hWndParent) End If ' Get the first child of the parent hWndCur = CEW_W32_GetWindow(m_hWndParent, CEW_GW_CHILD) If hWndCur <> 0 Then EnumerateActiveWindow = EnumerateSiblings(hWndCur, 0) End Function %REM Function GetWindowTitle Description: Get the window title %END REM Private Function GetWindowTitle(hWnd As Long) As String Dim strWindowTitle As String Dim lWindowTitleLen As Long strWindowTitle = String(255, 0) lWindowTitleLen = CEW_W32_GetWindowText(hWnd, strWindowTitle, 255) If lWindowTitleLen > 0 Then GetWindowTitle = Left(strWindowTitle, lWindowTitleLen) End If End Function %REM Function GetWindowTitleParent Description: Return m_strWindowTitleParent %END REM Function GetWindowTitleParent() As String GetWindowTitleParent = m_strWindowTitleParent End Function %REM Function EnumerateSiblings Description: Comments for Function %END REM Private Function EnumerateSiblings(ByVal hWnd As Long, ByVal lInputDepth As Long) As Boolean Dim hWndChild As Long Dim lDepth As Long Dim hWndParent As Long lDepth = lInputDepth + 1 While hWnd <> 0 Call m_listHandles.AddHandle(hWnd, lDepth) hWndChild = CEW_W32_GetWindow(hWnd, CEW_GW_CHILD) If hWndChild <> 0 Then Call EnumerateSiblings(hWndChild, lDepth) hWnd = CEW_W32_GetWindow(hWnd, CEW_GW_HWNDNEXT) If EnumerateSiblings = False Then EnumerateSiblings = True Wend End Function End Class %REM Class CSearchBarManager Description: Derived class of CEnumerateWindows, which will find the Search Bar pane and it's controls %END REM Class CSearchBarManager As CEnumerateWindows Private m_hWndSearchBar As Long Private m_strLanguage As String Private m_listSearchButtonCaptionsByCaption List As String Private m_hWndSearchBarButton As Long %REM Sub New Description: %END REM Sub New ' Set up the list of Search Bar Button (yes, "Search"-button that activates a search) language codes ' Unfortunately I haven't found a way of retrieving the language of the Notes client itself. It's easy to ' get the user's preferred language etc with formulas like @LanguagePreference([REGION]). However, this is ' not the same as the client-language. Below is a list of search-captions for several languages. Just used ' http://www.nicetranslator.com/# to find the words. NOTE THAT THIS ISN'T NECESSARY CORRECT FOR THE ' NOTES CLIENT!!!! Also note, that nicetranslator didn't seem to pay too much attention to extact case, therefore ' I define the list in a local list, and transfer an lowercased version to the real table Dim listSearchCaption List As String listSearchCaption("البحث") = "Arabic" listSearchCaption("търсене") = "Bulgarian" listSearchCaption("Cerca") = "Catalan" listSearchCaption("搜索") = "Chinese Simplified" listSearchCaption("搜索") = "Chinese Traditional" listSearchCaption("Søg") = "Danish" listSearchCaption("Zoek") = "Dutch" listSearchCaption("Search") = "English" listSearchCaption("Otsing") = "Estonian" listSearchCaption("Etsi") = "Finnish" listSearchCaption("recherche") = "French" listSearchCaption("Suche") = "German" listSearchCaption("Αναζήτηση") = "Greek" listSearchCaption("recherche") = "Haitian Creole" listSearchCaption("חיפוש") = "Hebrew" listSearchCaption("खोज") = "Hindi" listSearchCaption("nrhiav") = "Hmong Daw" listSearchCaption("Keresés") = "Hungarian" listSearchCaption("pencarian") = "Indonesian" listSearchCaption("ricerca") = "Italian" listSearchCaption("検索") = "Japanese" listSearchCaption("검색") = "Korean" listSearchCaption("meklēšanas") = "Latvian" listSearchCaption("Paieška") = "Lithuanian" listSearchCaption("Søk") = "Norwegian" listSearchCaption("wyszukiwanie") = "Polish" listSearchCaption("pesquisa") = "Portuguese" listSearchCaption("Căutare") = "Romanian" listSearchCaption("Поиск") = "Russian" listSearchCaption("vyhľadávanie") = "Slovak" listSearchCaption("iskanje") = "Slovenian" listSearchCaption("búsqueda") = "Spanish" listSearchCaption("Sök") = "Swedish" listSearchCaption("ค้นหา") = "Thai" listSearchCaption("Arama") = "Turkish" listSearchCaption("Пошук") = "Ukrainian" listSearchCaption("tìm kiếm") = "Vietnamese" ' Now transfer the listSearchCaption to the real m_listSearchButtonCaptionsByCaption, ' which has a ListTag lowercased! ForAll elemDef In listSearchCaption m_listSearchButtonCaptionsByCaption(LCase(ListTag(elemDef))) = elemDef End ForAll End Sub %REM Function IsSearchButtonCaption Description: Check whether strCaption is one of the Search Button Caption languages defined by m_listSearchButtonCaptionsByCaption. %END REM Private Function IsSearchButtonCaption(ByVal strInCaption As String) As Boolean Dim strCaption As String strCaption = LCase(strInCaption) If IsElement(m_listSearchButtonCaptionsByCaption(strCaption)) = True Then m_strLanguage = m_listSearchButtonCaptionsByCaption(strCaption) IsSearchButtonCaption = True End If End Function %REM Function GetSearchBarHWND Description: Return the handle of the search bar %END REM Function GetSearchBarHWND() As Long GetSearchBarHWND = m_hWndSearchBar End Function %REM Function GetSearchBarHWNDAsHex Description: Return GetSearchBarHWND as Hex string %END REM Function GetSearchBarHWNDAsHex() As String GetSearchBarHWNDAsHex = Hex(m_hWndSearchBar) End Function %REM Function GetSearchBarButtonHWND Description: Return the handle of the search bar button %END REM Function GetSearchBarButtonHWND() As Long GetSearchBarButtonHWND = m_hWndSearchBarButton End Function %REM Function GetSearchBarButtonHWNDAsHex Description: Return GetSearchBarButtonHWND as Hex string %END REM Function GetSearchBarButtonHWNDAsHex() As String GetSearchBarButtonHWNDAsHex = Hex(m_hWndSearchBarButton) End Function %REM Function FindSearchBar Description: Find the Search Bar Control. It also find the Search-button, IF the Search Button Caption is correctly defined in the New-method :-) If Search Bar is found the method returns non-zero. If the Search Bar isn't visible, it returns 0 %END REM Function FindSearchBar() As Long Dim handle As CEW_TYPE_HANDLE Dim handleParent As CEW_TYPE_HANDLE Dim strLastNotesProgTitle As String Dim strTmp As String Dim rect As CEW_TYPE_RECT Dim lRc As Long Dim hDC As Long On Error Resume Next ' Enumerate all windows right now If EnumerateActiveWindow() = True Then ForAll elemHandleEntry In m_listHandles.m_listHandles handle = elemHandleEntry Select Case handle.m_strClass Case "IRIS.tedit": ' If thge parent has classname NotesSubprog then we have a search bar. NOTE how I also ' check for the last recorded NotesSubprog-title. That should match the title of the ' absolute parent (ie the Notes client, which reveal which view we are in). ' Finally I check for the visibility of the search bar. First I tried IsWindowVisible(), but ' that is pretty unreliable, since if ANY of it's ancestors has WS_VISIBLE set, IsWindowVisible() ' returns true. This is at best - extremely unprecise for my use. Therefore the GetClipBox is ' much better, since it actually reveal whether the pixels is visible to the user!! If IsElement(m_listHandles.m_listHandles(CStr(handle.m_hWndParent))) = True Then handleParent = m_listHandles.m_listHandles(CStr(handle.m_hWndParent)) If handleParent.m_strClass = "NotesSubprog" Then If InStr(m_strWindowTitleParent, strLastNotesProgTitle) > 0 Then hDC = CEW_W32_GetDC(handle.m_hWnd) If CEW_W32_GetClipBox(hDC, rect) = CEW_SIMPLEREGION Then m_hWndSearchBar = handle.m_hWnd FindSearchBar = m_hWndSearchBar End If lRc = CEW_W32_ReleaseDC(handle.m_hWnd, hDC) End If End If End If Case "NotesSubprog": ' For each window with class NotesSubprog I remember the title of the window. ' This will eventually be the same as the parent window title when looking for the ' search bar in a SPECIFIC VIEW! strTmp = GetWindowTitle(handle.m_hWnd) ' I don't add the blank titles, as the search bar parent actually don't a have a title! If Len(strTmp) > 0 Then strLastNotesProgTitle = strTmp End If Case "IRIS.bmpbutton": ' Also look for the Searh Button Caption. As soon as found, I don't care about finding ' it again If m_hWndSearchBarButton = 0 Then If IsElement(m_listHandles.m_listHandles(CStr(handle.m_hWndParent))) = True Then handleParent = m_listHandles.m_listHandles(CStr(handle.m_hWndParent)) If handleParent.m_strClass = "NotesSubprog" Then If InStr(m_strWindowTitleParent, strLastNotesProgTitle) > 0 Then If IsSearchButtonCaption(handle.m_strTitle) = True Then m_hWndSearchBarButton = handle.m_hWnd End If End If End If End If End If End Select ' If we have found BOTH the Search Bar edit control AND the Search Button Caption, we ' can exit. If FindSearchBar <> 0 And m_hWndSearchBarButton <> 0 Then Exit ForAll End ForAll End If End Function %REM Sub ClickSearchButton Description: Click the Search Button %END REM Sub ClickSearchButton() Stop If m_hWndSearchBarButton = 0 Then Exit Sub Call CEW_W32_PostMessage(m_hWndSearchBarButton, CEW_WM_ACTIVATE, CEW_WA_ACTIVE, 0) ' Press the enter key CEW_W32_Keybd_Event CEW_VK_RETURN, 0, 0, 0 ' Press ENTER CEW_W32_Keybd_Event CEW_VK_RETURN, 0, CEW_KEYEVENTF_KEYUP, 0 ' Eelease ENTER ' The SendMessage nor PostMessage BM_CLICK works... 'Call CEW_W32_SendMessage(m_hWndSearchBarButton, CEW_BM_CLICK, 0, 0) ' Neither does SendMessage nor PostMessage WM_KEYDOWN/WM_KEYUP ... 'Call CEW_W32_PostMessage(m_hWndSearchBarButton, CEW_WM_KEYDOWN, CEW_VK_RETURN, 0) 'Call CEW_W32_PostMessage(m_hWndSearchBarButton, CEW_WM_KEYUP, CEW_VK_RETURN, 0) End Sub %REM Sub SetSearchBarText Description: Places text on the Search Bar %END REM Sub SetSearchBarText(strText As String) If m_hWndSearchBar > 0 Then Call CEW_W32_SetWindowText(m_hWndSearchBar, strText) End If End Sub %REM Function GetSearchBarText Description: Return whatever text the search bar contains right now %END REM Function GetSearchBarText() As String If m_hWndSearchBar > 0 Then GetSearchBarText = GetWindowTitle(m_hWndSearchBar) End If End Function End Class