ZeroEffect Posted December 20, 2005 Posted December 20, 2005 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 21, 2005 Posted December 21, 2005 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. Quote
ZeroEffect Posted December 21, 2005 Author Posted December 21, 2005 Thanks 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 didn't expect a response so soon. Ok well using Spy++ I couldn't 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. 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, I'll 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 can't 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.xtremedotnettalk.com/showthread.php?t=79579 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 21, 2005 Posted December 21, 2005 It's C# code, sorry =( /// <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); } /// <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 mouse's 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 mouse's 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); public const uint MOUSEEVENTF_LEFTDOWN = 0x0002; public const uint MOUSEEVENTF_LEFTUP = 0x0004; Quote
ZeroEffect Posted December 21, 2005 Author Posted December 21, 2005 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. Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 21, 2005 Posted December 21, 2005 using reflector.... 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. Quote
ZeroEffect Posted December 21, 2005 Author Posted December 21, 2005 using reflector.... 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 it's 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 21, 2005 Posted December 21, 2005 try using Cursor.Position to set the mouse position, and then using mouse_event to emulate the clicking. Quote
ZeroEffect Posted December 23, 2005 Author Posted December 23, 2005 try using Cursor.Position to set the mouse position' date=' and then using mouse_event to emulate the clicking.[/quote'] 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. It's the holidays so my searching will be limited this comming weekend. Any more thoughts, Thanks ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 23, 2005 Posted December 23, 2005 DLLImporting the GetWindowInfo Function will do the trick. I have code for this but it's in c#. The code is modified code from a Win32Window class that a microsoft developer had created. Quote
ZeroEffect Posted December 23, 2005 Author Posted December 23, 2005 Thanks for the links. I was trying this. 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. I'll check those out later on tonight. Thanks again, ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 24, 2005 Posted December 24, 2005 reflectored c# 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. Quote
ZeroEffect Posted December 24, 2005 Author Posted December 24, 2005 argh Thanks for your help with this, here is my code as of now. '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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 25, 2005 Posted December 25, 2005 (edited) Ok, here's code I created in a console app 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 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 it's 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. Edited December 26, 2005 by HJB417 Quote
ZeroEffect Posted December 26, 2005 Author Posted December 26, 2005 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. 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted December 26, 2005 Author Posted December 26, 2005 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 Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted December 27, 2005 Author Posted December 27, 2005 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? I'll be reading and looking into this a bit tomorrow. Thanks for any more help you may give. ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 27, 2005 Posted December 27, 2005 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 you're looking for. Quote
ZeroEffect Posted December 27, 2005 Author Posted December 27, 2005 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 it's 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 won't work. Thanks for your help ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 27, 2005 Posted December 27, 2005 The former (until the window is destroyed). Quote
ZeroEffect Posted December 28, 2005 Author Posted December 28, 2005 The former (until the window is destroyed). Damn, well he is my current thought process, I haven'y been able to test this but. I'm thinking that if I can find the program in the task manager get it's PID number I should be able to find the window and any child windows. Since I'm not at the computer with VB.Net on it I can't try anything but I don't see why that wouldn't be possible, I'll be doing some research on it. I know i reads something about get process ID or something of that nature. I'll hunt through the MNSD site tonight. Plaease feel free to comment on my thought process above and it you think it would work or if you have a better direction to go. Thanks again ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted December 28, 2005 Author Posted December 28, 2005 Ok well here is what I found and I think if I incorporate this into the other code it'll work just fine. Imports System.Diagnostics Dim returnValue() As Diagnostics.Process Dim Handle As IntPtr returnValue = Process.GetProcessesByName("notepad") Handle = returnValue(0).Handle Any thoughts about doing it this way? Thanks ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted December 29, 2005 Author Posted December 29, 2005 Opps I had to change this line. Handle = returnValue(0).Handle to Handle = returnValue(0).MainWindowHandle Again Thanks for you help ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
ZeroEffect Posted December 29, 2005 Author Posted December 29, 2005 New Problem Ok Eveything is great but, The window I need to grab is 1, a child window and 2, it has no caption. I have been lookinig into this, C# Code [DllImport("User32.dll")] public static extern Boolean EnumChildWindows(int hWndParent,Delegate lpEnumFunc,int lParam); but I can't seem to get it to work while trying to convert it to VB.NET <DllImport("user32.dll", SetLastError:=True)> _ Shared Function EnumChildWindows(ByVal hWndParent As IntPtr, ByVal lpEnumFunc As ?Not Sure?, ByVal lParam As Integer) As IntPtr End Function I have seen some examples but nothing it working for me. I am still reading up on it to learn more but any help is welcome. Thanks ZeroEffect Quote If you can't find it, Build It. There is no place Like 127.0.0.1 also don't forget 1 + 1 = 10
HJB417 Posted December 29, 2005 Posted December 29, 2005 Try using other properties, like the classname and the window dimensions. GetClassName. Quote
Recommended Posts