I needed some code to find all the top windows of Excel, that is of class XLMAIN and then filter these by process id. This is because elsewhere I am writing some C# code to acquire an instance of Excel via its window handles but all I have been given is a process id. Because I like to test logic in VBA I have written a VBA version. I am happy to share.
To begin, I use FindWindowEx to find all the windows I am interested in. All Excel instances have a main window of a class XLMAIN, and all of these have a parent window of the desktop. So supply zero for the first argument to signify the desktop and supply XLMAIN as the third argument, the fourth argument ignored. The second argument is used but varies, it allows us to loop through multiple results, we supply the previous result to get the next result. So I wrote a function to collect these to a collection, the function is called AllWindowsByClass(). The function is paramterised so that it can find windows of other class types.
Once in possession of a collection of window handles, I want to filter by a process id that I have been given. GetWindowThreadProcessId is the windows API that is best for this. In the function FilterWindowHandlesByProcessId() I loop through the a collection of handles and filter them to a new collection.
Enjoy!
Option Explicit
Option Private Module
Private 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
Private Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Function AllWindowsByClass(ByVal sClass As String) As VBA.Collection
Dim colRet As VBA.Collection
Set colRet = New VBA.Collection
Dim hWndParent As Long
hWndParent = FindWindowEx(0, 0, sClass, vbNullString)
While hWndParent <> 0
colRet.Add hWndParent
hWndParent = FindWindowEx(0, hWndParent, sClass, vbNullString)
Wend
Set AllWindowsByClass = colRet
End Function
Private Function FilterWindowHandlesByProcessId(ByVal colWindowsHandles As VBA.Collection, ByVal lFilterProcessId As Long) As VBA.Collection
Dim colRet As VBA.Collection
Set colRet = New VBA.Collection
Dim lLoop As Long
For lLoop = 1 To colWindowsHandles.Count
Dim lWinHandle As Long
lWinHandle = colWindowsHandles.Item(lLoop)
Dim lProcessId As Long
GetWindowThreadProcessId lWinHandle, lProcessId
If lProcessId = lFilterProcessId Then
colRet.Add lWinHandle
End If
Next
Set FilterWindowHandlesByProcessId = colRet
End Function
Here are some test functions but they have hard coded values that were valid for me and I determined whilst looking at Spy++ and other Windows diagnostic tools. Nevertheless they demonstrate how to call the above functions.
'************************************************************************************************
'* TEST FUNCTIONS
'************************************************************************************************
Private Sub TestAllWindowsByClass()
Dim col As VBA.Collection
Set col = AllWindowsByClass("XLMAIN")
Debug.Assert col.Count = 2 'may differ for you!
End Sub
Private Sub TestFilterWindowHandlesByProcessId()
Dim colWinHandles As VBA.Collection
Set colWinHandles = AllWindowsByClass("XLMAIN")
Debug.Assert colWinHandles.Count = 2
Dim lTestProcessId As Long
lTestProcessId = 24272 ' a process currently running on my PC, probably will differ for you!
Dim colFiltered As VBA.Collection
Set colFiltered = FilterWindowHandlesByProcessId(colWinHandles, lTestProcessId)
Debug.Assert colFiltered.Count = 1
End Sub
No comments:
Post a Comment