Windows Handle

ZeroEffect

Well-known member
Joined
Oct 24, 2004
Messages
180
Location
Detroit, MI
Thanks for taking a look :)


Right now I am using Send Keys to acomplish playing and stoping audio on one of my computers but the problem is it is the same key. So if the player has finished playing the audio and I turn off the switch the audio starts again. What I would like to do is get the window that has the play and stop buttons and "send a message" to the application telling it which button to "hit". Can this be done in .NET or would it be better to do this with vb6? My only catch is the program has to have hardware attached to it so I have to build the application install it try it and so on.


Thanks For any help you may be able to provide.

ZeroEffect
 
Is it possible to enumerate the controls of the window? Use spy++ and see if you can get the handle of the stop and play button.
 
Thanks

HJB417 said:
Is it possible to enumerate the controls of the window? Use spy++ and see if you can get the handle of the stop and play button.


Thanks, I didnt expect a response so soon. Ok well using Spy++ I couldnt get a handle on the buttons but I did get the window and the X,Y values of the mouse. So that is cool and I have figured out how to find a window and send a key to it, but how do I simulate a mouse click.

Here is my code.

Code:
    Public Structure MSG
        Public hwnd As Integer
        Public message As Integer
        Public wParam As Integer
        Public lParam As Integer
        Public time As Integer
        Public pt As Integer
    End Structure

    Private Const WM_KEYDOWN = &H100
    Private Const WM_KEYUP = &H101
    Public Const WM_CHAR = &H102
    Private Const WM_LBUTTONDOWN = &H201
    Private Const WM_LBUTTONUP = &H202

    Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
    Private Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hWnd1 As Integer, ByVal hWnd2 As Integer, ByVal lpsz1 As String, ByVal lpsz2 As String) As Integer
    Declare Function TranslateMessage Lib "user32.dll" (ByRef lpMsg As MSG) As Integer
    Declare Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As UIntPtr, ByVal lParam As IntPtr) As IntPtr
    Dim PTools_hWnd As Integer
    Dim myMsg As MSG = New MSG


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        PTools_hWnd = FindWindow(vbNullString, "Untitled - NotePad")  FindWindow("Notepad", vbNullString)
        Dim hwnd As Integer = FindWindow(vbNullString, "Untitled - NotePad") /// assuming you have notepad open.
        PTools_hWnd = FindWindowEx(hwnd, 0, "Edit", vbNullString)
        MsgBox(PTools_hWnd)
        If Not PTools_hWnd = 0 Then
            myMsg.hwnd = PTools_hWnd
            myMsg.message = WM_KEYDOWN 
            myMsg.wParam = Keys.B
            myMsg.lParam = 0
            Dim ret As Integer
            ret = TranslateMessage(myMsg)
            textbox1.text = ret
        End If
    End Sub

I am using Notepad as a test bed, Ill have to build the app "Blind" before installing it and testing. If I could get the simulated mouse click to work I would be good to go. But I cant seem to find the how, I am still looking while this is going on. I did find an example but it is in c? and I am un able to translate all of it.

http://www.computerhelp.forum/showthread.php?t=79579
 
Its C# code, sorry =(

Code:
/// <summary>
/// Performs a left-button mouse click.
/// </summary>
public void Click()
{
	//SetForegroundWindow();
	NativeMethods.mouse_event(NativeMethods.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, IntPtr.Zero);
	NativeMethods.mouse_event(NativeMethods.MOUSEEVENTF_LEFTUP, 0, 0, 0, IntPtr.Zero);
}


Code:
/// <summary>
/// Synthesizes mouse motion and button clicks.
/// </summary>
/// <param name="dwFlags">Specifies various aspects of mouse motion and button clicking.</param>
/// <param name="dx">Specifies the mouses absolute position along the x-axis or its amount of motion since the last mouse event was generated, depending on the setting of MOUSEEVENTF_ABSOLUTE.</param>
/// <param name="dy">Specifies the mouses absolute position along the y-axis or its amount of motion since the last mouse event was generated, depending on the setting of MOUSEEVENTF_ABSOLUTE.</param>
/// <param name="dwData">If dwFlags contains MOUSEEVENTF_WHEEL, then dwData specifies the amount of wheel movement. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user.</param>
/// <param name="dwExtraInfo">Specifies an additional value associated with the mouse event.</param>
[DllImport("user32.dll", SetLastError=true)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, IntPtr dwExtraInfo);

