WM_SETTEXT & WM_GETTEXT delay/lag issue - vb.net

I have tried automating a few external window's Combo box controls using the API functions, SendMessage and PostMessage and using the Window Messages WM_SETTEXT WM_GETTEXTLENGTH and WM_GETTEXT.
I can get this to work when I run through the code for example pressing F11 through each line or putting a breakpoint in and running it at a certain point, but when I run it as normal, the WM_SETTEXT message doesn't update the Combo box.
It seems to be an issue with the speed that the code is running so my first solution was to put a piece of validation that ensures the text is in the combo box, but for some reason the WM_GETTEXT retrieves the text that should be in the text box even though its not visible.
The code below is a snippet of the kind of validation I've tried, my question here would be is there any sort of wait until method I could use to ensure that the text actually gets entered, I know about the Sleep API but I would ideally like something that actually makes sure that text is there and doesn't load f x seconds and hopes that the text is there.
Any ideas are appriciated.
Many Thanks
VB.net Code Example:
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
Public Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hWnd
As Int32, _
ByVal wMsg As Int32, ByVal wParam As Int32, ByVal 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

Related

User32 sendmessage works only for some messages

I am looking to send key and mouse events to notepad.exe, however not necessarily have the window be active. In some cases the key events are actual typing operations and in other cases the key input represents shortcut key combinations.
Using SendKeys works, but obviously needs focus. I would prefer to have that operate in the background so am investivating SendMessage from user32.dll. I can see it has different signatures based on what the message code is, and this may be the origin of the problem.
Starting with
Dim notepad() As Process = Process.GetProcessesByName("notepad")
Dim actualWindow As IntPtr = FindWindowEx(notepad(0).MainWindowHandle, IntPtr.Zero, "Edit", Nothing)
and using
<System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SendMessageA")>
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As IntPtr
End Function
if I send
SendMessage(actualWindow, KeyEventType.WM_SETTEXT, 0, "Text")
then the editor text is replaced with "Text".
If however I use
<System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SendMessageA")>
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As IntPtr
End Function
and send
SendMessage(actualWindow, KeyEventType.WM_KEYDOWN, &H4C, 0)
SendMessage(actualWindow, KeyEventType.WM_KEYUP, &H4C, 0)
or even
SendMessage(notepad(0).MainWindowHandle, KeyEventType.WM_KEYDOWN, &H4C, 0)
SendMessage(notepad(0).MainWindowHandle, KeyEventType.WM_KEYUP, &H4C, 0)
where &H4C corresponds to the 'L' key (1.), no letter 'l' appears in the editor.
I am moderately convinced that the function signatures are correct for both WM_KEYDOWN/WM_KEYUP (2.) and WM_SETTEXT (3.). I think the problem might be one of 'focus' but am not sure how to tackle that if I want the operations to occur in the background. How can keystrokes be sent to the text editor?
The enumerations are defined as:
Public Enum KeyEventType As Int32
WM_SETTEXT = &HC
WM_KEYDOWN = &H100
WM_KEYUP = &H101
End Enum

How to get non-ansi string from external application displayed correctly?

