Sign in to follow this  
Followers 0
kamyers1

Send Characters to Inactive Window

7 posts in this topic

#1 ·  Posted (edited)

Using AutoitX (not Autoit!), I would like to send characters to an inactive window in order to force that window to execute one of its menu options. The menu is non-standard, so WinMenuSelectItem doesn't work. Send works, but only if the window is active, and that isn't adequate for my needs. ControlSend can send characters to various controls on the window, but I can't find any way to get it to activate the menu items. The window in question is the main form (ThunderRT6MDIForm) of a VB6 based application. I thought that I had another solution, but it doesn't work. Here is what I tried:

1. I get the window handle using WinGetHandle from AutoitX. That part works ok, and I have verified that the correct handle value is returned by using other tools.

2. I wrote a little VB utility to send characters to a specific window handle by using the SendMessageA Windows API function.

When I run the VB utility, still no luck! My VB code is pretty simple, and I output stuff from each step in my code so that I can verify they are working as anticipated. Everything seems perfect until I make the actual API call. But then there is no response from the application, and I get back a result value of 8975925931411505152 from the API call. I assume that is some kind of error code, but have no idea what it means. My VB code is posted below. Can anyone here help out?

Thanks,

Kevin M.

With a new Notepad window open, if I run the code listed below from a console window, here is what I get:

C:\myApp\bin>test.vbs

handle=1512254

SendCharsByHandle 1512254 ☼

nArgs=2

Handle=1512254

nArg=2, MyStr=☼, nChars=1

wparam=15

handle=1512254, message=258, wparam=15, lparam=0

result=8975925931411505152

Here is test.vbs:

set AutoIt=WScript.CreateObject("AutoItX3.Control")
set Shell=CreateObject("WScript.Shell")
AppWindowName="Untitled - Notepad"

AutoIt.WinActivate AppWindowName
WScript.Sleep(1000)

handle=AutoIt.WinGetHandle(AppWindowName)
WScript.Echo "handle=" & CLng("&h" & handle)

rem char(15) is CTRL-O, should execute Notepad's File->Open menu item.
command="SendCharsByHandle " & CLng("&h" & handle) & " " & chr(15)
WScript.Echo command
execCmd(command)

WScript.Quit(0)


function execCmd(command)
    set oCommand=shell.exec(command)
    do until oCommand.Status=1 and oCommand.StdOut.AtEndOfStream
        if oCommand.StdOut.AtEndOfStream then
            WScript.Sleep 100
        else
            WScript.Echo oCommand.StdOut.ReadLine
        end if
    loop 'until oCommand.Status=1 and oCommand.StdOut.AtEndOfStream
    execCmd=oCommand.ExitCode
end function 'execCmd

Here is SendCharsByHandle.VB:

Imports System

Module SendKeysByHandle

    Public Declare Function SendMessage Lib "user32" _
    Alias "SendMessageA" (ByVal hwnd As Long, _
                         ByVal wMsg As Long, _
                         ByVal wparam As Long, _
                         ByVal lparam As Long) As Long

    Sub Main()

        Dim MyStr As String
        Dim nArgs, nArg, nChars, nChar As Short
        Dim result As Long
        Dim handle As Long
        Dim message As Long
        Dim wparam As Long
        Dim lparam As Long

        Const WM_CHAR = &H102

        message = WM_CHAR 'WM_CHAR message constant
        lparam = 0

        nArgs = UBound(Environment.GetCommandLineArgs())
        Console.WriteLine("nArgs=" & nArgs)

        If nArgs > 0 Then

            If IsNumeric(Environment.GetCommandLineArgs(1)) Then

                handle = CLng(Environment.GetCommandLineArgs(1))
                Console.WriteLine("Handle=" & handle)

                For nArg = 2 To nArgs

                    MyStr = Environment.GetCommandLineArgs(nArg)
                    nChars = Len(MyStr)
                    Console.WriteLine("nArg=" & nArg & ", MyStr=" & MyStr & ", nChars=" & nChars)

                    For nChar = 1 To Len(MyStr)

                        wparam = CLng(Asc(Mid(MyStr, nChar, 1)))
                        Console.WriteLine("wparam=" & wparam)

                        Console.WriteLine("handle=" & handle & ", message=" & message & ", wparam=" & wparam & ", lparam=" & lparam)
                        result = SendMessage(handle, message, wparam, lparam)
                        Console.WriteLine("result=" & result)

                    Next

                Next

            Else

                Console.WriteLine("Invalid arguments.  Correct syntax is: SendKeysByHandle <handle> <string>")

            End If

        Else

            Console.WriteLine("Invalid arguments.  Correct syntax is: SendKeysByHandle <handle> <string>")

        End If

    End Sub

