# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  Find Window handle by Partial Caption

## RobDog888

I didnt see any code in here yet for this so here it is. 
It will find the first window handle that contains the string passed.

For ex. if you want to find a window handle of Outlook but you dont know 
which folder is currently being viewed (the folder name preceds ' - Microsoft 
Outlook') you can go and pass " - Microsoft Outlook" and it will find Outlooks 
handle if it is running.


```
Option Explicit
'<VB/OUTLOOK GURU 01/30/2004 - CODE TO FIND WINDOW HANDLE BASED ON PARTIAL WINDOW CAPTION>
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) 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 GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long

Private Const GW_HWNDNEXT = 2

Private Sub Command1_Click()
    Dim lhWndP As Long
    If GetHandleFromPartialCaption(lhWndP, " - Microsoft Outlook") = True Then
        MsgBox "Found Window Handle: " & lhWndP, vbOKOnly + vbInformation
    Else
        MsgBox "Window ' - Microsoft Outlook' not found!", vbOKOnly + vbExclamation
    End If
End Sub

Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
    Dim lhWndP As Long
    Dim sStr As String
    GetHandleFromPartialCaption = False
    lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
    Do While lhWndP <> 0
        sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
        GetWindowText lhWndP, sStr, Len(sStr)
        sStr = Left$(sStr, Len(sStr) - 1)
        If InStr(1, sStr, sCaption) > 0 Then
            GetHandleFromPartialCaption = True
            lWnd = lhWndP
            Exit Do
        End If
        lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
    Loop
End Function
```

----------


## manavo11

Don't know if it's the same or similar but http://www.vbforums.com/showpost.php...7&postcount=36

----------


## RobDog888

I hadn't seen that code yet. I will check it out later when I get some time. 
Probably similar logic though, oh well.   :Frown:

----------


## Krass

Hi RobDog888,

The solution you suggested works #1 for me - Thanks.  I have a question for you now tho.  Once I've found the window I want, I am now interested in doing the same procedure, but now I want to find CONTROLS handle inside that window, based on a partial caption.

Ex.:  The window in question has 4 buttons, always with different restaurant names as captions.  I'd need to find the button that has, for exemple, "Burger" in the button caption.  I used WinInspector to watch the window and I can see the buttons captions correctly.

Do you have an idea on how I could achieve that?

Thanks a lot.

----------


## RobDog888

There is a property called a Control ID. This is the ID of the control and doesnt change. If you can see it with WinInspector then there is an API call to get it - GetDlgCtrlID.

----------


## Krass

If I got that right, I need to use the GetDlgCtrlID API to get the control ID?  If so, then I won't need to use it since Winspector Spy gives me that ID number, and you were right, it never changes - thanks for the tip.

But now, I guess I'd need some of your professionnal help on how to use that ID number.  I will need to check the caption of that button (that I will extract and use in a variable).

Afterward, when all of this is working well, I will need to click that button.  I normally use mouse_event MOUSEEVENTF_MOVE (considering I have the X,Y coordinates) to click those buttons, but it'd be great if an API could click it for me.  That way it wouldn't move my mouse on the screen and I could work at the same time.

The Control ID I want to extract the caption and click is "4001".

Thanks for your very useful help

----------


## RobDog888

Yes, you can SendMessage with the BM_CLICK parameter to do the button click without moving your mouse at all.

Not exactly, but the way it works is to enumerate the child windows checking each's DlgCtlID for a match of "4001". When you find the match then you use that handle to GetWindowText to retrieve the button text. Then that same handle can be used to SendMessage to click it.

 :Smilie:

----------


## Krass

I played with this quite a bit and things are progressing - thanks.

I got a small problem.
I am pretty sure you can easily point me in the right direction again on that one.  You suggested to use SendMessage and BM_CLICK to click the buttons.  That worked well, but only on SOME controls.  Winspector detects some controls with the "Button" class name and these are working pretty good.

BUT there are some other buttons I want to click that are using the "AfxWnd42" class name.  BM_CLICK doesn't work on those.  Any idea on how to click those?

btw, thank you for replying my posts so quickly - I'm progressing a whole lot more faster.

----------


## RobDog888

Sometimes I find that I have to send the bm_click message 2x but dont try on some buttons that do an action other then a close or cancle.  :Wink: 

I came across this 



> Those Afx window classes were custom classes distributed with early versions of MFC, before the "Common Controls" concept was introduced.
> 
> Many people have tried to "read" text from them but you CAN'T, not with messages anyway! 
> 
> They're almost certainly owner-drawn - the text is not delivered by messages, it's "hand-drawn" into the window's DC


And that may well be the reason.  :Frown: 

About the only thing I see here is with subclassing the pother program which is aloot of work. Here i a good article on subclassing that may lead you to either dispare or guidence to a possible solution.

http://www.codeproject.com/system/ap..._unleashed.asp

Good luck as this is about as much help on it that I think I can give.  :Wink:

----------


## Krass

Strange because I WAS able to read the button caption of a Afx window class using GetText.  ...And clicking the control twice or more didn't help neither.

So if there's no other solution I have no choice but to stick with those mouse_event MOUSEEVENTF_MOVE & CLICK methods.  Sure it would have been better to use SendMessage/BM_CLICK and free my mouse mouvements.  In that matter, would there be any api to "fake/simulate" a click to X,Y coords without affecting my mouse?

----------


## RobDog888

You could also send a WM_LBUTTONDOWN and WM_LBUTTONUP on the button to simulate a left mouse click without moving the mouse.

----------


## Krass

SendMessage wasn't working - PostMessage made the trick.

With winspector, along with all properties (ID, name, etc) there's a "Class specific" property which is set to "checked" or "unchecked".  Do you have an idea on how to retrieve that information?

Thanks.

----------


## RobDog888

Sorry but nope. Maybe a new thread in the API forum on this will get more input as members may think your just having issues with this code when its more about this special case.  :Wink:

----------


## tony007

Rob thank u for sharing this code. coul u tell me how to bring the window that we suppli its caption to front . I mean bring it to frong if it is behind windows explorers  or other windows .thank

----------


## _JiMMiE_

*How would i go about doing this in .NET 2005 ? this is what iv got so far having some trouble though.*


VB Code:
Option Strict Off
Option Explicit On
Public Class Form1
     '<VB/OUTLOOK GURU 01/30/2004 - CODE TO FIND WINDOW HANDLE BASED ON PARTIAL WINDOW CAPTION>
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, _
    ByVal lpWindowName As String) 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 GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
    Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
     Private Const GW_HWNDNEXT = 2
     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim lhWndP As Long
        If GetHandleFromPartialCaption(lhWndP, " - Microsoft Outlook") = True Then
            MsgBox("Found Window Handle: " & lhWndP, vbOKOnly + vbInformation)
        Else
            MsgBox("Window ' - Microsoft Outlook' not found!", vbOKOnly + vbExclamation)
        End If
     End Sub
    Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
        Dim lhWndP As Long
        Dim sStr As String
        GetHandleFromPartialCaption = False
        lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
        Do While lhWndP <> 0
        sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
            GetWindowText(lhWndP, sStr, Len(sStr))
            sStr = Left$(sStr, Len(sStr) - 1)
            If InStr(1, sStr, sCaption) > 0 Then
                GetHandleFromPartialCaption = True
                lWnd = lhWndP
                Exit Do
            End If
            lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
        Loop
    End Function
