|
-
Feb 9th, 2014, 04:57 PM
#1
Thread Starter
New Member
Strange SendMessage API Lag/Delay Issue
Hello
I am trying to automate a few non standard systems by using common API's such as SendMessage & PostMessage. I can use these API's as I intended, however there are issues where the code seems to go too fast. It's simple enough to have some sort of wait code for automating things such as Internet explorer by doing a Do Until ReadyState..., but when trying something similar on external windows, I seem to hit a problem.
Take for instance this example; I need to send text to a textbox with WM_SETTEXT, I can get the handles no problem, I can also run this no successfully if I press F11 and step into each line, if however I let it run at normal speed, the code doesn't end up entering the text into the textbox, my solution was to have the code do another SendMessage to do WM_GETTEXTLENGTH & WM_GETTEXT so that the code can check if the text has been entered to the textbox and loop until it is there, to my surprise however, the WM_GETTEXTLENGTH & WM_GETTEXT actually think that the text has been entered even though there is no text in the textbox, it even knows what text is supposed to be in there. Even more perculier is that when I do WM_GETTEXTLENGTH & WM_GETTEXT again on that control, it goes back to saying that there is no text within the textbox.
Code:
Private Enum WindowMessages
WM_SETTEXT = &HC
WM_GETTEXT = &HD
WM_GETTEXTLENGTH = &HE
End Enum
Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Int32, ByVal wMsg As Int32, ByVal _
wParam As Int32, lParam As String) As Int32
Private Sub TestExample(ByVal hWndTextBox As Int32, ByVal TextString As String)
'####Using the hWnd of a Combo box control and a string to enter in the combo box.####
Dim TextLength As Int32
Dim ItemText As String
Do Until ItemText = TextString
SendMessage(hWndTextBox, WM_SETTEXT, 0, TextString) > 0
TextLength = SendMessage(hWndTextBox, WM_GETTEXTLENGTH, 0, 0)
ItemText = Space(TextLength)
SendMessage(hWndTextBox, WM_GETTEXT, TextLen + 1, ItemText)
Loop
End Sub
It sounds a bit confusing, I'm puzzled myself, if you have any solutions I would really like to know, the Sleep API would make it partially work as it makes the system physically wait, but I know that theres the possibility that would fail sometimes too, is there some sort of screen paint/refresh that would work or something?
I look forward to any solutions.
Many Thanks
Xenac
-
Feb 11th, 2014, 11:56 AM
#2
Re: Strange SendMessage API Lag/Delay Issue
Are you using VB6? If so, the following lines have a syntax error:
Code:
SendMessage(hWndTextBox, WM_SETTEXT, 0, TextString) > 0
SendMessage(hWndTextBox, WM_GETTEXT, TextLen + 1, ItemText)
It looks like you are checking the return value of the WM_SETTEXT message to see if it succeeded. However, it seems your code above just ignores the result anyway and proceeds to retrieve the text regardless of whether it was successfully set in the first place.
I've done some tests in Win 7 and I've been able to set and retrieve the text of other processes' Edit windows (and some other window classes too) very rapidly using both A & W versions of the SendMessage API. I'm not certain what the problem might be in your case. Can you reveal more clues?
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)
-
Feb 13th, 2014, 05:15 PM
#3
Thread Starter
New Member
Re: Strange SendMessage API Lag/Delay Issue
Thanks for your reply Bonnie West
In answer to your questions:
I am using Visual Studio 2010 or (VB 10.0). I've created a better example below which removes the syntax error and is easier to test.
The part where the WM_SETTEXT is was supposed to set the text to something e.g. "F:\TestDocument.txt", then do a WM_GETTEXTLEN & WM_GETTEXT to see if the WM_SETTEXT has been entered; if the WM_GETTEXT brought back information saying that the text didn't match what the WM_SETTEXT entered then it would loop until there was a physical sign of the text being entered. The problem that occurs is that the WM_GETTEXT brings back information that there is text in the box even though it's not actually there.
The new example of the code tests the same issues with Notepad (as it's a good example system to test), in this scenario the code opens Notepad and initiates the Open dialog, which works fine, but the actual WM_SETTEXT does the same thing again. When you run it you will see that the message box brings back the text that should be in the file name combobox edit control even though the combobox has nothing in it.
Should I be using something like ReplyMessage to confirm if somethings gone through maybe?
Code:
Public Class TestClass
Private Enum APIMessages
WM_COMMAND = &H111
WM_SETTEXT = &HC
WM_GETTEXT = &HD
WM_GETTEXTLENGTH = &HE
GW_HWNDNEXT = &H2
BM_CLICK = &HF5
End Enum
'####Declarations.####
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
ByVal hWnd As Int32, _
ByVal wMsg As Int32, _
ByVal wParam As Int32, _
ByVal lParam As String _
) As Int32
Public Declare Function SendMessageCallback Lib "user32" Alias "SendMessageCallbackA" ( _
ByVal hwnd As Int32, _
ByVal Msg As Int32, _
ByVal wParam As Int32, _
ByVal lParam As String, _
ByVal lpResultCallBack As Int32, _
ByVal dwData As Int32 _
) As Int32
Public Declare Function GetParent Lib "user32" (ByVal hWnd As Int32) As Int32
Private Declare Function GetTopWindow Lib "user32.dll" (ByVal hwnd As Int32) As Int32
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As Int32
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _
ByVal hWndParent As Int32, _
ByVal hWndChildAfter As Int32, _
ByVal lpszClassName As String, _
ByVal lpszWindowName As String _
) As Int32
Public Sub OpenNotepad()
'####Open Notepad and then initiate the open dialog.####
On Error GoTo ErrHandler
Dim TestFileOpen As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\test.txt"
Dim MSNotepad As Int32
Dim OpenDialog As Int32 = 0
Dim Wnd_ComboBoxEx32 As Int32
Dim Wnd_ComboBox As Int32
Dim Wnd_Edit As Int32
Dim Wnd_OpenBtn As Int32
Dim StartProcess As Process = Process.Start("notepad.exe")
If FindWindow("Notepad", vbNullString) > 0 Then Err.Raise(440, , "Must close other versions of the window.")
'\\Get the handle of the application that has been opened.
Do Until MSNotepad > 0
MSNotepad = (StartProcess.MainWindowHandle)
Loop
'\\Initiate the Open file command.
SendMessageCallback(MSNotepad, APIMessages.WM_COMMAND, &H2, &H0, &H0, &H0)
Do Until OpenDialog > 0
OpenDialog = FindWindow(vbNullString, "Open")
Loop
'\\Determine if the window is the correct one by getting it's parent.
'If GetParent(OpenDialog) = MSNotepad Then MsgBox("Found the open window successfully!")
'If OpenDialog = 0 Then Err.Raise(440, , "Unable to find the open dialog.")
'\\Find the filename combobox.
Wnd_ComboBoxEx32 = WaitForFindWindow(OpenDialog, "ComboBoxEx32")
'\\Find the comboboxes inner control.
Wnd_ComboBox = WaitForFindWindow(Wnd_ComboBoxEx32, "ComboBox")
'\\Find the comboboxes edit control.
Wnd_Edit = WaitForFindWindow(Wnd_ComboBox, "Edit")
'\\Type the filename within the filename combobox.
SendMessage(Wnd_Edit, APIMessages.WM_SETTEXT, &H0, TestFileOpen)
MsgBox(GetText(Wnd_Edit))
'\\Find the Open button control.
Wnd_OpenBtn = WaitForFindWindow(OpenDialog, "Button", "&Open")
'\\Type the filename within the filename combobox.
SendMessage(Wnd_OpenBtn, BM_CLICK, &H0, &H0)
Exit Sub
ErrHandler:
MsgBox("The following error occured:" & vbCr & vbCr & Err.Description, vbCritical)
End Sub
Private Function GetText(ByVal hWnd As Int32) As String
'####Gets the textlength and text contained.####
Dim TextLen As Integer = SendMessage(hWnd, APIMessages.WM_GETTEXTLENGTH, vbNullString, vbNullString) + 1
Dim Buffer As String = New String(" "c, TextLen)
SendMessage(hWnd, APIMessages.WM_GETTEXT, TextLen, Buffer)
GetText = Buffer
End Function
Private Function WaitForFindWindow( _
ByVal hWnd As Int32,
Optional ByVal lpszClassName As String = vbNullString,
Optional ByVal lpszWindowName As String = vbNullString) As Int32
'####Runs FindWindowEx and waits for the control to get a hWnd value.####
Dim cWnd As Int32
Do Until cWnd > 0
cWnd = FindWindowEx(hWnd, vbNullString, lpszClassName, lpszWindowName)
Loop
WaitForFindWindow = cWnd
End Function
End Class
-
Feb 13th, 2014, 06:01 PM
#4
Re: Strange SendMessage API Lag/Delay Issue
Unfortunately, I don't code in VB.Net so I may not be able to help you much. However, I did try reproducing the core part of your problem using VB6. I was able to successfully send some text (via SendMessageW WM_SETTEXT) to the File name ComboBox of the Open dialog box. In your latest code above, you're still ignoring the return value of the WM_SETTEXT message. Assuming the call failed, your code continues with the next SendMessage calls regardless. You should be checking the return value and should proceed only if successful. You should also check the description of the last API error, if any. Sorry, but I still have no idea why your code doesn't work correctly.
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)
Tags for this Thread
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
|