Code:
public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
public const uint MOUSEEVENTF_LEFTUP = 0x0004;
 
Well I understande some of what you posted but I am not folowwing 100%. I can send keyboard strokes all day but a mouse click grrr. Still working on it though.
 
using reflector....
Code:
Private Shared Sub Click()
      Class1.mouse_event(2, 0, 0, 0, IntPtr.Zero)
      Class1.mouse_event(4, 0, 0, 0, IntPtr.Zero)
End Sub

<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Sub mouse_event(ByVal dwFlags As UInt32, ByVal dx As UInt32, ByVal dy As UInt32, ByVal dwData As UInt32, ByVal dwExtraInfo As IntPtr)
End Sub

Private Const MOUSEEVENTF_LEFTDOWN As UInt32 = 2
Private Const MOUSEEVENTF_LEFTUP As UInt32 = 4

and import the System.Runtime.InteropServices namespace.
 
HJB417 said:
using reflector....
Code:
Private Shared Sub Click()
      Class1.mouse_event(2, 0, 0, 0, IntPtr.Zero)
      Class1.mouse_event(4, 0, 0, 0, IntPtr.Zero)
End Sub

<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Sub mouse_event(ByVal dwFlags As UInt32, ByVal dx As UInt32, ByVal dy As UInt32, ByVal dwData As UInt32, ByVal dwExtraInfo As IntPtr)
End Sub

Private Const MOUSEEVENTF_LEFTDOWN As UInt32 = 2
Private Const MOUSEEVENTF_LEFTUP As UInt32 = 4

and import the System.Runtime.InteropServices namespace.


Cool now I am getting mouse click but it will only click where the mouse is I need to set the x & y and get the click to happen there. If I set the x & y values it still only clicks under the mouse. Also this is only done on the screen not on a specific window. How can this be sent to a window. I found this "MOUSEEVENTF_ABSOLUTE" but no example of how to use it or what its value is. So I am still hunting away.

here is the spy++ capture
00020534 S WM_SetCursor hwnd:00020532 nHittest:HTMENU wMouseMsg:WM_LBUTTONDOWN

does this help?

Thanks for your help

ZeroEffect
 
HJB417 said:
try using Cursor.Position to set the mouse position, and then using mouse_event to emulate the clicking.


Thanks for this clip of info. I was able to move the cursor and then get a mouse click. This works great to a point. My app will be in the systray waiting for input and the window that the play controls are on is movable. So this could cause a problem. I was looking into trying to use Keys.Lbutton but again the use of this is not really clear. I have tried this in my "key Press" code but nothing happens. Now if find windows could give me the x,y of the found window/child window I could make this work. Its the holidays so my searching will be limited this comming weekend.

Any more thoughts,

Thanks

ZeroEffect
 
Thanks for the links. I was trying this.

Code:
    Declare Function GetWindowRect Lib "user32" Alias "GetWindowRect" (ByRef hwnd As Integer, ByRef lpRect As RECT) As Integer

But I could only get it to return zero values for the four side of a rectangle. Ill check those out later on tonight.

Thanks again,

ZeroEffect
 
reflectored c# code
Code:
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function GetWindowRect(ByVal hwnd As IntPtr, <Out> ByRef rectangle As Rectangle) As Boolean
End Function

<STAThread> _
Private Shared Sub Main(ByVal args As String())
      Dim rectangle1 As Rectangle
      If Not Class1.GetWindowRect(New IntPtr(658582), rectangle1) Then
            Throw New Win32Exception
      End If
End Sub

658582 was the handle of an instance of notepad I had. Include the following namespaces.

System;
System.Runtime.InteropServices;
System.Drawing;

and the dll
System.Drawing

and a valid handle to a win32 window and it should work.
 
argh

Thanks for your help with this, here is my code as of now.

