APIs FindWindow and GetWindowText

D

Dave D-C

Hello,
I've had occasion to , given a Window Caption, to want the HWnd.
FindWindow works some of the time.
But my routine zSrch, using GetWindowText, searches all the
windows and finds some that FindWindow doesn't.
Can somebody explain this?

This is Excel97 on Win98. (Book1 is open)
The 1st Msgbox shows both methods working.
The 2nd Msgbox shows FindWindow doesn't find.

Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As
Long
Declare Function GetWindowTextLength Lib "user32" Alias
"GetWindowTextLengthA" ( _
ByVal hWnd As Long) As Long

Sub Sub1()
Call Sub2("Microsoft Excel - Book1") ' hWnds =
Call Sub2("Worksheet Menu Bar") ' hWnds <>
End Sub

Sub Sub2(WinCaption$) ' displays hWnds
Dim hWnd1&, hWnd2&
hWnd1 = zFind(WinCaption)
hWnd2 = zSrch(WinCaption)
MsgBox WinCaption & ": " & hWnd1 & ", " & hWnd2
End Sub

Function zFind&(WinCaption$)
zFind = FindWindow(vbNullString, WinCaption)
End Function

Function zSrch&(WinCaption$) ' search all windows
Dim hWnd&, iLen1%, iLen2%, sStr$
For hWnd = 0 To 9999 ' search
iLen1 = GetWindowTextLength(hWnd)
sStr = Space$(iLen1 + 1)
iLen2 = GetWindowText(hWnd, sStr, iLen1 + 1)
If iLen1 <> iLen2 Then Stop ' check
sStr = Left$(sStr, iLen1)
If sStr = WinCaption Then Exit For
Next hWnd
If hWnd = 10000 Then Stop ' window not found
' at this point hWnd has a caption of WinCaption
zSrch = hWnd
End Function
 
T

Tom Ogilvy

From MSDN:

FindWindow Function

The FindWindow function retrieves a handle to the top-level window whose
class name and window name match the specified strings. This function does
not search child windows. This function does not perform a case-sensitive
search.

http://msdn2.microsoft.com/en-us/library/ms633499.aspx

You function doesn't restrict itself to top level windows.
 
R

RB Smissaert

This code should do the job:

Option Explicit
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetWindow Lib "user32" _
(ByVal hWnd As Long, _
ByVal wCmd As Long) As Long
Private Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hWnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
Private Declare Function GetClassName Lib "user32" _
Alias "GetClassNameA" _
(ByVal hWnd As Long, _
ByVal lpClassName As String, _
ByVal nMaxCount As Long) As Long
Private Const GW_HWNDNEXT = 2
Private Const GW_CHILD = 5

Function FindWindowHwndLike(hWndStart As Long, _
ClassName As String, _
WindowTitle As String, _
Level As Long, _
lHolder As Long) As Long

'finds the first window where the class name starts with ClassName
'and where the Window title starts with WindowTitle, returns Hwnd
'-----------------------------------------------------------------
Dim hWnd As Long
Dim sWindowTitle As String
Dim sClassName As String
Dim r As Long
Static bFound As Boolean

If Level = 0 Then
bFound = False
End If

If bFound Then
Exit Function
End If

'Initialize if necessary. This is only executed
'when level = 0 and hWndStart = 0, normally
'only on the first call to the routine.
'----------------------------------------------
If Level = 0 Then
If hWndStart = 0 Then
hWndStart = GetDesktopWindow()
End If
End If

'Increase recursion counter
'--------------------------
Level = Level + 1

'Get first child window
'----------------------
hWnd = GetWindow(hWndStart, GW_CHILD)

Do While hWnd > 0 And bFound = False

'Search children by recursion
'----------------------------
lHolder = FindWindowHwndLike(hWnd, _
ClassName, _
WindowTitle, _
Level, _
lHolder)

'Get the window text
'-------------------
sWindowTitle = Space$(255)
r = GetWindowText(hWnd, sWindowTitle, 255)
sWindowTitle = Left$(sWindowTitle, r)

'get the class name
'------------------
sClassName = Space$(255)
r = GetClassName(hWnd, sClassName, 255)
sClassName = Left$(sClassName, r)

If (InStr(1, sWindowTitle, WindowTitle, vbBinaryCompare) > 0 Or _
sWindowTitle = WindowTitle) And _
(sClassName Like ClassName & "*" Or _
sClassName = ClassName) Then
bFound = True
FindWindowHwndLike = hWnd
lHolder = hWnd
Exit Function
End If