End Class

----------


## |2eM!x

whats the error?

----------


## _JiMMiE_

*this is what it looks like on my end



seem to be having trouble with these 2 lines*    


VB Code:
sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
 sStr = Left$(sStr, Len(sStr) - 1)

----------


## RobDog888

VB Code:
sStr = sStr.PadRight(GetWindowTextLength(lhWndP) + 1, Chr(0))

VB Code:
sStr = sStr.Length - 1
 :Smilie:

----------


## _JiMMiE_

*ok almost there i think but im getting this now on the new code that you gave me   *  
 "A call to PInvoke function 'WindowsApplication1!WindowsApplication1.Module1::GetWindowTextLength' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature."




Module

VB Code:
Module Module1
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, _
    ByVal lpWindowName As String) 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 GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
    Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
     Private Const GW_HWNDNEXT = 2
     Public Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
        Dim lhWndP As Long
        Dim sStr As String
        GetHandleFromPartialCaption = False
        lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
        Do While lhWndP <> 0
            sStr = sStr.PadRight(GetWindowTextLength(lhWndP) + 1, Chr(0))
            GetWindowText(lhWndP, sStr, Len(sStr))
            sStr = sStr.Length - 1
            If InStr(1, sStr, sCaption) > 0 Then
                GetHandleFromPartialCaption = True
                lWnd = lhWndP
                Exit Do
            End If
            lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
        Loop
    End Function
End Module


call function


VB Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim lhWndP As Long
        If GetHandleFromPartialCaption(lhWndP, "window looking") = True Then
            MsgBox("Found Window Handle: " & lhWndP, vbOKOnly + vbInformation)
        Else
            MsgBox("Window ' - Microsoft Outlook' not found!", vbOKOnly + vbExclamation)
        End If
     End Sub

----------


## RobDog888

Ok, just noticed that you are using the VB 6 version of the API calls. You need to use the VB.NET syntax.

VB Code:
Option Explicit On 
Option Strict On
 Imports System.Runtime.InteropServices
 Module Module1
 Inherits System.Windows.Forms.Form
 <DllImport("user32", EntryPoint:="GetWindowTextLength")> _
Private Shared Function GetWindowTextLength  ( _
                      ByVal hwnd As Int32) As Int32
End Function
 <DllImport("user32", EntryPoint:="FindWindow")> _
Private Shared Function FindWindow ( _
                      ByVal lpClassName As String, _
                      ByVal lpWindowName As String) As Int32
End Function
 <DllImport("user32", EntryPoint:="GetWindowText")> _
Private Shared Function GetWindowText ( _
                      ByVal hwnd As Int32, _
                      ByVal lpString As String, _
                      ByVal cch As Int32) As Int32
End Function
 <DllImport("user32", EntryPoint:="GetWindow")> _
Private Shared Function GetWindow ( _
                      ByVal hwnd As Int32, _
                      ByVal wCmd As Int32) As Int32
End Function
 Private Const GW_HWNDNEXT As Int32 = 2
 '...

----------


## _JiMMiE_

*ok this is what im getting now 

thanks so much for your help *  





VB Code:
Module Module1
    Private Declare Function GetWindowTextLength Lib "user32.dll" Alias "GetWindowTextLengthA" ( _
                          ByVal hwnd As Int32) As Int32
     Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _
                          ByVal lpClassName As String, _
                          ByVal lpWindowName As String) As Int32
     Private Declare Function GetWindowText Lib "user32.dll" Alias "GetWindowTextA" ( _
                          ByVal hwnd As Int32, _
                          ByVal lpString As String, _
                          ByVal cch As Int32) As Int32
     Private Declare Function GetWindow Lib "user32.dll" ( _
                          ByVal hwnd As Int32, _
                          ByVal wCmd As Int32) As Int32
     Private Const GW_HWNDNEXT As Int32 = 2
      Public Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
        Dim lhWndP As Long
        Dim sStr As String
        GetHandleFromPartialCaption = False
        lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
        Do While lhWndP <> 0
            sStr = sStr.PadRight(GetWindowTextLength(lhWndP) + 1, Chr(0))
            GetWindowText(lhWndP, sStr, Len(sStr))
            sStr = sStr.Length - 1
            If InStr(1, sStr, sCaption) > 0 Then
                GetHandleFromPartialCaption = True
                lWnd = lhWndP
                Exit Do
            End If
            lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
        Loop
    End Function
End Module

----------


## _JiMMiE_

*lol just saw that you updated the code sorry let me try that now see if it helps thanks so much for your time*

----------


## RobDog888

Change the dimensioning of the sStr var like so.


VB Code:
Dim sStr As String = String.Empty

----------


## _JiMMiE_

*ok i got the code to run in debug but its not picking up my window i know the partial caption is right its same as the one i have for VB6 and it works..... its doing the else line like if it didnt find the window at all and im sure its there.*