Code:
The Imports

Imports System
Imports System.Runtime.InteropServices
Imports System.Drawing

Structure

    Public Structure MSG
        Public hwnd As IntPtr
        Public message As Integer
        Public wParam As Integer
        Public lParam As Integer
        Public time As Integer
        Public pt As Integer
    End Structure

Declare Functions

    Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr

    Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hWnd1 As IntPtr, ByVal hWnd2 As Integer, ByVal lpsz1 As String, ByVal lpsz2 As String) As IntPtr

    Declare Function GetWindowRect Lib "user32" Alias "GetWindowRect" (ByRef hwnd As IntPtr, ByRef lpRect As Rectangle) As Boolean

    Declare Function TranslateMessage Lib "user32.dll" (ByRef lpMsg As MSG) As Integer

    Declare Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As UIntPtr, ByVal lParam As IntPtr) As IntPtr

Declare Sub mouse_event Lib "user32.dll" (ByVal dwFlags As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal dwData As Integer, ByVal dwExtraInfo As IntPtr)

Const

Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Public Const WM_CHAR = &H102

Variables

Dim PTools_hWnd As IntPtr
Dim myMsg As MSG = New MSG

The Code

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Try
            Dim ret As Integer
            Dim Rec1 As Rectangle = New Rectangle
            Find Window

            Dim hwnd As IntPtr = FindWindow(vbNullString, "Untitled - NotePad") Using Notepad as example to real program
            PTools_hWnd = FindWindowEx(hwnd, 0, "Edit", vbNullString) finds child windows

            Set the send message Structure, works great for keystrokes
            myMsg.hwnd = PTools_hWnd
            myMsg.message = WM_KEYDOWN
            myMsg.wParam = Keys.L
            myMsg.lParam = 0

            ret = TranslateMessage(myMsg)

            Get Left, Right, Top and Bottom of Form1
            GetWindowRect(hwnd, Rec1)

            GetWindowRect returning zero values
            Tried both hwnd & PTools_hWnd

            MoveCursor((Rec1.Left + 25), (Rec1.Top + 25))
            mouse_event(2, 0, 0, 0, IntPtr.Zero)
            mouse_event(4, 0, 0, 0, IntPtr.Zero)

        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try

    End Sub

    Private Sub MoveCursor(ByVal Fx As Integer, ByVal Fy As Integer)
        If Me.Cursor.Equals(Cursors.Default) Then
            Me.Cursor = New Cursor(Cursor.Current.Handle)
            Cursor.Position = New Point(Fx, Fy)
            Me.Cursor = Cursors.Default
        End If
    End Sub

Using the code above I get the letter "l" enter in notepad and the mouse moved to the upper left hand corner and a mouse click. If the rec1 had values it would click "file" on the notepad menu.

any thoughts

Thanks

ZeroEffect
 
Ok, heres code I created in a console app
[VB]
Imports System
Imports System.Runtime.InteropServices
Imports System.Drawing
Imports System.Windows.Forms
Imports System.ComponentModel


Module Module1

<DllImport("user32.dll", SetLastError:=True)> _
Public Function FindWindowA(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Public Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Public Sub BringWindowToTop(ByVal window As IntPtr)
End Sub

<DllImport("user32.dll", SetLastError:=True)> _
Public Function FindWindowExA(ByVal hWnd1 As IntPtr, ByVal hWnd2 As Integer, ByVal lpsz1 As String, ByVal lpsz2 As String) As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Public Function GetWindowRect(ByVal hwnd As IntPtr, <Out()> ByRef rectangle As Rectangle) As Boolean
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Public Function TranslateMessage(ByRef lpMsg As Message) As Integer
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As UIntPtr, ByVal lParam As IntPtr) As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Public Sub mouse_event(ByVal dwFlags As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal dwData As Integer, ByVal dwExtraInfo As IntPtr)
End Sub

Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Public Const WM_CHAR = &H102

<STAThread()> _
Sub Main()
Try
Dim ret As Integer
Dim Rec1 As Rectangle = New Rectangle
Find Window