'Get next child window
'---------------------
hWnd = GetWindow(hWnd, GW_HWNDNEXT)
Loop

FindWindowHwndLike = lHolder

End Function


RBS
 
P

Peter T

When it comes to Toolbar windows I suspect there's more to finding them than
merely the issue of 'Level' and Child parent relations (levels & parentage
are not necessarily the same). Eg if you pass the classname "MsoCommandBar"
your result will probably be the same (subject only one instance of Excel).

Try undocking your main toolbar "Worksheet Menu Bar". I expect you will now
find that FindWindow will return a value, but not the same value as returned
with your use of GetWindowText. If you re-dock the toolbar FindWindow will
probably continue to return the same non-zero value.

I don't know your overall objective but whatever it is, if you want to
read/write to a toolbar window, you will probably find you need to know
whether the toolbar is currently docked before knowing which hWin to use.

You may find the routine posted in this link useful. Paste all the code into
a new module, run Test() to call MySpy (discard Sub Test2 at the bottom).

http://groups.google.co.uk/group/microsoft.public.excel.programming/msg/60ef
24582e3ca73f?dmode=source&hl=en

or if the above has wrapped -
http://tinyurl.com/23hqs2

Regards
Peter T


Dave D-C said:
Hello,
I've had occasion to , given a Window Caption, to want the HWnd.
FindWindow works some of the time.
But my routine zSrch, using GetWindowText, searches all the
windows and finds some that FindWindow doesn't.
Can somebody explain this?

This is Excel97 on Win98. (Book1 is open)
The 1st Msgbox shows both methods working.
The 2nd Msgbox shows FindWindow doesn't find.

Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As
Long
Declare Function GetWindowTextLength Lib "user32" Alias
"GetWindowTextLengthA" ( _
ByVal hWnd As Long) As Long

Sub Sub1()
Call Sub2("Microsoft Excel - Book1") ' hWnds =
Call Sub2("Worksheet Menu Bar") ' hWnds <>
End Sub

Sub Sub2(WinCaption$) ' displays hWnds
Dim hWnd1&, hWnd2&
hWnd1 = zFind(WinCaption)
hWnd2 = zSrch(WinCaption)
MsgBox WinCaption & ": " & hWnd1 & ", " & hWnd2
End Sub

Function zFind&(WinCaption$)
zFind = FindWindow(vbNullString, WinCaption)
End Function

Function zSrch&(WinCaption$) ' search all windows
Dim hWnd&, iLen1%, iLen2%, sStr$
For hWnd = 0 To 9999 ' search
iLen1 = GetWindowTextLength(hWnd)
sStr = Space$(iLen1 + 1)
iLen2 = GetWindowText(hWnd, sStr, iLen1 + 1)
If iLen1 <> iLen2 Then Stop ' check
sStr = Left$(sStr, iLen1)
If sStr = WinCaption Then Exit For
Next hWnd
If hWnd = 10000 Then Stop ' window not found
' at this point hWnd has a caption of WinCaption
zSrch = hWnd
End Function


----== Posted via Newsfeeds.Com - Unlimited-Unrestricted-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption
=----
 
D

dacromley

