Jump to content

ControlSend becomes unreliable when other activity is going on.


Digger
 Share

Recommended Posts

I’m working on a program to interact with a RUMBA terminal emulator window that is accessing an IBM host. I started out using Send() to send keystrokes to the session, including ^a^c to send window contents to the clipboard, but rapidly found that this was too susceptible to being disrupted by any other activity that was going on on the PC.

I switched over to using ControlSend() and found this to be more reliable, but in order to get the window contents I finally ended up having to use the Emulator High Level Language API (EHLLAPI) capability of RUMBA to send a request that returns the window contents as a string.

My ultimate goal is to try to get the program to the point that it can run completely unattended, with no visible windows, on a server.

The problem that I’m having is that it appears that ControlSend() is somehow either interacting with, or being influenced by other windows on the system.

When I tried running multiple instances of my program, each interacting with a different window, and each window having a different title, I would regularly see indications that not all of the keystrokes were reaching a particular window. As an experiment I wrote a small reproducer. It

  • runs Notepad,
  • figures out what the Window handle is
  • sets the window title to that random number
  • then loops 100 times, using ControlSend() to send "This is some text.{ENTER}{UP}{DOWN}{LEFT}{RIGHT}" to that instance of Notepad.
$iPID = Run("Notepad.exe")
;Allow window to initialize...
Sleep(500)
;Get HWND.
$hWnd = _GetHwndFromPID($iPID)
$nRand = Random(1, 100000)
WinSetTitle($hWnd, "", $nRand)
For $i = 1 To 100
ControlSend($hWnd, "", "Edit1", "This is some text.{ENTER}{UP}{DOWN}{LEFT}{RIGHT}")
Next

Func _GetHwndFromPID($PID)
$hWnd = 0
$winlist = WinList()
Do
  For $i = 1 To $winlist[0][0]
   If $winlist[$i][0] <> "" Then
    $iPID2 = WinGetProcess($winlist[$i][1])
    If $iPID2 = $PID Then
     $hWnd = $winlist[$i][1]
     ExitLoop
    EndIf
   EndIf
  Next
Until $hWnd <> 0
Return $hWnd
EndFunc   ;==>_GetHwndFromPID

If I run a single instance of the above program I end up with a Notepad window with 100 lines of text that each say “This is some text.”

If I take that script and compile it to an exe, and then launch that exe a few times, waiting a couple of seconds between each launch, I’ll often find that the text in the resulting Notepad windows will have random changes in capitalization, and the occasional line where the text was written more than once.

I suspect that the ultimate solution would be to implement my own send routine that would use the EHLLAPI to send the text to the terminal emulator, but I really don’t want to have to try to recreate the parser that is used by Send() and ControlSend() which parses out the special characters.

I’m hoping that someone has a suggestion on how I can keep ControlSend from interacting with or being influenced by other actions on the PC. Barring that, I’d be very interested in knowing if there is an easy way that I could parse a string containing special characters in the form that Send() and ControlSend() accept, and generate a string that I could send to the API.

Link to comment
Share on other sites

Did you try SendKeepActive?

SendKeepActive Attempts to keep a specified window active during Send().

SendKeepActive ( "title" [, "text"] )

Parameters

title The title of the window to activate.

See Title special definition.

Use a blank title to disable the function.

text [optional] The text of the window.

Return Value

Success: Returns 1.

Failure: Returns 0 if window is not found.

Remarks

Attempts to reset the active window in between each simulated keystroke from Send().

Edited by enaiman

SNMP_UDF ... for SNMPv1 and v2c so far, GetBulk and a new example script

wannabe "Unbeatable" Tic-Tac-Toe

Paper-Scissor-Rock ... try to beat it anyway :)

Link to comment
Share on other sites

I don't believe that this problem has anything to do with keyboard layout, as I am using a US keyboard. If you disagree, or have doubts, I suggest trying the reproducer code for yourself to see what results you get.

Simply compile the code to an exe and then launch it multiple times, giving each instance a couple of seconds to get started before firing off the next program. Then look at the resulting notepad windows to see whether they contain anything other than 100 lines of text that say "This is some text."

Link to comment
Share on other sites

I've seen this problem myself in the past. The only way I was able to solve it was to do a controlsend, then read the field the data was sent to and compare it. If it didn't match it would get sent again. It slowed things down a bit but it did solve the problem.

Link to comment
Share on other sites

To some extent I'm already doing that. The program is basically positioning to a field on the screen, entering a search key, and then sending an "F1" to initiate a search. My program looks at the resulting screen and only accepts the results as valid if the search key that was used matches a field on the results screen.

The bad thing is that some of the keys that are going to the terminal emulator are for the purpose of positioning to a field. When they get lost or changed I can end up putting things into fields where they aren't supposed to go, and there is no telling what the adverse consequences could be.

In addition, while the program is running I can see signs that it is messing with other windows. If I bring up a document in a Word window and try to type in it while the program is running I will get similar odd behavior. This means that I have to pretty much dedicate the computer to the single task of running a single instance of the program.

Link to comment
Share on other sites

lol... this is easier than ur making it out to be. ... all u need is a sleep in between each send because it takes time for the control send to actually send...

Try this:

$iPID = Run("Notepad.exe")
;Allow window to initialize...
Sleep(500)
;Get HWND.
$hWnd = _GetHwndFromPID($iPID)
$nRand = Random(1, 100000)
WinSetTitle($hWnd, "", $nRand)
For $i = 1 To 100
ControlSend($hWnd, "", "Edit1", "This is some text.{ENTER}{UP}{DOWN}{LEFT}{RIGHT}")
Sleep(150)
Next
Func _GetHwndFromPID($PID)
$hWnd = 0
$winlist = WinList()
Do
  For $i = 1 To $winlist[0][0]
   If $winlist[$i][0] <> "" Then
    $iPID2 = WinGetProcess($winlist[$i][1])
    If $iPID2 = $PID Then
     $hWnd = $winlist[$i][1]
     ExitLoop
Link to comment
Share on other sites

lol... this is easier than ur making it out to be. ... all u need is a sleep in between each send because it takes time for the control send to actually send...

If you think this is so easy, why don't you give your proposed solution a try? Compile it to an EXE and run it 5 times and let me know how it turns out...

Link to comment
Share on other sites

I actually went ahead and bit the bullet. I wrote a parser that was adequate to support the majority of tags that Send and ControlSend accept. It turns those tags into mnemonics that the ehllapi Send Key function recognizes.

This was the "right" thing to do anyway. By implementing the ehllapi "Send Key" and "Wait" functions I've got my program waiting for the host to be ready for the next command rather than having to build in a bunch of sleep commands. The program is running much faster, and I've got 3 instances of it running right now, each interacting with a different host, and they are all running without interfering with each other.

Unless someone comes up with a viable solution then I guess the next step is to submit this issue as a bug.

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...