Dim hwnd As IntPtr = FindWindowA(vbNullString, "Untitled - NotePad") Using Notepad as example to real program
Dim PTools_hWnd As IntPtr = FindWindowExA(hwnd, 0, "Edit", vbNullString) finds child windows

Dim myMsg As Message = New Message
Set the send message Structure, works great for keystrokes
myMsg.HWnd = PTools_hWnd
myMsg.Msg = WM_KEYDOWN
myMsg.WParam = New IntPtr(Keys.L)
myMsg.LParam = IntPtr.Zero

ret = TranslateMessage(myMsg)

Get Left, Right, Top and Bottom of Form1
Dim success As Boolean = GetWindowRect(hwnd, Rec1)
Debug.WriteLine(String.Format("Rec1={0}", Rec1))

If Not success Then
Throw New Win32Exception
End If

GetWindowRect returning zero values
Tried both hwnd & PTools_hWnd

MoveCursor((Rec1.Left + 25), (Rec1.Top + 25))
If Not SetForegroundWindow(hwnd) Then
Throw New Win32Exception
End If
mouse_event(2, 0, 0, 0, IntPtr.Zero)
mouse_event(4, 0, 0, 0, IntPtr.Zero)

Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub

Private Sub MoveCursor(ByVal Fx As Integer, ByVal Fy As Integer)
Dim newPoint As Point = New Point(Fx, Fy)
Debug.WriteLine(String.Format("Old mouse position = {0}.", Cursor.Position))
Debug.WriteLine(String.Format("Moving mouse to {0}.", newPoint))
Cursor.Position = newPoint
Debug.Assert(newPoint.Equals(Cursor.Position), "The cursor position was not set to the desired location.")
End Sub

End Module
[/VB]

There are a few differences between your code and mine, the most important being the declaration of the native methods such as GetWindowRect. Note that I declare my methods using <DLLImport> and use the SetLastError attribute because all of those imported functions modify the error code (according to the msdn documentation). I needed to use BringWindowToTop to make sure the notepad window was visible before doing any mouse clicking otherwise the click could be sent to the wrong screen.

I also use the Message Structure instead of the MSG structure you declared. Nothing major, but its less code that needs to be maintained =]

I also use Debug.WriteLine to write debug info to any listeners (the output window in vs.net always listens) and I use Debug.Assert to make sure the cursor position was moved to the desired location.
 
Last edited by a moderator:
THANK YOU!

I was able to get it to work in a console app, just like you you created. Now when moving it over to a Form based app I am getting this error.

Code:
System.InvalidProgramException: Error: PInvoke item (field,method) must be Static.

Full message

System.InvalidProgramException: Error: PInvoke item (field,method) must be Static.
   at ProToolsController.Form1.FindWindowA(String lpClassName, String lpWindowName)
   at ProToolsController.Form1.Button1_Click(Object sender, EventArgs e) in C:\Local Documents\VB .NET Projects\ProToolsController\ProToolsController\Form1.vb:line 145

Line Causing the error,

Dim hwnd As IntPtr = FindWindowA(vbNullString, "Untitled - NotePad") Using Notepad as example to real program

I just wanted to give you an update I am currently looking into the error. If you have any thoughts feel free to let me know.

Thank you again,

ZeroEffect
 
I was able to fix the errors by changing the Public Functions to Shared Functions and all works great. Thank you again HJB417 for your help. I have learned alot from this process. Thank you again.

ZeroEffect
 
Crude small problem, this works great if my window caption never changes but it will be. I was think of that while driving today. So is it possible to grab a window by part of the caption? Ill be reading and looking into this a bit tomorrow.

Thanks for any more help you may give.

ZeroEffect
 
The caption may change but the handle of/to the window will most likely be the same for the life of the application, so try to use handles whenever possible - or - you can enumerate all of the child windows and compare the title with what youre looking for.
 
I could do that, now when you say life of the application do you mean from the time the shortcut is clicked and the program is running or untill the app is uninstalled. I am thinking its the ladder of the two but I wan to be sure. The reason I ask is this will be used by other people and if they close the app that I am looking for and the handle is reset well then my "add-on wont work.

Thanks for your help

ZeroEffect
 
Back
Top