This code should do the job:
.. Function FindWindowHwndLike( ..

I don't know what you guys get for "moderating" this group, but I
recommend you get a raise.
This function is EXACTLY what I want. I spent quite a bit of time on
it and in so doing took out
(what I think to be) unneeded recursion variables and LIKE syntax.
So I humbly present my improved version. It seems to work the same as
yours.
But let me thank you again for a VERY enlightening post. D-C

Option Explicit

Private Declare Function GetDesktopWindow& Lib "user32" ()
Private Declare Function GetWindow& Lib "user32" ( _
ByVal hWnd As Long, _
ByVal wCmd As Long)
Private Declare Function GetWindowText& Lib "user32" _
Alias "GetWindowTextA" ( _
ByVal hWnd As Long, _
ByVal lpString As String, _
ByVal cch As Long)
Private Declare Function GetClassName& Lib "user32" _
Alias "GetClassNameA" ( _
ByVal hWnd As Long, _
ByVal lpClassName As String, _
ByVal nMaxCount As Long)
Private Const GW_HWNDNEXT = 2
Private Const GW_CHILD = 5

Dim zTraceRow&

Sub Test1()
zTraceRow = 0
ActiveSheet.Cells.Clear
MsgBox LoopFindWindow(0, "XLMAIN", "Microsoft Excel")
End Sub

Function LoopFindWindow(hWndStart&, ClassName$, WindowTitle$)
' based on FindWindowHwndLike by RB Smissaert
Dim hWndV&, hWndCh&, sWindowTitle$, sClassName$, r%
hWndV = hWndStart
' 1st time is 0
If hWndV = 0 Then hWndV = GetDesktopWindow()
Do While hWndV > 0 ' loop thru siblings
' Get the window text
sWindowTitle = Space$(255)
r = GetWindowText(hWndV, sWindowTitle, 255)
sWindowTitle = Left$(sWindowTitle, r)
' get the class name
sClassName = Space$(255)
r = GetClassName(hWndV, sClassName, 255)
sClassName = Left$(sClassName, r)
If 1 Then zTrace hWndV, sClassName, sWindowTitle
' if satisfactory, then done
If sWindowTitle Like WindowTitle & "*" And _
sClassName Like ClassName & "*" Then
LoopFindWindow = hWndV
Exit Function
End If
' check for child window
hWndCh = GetWindow(hWndV, GW_CHILD)
' if yes, use recursion
If hWndCh <> 0 Then hWndCh = LoopFindWindow(hWndCh, ClassName,
WindowTitle)
' if a hit, then done
If hWndCh <> 0 Then
LoopFindWindow = hWndCh
Exit Function
End If
' next sibling
hWndV = GetWindow(hWndV, GW_HWNDNEXT)
Loop
LoopFindWindow = 0 ' no hit
End Function

Sub zTrace(hWnd&, sC$, sT$)
zTraceRow = zTraceRow + 1
Cells(zTraceRow, 1) = hWnd
Cells(zTraceRow, 2) = sC
Cells(zTraceRow, 3) = sT
DoEvents
End Sub
 
R

RB Smissaert

As it worked and speed never was an issue I never looked if it could
be improved, but will have a look now and thanks in case it can.

RBS
 
D

Dave D-C

RB Smissaert said:
As it worked and speed never was an issue I never looked if it could
be improved, but will have a look now and thanks in case it can.

Insignificant speed issue, but for me, a clarity issue.
But it's probably because I spent so much time on doing it my way.
Thanks again, D-C
 
D

Dave D-C

D-C said:
Insignificant speed issue, ,,

What I meant to say is that there is an insignificant gain in speed
because the same NEXT/CHILD path is followed.

I have been obsessed with these Windows APIs since your (RBS)
"Function FindWindowHwndLike" post of Sep 9.
Here is a pretty program which charts the active Windows structure,
siblings left-to-right, children bottom to top. D-C

Option Explicit
Private Declare Function GetDesktopWindow& Lib "user32" ()
Private Declare Function GetWindow& Lib "user32" ( _
ByVal hWnd As Long, _
ByVal wCmd As Long)
Private Declare Function GetWindowText& Lib "user32" _
Alias "GetWindowTextA" ( _
ByVal hWnd As Long, _
ByVal lpString As String, _
ByVal cch As Long)
Private Declare Function GetClassName& Lib "user32" _
Alias "GetClassNameA" ( _
ByVal hWnd As Long, _
ByVal lpClassName As String, _
ByVal nMaxCount As Long)
Private Const GW_HWNDNEXT = 2
Private Const GW_CHILD = 5

Dim gChartName$, gVert%, gRow&

Sub ChartWindowsMain()
ThisWorkbook.Activate
Charts.Add
gChartName = ActiveChart.Name
ActiveChart.ChartType = xlXYScatterLines
ActiveChart.Location Where:=xlLocationAsNewSheet
ActiveChart.HasLegend = False
Sheets("sheet1").Activate
ActiveSheet.Cells.Clear
gVert = 0 ' vertical on chart
gRow = 1 ' row on sheet
Call ChartWindowsSub(0, 1, 1) ' hwnd, row, horiz
Sheets(gChartName).Activate
Beep
End Sub

Sub ChartWindowsSub(hWndStart&, pRow1&, pHoriz%)
' based on FindWindowHwndLike by RB Smissaert
' These const's are to flag certain windows on sheet1
Const ClassName = "Vba", WindowTitle = ""
Dim hWndV&, hWndCh&, sWindowTitle$, sClassName$, r%
Dim Horiz%, Vert%, Children As New Collection, nChildren%, tArray
gVert = gVert + 1 ' each set of children has different vertical
Horiz = pHoriz ' this set starts here
Vert = gVert ' and here
hWndV = hWndStart
' 1st time is 0
If hWndV = 0 Then hWndV = GetDesktopWindow()
Do While hWndV > 0 ' first, loop thru siblings and save children
' Get the window text
sWindowTitle = Space$(255)
r = GetWindowText(hWndV, sWindowTitle, 255)
sWindowTitle = Left$(sWindowTitle, r)
' get the class name
sClassName = Space$(255)
r = GetClassName(hWndV, sClassName, 255)
sClassName = Left$(sClassName, r)
Call zChart(Horiz, Vert, hWndV, sClassName, sWindowTitle)
' check for highlighting on sheet1
If sClassName Like ClassName & "*" And _
sWindowTitle Like WindowTitle & "*" Then
Cells(gRow - 1, 3).Interior.ColorIndex = 3 ' red
End If
' check for child and save it
hWndCh = GetWindow(hWndV, GW_CHILD)
If hWndCh <> 0 Then
nChildren = nChildren + 1
' using collection because it's easier with unknown dimension?
Children.Add Array(hWndCh, Horiz, Vert), Format(nChildren)
End If
Horiz = Horiz + 1
hWndV = GetWindow(hWndV, GW_HWNDNEXT)
Loop
' This set of siblings finished, addSeries
Call zAddSeries(pRow1, gRow - 1)
' Now go backwards thru children
' Going backwards prevents lines crossing
Do While nChildren > 0
' this is the parent node
tArray = Children(nChildren)
hWndV = tArray(0)
Horiz = tArray(1)
Vert = tArray(2)
Call zChart(Horiz, Vert, 0, "", "")
' Recursive call for siblings
Call ChartWindowsSub(hWndV, gRow - 1, Horiz)
nChildren = nChildren - 1
Loop
End Sub

Sub zChart(pHoriz%, pVert%, phWnd&, pC$, pT$)
' Populate sheet1
Cells(gRow, 1) = pHoriz
Cells(gRow, 2) = pVert
If phWnd <> 0 Then Cells(gRow, 3) = phWnd
Cells(gRow, 4) = pC
Cells(gRow, 5) = pT
gRow = gRow + 1
End Sub

Sub zAddSeries(pRow1&, pRow9&)
' Add series to chart
Sheets(gChartName).Activate
ActiveChart.SeriesCollection.NewSeries
With
ActiveChart.SeriesCollection(ActiveChart.SeriesCollection.Count)
.XValues = "=Sheet1!" & zR1R2C(pRow1, pRow9, 1)
.Values = "=Sheet1!" & zR1R2C(pRow1, pRow9, 2)
.MarkerBackgroundColorIndex = 1
.MarkerForegroundColorIndex = 1
.MarkerStyle = xlCircle
.MarkerSize = 4
.Border.ColorIndex = 1
.Border.Weight = xlThin
.Border.LineStyle = xlContinuous
End With
ActiveChart.ApplyDataLabels Type:=xlDataLabelsShowNone,
LegendKey:=False
Sheets("Sheet1").Activate
End Sub

Function zR1R2C$(pRow1&, pRow2&, pCol%)
' make R1C1:R2C1
zR1R2C = "R" & Format(pRow1) & "C" & Format(pCol) & _
":R" & Format(pRow2) & "C" & Format(pCol)
End Function
 
R

RB Smissaert

Yes, interesting stuff, but didn't mean to cause an obsession ...
Better to put in the main Sub Application.ScreenUpdating = False
Also I get an error at ActiveChart.SeriesCollection.NewSeries
after some 2053 windows, 1004, application defined or object defined error.
Haven't looked yet why this is.

RBS
 
D

Dave D-C

RB Smissaert said:
Yes, interesting stuff, but didn't mean to cause an obsession ...
Better to put in the main Sub Application.ScreenUpdating = False
Also I get an error at ActiveChart.SeriesCollection.NewSeries
after some 2053 windows, 1004, application defined or object defined error.
Haven't looked yet why this is.

Well, rats. My WIN98 system has 300 windows with just excel running
and it looks great. With 8 applications running, the count goes up to
to just 1000 windows and the chart gets pretty crowded.

Maybe I'll work on a version that charts to a *.bmp file.
Or maybe a flatbed plotter :)
But I doubt it. I need to get a life. It's been great fun.
Regards, Dave D-C
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top