VB Code:
Option Explicit On
Option Strict Off
 Imports System.Runtime.InteropServices
   Module Module1
      Private Const GW_HWNDNEXT As Int32 = 2
    <DllImport("user32", EntryPoint:="GetWindowTextLength")> _
    Public Function GetWindowTextLength( _
                          ByVal hwnd As Int32) As Int32
    End Function
     <DllImport("user32", EntryPoint:="FindWindow")> _
    Public Function FindWindow( _
                          ByVal lpClassName As String, _
                          ByVal lpWindowName As String) As Int32
    End Function
     <DllImport("user32", EntryPoint:="GetWindowText")> _
    Public Function GetWindowText( _
                          ByVal hwnd As Int32, _
                          ByVal lpString As String, _
                          ByVal cch As Int32) As Int32
    End Function
     <DllImport("user32", EntryPoint:="GetWindow")> _
    Public Function GetWindow( _
                          ByVal hwnd As Int32, _
                          ByVal wCmd As Int32) As Int32
    End Function
        Public Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
        Dim lhWndP As Long
        Dim sStr As String = String.Empty
        GetHandleFromPartialCaption = False
        lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
        Do While lhWndP <> 0
            sStr = sStr.PadRight(GetWindowTextLength(lhWndP) + 1, Chr(0))
            GetWindowText(lhWndP, sStr, Len(sStr))
            sStr = sStr.Length - 1
            If InStr(1, sStr, sCaption) > 0 Then
                GetHandleFromPartialCaption = True
                lWnd = lhWndP
                Exit Do
            End If
            lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
        Loop
    End Function
End Module


VB Code:
Public Class Form1
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim lhWndP As Long
        If GetHandleFromPartialCaption(lhWndP, "Play money") = True Then
            MsgBox("Found Window Handle: " & lhWndP, vbOKOnly + vbInformation)
        Else
            MsgBox("Window ' - Microsoft Outlook' not found!", vbOKOnly + vbExclamation)
        End If
     End Sub
End Class

----------


## RobDog888

Step through the "GetHandleFromPartialCaption" procedure checking the values of each var.

----------


## _JiMMiE_

*ok iv'e run over and over this and cant get it to work the same function in VB6 works fine the code runs in debug fine but GetHandleFromPartialCaption is always false here is what i have so far i put it all in the form thinkin the module was messing it up some how but it still does the same thing im pulling my hair out   ok here it is tell me what you think*



VB Code:
Option Explicit On
Option Strict Off
 Imports System.Runtime.InteropServices
Public Class Form1
    Inherits System.Windows.Forms.Form
     <DllImport("user32", EntryPoint:="GetWindowTextLength")> _
    Private Shared Function GetWindowTextLength( _
                          ByVal hwnd As Int32) As Int32
    End Function
     <DllImport("user32", EntryPoint:="FindWindow")> _
    Private Shared Function FindWindow( _
                          ByVal lpClassName As String, _
                          ByVal lpWindowName As String) As Int32
    End Function
     <DllImport("user32", EntryPoint:="GetWindowText")> _
    Private Shared Function GetWindowText( _
                          ByVal hwnd As Int32, _
                          ByVal lpString As String, _
                          ByVal cch As Int32) As Int32
    End Function
     <DllImport("user32", EntryPoint:="GetWindow")> _
    Private Shared Function GetWindow( _
                          ByVal hwnd As Int32, _
                          ByVal wCmd As Int32) As Int32
    End Function
     Private Const GW_HWNDNEXT As Int32 = 2
       Private Function GetHandleFromPartialCaption(ByRef lWnd As Int32, ByVal sCaption As String) As Boolean
        Dim lhWndP As Long
        Dim sStr As String = String.Empty
        GetHandleFromPartialCaption = False
        lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
        Do While lhWndP <> 0
            sStr = sStr.PadRight(GetWindowTextLength(lhWndP) + 1, Chr(0))
            GetWindowText(lhWndP, sStr, Len(sStr))
            sStr = sStr.Length - 1
            If InStr(1, sStr, sCaption) > 0 Then
                GetHandleFromPartialCaption = True
                lWnd = lhWndP
                Exit Do
            End If
            lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
        Loop
    End Function
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim lhWndP As Long
        GetHandleFromPartialCaption(lhWndP, "look window")
        MsgBox("HANDLE.. " & lhWndP, vbOKOnly + vbInformation)
    End Sub
 End Class

----------


## manavo11

Are you sure sStr.PadRight is the right way to go? It seems like the only change that was made, and reading the description (and not knowing .Net) it seems like it doesn't do the same as the VB6 code...

----------


## _JiMMiE_

Thanks for the reply..... im not real sure of what im doing wrong i was hoping maybe robdog could help maybe he has been busy   :Blush:  or just has not come up with a fix just yet, either way i dont have a problem waiting   :Smilie:

----------

