-
[RESOLVED] [2005] Sending Keys to other application Save
I'm having trouble finding a good process to do this, so maybe someone can help me. I need to enter a file path into a "Save As" dialog box that comes up from another application. I think I can handle the keystrokes, but I don't know how to find the right window.
This is basically trying to port VB6 code to 2005. The old way of doing it used something like this...
Code:
ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, Start, proc)
Do Until hWndApp <> 0
hWndApp = FindWindow(vbNullString, windText)
If hWndApp = 0 Then hWndApp = FindWindow(vbNullString, windText2)
Loop
This seems to be how the old code worked it, but I can't really find anything comperable to what it was doing.
-
Re: [2005] Sending Keys to other application Save
You should use MS Spy++ to identify the window heirarchy and class names etc. FindWindow will only find tha parent top level window. You need to use findWindowEx to find child windows/control.
-
Re: [2005] Sending Keys to other application Save
I think I've got the controls I need from Spy ++. Didn't know that tool existed. ^.^
The only thing left for me...I can't get a check box to get clicked. I can get the keys typed in the text box now and changed something in a drop down menu. However, I can't get to a check box I need checked because it's not in the tab order. I'm trying to use the SendInputToWindow class that TT(n) had posted elsewhere to click on it, but it's not clicking. I don't know if the name of the control is wrong or not...
Here's what I'm doing so far.
Code:
Dim s As SendInputToWindow
s = New SendInputToWindow
AppActivate("Save As")
System.Threading.Thread.Sleep(500)
My.Computer.Keyboard.SendKeys("C:\Josh.dxf", False)
System.Threading.Thread.Sleep(500)
My.Computer.Keyboard.SendKeys("{TAB}", False)
System.Threading.Thread.Sleep(500)
My.Computer.Keyboard.SendKeys("DXF", False)
System.Threading.Thread.Sleep(500)
s.MouseSend(s.CArr(s.GetHandle("Save As"), 1, "Selected Objects Only", 1))
-
Re: [2005] Sending Keys to other application Save
Oh I thought you would continue on with using the API solution as its a more stable then sendkeys. If its not in the tab order then you will have to use apis to directly check it.
-
Re: [2005] Sending Keys to other application Save
The problem I have...I didn't understand the API way of doing it...this is old code and I'm only a noob when it comes to programming right now. If you think it'll be more stable, I'll go with it...but maybe you could help explain how I should go about doing it.
Thanks.
-
Re: [2005] Sending Keys to other application Save
Sure no prob.
Using the FindWindow API it will find the window handle of your desired application by its caption and window class name. Using that partent window handle you can pass that with the FindWindowEx api to get the child window handles. You can then recall the findwindowex api to drill down the window heirarchy to the level where your desired control is located. Then once you have that conrols handle you can use that with SendMessage to send whatever you want to that control. IE: setting the text in a textbox or checking a checkbox :)
Just remember that window handles change with each instanciation of the windows. Since you are working with window handles and messaging there is more stability as it doesnt 100% rely upon the control having activation or focus like sendkeys does
-
Re: [2005] Sending Keys to other application Save
Hmmm...well, I guess I could just pull in the .bas file that the previous version was using...I guess my problem is that, API is kinda legacy for this. The main reason I'm trying to convert this old code is get things out of VB 6 and into .Net. While API will still work with .Net, I was just hoping there was an easier way to do it. For now, I guess I'll have to read up on API and how to reimplement it.
And, when I add the declare for the SendMessage, it tells me I can't use As Any in a declaration.
-
Re: [2005] Sending Keys to other application Save
APIs are not legacy. They are the core of unmanaged code.
With each new OS there will be new additional APIs. Vista just added 7000+ new APIs. .NET supports and works fine with APIs.
Legacy code would be considered VB 6 or earlier. ;)
-
Re: [2005] Sending Keys to other application Save
Yeah...which is the problem I seem to be running into. @_@ My god, it's hard to figure out...
Ok...I think I know what I'm doing...somewhat. I'll try to explain my problem a little more and see if that helps.
My program needs to automate a save feature in another program. It opens a "Save As" dialog that my program then needs to grab onto. I build the path to save the file to, I just need to insert it into the text box, change the file type, check a check box on the form, and then hit the save button.
The code to do all of this is in two parts. The first part is a BAS file (which I still don't know what a BAS is or what it does) that makes all of the declares to the API I need. I'll post that first.
Code:
Option Explicit
Public Const NORMAL_PRIORITY_CLASS = &H20&
Public Const INFINITE = -1&
Public Const WM_GETTEXT = &HD
Public Const WM_SETTEXT = &HC
Public Const BM_CLICK = &HF5
Public Const CB_FINDSTRING = &H14C
Public Const WM_CHAR = &H102
Public Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Public Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type
Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function SetFocusAPI Lib "user32" Alias "SetForegroundWindow" (ByVal hwnd As Long) As Long
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Public Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Function EnumChildProc(ByVal lhWnd As Long, ByVal lParam As Long) As Long
Dim retval As Long
Dim WinTitleBuf As String * 255
Dim WinTitle As String
Dim WinClassBuf As String * 255
Dim WinClass As String
Dim i As Long
Dim cboCount As Long
Dim srcstr As String
retval = GetClassName(lhWnd, WinClassBuf, 255)
WinClass = StripNulls(WinClassBuf)
retval = GetWindowText(lhWnd, WinTitleBuf, 255)
WinTitle = StripNulls(WinTitleBuf)
If WinTitle = "&Selected Objects Only" Then
SendMessage lhWnd, BM_CLICK, 0, 0
End If
If WinClass = "Edit" Then
gFileName = """" & gFileName & """"
SendMessage lhWnd, WM_SETTEXT, 0, ByVal gFileName
End If
If WinClass = "ComboBox" Then
srcstr = "DXF Files"
i = SendMessage(lhWnd, CB_FINDSTRING, -1, ByVal srcstr)
If i = -1 Then
Else
srcstr = Asc("d")
SendMessage lhWnd, WM_CHAR, srcstr, ByVal CLng(1)
End If
End If
If WinTitle = "&Open" Or WinTitle = "&Save" Then
gHandle = lhWnd
End If
EnumChildProc = True
End Function
Function StripNulls(OriginalStr As String) As String
If (InStr(OriginalStr, Chr(0)) > 0) Then
OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
End If
StripNulls = OriginalStr
End Function
This second snipit of code is the form code that calls the command to save it. The weird thing here is we use a file called savePict.exe to shell out and find the handle of the Save As dialog. We might be able to get rid of that if I can find an easier way to do this code.
Code:
Private Function ExecCmd(cmdline$, sendkeysStr)
Dim proc As PROCESS_INFORMATION
Dim Start As STARTUPINFO
Dim hInstance As Long
Dim processID As Long
Dim L As Long
Dim hWndApp As Long
Dim ret&
Dim windText As String
Dim windText2 As String
Dim lParam As Long
Dim lRet As Long
windText = "Open"
windText2 = "Save As"
Start.cb = Len(Start)
ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, Start, proc)
Do Until hWndApp <> 0
hWndApp = FindWindow(vbNullString, windText)
If hWndApp = 0 Then hWndApp = FindWindow(vbNullString, windText2)
Loop
Sleep 1000
SetFocusAPI hWndApp
gFileName = sendkeysStr
Sleep 1000
lRet = EnumChildWindows(hWndApp, AddressOf EnumChildProc, lParam)
Sleep 1000
SendMessage gHandle, BM_CLICK, 0, 0
Do
ret& = WaitForSingleObject(proc.hProcess, INFINITE)
DoEvents
Loop Until ret& <> 258
Call GetExitCodeProcess(proc.hProcess, ret&)
Call CloseHandle(proc.hProcess)
ExecCmd = ret&
End Function
-
Re: [2005] Sending Keys to other application Save
A bas file in both VB 6 and in .NET is nothing more then a Module. Its a stateless class file.
Looks like you have more going on in some other areas but that main function you have there is the code to do a Save or Open. Is it not working?
-
Re: [2005] Sending Keys to other application Save
While this might be a little complex of a process, the reason we did it was to not have Send Keys, as we needed people that ran this program to continue working in other programs while this, and the program it is trying to handle, works on what they're supposed to.
I'm more then willing to tear this code down and start from scratch, as long as it does what I need it to. I guess I just don't know where I need to start with it.
Again, sorry if I seem inept...I'm not...I'm just right out of school. ^_^;;
-
Re: [2005] Sending Keys to other application Save
And the main problem is that some of the declares, as well as definitions in the function in the seperate module, are not working. It's giving me an error stating that I can't use the "lparam As Any" in a declaration.
Thanks for the help so far.
-
Re: [2005] Sending Keys to other application Save
Ah, there is the other procedure. I'll look at it. Im sure we can just modify it but when you step through the code how far do you get until it fails?
No need to apologize ;)
-
Re: [2005] Sending Keys to other application Save
Ok, lets start with the api declarations.
The format of them will be a little diferent in .net. If you dont have it I would highly recommend downloading the API Viewer utility (link in my signature) as it has an option to change the syntax formatting for VB 6 or vb.net etc.
A long in vb 6 is now a Integer in .net (same capabilities). A Type is now a Structure etc.
So we can take the Type and modify it this way.
Code:
Public Structure PROCESS_INFORMATION
Public hProcess As Integer
Public hThread As Integer
Public dwProcessID As Integer
Public dwThreadID As Integer
End Structure
Now there is one other issue we need to do. We need to add an attribute to the unmanaged code declaration.
At the top of your Module you will include the Imports statement to reference the InteropServices class so we can use the attributes we need.
Code:
Imports System.Runtime.InteropServices
Module myModule
<StructLayout(LayoutKind.Sequential)> _
Public Structure PROCESS_INFORMATION
Public hProcess As Integer
Public hThread As Integer
Public dwProcessID As Integer
Public dwThreadID As Integer
End Structure
'Your other API code will go in here.
End Module
-
Re: [2005] Sending Keys to other application Save
Here is how you would format your API functions.
Code:
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="GetClassName")> _
Public Function GetClassName (ByVal hwnd As Int32, ByVal lpClassName As String, ByVal nMaxCount As Int32) As Int32
End Function
And thats about it for you code. Just aply that same logic formatting syntax and let me know.
-
Re: [2005] Sending Keys to other application Save
Quote:
I'm trying to use the SendInputToWindow class that TT(n) had posted elsewhere to click on it, but it's not clicking. I don't know if the name of the control is wrong or not...
Here's what I'm doing so far.
Code:
Dim s As SendInputToWindow
s = New SendInputToWindow
AppActivate("Save As")
System.Threading.Thread.Sleep(500)
My.Computer.Keyboard.SendKeys("C:\Josh.dxf", False)
System.Threading.Thread.Sleep(500)
My.Computer.Keyboard.SendKeys("{TAB}", False)
System.Threading.Thread.Sleep(500)
My.Computer.Keyboard.SendKeys("DXF", False)
System.Threading.Thread.Sleep(500)
s.MouseSend(s.CArr(s.GetHandle("Save As"), 1, "Selected Objects Only", 1))
The parameters look wrong.
First is the wName parameter. What is the title of the main window? I dont think you need GetHandle in there. The CArr can get you the handle of any child(check box) window, by inserting all strings for the wNames, not handles.
Second is the wIndex param. Use 1 if there is only one title with that name.
Third is the isMenu param. Use 1 for a menu, and -1 for a system menu.
Fourth is the mButtons param. Specify "l" for left click.
And so on....
You should probably use SendMessage or PostMessage API in your case as RobDogg said, since it would be shorter and more direct. Just add those API to the class to make it easier.
BTW "As Any", you can replace with "As Object", or the specific data type that the method is using, ie String, Int32 etc...
Oh yeah and AppActivate is practically useless, and not very reliable.
Try
Code:
s.WindowForceSetForeground(s.GetHandle("WindowTitleHere"))
Dont hesitate to ask questions here.:wave:
-
Re: [2005] Sending Keys to other application Save
CrystalBlue,
I see what happened, you're using a getforegroundwindow command example that I listed. I dont think it will work in this case.
This should make it easier to try and use:
Code:
Dim sArray() As String, hwnd As Int32
sArray = s.CArr("Save As", 1, "Selected Objects Only", 1)
hwnd = s.GetHandle(sArray)
s.MouseSend(hwnd)
You should be able to use this even if you use SendMessage/PostMessage.
Make sure that the checkbox "Selected Objects Only", is not the child window of a frame(GroubBox).
Otherwise the array would be:
Code:
sArray = s.CArr("Save As", 1, "GroupBox1" , 1, "Selected Objects Only", 1)
You may need to use the class name if there is no text on the groupbox/frame. Use spy++ to find the window paths.
-
Re: [2005] Sending Keys to other application Save
Bah, I missed that part that he stated he was using your code in #3. I thought it was the legacy code originally posted.
-
Re: [2005] Sending Keys to other application Save
Sorry. I should have been more clear. :(
I changed the declares in the old code, but I'm still having a problem. Here's what that old code looks like with the changes you suggested and put into 2005.
This is the module that I renamed and placed in the code.
Code:
Option Explicit On
Imports System.Runtime.InteropServices
Module WindowHandler
Public gFileName As String
Public gHandle As String
Public Const NORMAL_PRIORITY_CLASS = &H20&
Public Const INFINITE = -1&
Public Const WM_GETTEXT = &HD
Public Const WM_SETTEXT = &HC
Public Const BM_CLICK = &HF5 '0x00F5
Public Const CB_FINDSTRING = &H14C
Public Const WM_CHAR = &H102
Public Structure STARTUPINFO
Public cb As Int32
Public lpReserved As String
Public lpDesktop As String
Public lpTitle As String
Public dwX As Int32
Public dwY As Int32
Public dwXSize As Int32
Public dwYSize As Int32
Public dwXCountChars As Int32
Public dwYCountChars As Int32
Public dwFillAttribute As Int32
Public dwFlags As Int32
Public wShowWindow As Integer
Public cbReserved2 As Integer
Public lpReserved2 As Int32
Public hStdInput As Int32
Public hStdOutput As Int32
Public hStdError As Int32
End Structure
<StructLayout(LayoutKind.Sequential)> Public Structure PROCESS_INFORMATION
Public hProcess As Int32
Public hThread As Int32
Public dwProcessID As Int32
Public dwThreadID As Int32
End Structure
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="GetClassName")> Public Function GetClassName(ByVal hwnd As Int32, ByVal lpClassName As String, ByVal nMaxCount As Int32) As Int32
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="GetWindowText")> Public Function GetWindowText(ByVal hwnd As Int32, ByVal lpString As String, ByVal cch As Int32) As Int32
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="SendMessage")> Public Function SendMessage(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Object) As Int32
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="FindWindow")> Public Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As Int32
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="SetFocusAPI")> Public Function SetFocusAPI(ByVal hwnd As Int32) As Int32
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="EnumChildWindows")> Public Function EnumChildWindows(ByVal hWndParent As Int32, ByVal lpEnumFunc As Int32, ByVal lParam As Int32) As Int32
End Function
<DllImport("kernal32.dll", CharSet:=CharSet.Auto, EntryPoint:="CreateProcessA")> Public Function CreateProcessA(ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Int32, ByVal lpThreadAttributes As Int32, ByVal bInheritHandles As Int32, ByVal dwCreationFlags As Int32, ByVal lpEnvironment As Int32, ByVal lpCurrentDirectory As String, ByVal lpStartupInfo As STARTUPINFO, ByVal lpProcessInformation As PROCESS_INFORMATION) As Int32
End Function
<DllImport("kernal32.dll", CharSet:=CharSet.Auto, EntryPoint:="Sleep")> Public Sub Sleep(ByVal dwMilliseconds As Int32)
End Sub
<DllImport("kernal32.dll", CharSet:=CharSet.Auto, EntryPoint:="WaitForSingleObject")> Public Function WaitForSingleObject(ByVal hHandle As Int32, ByVal dwMilliseconds As Int32) As Int32
End Function
<DllImport("kernal32.dll", CharSet:=CharSet.Auto, EntryPoint:="GetExitCodeProcess")> Public Function GetExitCodeProcess(ByVal hProcess As Int32, ByVal lpExitCode As Int32) As Int32
End Function
<DllImport("kernal32.dll", CharSet:=CharSet.Auto, EntryPoint:="CloseHandle")> Public Function CloseHandle(ByVal hObject As Int32) As Int32
End Function
<DllImport("kernal32.dll", CharSet:=CharSet.Auto, EntryPoint:="GetTempPath")> Public Function GetTempPath(ByVal nBufferLength As Int32, ByVal lpBuffer As String) As Int32
End Function
Function EnumChildProc(ByVal lhWnd As Int32, ByVal lParam As Int32) As Int32
' this isn't the best way but it works and no time to enhance
Dim retval As Int32
Dim WinTitleBuf As String
Dim WinTitle As String
Dim WinClassBuf As String
Dim WinClass As String
Dim i As Int32
Dim cboCount As Int32
Dim srcstr As String
retval = GetClassName(lhWnd, WinClassBuf, 255)
WinClass = StripNulls(WinClassBuf)
retval = GetWindowText(lhWnd, WinTitleBuf, 255)
WinTitle = StripNulls(WinTitleBuf)
If WinTitle = "&Selected Objects Only" Then
SendMessage(lhWnd, BM_CLICK, 0, 0)
End If
If WinClass = "Edit" Then
gFileName = """" & gFileName & """"
SendMessage(lhWnd, WM_SETTEXT, 0, gFileName)
End If
If WinClass = "ComboBox" Then
srcstr = "DXF Files"
i = SendMessage(lhWnd, CB_FINDSTRING, -1, srcstr)
If i = -1 Then
Else
srcstr = Asc("d")
SendMessage(lhWnd, WM_CHAR, srcstr, CLng(1))
End If
End If
If WinTitle = "&Open" Or WinTitle = "&Save" Then
gHandle = lhWnd
End If
EnumChildProc = True
End Function
Function StripNulls(ByVal OriginalStr As String) As String
If (InStr(OriginalStr, Chr(0)) > 0) Then
OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
End If
StripNulls = OriginalStr
End Function
End Module
This part is the execution code in the form.
Code:
Private Function ExecCmd(ByVal cmdline As String, ByVal sendkeysStr As String)
Dim proc As PROCESS_INFORMATION
Dim Start As STARTUPINFO
Dim hInstance As Long
Dim processID As Long
Dim L As Long
Dim hWndApp As Long
Dim ret&
Dim windText As String
Dim windText2 As String
Dim lParam As Long
Dim lRet As Long
windText = "Open"
windText2 = "Save As"
Start.cb = Len(Start)
ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, vbNullString, Start, proc)
Do Until hWndApp <> 0
hWndApp = FindWindow(vbNullString, windText)
If hWndApp = 0 Then hWndApp = FindWindow(vbNullString, windText2)
Loop
Sleep(1000)
SetFocusAPI(hWndApp)
gFileName = sendkeysStr
Sleep(1000)
lRet = EnumChildWindows(hWndApp, AddressOf EnumChildProc, lParam)
Sleep(1000)
SendMessage(gHandle, BM_CLICK, 0, 0)
Do
ret& = WaitForSingleObject(proc.hProcess, INFINITE)
DoEvents()
Loop Until ret& <> 258
Call GetExitCodeProcess(proc.hProcess, ret&)
Call CloseHandle(proc.hProcess)
ExecCmd = ret&
End Function
I'm getting an error on the AddressOf...it doesn't know what it's trying to do, and neither do I. It looks like it's trying to use EnumChildProc, but it never passed it parameters...so...
And about the other code I was trying to use from SendInputToWindow...
I can get the Save box to come to the foreground, but I can't get the box checked. I'll admit, I know little to nothing about window and control handles. And I can use Spy++ to pull up the active processes and find the form and control I want...but passed that, I get confused. I don't know what name I'm supposed to look for, what the numbers mean, if the control is a child of something...
And now I'm cursing my school for never teaching me what a handle is or how to use it in code. Never learned it, never had to use it, and now I don't know what's going on.
-
Re: [2005] Sending Keys to other application Save
Quote:
I can use Spy++ to pull up the active processes and find the form and control I want...but passed that, I get confused. I don't know what name I'm supposed to look for, what the numbers mean, if the control is a child of something...
You're almost there!
Using spy++ you've found the "Save As" window.
Code:
Dim hwnd as int32 = FindWindow(nothing, "Save As")
Now you hav the handle of it.
Then you've opened the plus sign, to see the children paths of "save as".
The window text is on the left, and the class names are on the right, as opposed to FindWindow which places the class name on the left.
I assume that the child window is right here under your nose, and it should have window text "Selected Objects Only", and should have the class name "Button". If this is true then:
Code:
Dim cwnd As Int32 = FindWindowEx(hwnd, 0, "Button", "Selected Objects Only")
If it was not true, then what window NAME/s, did you find to get to the control? This is very very similar to a folder path, and is why s.GetHandle is easier, but you should also know how to do it this way. List the text and class names in order, so that you know the order of their relationship.
Make a list of the text and class names, to show here.
You can omit the class name if it's dynamically changing, by using Nothing.
Code:
Dim cwnd As Int32 = FindWindowEx(hwnd, 0, Nothing, "Selected Objects Only")
If that is indeed the handle, then use SendMessage like this:
Code:
SendMessage(cwnd, WM_KEYDOWN, VK_SPACE, Nothing)
SendMessage(cwnd, WM_KEYUP, VK_SPACE, Nothing)
You'll need these also:
Code:
Const WM_KEYDOWN As Int32 = 256
Const WM_KEYUP As Int32 = 257
Const VK_SPACE As Int32 = 32
In fact all of your numerical constants should have the As Int32 like this.
-
Re: [2005] Sending Keys to other application Save
Here's what I found. Drilling down through Spy++, I found the window. It lists it like this...
Quote:
"Save As" #32770 (Dialog)
I then look for the Selected Objects Only, but I can't seem to find it. I think this might be the problem. When I look, I have a few collapsed items still.
Quote:
"" #32770 (Dialog)
When I open up this one, it gets me finally to the button I've been looking for.
Quote:
"&Selected Objects Only" Button
So, I guess it was enclosed in something else. But...the problem is, the thing it's enclosed in doesn't have a name to it, so I don't know how to get to it.
-
Re: [2005] Sending Keys to other application Save
Also, in case this helps, this is the text file I saved from Spy++ that shows the dialog and it's children.
Quote:
Window 004C02A8 "Save As" #32770 (Dialog)
-Window 0031019E "Save &in:" Static
-Window 00350158 "SVG-Exporter" ComboBox
-Window 00420392 "" Static
-Window 001A0354 "" ToolbarWindow32
-Window 009003D6 "" ListBox
-Window 000D0318 "" SHELLDLL_DefView
--Window 001202AE "FolderView" SysListView32
-Window 00D802F0 "File &name:" Static
-Window 001803CC "" Edit
-Window 007D0362 "Save as &type:" Static
-Window 009802E8 "Enhanced Meta Files " ComboBox
-Window 00450366 "Open as &read-only" Button
-Window 000D0342 "&Save" Button
-Window 001C03C0 "Cancel" Button
-Window 002702E2 "&Help" Button
-Window 001703CE "" #32770 (Dialog)
--Window 003E0390 "&Selected Objects Only" Button
-
Re: [2005] Sending Keys to other application Save
Okay that's an awesome start, and exactly what we needed to find where the handle is.
If it doesn't have a name, then you usually specify Nothing in that param:
Code:
FindWindowEx(hwnd, 0,Nothing, Nothing)
This might still not work, depending on the other child names.
So here's what you do then:
If it were say the 15th child window down not including the grandchildren.
Code:
Dim hwnd, cwnd, ccwnd As Int32
hwnd = FindWindow(Nothing, "Save As")
For i As Int32 = 1 to 15
cwnd = FindWindowEx(hwnd, cwnd, Nothing, Nothing)
Next
ccwnd = FindWindowEx(cwnd, 0, "Button", "&Selected Objects Only")
MessageBox.Show(ccwnd.ToString)
Now you should get a handle, and it should be the right one according to your list.
Tip:
When using SPy++ you can make the window visibly Blink, to help find out if it's the right one.
Simply select the window line in question, and then right click, and choose "Highlight".
The window will blink for a couple of seconds, around the outer edge.
If Highlight is not available, then it's not a regular style window, and usually is not the one that you're looking for.
-
Re: [2005] Sending Keys to other application Save
Am I doing something wrong? FindWindowEX isn't a function...or, at least, it's not picking it up as one.
EDIT:
Never mind...I can't read my API's. >.< I haven't added it yet.
-
Re: [2005] Sending Keys to other application Save
Ok...that's a very not-nice error.
Code:
For i As Int32 = 1 To 15
->>ERROR HERE cwnd = FindWindowEx(hwnd, cwnd, Nothing, Nothing)
Next
Quote:
A call to PInvoke function 'DrawingCreator!DrawingCreator.WindowHandler::FindWindowEx' 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.
-
Re: [2005] Sending Keys to other application Save
I've never seen FindWindowEx do that.
How is it declared? Try both ways...
Try it declared the older way, and what RobDogg showed you.
Older:
Code:
Public Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 as int32, byval hWnd2 as int32, byval lpsz1 as String, byval lpsz2 As String) As Int32
----
Oh yeah, and if the 15 sibling windows are dynamic(change depending on the user input etc) Then you'd use a Do Loop instead of a For Next. This way you exit on when the last sibling cwnd is reached and is 0.
Code:
If cwnd= 0 then Exit Do
You'd also stick in the GetWinTextClassNames inside the loop, so that you can match it up directly in code. This is an extra step and is not always needed.
Code:
Do
cwnd = FindWindowEx(hwnd, cwnd, Nothing, Nothing)
If cwnd = 0 Then Exit Do
If s.GetWinTextClassNames(cwnd).lpText = "TheTextYou'reLookingForHere" then Exit Do
If s.GetWinTextClassNames(cwnd).lpClassName = "TheClassNameYou'reLookingForHere" then Exit Do
Loop
And Nothing is pretty much the same as "" in this case.
-
Re: [2005] Sending Keys to other application Save
Instead of a For Next loop you should use EnumWindows API instead.
-
Re: [2005] Sending Keys to other application Save
Is there a place that I can look at these API's and what the parameters should be, as well as what the functions are supposed to do?
-
Re: [2005] Sending Keys to other application Save
The API Guide has most of the related ones, with descriptions etc, but it doesn't work on Vista(to my knowledge). v3.7.
You should probably build your own mini API module(or wrap) with current conversions, for the API that you commonly use. It will save time reconverting APIGuide examples over and over.
There are more than enough API's, so make it digestable for yourself.
A module, or class can be opened into the IDE, so that you can pluck out the functions/s you want, without having the module directly part of the project.
I've found this useful countless times.
-
Re: [2005] Sending Keys to other application Save
The API Viewer installs just fine on Vista. I didnt bother installing the API Guide as I just go to the website instead.
-
Re: [2005] Sending Keys to other application Save
Quote:
The API Viewer installs just fine on Vista.
Yeah but it doesn't have any details for the related functions like the Guide does.
I've looked in the Extras menu, and tried "Advanced information' (F2), but nothing comes up for any of these API. I wonder if any do.
If you have the API Guide installed(non Vista) then you can press (F4), and the APIViewer will bring up the APIGuide with the selected function.
I guess this would be a good place to build a personal library, but I like my own idea better.
Hey RobDogg,
Do you think that a DoLoop or ForNext would be less efficient than the EnumWindows?
I was thinking that using a next or loop would be easier and less bulky, but if you think that EnumWindows is more reliable for some reason, then let me know. I realize that ForNext would only be used when you know the windows are fixed, and so that eliminates the need for another API.
The DoLoop can be used no matter how many sibling windows there are, and I still consider it a closed loop, since it WILL exit when the handle = 0, just like EnumWindows.
The question is if it's really better, or just the same.
-
Re: [2005] Sending Keys to other application Save
Well you can also install the addin "PInvoke" from www.pinvoke.com as its the API utility addin for .NET. I personally like the API Viewer still as its still better IMO then PInvoke.
Guess partly too cause I dont need the Guide and only on occasion use the allapi website.
-
Re: [2005] Sending Keys to other application Save
RobDogg,
I couldn't find the addin PInvoke, on the link you gave.
It just had icon and font downloads, with little else at all.
Maybe it's not available at the moment, or maybe I missed something.
-
Re: [2005] Sending Keys to other application Save
TTn: Wow...I think I'm acting stupid here...but why do you have GetWinTextClassNames private? How do I get to the function when it's private?
-
Re: [2005] Sending Keys to other application Save
-
Re: [2005] Sending Keys to other application Save
Thanks RobDogg!
CrystalBlue,
Just change Private to Public. That's it.
It's used internally by the class itself.
It is private so novice users will not be confused by to many methods, but an intermediate or advanced will know to change the visibility if they want.
I've updated the SendInputToWindow class, and it's now called InputToWin. Just take a look at my signature below. It's pretty close to SendInputToWindow, but some of the minor quirks were fixed, and it works good on Vista too. A few of the parameters are a little different, so read the details before using it.
-
Re: [2005] Sending Keys to other application Save
I'm still getting this unbalanced error. I'm using the PInvoke.net add in, and now it's creating the declare statements incorrectly. It's turning them into privates, making them shared, and they're turning Strings into System.Text.StringBuilder. *gets confused again*
-
Re: [2005] Sending Keys to other application Save
CrystalBlue,
So wait a minute, ... are you getting an unbalanced error with my class as it is?
You should be able to use the class as it is, without error.
I've tested it on several different machines, and can't see a problem.
Then you'd change the few functions you want visible from Private to Public.
Unless maybe the computer is a laptop(Toshiba Satellite or similar) that is missing certain .NEt files. In that case you're kinda screwed by the company.
Also be sure to place this import up top when declaring API
Code:
Imports System.Runtime.InteropServices
Just in case you're trying some api outside of my class.
I haven't tried the pInvoke, because it wanted an email address, and said something about a 14 day trial. Blah.
It probably does import them as private by default, as usual.
The string builder is an object which allows fast string manipulation.
By putting .ToString on the object, it will behave just like a string.
I wish I could help more, but I just don't know enough about what you've got going on so far.
-
Re: [2005] Sending Keys to other application Save
The PInvoke is just an addition to a trial of a program the company wants you to buy. I just got the thing and only extracted the PInvoke.exe from it and got rid of the rest. It works just fine.
And I'm sorry about not making much sense. Working on 3 different projects kinda does that after a few days. @.@;;
I'll finish my tweaks to something and then let you know where I'm at. Thanks for the help so far.
-
Re: [2005] Sending Keys to other application Save
Alright...I'm sure you're going to be repeating yourself, TTn, so I'm sorry before I ask this.
I've finally got the handle ID (I think that's what I'm supposed to call it) of the Save As window that I need. I used this method to get it.
Code:
Do Until hWndApp <> 0
If hWndApp = 0 Then
hWndApp = FindWindow(vbNullString, windText2)
End If
Loop
Now...how do I go about finding the children that will get me to the button I need to check? Is that where I use the FindWindowEX?
-
Re: [2005] Sending Keys to other application Save
Update...but not a happy one.
I'm getting what LOOKS like the final button I need...but the MouseToSend doesn't seem to be sending the mouse click like it should. It keeps centering the mouse cursor over the top left of my main window and clicking there.
-
Re: [2005] Sending Keys to other application Save
Okay you don't need to use a Do Loop for the FindWindow.
This should do the exact same thing.
Code:
hWndApp = FindWindow(vbNullString, windText2)
Yes then use FindwindowEx in a do loop as I showed you.
Code:
Dim cwnd, ccwnd As Int32
Do
'Loop through all child windows on the main form
cwnd = FindWindowEx(hWndApp , cwnd, vbNullString, vbNullString)
If cwnd = 0 Then Exit Do 'if there are no more windows
'Search each grand child window(if any) for the checkbox button
ccwnd = FindWindowEx(cwnd, 0, "Button", "&Selected Objects Only")
If ccwnd <> 0 then Exit Do 'Exit if we find it!!
Loop
Messagebox.Show(ccwnd.ToString)'here is the handle!!
I dont think you have the handle of the grand child window yet, since it's clicking on the main window.
The above example should give you the exact handle, based upon your Spy+ list.
I recommend trying the SendMessage or PostMessage API first, to send the space key to the checkbox. Here is how my class would do it internally:
Code:
SendMessage(ccwnd, WM_KEYDOWN, VK_SPACE, Nothing)
SendMessage(ccwnd, WM_KEYUP, VK_SPACE, Nothing)
or
Code:
PostMessage(ccwnd, WM_KEYDOWN, VK_SPACE, Nothing)
PostMessage(ccwnd, WM_KEYUP, VK_SPACE, Nothing)
You'd need these constants to do it:
Code:
Const WM_KEYDOWN As Int32 = 256
Const WM_KEYUP As Int32 = 257
Const VK_SPACE As Int32 = 32
-
Re: [2005] Sending Keys to other application Save
Oh, thank god it's working. T_T I'm able to get it to click now. So it was just a matter of sending the space key to the handle. I could have sworn I had gotten the handle yesterday...but then again, I was hitting my head against this problem for a good 6 hours yesterday.
Thanks a lot for all the help...this program is still from being completed, but at least you got me over the hardest part. ^.^ Thanks!
-
Re: [RESOLVED] [2005] Sending Keys to other application Save
I'm glad to have helped.:wave: