• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 420
  • Last Modified:

Is the send message API for me?

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
bengelhart
Asked:
bengelhart
  • 31
  • 16
  • 2
1 Solution
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
bengelhartAuthor Commented:
So I hav eto send individual letters with Asc()?  Or does Asc() support putting a word into it like this:  Asc("country")  ?
0
 
butterhookCommented:
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
 
bengelhartAuthor Commented:
Oh wow.  Alright.  Well if that is the way it works I will give it a try.  Thanks.
0
 
butterhookCommented:
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
 
bengelhartAuthor Commented:
I will just send the individual characters in a for loop.   I will respond when I test this tomorrow.  Thanks!
0
 
bengelhartAuthor Commented:
What does the long value returned from SendMEssage represent?
0
 
bengelhartAuthor Commented:
Also what is the const for keyup?  You have key up in your loop that I am testing in my app.
0
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
Thanks!!
0
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
Your welcome :)

Its really not necessary to send the WM_KEYUP after the first "s" you can send the next "s" directly
0
 
bengelhartAuthor Commented:
Okay.  Thanks again.
0
 
bengelhartAuthor Commented:
If I put Asc("Don")  will it type a captial D?
0
 
iHadiCommented:
Yes, if you put Asc("Any String") it is equivelant to the Asc of the first letter i.e: Asc("A")
0
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
Still nothing.   It goes halfway through my code without sending any keystrokes to notepad before I stop it.  Am I using it incorrectly?
0
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
Yes it is Windows 2000 with an old dos based app running on it.
0
 
bengelhartAuthor Commented:
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
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
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
 
bengelhartAuthor Commented:
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
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
Are you using VB6 or VB.NET? The code I provided you is for VB6.
0
 
bengelhartAuthor Commented:
Ahhhhh..  I am using VB.NET.  
0
 
bengelhartAuthor Commented:
Do you have the .NET equivalent?
0
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
Excellent.  It is working well on Notepad, I will test on the other app tomorrow.  Thanks for your help!!!
0
 
bengelhartAuthor Commented:
What is the const value for WM_SETTEXT?
0
 
bengelhartAuthor Commented:
One thing I noticed is your string values all have to be in capital letters to send correctly.
0
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
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
 
bengelhartAuthor Commented:
Alright I got everything working!  Thanks so much for your help!!  
0
 
iHadiCommented:
You're welcome
0
 
bengelhartAuthor Commented:
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
 
iHadiCommented:
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
 
bengelhartAuthor Commented:
Sounds good.  Nice and easy.  Thank you so much for your help!!!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Cloud Class® Course: Microsoft Exchange Server

The MCTS: Microsoft Exchange Server 2010 certification validates your skills in supporting the maintenance and administration of the Exchange servers in an enterprise environment. Learn everything you need to know with this course.

  • 31
  • 16
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now