End Module
Edited by kamyers1

Share this post


Link to post
Share on other sites



You will need to concede after some amount of trying that some apps are coded to ignore messages if the window is not active.

Lar.


f_mrcleansmalm_77ce002.jpgAutoIt has helped make me wealthy

Share this post


Link to post
Share on other sites

But I tried several different apps including Notepad and Calculator, and none of them responded! I suspect there is probably something wrong with my VB code, maybe something do with the handle. Perhaps the handle value needs to get converted from a Long to a "true" handle, but I don't know how to do that if so. Was hoping that some of the gurus on this site might know. I have also posted to Microsoft's VB forum with no luck so far.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

I made some corrections to my VB code that eliminates the non-zero result value from calling SendMessage, but I'm still not having any luck getting apps to respond to the sent text in any way, not even if they are active. Not even simple apps like Notepad and Calculator. Latest VB code below...

Imports System.Runtime.InteropServices
Imports System

Module SendCharsByHandle

    <DllImport("user32", EntryPoint:="FindWindow")> _
    Private Function FindWindow( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Int32
    End Function

    <DllImport("user32", EntryPoint:="GetWindow")> _
    Private Function GetWindow( _
        ByVal hwnd As Int32, _
        ByVal wCmd As Int32) As Int32
    End Function

    <DllImport("user32", EntryPoint:="GetWindowText")> _
    Private Function GetWindowText( _
        ByVal hwnd As Int32, _
        ByVal lpString As System.Text.StringBuilder, _
        ByVal cch As Int32) As Int32
    End Function

    <DllImport("user32", EntryPoint:="SendMessageA")> _
    Private Function SendMessage( _
        ByVal hwnd As UInteger, _
        ByVal wMsg As UInteger, _
        ByVal wparam As UInteger, _
        ByVal lparam As UInteger) As UInteger
    End Function

    Sub Main()

        Dim MyStr As String
        Dim nArgs, nArg, nChars, nChar As Short
        Dim result As UInteger
        Dim handle, handle2 As UInteger
        Dim message As UInteger
        Dim wparam As UInteger
        Dim lparam As UInteger
        Dim gotHandle As Boolean

        Const WM_CHAR As UInteger = &H102

        message = WM_CHAR 'WM_CHAR message constant
        lparam = 0

        nArgs = UBound(Environment.GetCommandLineArgs())
        Console.WriteLine("nArgs=" & nArgs)

        If nArgs > 0 Then

            If IsNumeric(Environment.GetCommandLineArgs(1)) Then

                handle = CLng(Environment.GetCommandLineArgs(1))
                gotHandle = GetHandleFromPartialCaption(handle2, "Untitled - Notepad")
                Console.WriteLine("Handle=" & handle & ", handle2=" & handle2)
                If gotHandle Then handle = handle2

                For nArg = 2 To nArgs

                    MyStr = Environment.GetCommandLineArgs(nArg)
                    nChars = Len(MyStr)
                    Console.WriteLine("nArg=" & nArg & ", MyStr=" & MyStr & ", nChars=" & nChars)

                    For nChar = 1 To Len(MyStr)

                        wparam = CLng(Asc(Mid(MyStr, nChar, 1)))
                        Console.WriteLine("wparam=" & wparam)

                        Console.WriteLine("handle=" & handle & ", message=" & message & ", wparam=" & wparam & ", lparam=" & lparam)
                        result = SendMessage(handle, message, wparam, lparam)
                        Console.WriteLine("result=" & result)

                    Next

                Next

            Else

                Console.WriteLine("Invalid arguments.  Correct syntax is: SendKeysByHandle <handle> <string>")

            End If

        Else

            Console.WriteLine("Invalid arguments.  Correct syntax is: SendKeysByHandle <handle> <string>")

        End If

    End Sub

    Private Function GetHandleFromPartialCaption(ByRef lWnd As Int32, ByVal sCaption As String) As Boolean

        Dim Caption As New System.Text.StringBuilder(256)
        Dim lhWndP As Int32

        Const GW_HWNDNEXT As Int32 = 2

        GetHandleFromPartialCaption = False
        lhWndP = FindWindow(vbNullString, vbNullString)
        Do While lhWndP <> 0
            GetWindowText(lhWndP, Caption, Caption.Capacity)
            If InStr(1, Caption.ToString(), sCaption) > 0 Then
                GetHandleFromPartialCaption = True
                lWnd = lhWndP
                Exit Do
            End If
            lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
        Loop

    End Function

End Module
Edited by kamyers1

Share this post


Link to post
Share on other sites

Sorry, just posted re-formatted code. I wasn't familiar with this forum's methods for handling code.

Share this post


Link to post
Share on other sites

Ok, been working on this all day and all night, still no luck. I've got to be missing something simple. Currently I am able to send text to the Edit control in a Notepad application using VB, but I can't seem to figure out the right mechanism for sending accelerator keys, e.g. Ctrl-O for File->Open, even if the Notepad window is already active. I know that can be done, because the AutoItX Send method can do it, so I must be doing something wrong.

Once again, my purpose in doing this is that I need to send accelerator keys to an *inactive* window, which AutoItX can't currently seem to do (I've already tried). So I am writing VB code to try to work around this limitation. But if I can't manage to send the appropriate messages to a simple application like Notepad when it is already active, then I don't stand a prayer of accomplishing my real goal with other apps that aren't currently active. I know this isn't a VB forum, but folks on here are experienced with writing code to control other apps, so I am hoping that someone can spot what I am missing. Current version of code that I am trying to run is listed below. By the way, Notepad "beeps" when this code runs, and Winspector Spy seems to show that it is receiving the appropriate messages, so surely I must be fairly close...