I want to obtain the text from a chat room. And to do that I use the Marshal class to get the string pointer and convert back to a string using Marshal.PtrToStringUni. My target string is written in Vietnamese (UTF-8, codepage Windows-1258). And I could not get it displayed correctly (result shows weird Chinese and symbols). What should I change in the code below to get it right?. Thank you ~
'API Declaration
Declare Auto Function SendMessage Lib "user32.dll"(ByVal hWnd As IntPtr, ByVal msg As Integer, _
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
'Sub to get chat room text
' I already got the handle of the chat text area (RoomText)
Private Sub GetText()
'get text length
length = SendMessage(RoomText, WM_GETTEXTLENGTH, 0, 0) + 1
'Alloc memory for the buffer that receives the text
Dim Handle As IntPtr = Marshal.AllocHGlobal(length)
'send WM_GETTEXT message to the chat window
Dim NumText As Integer = SendMessage(Hwnd, WM_GETTEXT, length, Handle)
'copy the characters from the unmanaged memory to a managed string
Dim Text As String = Marshal.PtrToStringUni(Handle)
'Display the string using a textbox
TextBox1.AppendText(Text)
End Sub
Here is the result of the above code:
P/S: In other efforts, I tried the SendMessageW, and SendMessageA functions, and only SendMessageA results the string with mix of English and question mark (something like ng?y de?p...). SendMessageW returns weird characters.
'API Declaration
Public Declare Function SendMessage Lib "user32.dll" Alias "SendMessageW" (ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
'Sub to get chat room text
' I already got the handle of the chat text area (RoomText)
Private Sub GetText()
'get text length
length = SendMessage(RoomText, WM_GETTEXTLENGTH, 0, 0) + 1
'Alloc memory for the buffer that receives the text
'Be noted that Length is the string character count, but Marshal.AllocHGlobal need byte count.
'in VB.net string, a character use 2 byte so I put *2
Dim Handle As IntPtr = Marshal.AllocHGlobal(length*2)
'send WM_GETTEXT message to the chat window
Dim NumText As Integer = SendMessage(Hwnd, WM_GETTEXT, length, Handle)
'copy the characters from the unmanaged memory to a managed string
Dim Text As String = Marshal.PtrToStringUni(Handle)
'Display the string using a textbox
TextBox1.AppendText(Text)
End Sub

How can I get properties of controls contained in a popup message box using VB.Net

I'm working on a VB.Net project where in part of it I catch a pop up message box when it's displayed and handle it in some way.
My problem is that I have to know what buttons are included in this pop up window (their captions mainly).
Is this possible? Can someone please tell me how this can be done? A sample would be much appreciated.
Thanks.
Update:
Since I got a down vote, and Ed Cottrell told me that this is because of not adding any code related to my question. Now I have the answer to my question so I'm adding some code:
My app catches a pop-up window displayed by another windows application, I used EnumWindows API to know when a new pop-up window is displayed.
Public Declare Function EnumWindows Lib "User32.dll" (ByVal WNDENUMPROC As EnumWindowDelegate, ByVal lparam As IntPtr) As Boolean
Delegate Function EnumWindowDelegate(ByVal hWnd As IntPtr, ByVal Lparam As IntPtr) As Boolean
When I catch this window, I use its handle that I got from EnumWindows result and get its child windows using EnumChildWindows (which are the controls I'm looking for , since controls are kind of windows too):
APIs I used
<DllImport("User32.dll")> _
Public Function EnumChildWindows _
(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, _
ByVal lParam As IntPtr) As Boolean
End Function
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
' Get window text length signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
' Get window text signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As StringBuilder) As Int32
Structure ApiWindow
Public Structure ApiWindow
Public MainWindowTitle As String
Public ClassName As String
Public hWnd As Int32
End Structure
Functions
Public Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ChildrenList As New List(Of IntPtr)
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
Public Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Dim ChildrenList As List(Of IntPtr) = GCHandle.FromIntPtr(Parameter).Target
If ChildrenList Is Nothing Then Throw New Exception("GCHandle Target could not be cast as List(Of IntPtr)")
ChildrenList.Add(Handle)
Return True
End Function
Now when I have the handle of pop-up window (parentHandle), I can get its children windows:
Dim hndls() As IntPtr = GetChildWindows(parentHandle)
Dim window As ApiWindow
For Each hnd In hndls
window = GetWindowIdentification(hnd)
'Add Code Here
Next
Where GetWindowIdentification is:
''' <summary>
''' Build the ApiWindow object to hold information about the Window object.
''' </summary>
Public Function GetWindowIdentification(ByVal hwnd As Integer) As ApiWindow
Const WM_GETTEXT As Int32 = &HD
Const WM_GETTEXTLENGTH As Int32 = &HE
Dim window As New ApiWindow()
Dim title As New StringBuilder()
' Get the size of the string required to hold the window title.
Dim size As Int32 = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)
' If the return is 0, there is no title.
If size > 0 Then
title = New StringBuilder(size + 1)
SendMessage(hwnd, WM_GETTEXT, title.Capacity, title)
End If
' Set the properties for the ApiWindow object.
window.MainWindowTitle = title.ToString()
window.hWnd = hwnd
Return window
End Function
I found this answer on the net on another website, I thought I should share it in case someone sees this question in the future:
Buttons are nothing but more windows. You just look for more child
windows of the message box window you found. You can NOT just get
the handle from a window and then try to cast it to a Form object and
expect all the properties to work. It just doesn't work that way.

Get Text From Specific Textboxes From External Application - Visual Basic .Net

I can get text from external application text box but now I want to get text from my desired text box from external application.
My English is not so good that's why see Image Below.
The Below Code Return The First Text Box Value Only.
Imports System.Runtime.InteropServices
Public Class Form1
Private Const WM_GETTEXT As Integer = &HD
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal msg As Integer, _
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
ByVal childAfter As IntPtr, _
ByVal lclassName As String, _
ByVal windowTitle As String) As IntPtr
End Function
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'Find the running notepad window
Dim Hwnd As IntPtr = FindWindow(Nothing, TextBox1.Text)
'Alloc memory for the buffer that recieves the text
Dim Handle As IntPtr = Marshal.AllocHGlobal(100)
'send WM_GWTTEXT message to the notepad window
Dim NumText As Integer = SendMessage(Hwnd, WM_GETTEXT, 50, Handle)
'copy the characters from the unmanaged memory to a managed string
Dim Text As String = Marshal.PtrToStringUni(Handle)
'Display the string using a label
Label1.Text = Text
'Find the Edit control of the Running Notepad
Dim ChildHandle As IntPtr = FindWindowEx(Hwnd, IntPtr.Zero, "Edit", Nothing)
'Alloc memory for the buffer that recieves the text
Dim Hndl As IntPtr = Marshal.AllocHGlobal(200)
'Send The WM_GETTEXT Message
NumText = SendMessage(ChildHandle, WM_GETTEXT, 200, Hndl)
'copy the characters from the unmanaged memory to a managed string
Text = Marshal.PtrToStringUni(Hndl)
'Display the string using a label
Label2.Text = Text
End Sub
End Class
You'll have to loop through children of the main window (External Application) and get their properties.
You'll use the following:
<DllImport("User32.dll")> _
Public Function EnumChildWindows _
(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, _
ByVal lParam As IntPtr) As Boolean
End Function
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Public Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ChildrenList As New List(Of IntPtr)
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
For for details, check this How can I get properties of controls contained in a popup message box using VB.Net
' try this on excel vbe
External_application_handle=findwindow(vbNullString,"External_application")
textbox_1_handle=findwindowex(External_application_handle,0&,"Edit",vbNullString)
next_handle=textbox_1_handle
textbox_2_handle=findwindowex(External_application_handle,next_handle,"Edit",vbNullString")
Length = SendMessage(textbox_2_handle, WM_GETTEXTLENGTH, 0,0)
buffer$=space(Length)
call sendmessage(textbox_2_handle,Length+1,buffer$)
msgbox buffer

VB.NET Send Tab key to another application window

I want to send "{TAB}" Key to another application window(send the key to the window not to textbox).
I tried:
SendMessage(hWnd, WM_SETHOTKEY, VK_TAB, 0)
Nothing happened.
my goal is:
send tab key to my application Or other application when the application window is not in focus.
(i know that sendkey is not professional in this case there is no choice(This is the first time that I'm using it).)
I made many attempts and I always returned to the same result:
Nothing happened.
Does anyone know the answer?
SendKeys requires the application that you are sending the Keys to, to be active.
From above Link:
Use SendKeys to send keystrokes and keystroke combinations to the active application.
I order to get around this limitation you will have to resort to using the WinApi Functions.
FindWindow pInvoke.net
FindWindowEx pInvoke.net
sendMessage pInvoke.net
See this MSDN Forum Post for an example
Here is a modified example from that Posting:
Public Class Form1
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd As IntPtr, ByVal hWndChildAfterA As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
Const WM_SETTEXT As Integer = &HC
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim destination As IntPtr = FindWindow(Nothing, "Untitled - Notepad")
Dim destControl As IntPtr = FindWindowEx(destination, IntPtr.Zero, "Edit", Nothing)
SendMessage(destControl, WM_SETTEXT, IntPtr.Zero, "Hello" & vbTab & "GoodBye" & vbCrLf)
End Sub
End Class
Added an Additional Example using WM_KEYDOWN I created another small application with the Window Title set to TestForm and overrode the WndProc Method to determine if the application got the TabKey.
Sending Form
Public Class Form1
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
Const WM_KEYDOWN As Integer = &H100
Const VK_TAB As Integer = &H9
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim destination As IntPtr = FindWindow(Nothing, "TestForm")
SendMessage(destination, WM_KEYDOWN, VK_TAB, 0)
End Sub
End Class
Test Form
Put a breakpoint on MyBase.WndProc(m) and look at m to see what has been sent.
Public Class Form1
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
End Sub
End Class
Having struggled with this type of this a few times before, i would suggest a couple of things to look at.
The 1st is autoit which includes a dll you can reference from vb.net, and is very simple you use, and well documented. I tend to use that whenever i need to control a 3rd party program.
The other is the ui automation classes
See this for an example:
http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html
you need make the other window active first. check Change focus to another window in VB.NET . then use send key.