|
-
Oct 4th, 2006, 12:29 PM
#1
Thread Starter
No place like 127.0.0.1
-
Oct 4th, 2006, 05:55 PM
#2
Re: [2005] Controlling a program without an SDK or command line input
Interacting with external applications means using the Windows API. Many, many posts dedicated to the subject. Even a whole forum.
-
Oct 4th, 2006, 06:24 PM
#3
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Shba. Cut me some slack. It's been a while. Plus I've never done API in .NET, only in VB6. I'll do some research though.
Thanks as awlays JMC.
-
Oct 4th, 2006, 06:29 PM
#4
Re: [2005] Controlling a program without an SDK or command line input
Check out gigemboy's signature for various API tools and examples.
-
Oct 4th, 2006, 08:42 PM
#5
Re: [2005] Controlling a program without an SDK or command line input
Look into SendMessage, using the WM_LBUTTONUP and WM_LBUTTONDOWN constants in succession in order to simulate a click on a button. You would have to enumerate the controls in order to find the handle to the button you wish to press, and then send those two SendMessage commands in order to "click" the button. The main problem with API in .NET is getting the declarations correct, and APIViewer 2004 (in sig) should help with that. Just a matter of changing it to .NET mode in the options, and looking up the function you wish to use. Then just copy and paste...
You could also use WinID (in sig) or Spy++ in order to see the properties for that button, like the class and name, and try to retrieve the handle using that, or also find if it is in some sort of container control on the form to better know how far you have to drill down in order to get to it...
Or if youre feeling really adventurous, you can check out APISpy (in sig), which should autogenerate some code for you if you choose the button. I believe the output would be VB6-style, just be a matter of cleaning it up a little for .NET...
There is also an example in the .NET codebank on enumerating all windows and children, which might help when trying to find the handle to the button you are wanting...
Last edited by gigemboy; Oct 4th, 2006 at 08:56 PM.
-
Oct 4th, 2006, 09:23 PM
#6
Re: [2005] Controlling a program without an SDK or command line input
As far as your question on waiting for the app to finish, you can experiment with Process.WaitForInputIdle(). It should wait until the app is at an idle state before returning and running the code that you want, though not too sure what exactly designates a particular app as idle, especially if multithreading is involved and all the work is being done in the background with a "idle" and responsive interface. I usually use it to just wait for an application to start up, but you might be able to test it to see if it gets the job done or not. An example of how you can try to test is below:
VB Code:
'replace "firefox" with your process name
Dim Procs() As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessesByName("firefox")
Select Case Procs.Length
Case 0
MessageBox.Show("No instances found!")
Case 1
'only one instance, so run our code
Procs(0).WaitForInputIdle()
'run the code here that you wish to try after the app is done and idle
'function won't return until the app is at an idle state...
Case Is > 1
MessageBox.Show("Multiple instances found! What do you want to do?")
End Select
Essentially you would do this after you had added your files and clicked the button to start the conversion or encoding or whatever its doing. Then you run that piece after the tunebite app is running to see if you can get it to wait until its done and run the code you want (after youve added something there to test). Just something you can try...
-
Oct 5th, 2006, 02:51 AM
#7
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Thanks gigem. The WaitForInputIdle didn't work, but I thought of another idea: Is there any way to get the value of the CPU column that shows up in the taskmanager? That would make it easy for me to tell if it is working or not.
-
Oct 5th, 2006, 03:29 AM
#8
Re: [2005] Controlling a program without an SDK or command line input
Trying to figure out a non API way to do it, and looking at the Process class, I see a PrivilegedProcessorTime property that seems to be the sum of the total time that the process has taken on the processor. If the app is completely idle and not taking any cpu cycles, this property shouldn't increase at all. If it is running and taking processor cycles, you should see it increasing. You could find a way to first, check what the property is for that process before you click the button, and then check the property at a set interval in a timer when the external app is running. You should see it steadily increase each increment, so it would be a matter of stopping the timer when the previous PrivilegedProcessorTime property value is equal to the current PrivilegedProcessorTime property value (or really close to each other, since the accuracy is very small increments of time), and then running the code you want when the timer stops...
VB Code:
'sample code that you can add in the post above to check the privilegedprocessortime
MessageBox.Show(Procs(0).PrivilegedProcessorTime.ToString)
Last edited by gigemboy; Oct 5th, 2006 at 03:37 AM.
-
Oct 5th, 2006, 01:06 PM
#9
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Perfect. I can tell when the program is working or not really easily now. Now I need to get the button part working. I read a few articles and put together the code below. I get an error about Pinvoke and unmanged code when I run it though.
I get the error on this line: "ptrChild = GetWindow(ParentWindowHandle, GW_CHILD)"
Any thoughts?
VB Code:
Public Class Form1
Const GW_CHILD As Integer = 5
Const GW_HWNDNEXT As Integer = 2
Const WM_GETTEXT As Integer = &HD
Const WM_GETTEXTLENGTH As Integer = &HE
Const BM_SETSTATE As Integer = &HF3
Const WM_LBUTTONUP As Integer = &H202
Const WM_LBUTTONDOWN As Integer = &H201
Declare Auto Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Long) As IntPtr
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, _
ByVal wParam As IntPtr, ByRef lParam As IntPtr) As IntPtr
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, _
ByVal wParam As Integer, ByRef lParam As IntPtr) As Integer
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, _
ByVal wparam As Integer, ByVal lparam As System.Text.StringBuilder) As IntPtr
Public Function GetWindows(ByVal ParentWindowHandle As IntPtr) As IntPtr()
Dim ptrChild As IntPtr
Dim ptrRet() As IntPtr
Dim iCounter As Integer
'get first child handle...
ptrChild = GetWindow(ParentWindowHandle, GW_CHILD)
'loop through and collect all child window handles...
Do Until ptrChild.Equals(IntPtr.Zero)
'process child...
ReDim Preserve ptrRet(iCounter)
ptrRet(iCounter) = ptrChild
'get next child...
ptrChild = GetWindow(ptrChild, GW_HWNDNEXT)
iCounter += 1
Loop
'return...
Return ptrRet
End Function
Public Function GetWindowText(ByVal WindowHandle As IntPtr) As String
Dim ptrRet As IntPtr
Dim ptrLength As IntPtr
'get length for buffer...
ptrLength = SendMessage(WindowHandle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero)
'create buffer for return value...
Dim sbText As New System.Text.StringBuilder(ptrLength.ToInt32 + 1)
'get window text...
ptrRet = SendMessage(WindowHandle, WM_GETTEXT, ptrLength.ToInt32 + 1, sbText)
'get return value...
Return sbText.ToString
End Function
Public Sub ClickButton(ByVal ButtonHandle As IntPtr)
'send the left mouse button "down" message to the button...
Call SendMessage(ButtonHandle, WM_LBUTTONDOWN, 0, IntPtr.Zero)
'send the left mouse button "up" message to the button...
Call SendMessage(ButtonHandle, WM_LBUTTONUP, 0, IntPtr.Zero)
'send the button state message to the button, telling it to handle its events...
Call SendMessage(ButtonHandle, BM_SETSTATE, 1, IntPtr.Zero)
End Sub
Public Sub LaunchAndCloseRegSvr()
'launch our process, we will see a dialog box with an OK button...
Dim clsProcess As System.Diagnostics.Process = System.Diagnostics.Process.Start("Regsvr32", " /?")
'get an array containing the handles to each of the child windows of the dialog...
Dim ptrChildWindows() As IntPtr = GetWindows(clsProcess.MainWindowHandle)
'loop through each child window, looking for the OK button...
For iCounter As Integer = 0 To ptrChildWindows.Length - 1
'grab the current handle to process...
Dim ptrCurrent As IntPtr = ptrChildWindows(iCounter)
'get the window text...
Dim sText As String = GetWindowText(ptrCurrent)
'check to see if this is the button we are looking for...
If sText = "OK" Then
'click the button to close the dialog...
ClickButton(ptrCurrent)
'done deal...
Exit For
Else
Debug.WriteLine(sText)
End If
Next
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
LaunchAndCloseRegSvr()
End Sub
End Class
-
Oct 5th, 2006, 01:11 PM
#10
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
In your declaration for getwindow. Change wCmd to an integer.
Last edited by mpdeglau; Oct 5th, 2006 at 01:15 PM.
-
Oct 5th, 2006, 01:16 PM
#11
Re: [2005] Controlling a program without an SDK or command line input
Well from an immediate glance I see wCmd as Long being used instead of Integer. Switch it to Integer, since that is what it should be in .NET. Also not sure of Auto Function is required. This was pulled from APIViewer 2004 as the declaration:
VB Code:
Declare Function GetWindow Lib "user32.dll" ( _
ByVal hwnd As Int32, _
ByVal wCmd As Int32) As Int32
Keeping the hwnd as IntPtr like you had should be fine and is actually a little more correct than using Int32....
-
Oct 5th, 2006, 02:35 PM
#12
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
After making that change, I get a NullReferenceException on this line: "For iCounter As Integer = 0 To ptrChildWindows.Length - 1"
-
Oct 5th, 2006, 03:08 PM
#13
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
Have you stepped through it to see what is happening? I took this from you
VB Code:
Dim clsProcess As System.Diagnostics.Process = System.Diagnostics.Process.Start("notepad")
'get an array containing the handles to each of the child windows of the dialog...
Dim ptrChildWindows() As IntPtr = GetWindows(clsProcess.MainWindowHandle)
The only thing I changed was I used notepad instead and it returned 2 values for me.
-
Oct 5th, 2006, 03:18 PM
#14
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Oddly, when I step through it I no longer get an error on that line, and when I run it with notepad I also get 2 values, but when I continue stepping through it I get an error on this line: "ptrLength = SendMessage(WindowHandle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero)"
It says EntryPointNotFoundException.
Here's how I changed GetWindow by the way:
VB Code:
Declare Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Int32) As IntPtr
-
Oct 5th, 2006, 03:26 PM
#15
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
It looks like the reason it is comming back null when you don't step through the app is because it doesn't have a MainWindowHandle yet. I added
VB Code:
Threading.Thread.Sleep(500)
between starting the process and using the handle and it worked. Note using 100 was not long enough.
-
Oct 5th, 2006, 03:42 PM
#16
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
That worked, but I still get the error that I noted in my last post.
-
Oct 5th, 2006, 03:44 PM
#17
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
YEah I'm still looking into that one. The error as far as I know means that you are using the wrong dll, but you're not. So I'm confused.
-
Oct 5th, 2006, 03:50 PM
#18
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Found it. This:
VB Code:
Declare Function SendMessage Lib "user32.dll" ( ... )
... Needed to be this ...
VB Code:
Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( ... )
-
Oct 5th, 2006, 03:52 PM
#19
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
The shouldn't alias doesn't matter I got it to work by declaring the apis like this:
VB Code:
Private Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As _
IntPtr, ByVal wMsg As Int32, ByVal wParam As IntPtr, ByVal lParam As _
IntPtr) As IntPtr
Private Declare Auto Function SendMessage Lib "user32" (ByVal hWnd As _
IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByRef lParam As _
IntPtr) As Integer
Private Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As _
IntPtr, ByVal wMsg As Integer, ByVal wparam As Integer, ByVal lparam As _
System.Text.StringBuilder) As IntPtr
Also I think your buttonup state might be wrong. The button went down for me, but didn't go back up.
-
Oct 5th, 2006, 03:54 PM
#20
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Okay. Now everything works except for the ButtonPress method. I tried walking through it to automatically give it some pause inbetween each SendMessage call, and it still only pushed the button down (I know this because the image of the button is depressed). Something isn't working about WM_LBUTTONUP. Not sure....
-
Oct 5th, 2006, 03:57 PM
#21
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Nevermind. When I actually added a Sleep to it (instead of walking through it) it worked fine on RegSvr32. Now I just need to find the name of the button on TuneBite. Hopefully it is a button and not an image or something... Not sure what I would do then.
EDIT: I also had to add this line: "Call SendMessage(ButtonHandle, BM_SETSTATE, 1, IntPtr.Zero)" After the button down (instead of just after the button up). This made it register the button down, then receive the button up message properly.
-
Oct 5th, 2006, 04:00 PM
#22
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
-
Oct 5th, 2006, 04:04 PM
#23
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Sigh... I need something other than the buttons name to identify it. They are all named with a blank string. I guess I could try pressing all of them until I figured out which index number was which button, but that doesn't seem very reliable. Any other identifying marks of a button? Besides just it's hWnd?
-
Oct 5th, 2006, 04:17 PM
#24
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
I just cycled through all 15 of the "buttons" and none of them were anything that responded to the BUTTONUP and BUTTONDOWN commands.
-
Oct 5th, 2006, 04:19 PM
#25
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
I doubt it will work, but try
[Highlight=VB]
Const WM_LBUTTONDBLCLK As Int32 = &H203
[Highlight=VB]
instead of button up and down
-
Oct 5th, 2006, 04:47 PM
#26
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Nope. As sloppy as this is, is there any way to control the mouse and send message to the mouse to click?
Or is there some other way to tell which one of the 15 controls is the image/button I need and then I can experiment with what commands to send to it?
-
Oct 6th, 2006, 12:38 PM
#27
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Okay. I figured out how to move the cursor. My last question is how to change the position of the external application window. I ran across a few APIs that look like they could do the job, but I'm not sure which to use.
-
Oct 6th, 2006, 12:48 PM
#28
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
I couldn't tell you for sure, but I would start with SetWindowPos.
-
Oct 6th, 2006, 01:22 PM
#29
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Okay. I can move the mouse. And the window has a default position that I can work with. The only problem remaining is that I can't simulate a click of the mouse, based on where the mouse is. Is that even possible?
Is it possible to send message to the cursor to tell it to click? I can move it to the right position using Windows.Forms.Cursor.Position, but there is not method or property to simulate a click as far as I can tell.
-
Oct 6th, 2006, 01:27 PM
#30
Frenzied Member
Re: [2005] Controlling a program without an SDK or command line input
Take a look at this thread #2
-
Oct 6th, 2006, 02:43 PM
#31
Re: [2005] Controlling a program without an SDK or command line input
Well if you have the cursor position over the button you want, you can try the WindowFromPoint API in order to get a handle to the window that the cursor is over. I think it returns the "most childish" handle (if you get my drift), which should be the handle to the button if the cursor is over it, as opposed to returning the parent window handle or something like that, although not 100% sure. I checked an old prog I tried and clicked a button on a form using GetCursorPos and WindowFromPoint, and it seemed to work, retrieving the handle to the button and not the parent container window, but in reading the documentation, I have some doubts, and don't have the prog anymore that the code was ran with to test. You can try it out and see if it works...
VB Code:
'pass in the x and y coordinates of your mouse point
Declare Function WindowFromPoint Lib "user32.dll" (ByVal xPoint As Integer, _
ByVal yPoint As Integer) _
As IntPtr
If that doesn't work, you can also try ChildWindowFromPoint
Last edited by gigemboy; Oct 6th, 2006 at 02:53 PM.
-
Oct 6th, 2006, 03:32 PM
#32
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
This is sloppy, but it works:
VB Code:
Private Declare Sub mouse_event Lib "user32.dll" ( _
ByVal dwFlags As Int32, _
ByVal dx As Int32, _
ByVal dy As Int32, _
ByVal cButtons As Int32, _
ByVal dwExtraInfo As Int32)
Private Declare Function GetMessageExtraInfo Lib "user32" () As Long
Const MOUSEEVENTF_LEFTDOWN As Int32 = &H2
Const MOUSEEVENTF_LEFTUP As Int32 = &H4
Const MOUSEEVENTF_MIDDLEDOWN As Int32 = &H20
Const MOUSEEVENTF_MIDDLEUP As Int32 = &H40
Const MOUSEEVENTF_MOVE As Int32 = &H1
Const MOUSEEVENTF_ABSOLUTE As Int32 = &H8000
Const MOUSEEVENTF_RIGHTDOWN As Int32 = &H8
Const MOUSEEVENTF_RIGHTUP As Int32 = &H10
....
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
Threading.Thread.Sleep(500)
'Mouse Up
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
Threading.Thread.Sleep(500)
-
Oct 6th, 2006, 05:26 PM
#33
Re: [2005] Controlling a program without an SDK or command line input
Well its API, not easy to make it look "pretty" So does that wrap everything up?
-
Oct 6th, 2006, 05:35 PM
#34
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
Not quite. I knew I would run into more problems. That's why I left the thread open.
I can't get the SetFocus API to work. I think it might be because the program has a system tray icon and it's POSSIBLE that it's counting the icon as it's own form and attempting to set focus to that, but here's what I have:
VB Code:
Declare Function SetFocus Lib "user32.dll" ( _
ByVal hwnd As Int32) As Int32
...
SetFocus(Procs(0).MainWindowHandle.ToInt32)
MainWindowHandle returns an IntPtr... I had to convert it to an Integer to pass it to the API. Not sure what I should do.
Should I jump back to the earlier code about getting all the different handels on the form and set focus to all of them and hopefully one is the actual main window?
EDIT: And just for the record, Procs(0) does have the program attached to it.
-
Oct 6th, 2006, 05:48 PM
#35
Re: [2005] Controlling a program without an SDK or command line input
Well there was an API function I found a few days ago in response to another thread on a different forum, and it is SwitchToThisWindow. And also later down in the thread there was an issue with minimized (or hidden windows, something like that), and another person responded what to do in those circumstances, so it could be exactly what you're looking for...
Check out the thread here: http://www.codeguru.com/forum/showth...93#post1462493
Last edited by gigemboy; Oct 6th, 2006 at 05:52 PM.
-
Oct 6th, 2006, 05:57 PM
#36
Thread Starter
No place like 127.0.0.1
Re: [2005] Controlling a program without an SDK or command line input
That worked PERFECTLY. Awesome. Thanks a ton. The rest is easy.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|