vb.net click button in another window

HaiFai
HaiFai used Ask the Experts™
on
Hi

I cant get this code to work .. i need to click yes button in another form called "Sample"
could anyone tell me what im doing wrong.

Imports System.Runtime.InteropServices

Public Class Form1
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Public Shared Function SendMessage(ByVal hWnd As Integer, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
    End Function
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, ByVal childAfter As IntPtr, ByVal lclassName As String, ByVal windowTitle As String) As IntPtr
    End Function
    Private Const WM_LBUTTONDOWN As Long = &H201
    Private Const WM_LBUTTONUP As Long = &H202
    Private Const BN_CLICKED As Integer = 245
    Sub CommunicateByClickButton(ByVal ApplicationTitle As String)
        Dim hwnd As IntPtr = IntPtr.Zero
        Dim hwndChild As IntPtr = IntPtr.Zero
        hwnd = FindWindow(Nothing, ApplicationTitle)
        If hwnd.Equals(IntPtr.Zero) Then
            MsgBox("Couldn't find the " & ApplicationTitle)
            Exit Sub
        Else
            AppActivate(ApplicationTitle)
        End If
        'Get a handle for the button with caption
        hwndChild = FindWindowEx(hwnd, 0, "Button", "Yes")
        'send BN_CLICKED message 
        If Not hwndChild.Equals(IntPtr.Zero) Then
            SendMessage(CType(hwndChild, Integer), BN_CLICKED, 0, IntPtr.Zero)
        End If
    End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  CommunicateByClickButton("Sample")
end sub
End Class

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
The code you're trying relies on the captions of the individual buttons to identify them. For example, it uses the following code to get a handle to the "Yes" button:

hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "Yes");
Which specifies "Button" for the name of the window class, and "Yes" for the name of the window (in the case of a button, this is the same as the caption text displayed on the button itself).

This code worked fine under Windows XP (and previous versions), where the buttons were identified with textual captions. The "Yes" button had a window name of "Yes", and thus "Yes" was displayed as the button's caption.

However, it looks like things have changed under Windows 7 (possibly under Vista as well)). Using Spy++ to investigate the Sample window confirms that the "Yes" button no longer has a window name of "Yes". In fact, it doesn't have a window name at all; the caption is NULL. Presumably, the new fancy look of the Forms required that buttons be custom drawn, thus the captions are no longer necessary to indicate which button corresponds to which function. The custom painting routines take care of drawing the necessary captions.

Since no button can be found with the window text you specified, a value of 0 (NULL) for (HwndChild) is returned for the window handle.

Author

Commented:
Is there any alternate way to click that yes button from .net ?
Commented:
the class names are auto-generated. You can't use FindWindowEx(), you have to iterate the controls with EnumChildWindows() and GetClassName().

COPY AND PASTE THIS CODE THEN TRY IT;

Imports System.Threading
Imports System.IO
Imports System.ComponentModel
Imports System.Text
Imports System.Runtime.InteropServices

Partial Public Class Form1
  Inherits Form

  <DllImport("user32")> _
  Public Shared Function EnumChildWindows(ByVal window As IntPtr, ByVal callback As EnumWindowProc, ByVal i As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
  End Function

  <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
  Private Shared Function GetClassName(ByVal hWnd As IntPtr, ByVal lpClassName As StringBuilder, ByVal nMaxCount As Integer) As Integer
  End Function

  <DllImport("user32.dll", SetLastError:=True)> _
  Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
  End Function
  Private hWnd As IntPtr = FindWindow(Nothing, "Sample")

  <DllImport("user32.dll", SetLastError:=True)> _
  Private Shared Function PostMessage(ByVal hWnd As IntPtr, <MarshalAs(UnmanagedType.U4)> ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
  End Function

  Const WM_click As Integer = 245
  Const WM_KEYUP As Integer = &H101
  Const WM_CHAR As Integer = &H102

  Public Sub New()
    InitializeComponent()
   
  End Sub


  ''' <summary>
  ''' Returns a list of child windows
  ''' </summary>
  ''' <param name="parent">Parent of the windows to return</param>
  ''' <returns>List of child windows</returns>
  Public Shared Function GetChildWindows(ByVal parent As IntPtr) As List(Of IntPtr)
    Dim result As New List(Of IntPtr)()
    Dim listHandle As GCHandle = GCHandle.Alloc(result)
    Try
      Dim childProc As New EnumWindowProc(AddressOf EnumWindow)
      EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle))
    Finally
      If listHandle.IsAllocated Then
        listHandle.Free()
      End If
    End Try
    Return result
  End Function

  ''' <summary>
  ''' Callback method to be used when enumerating windows.
  ''' </summary>
  ''' <param name="handle">Handle of the next window</param>
  ''' <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
  ''' <returns>True to continue the enumeration, false to bail</returns>
  Private Shared Function EnumWindow(ByVal handle As IntPtr, ByVal pointer As IntPtr) As Boolean
    Dim gch As GCHandle = GCHandle.FromIntPtr(pointer)
    Dim list As List(Of IntPtr) = TryCast(gch.Target, List(Of IntPtr))
    If list Is Nothing Then
      Throw New InvalidCastException("GCHandle Target could not be cast as List<IntPtr>")
    End If
    list.Add(handle)
    ' You can modify this to check to see if you want to cancel the operation, then return a null here
    Return True
  End Function

  ''' <summary>
  ''' Delegate for the EnumChildWindows method
  ''' </summary>
  ''' <param name="hWnd">Window handle</param>
  ''' <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
  ''' <returns>True to continue enumerating, false to bail.</returns>
  Public Delegate Function EnumWindowProc(ByVal hWnd As IntPtr, ByVal parameter As IntPtr) As Boolean


  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    For Each child As IntPtr In GetChildWindows(FindWindow(Nothing, "Sample"))
      Dim sb As New StringBuilder(100)
      GetClassName(child, sb, sb.Capacity)

      If sb.ToString.Contains("BUTTON") Then
        Dim wparam As UInteger = 0 << 29 Or 0
        PostMessage(child, WM_click, 0, IntPtr.Zero)
      End If
    Next
  End Sub
End Class

Open in new window

Author

Commented:
Nice thanks, it seems to work...
just need to change  
'If sb.ToString.Contains("BUTTON") Then' to
'If sb.ToString.Contains("Button") Then'

there seems to be one bug, if window is activated it will return "No" and if its not it will return "yes"
but its on since app i need to click is in backround it does not matter.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial