[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Is the send message API for me?

Posted on 2006-05-08
49
Medium Priority
?
410 Views
Last Modified: 2010-05-18
I have a send keys app right now.  I would like to be able to use the same functionality but send keys to a non active window.   This way I will be able to use a stop function and also be able to work with this app runnign in the background.   The send keys is just very dangerous if the window focus changes, and it is dificult to stop without putting stop code in every other line to check the window with focus.  So can I use send message to send keystrokes to another old application for data entry?  And if so, what is the correct way to use send message?
0
Comment
Question by:bengelhart
  • 31
  • 16
  • 2
49 Comments
 
LVL 13

Expert Comment

by:iHadi
ID: 16635491
Yes, your sollution is using SendMessage or PostMessage API

To do so you must first obtain the handle of the window you want to send the keystrokes to. To do so you can use the FindWindow API.

Next you send the KeyStroke using SendMessage:

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Const WM_KEYDOWN = &H100

Const VK_F11 = &H7A

Private Sub Command1_Click()
Dim WinHandle As Long
Dim Res As Long
Dim FindByClass As Boolean

FindByClass = False

If FindByClass = True Then
    ' if you want to find a window by its class ID
    WinHandle = FindWindow("Window class", vbNullString)
Else
    ' if you want to find a window by its caption
    WinHandle = FindWindow(vbNullString, "Window Caption")
End If

If WinHandle = 0 Then
    MsgBox "Window not found"
Else
    Res = SendMessage(WinHandle, WM_KEYDOWN, Asc("X"), 0) ' or instead of Asc("X") you can use VK_F11 to send F11
End If

End Sub
0
 

Author Comment

by:bengelhart
ID: 16635547
If I have my data in a dataset to send keystrokes over would I put my dataset item in the part that says "Asc("X")"?  Also how do I send tabs, enters, and F1s over as key strokes?  
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16635592
You must send the Asc value of the key you want to emulate, so you can replace Asc("X") with Asc(Single Character stored in your dataset)

to send tabs, enters and F1 past the following lines under the line which declares the sendmessage:

Const VK_TAB = &H9

Const VK_F1 = &H70

Const VK_RETURN = &HD

Now, to send a Tab, replace Asc("X") with VK_TAB , and for enters replace it with VK_RETURN , and for F1 replace it with VK_F1
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 

Author Comment

by:bengelhart
ID: 16639073
So I hav eto send individual letters with Asc()?  Or does Asc() support putting a word into it like this:  Asc("country")  ?
0
 
LVL 1

Expert Comment

by:butterhook
ID: 16639335
Since you are using the WM_KEYDOWN message to simulate sending individual keys to the window in question, you will have use a send message for every key (or key combo) you wish to send the other app.

e.g.
Dim word As String = "country"
Dim i As Integer

For i = 1 to len(word)
Dim ch As Long
ch = asc(mid(word, i, 1))
SendMessage(hWnd, WM_KEYDOWN, ch, 0)
SendMessage(hWnd, WM_KEYUP, ch, 0)
Next

Note that in the above code snippet it is assumed you have obtained the window handle of the control by the method shown by iHadi, i.e. by using the FindWindow API call.

0
 

Author Comment

by:bengelhart
ID: 16639370
Oh wow.  Alright.  Well if that is the way it works I will give it a try.  Thanks.
0
 
LVL 1

Expert Comment

by:butterhook
ID: 16639470
You can also look at the WM_SETTEXT message which can be used with SendMessage for sending an entire string to a control such as a textbox. You cannot send the F1 keystroke in this way however.
0
 

Author Comment

by:bengelhart
ID: 16639949
I will just send the individual characters in a for loop.   I will respond when I test this tomorrow.  Thanks!
0
 

Author Comment

by:bengelhart
ID: 16648387
What does the long value returned from SendMEssage represent?
0
 

Author Comment

by:bengelhart
ID: 16648419
Also what is the const for keyup?  You have key up in your loop that I am testing in my app.
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16651015
First of all the sendmessage returns a 0 if it fails(eg: didn't find the window with the handle you provided), and a nonzero value if it succeeded.

Const WM_KEYUP = &H101

0
 

Author Comment

by:bengelhart
ID: 16651039
Thanks!!
0
 

Author Comment

by:bengelhart
ID: 16651538
I notice that you use WM_KEYUP if you are typing the same letter.  SO if the string I am pushing through contains "ss", I will need to put in a WM_KEYUP for the first "s"?
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16651620
Your welcome :)

Its really not necessary to send the WM_KEYUP after the first "s" you can send the next "s" directly
0
 

Author Comment

by:bengelhart
ID: 16651632
Okay.  Thanks again.
0
 

Author Comment

by:bengelhart
ID: 16651680
If I put Asc("Don")  will it type a captial D?
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16652112
Yes, if you put Asc("Any String") it is equivelant to the Asc of the first letter i.e: Asc("A")
0
 

Author Comment

by:bengelhart
ID: 16652140
I get an error on building that "Any" is not supported in Declare Statements. (Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Any) As Long)
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16652939
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long

Const WM_KEYDOWN = &H100

Const VK_F11 = &H7A

Private Sub Command1_Click()
Dim WinHandle As Long
Dim Res As Long
Dim FindByClass As Boolean

FindByClass = False

If FindByClass = True Then
    ' if you want to find a window by its class ID
    WinHandle = FindWindow("Window class", vbNullString)
Else
    ' if you want to find a window by its caption
    WinHandle = FindWindow(vbNullString, "Window Caption")
End If

If WinHandle = 0 Then
    MsgBox "Window not found"
Else
    Res = SendMessage(WinHandle, WM_KEYDOWN, Asc("X"), 0) ' or instead of Asc("X") you can use VK_F11 to send F11
End If

End Sub

0
 

Author Comment

by:bengelhart
ID: 16664494
This code doesn't send anything to the other app.  Here is my code:

           
               WinHandle = FindWindow(vbNullString, "Untitled - Notepad")
               
                Res = SendMessage(WinHandle, WM_KEYDOWN, Asc("h"), 0)
                For p As Integer = 1 To Len(dt.Rows(intI).Item(0))
                    Dim ch As Long
                    ch = Asc(Mid("Matthew", p, 1))
                    Res = SendMessage(WinHandle, WM_KEYDOWN, ch, 0)
                    Res = SendMessage(WinHandle, WM_KEYUP, ch, 0)
                Next
                'SendKeys.SendWait("{TAB}")
                Res = SendMessage(WinHandle, WM_KEYDOWN, Asc(VK_TAB), 0)

0
 
LVL 13

Expert Comment

by:iHadi
ID: 16666707
Use PostMessage instead of SendMessage:

Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

               Dim P As Integer
               WinHandle = FindWindow(vbNullString, "Untitled - Notepad")
               
                Res = PostMessage(WinHandle, WM_KEYDOWN, Asc("h"), 0)
                For P = 1 To Len("Matthew")
                    Dim ch As Long
                    ch = Asc(Mid("Matthew", P, 1))
                    Res = PostMessage(WinHandle, WM_KEYDOWN, ch, 0)
                    Res = PostMessage(WinHandle, WM_KEYUP, ch, 0)
                Next
                'SendKeys.SendWait("{TAB}")
                Res = PostMessage(WinHandle, WM_KEYDOWN, VK_TAB, 0)
0
 

Author Comment

by:bengelhart
ID: 16668556
Still nothing.   It goes halfway through my code without sending any keystrokes to notepad before I stop it.  Am I using it incorrectly?
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16670475
What you are doing is sending the keystrokes to the parent window of the notepad not the edit window (textbox). The findwindow API returns the handle of the main form of notepad. To retrieve the handle of the edit window (textbox) of the notepad you should use the EnumChildWindows API and GetClassName API with the FindWindow API.
To demonstrate what's happening, execute the following code that shows you how the notepad responds to the keystrokes sent to the main form and the edit window:

'**************** Add the following code to a module: ***********************

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 FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function EnumChildWindows Lib "user32" (ByVal hwndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Public Const WM_KEYDOWN = &H100
Public Const VK_F1 = &H70
Public Const VK_F5 = &H74

Public EditHwnd As Long ' Holds the handle of the edit window of the notepad

Public Function WndEnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
    Dim bRet As Long
    Dim myStr As String * 50

    bRet = GetClassName(hwnd, myStr, 50)
    If (Left(myStr, 4) = "Edit") Then
        EditHwnd = hwnd
    End If

End Function

' **************** Add the following code to a form *****************
' Add two command buttons to a form Command1, Command2 then paste the following code in the form's code window:

Private Sub Command1_Click()
' This sub sends keystrokes to the main window
Dim WinHandle As Long
Dim Res As Long

WinHandle = FindWindow(vbNullString, "Untitled - Notepad")

Res = PostMessage(WinHandle, WM_KEYDOWN, VK_F1, 0) ' Shows help window

Res = PostMessage(WinHandle, WM_KEYDOWN, VK_F5, 0) ' Inserts date and time

End Sub


Private Sub Command2_Click()
' This sub sends keystrokes to the edit window
Dim WinHandle As Long
Dim Res As Long

WinHandle = FindWindow(vbNullString, "Untitled - Notepad")

EnumChildWindows WinHandle, AddressOf WndEnumChildProc, 10

Res = PostMessage(EditHwnd, WM_KEYDOWN, Asc("S"), 0) ' Sends s to the Edit window of the notepad

End Sub
0
 

Author Comment

by:bengelhart
ID: 16671052
Got it.  Will give it a try.  I am just testing this in notepad.  I am actually going to be sending it to an old dos program.  So let me try the first set of code on that, then I will try this new code if that doesn't work.  I didn't realize that it was so specific to MIDI forms.  Thank you for helping me through this.
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16671775
It doesn't have anything to do with MDI forms. What is meant here by child window is a control within another control. From the OS point of view nearly everything you see is a window. A form is a window, a button is a window, a textbox is a window ... . So what is meant by child window may be a button on a form where the form is the parent and the button is th child. That explains why nearly every control you see on your screen has a handle, thats simply because the OS considers it a window. By the way you are working on WINDOWS OS aren't you!!
0
 

Author Comment

by:bengelhart
ID: 16683767
Yes it is Windows 2000 with an old dos based app running on it.
0
 

Author Comment

by:bengelhart
ID: 16686787
Okay.  The sendmessage and postmessage did not work.   This is an old application that is simply type in text and hit enter.  There is no edit box or text fields.  When you tab to the next "field" in this app it is just a blinking cursor in front of a label that describes what to type in.   It is all black background old school stuff.  Will how do I get sendmessage or post message to work in this situation.  Now the sendkeys works great, I just don't want to have to make the application the active window.  
0
 

Author Comment

by:bengelhart
ID: 16690814
I am confused on how this could work for me.  In the app I am sending keys to, I need to specify each field in send message?  Or do I just have to reference the main window to send message?
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16690981
If you're using a consol window (Dos) use the following code:

Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long

Const WM_KEYDOWN = &H100

Private Sub Command1_Click()
Dim P As Integer
WinHandle = FindWindow("ConsoleWindowClass", vbNullString)
Res = PostMessage(WinHandle, WM_KEYDOWN, Asc("h"), 0)
End Sub
0
 

Author Comment

by:bengelhart
ID: 16691468
It isn't using a console window.  What are the class values that can be used there?  I am going to try the child window from you example on Notepad.  I will be testing it later this afternoon.  Thanks for staying with me on this!!!
0
 

Author Comment

by:bengelhart
ID: 16694227
When I am pasting in the code you gave me - EnumChildWindows(WinHandle, AddressOf WndEnumChildProc, 10),  Addressof is underlined and I get an error saying that 'Addressof' cannot be converted to 'Long' because 'Long' is not a delegate type.  
0
 

Author Comment

by:bengelhart
ID: 16694296
I am sorry this is taking so long here.   I know that once we get the actual handle of this window figured out, everything will work just fine.
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16694715
Are you using VB6 or VB.NET? The code I provided you is for VB6.
0
 

Author Comment

by:bengelhart
ID: 16695058
Ahhhhh..  I am using VB.NET.  
0
 

Author Comment

by:bengelhart
ID: 16702297
Do you have the .NET equivalent?
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16703370
Hi again,
As for this post taking too long, I feel happy helping people
The following code is the VB.NET Equevalent. I've tested it on my computer and everything is fine:


'**************** Add the following code to a module: ***********************

    Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Integer, ByVal lpClassName As String, ByVal nMaxCount As Integer) As Integer
    Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
    Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    Public Declare Function EnumChildWindows Lib "user32" (ByVal hwndParent As Integer, ByVal lpEnumFunc As delEnum, ByVal lParam As Integer) As Integer

    Public Const WM_KEYDOWN = &H100
    Public Const VK_F1 = &H70
    Public Const VK_F5 = &H74

    Public EditHwnd As Integer ' Holds the handle of the edit window of the notepad

    Public Delegate Function delEnum(ByVal Hwnd As Integer, ByVal lParam As Integer) As Integer

    Public Function WndEnumChildProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Integer
        Dim bRet As Integer
        Dim myStr As New String(" ", 50)

        bRet = GetClassName(hwnd, myStr, 50)
        If (Left(myStr, 4) = "Edit") Then
            EditHwnd = hwnd
        End If

    End Function

' **************** Add the following code to a form *****************
' Add two button controls to the form

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        ' This sub sends keystrokes to the main window
        Dim WinHandle As Integer
        Dim Res As Integer

        WinHandle = FindWindow(vbNullString, "Untitled - Notepad")

        Res = PostMessage(WinHandle, WM_KEYDOWN, VK_F1, 0) ' Shows help window

        Res = PostMessage(WinHandle, WM_KEYDOWN, VK_F5, 0) ' Inserts date and time


    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        ' This sub sends keystrokes to the edit window
        Dim WinHandle As Integer
        Dim Res As Integer

        WinHandle = FindWindow(vbNullString, "Untitled - Notepad")

        EnumChildWindows(WinHandle, New delEnum(AddressOf WndEnumChildProc), 10)

        Res = PostMessage(EditHwnd, WM_KEYDOWN, Asc("S"), 0) ' Sends s to the Edit window of the notepad

    End Sub
0
 

Author Comment

by:bengelhart
ID: 16703488
Thanks!!!!  I will test it out right away.  Now to find the handle of the child window of the old application I want to use this with.  Should I just add a couple lines of code in the EnumChildWindows function to print out each window and its name in a text box?  That way I can specify it in the same function to get the right handle (like how you specify the "edit" window for notepad).
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16703577
You must determine what class the control that the text goes in in your old app. Replace the 'edit' with it. I think it is an edit also, so the code will work with no problem (I think).
0
 

Author Comment

by:bengelhart
ID: 16704104
Excellent.  It is working well on Notepad, I will test on the other app tomorrow.  Thanks for your help!!!
0
 

Author Comment

by:bengelhart
ID: 16704172
What is the const value for WM_SETTEXT?
0
 

Author Comment

by:bengelhart
ID: 16704555
One thing I noticed is your string values all have to be in capital letters to send correctly.
0
 

Author Comment

by:bengelhart
ID: 16712715
Well it didn't work on the old application.  I got a classname value of msctls_statusbar32 from the mystr value in the following code:

Public Function WndEnumChildProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Integer
        Dim bRet As Integer, intCount As Integer
        Dim myStr As New String(" ", 50)

        bRet = GetClassName(hwnd, myStr, 50)
        If (Microsoft.VisualBasic.Left(myStr, 4) = "edit") Then
            EditHwnd = hwnd
        End If
        intCount = +intCount
        txtWindow.Text = intCount & "- " & myStr & vbCrLf & txtWindow.Text
End Function

Any ideas?  I bought a couple API books to try and figure out how to find the value of the window handle I need to send messages to this old application.
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16714357
if the following code doesn't work, I should see you app:

Public Function WndEnumChildProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Integer
        Dim bRet As Integer
        Dim myStr As New String(" ", 50)

        bRet = GetClassName(hwnd, myStr, 50)
        If (myStr.Trim = "msctls_statusbar32") Then
            EditHwnd = hwnd
        End If

    End Function
0
 

Author Comment

by:bengelhart
ID: 16718175
Thanks!  Will give this a try.  I whipped up an enumerate all windows script I will run if this doesn't work to see what the handle is.
0
 

Author Comment

by:bengelhart
ID: 16728379
Alright I got everything working!  Thanks so much for your help!!  
0
 
LVL 13

Expert Comment

by:iHadi
ID: 16730734
You're welcome
0
 

Author Comment

by:bengelhart
ID: 16733163
I am sorry to ask one more question, but if I wanted to put a stop button on this app to stop sending keystrokes should I just use system.threading.thread.abort()?
0
 
LVL 13

Accepted Solution

by:
iHadi earned 2000 total points
ID: 16737505
If you are sending the keystrokes from within a loop, consider the following:
' Add the a variable that tells the loop to stop sending keystrokes at the top of the form in the general area
Dim AbortSend as boolean = False

Private Sub MySendingSub()
    For...
        If AbortSend = True then Exit for
        send keystrokes
    Next
End Sub

Private Sub StopSending
    AbortSend = True
End Sub
0
 

Author Comment

by:bengelhart
ID: 16737617
Sounds good.  Nice and easy.  Thank you so much for your help!!!
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I’ve seen a number of people looking for examples of how to access web services from VB6.  I’ve been using a test harness I built in VB6 (using many resources I found online) that I use for small projects to work out how to communicate with web serv…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…
Suggested Courses

829 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question