Oh by the way, the WM_COMMAND version that is commented out below *does* work, but I don't want to use that for general purpose work, because it requires knowing menu id values that are application specific and a bit of a pain to come up with. Much nicer if I could just manage to send the stupid accelerator keys!

Thanks,

KM

Module crazy

    Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    Private Declare Auto Function PostMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
    Private Declare Auto Function GetWindow Lib "user32" (ByVal hWnd As IntPtr, ByVal uCmd As Integer) As IntPtr
    Private Const WM_KEYDOWN = &H100
    Private Const WM_CHAR = &H102
    Private Const WM_KEYUP = &H101
    Private Const WM_COMMAND = &H111
    Private Const GW_CHILD = 5

    Sub Main()

        Dim whnd As IntPtr = FindWindow(vbNullString, "untitled - notepad")
        Dim Cwhnd As IntPtr = GetWindow(whnd, GW_CHILD)

        'PostMessage(whnd, WM_KEYDOWN, &H11, &H11D0001)
        'PostMessage(whnd, WM_KEYDOWN, &H4F, &H440001)
        'PostMessage(whnd, WM_CHAR, &HF, &H440001)
        'PostMessage(whnd, WM_KEYUP, &H4F, &HC0440001)
        'PostMessage(whnd, WM_KEYUP, &H11, &HC11D0001)

        'PostMessage(whnd, WM_COMMAND, &H10002, &H0)

        PostMessage(Cwhnd, WM_KEYDOWN, &H11, &H1D0001)
        PostMessage(Cwhnd, WM_KEYDOWN, &H4F, &H180001)
        PostMessage(Cwhnd, WM_CHAR, &HF, &H180001)
        PostMessage(Cwhnd, WM_KEYUP, &H4F, &HC0180001)
        PostMessage(Cwhnd, WM_KEYUP, &H11, &HC01D0001)

        'Dim Txt As String = "CRAZY"
        'For x As Integer = 0 To Txt.Length - 1
        '    PostMessage(Cwhnd, WM_KEYDOWN, CInt(Asc(Txt.Chars(x))), &H2F0001)
        'Next

    End Sub

End Module

Share this post


Link to post
Share on other sites

What was the outcome of this...... like,  years later.  I'm having the exact same problem.  ControlSend and ControlClick do not work when the window is INACTIVE but work fine when it IS active.  The Class you gave is the EXACT same class I'm working with, so I'm assuming that you were working with Stealthbot.  I was not able to get it to work with Notepad, Calculator or ANY window for that matter.

 

I've Come to the conclusion that both ControlSend and ControlClick just simply do not work as described and are comepletely broken in AutoitX3.  I am pouring over the forums here and this is like the closes post I've found to the issue I am having.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0