Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EventGhost] - Enhancement - WinApi.SendKeys, Improves performance in SendRawCodes1 #314

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

kdschlosser
Copy link
Member

The old mechanics of this method would send one key at a time and then wait for the notification to get processed by the receiving window. The user32.SendInput function allows you to send multiple keystrokes at a time.

I have modified the code so it now sends all of the keystrokes in a single notification and then waits only once. The difference in performance is astonishing.

The xml below is a way of testing this. It is a very strange way you have to go about getting the Send Keystrokes action to use this method for sending keystrokes. You need to use Find Window to locate the window. then you have to bring the window to the front (this gives it focus) then in a python script you need to delete the contents of eg.lastFoundWindow, then run the Send Keys action. This will send the keys using this method.

I suggest using the code below on an unaltered version of EG so you can see the speed. the code below requires you to have notepad open as this is what it is going to send the keys to. The run the same test using this modified version.

<?xml version="1.0" encoding="UTF-8" ?>
<EventGhost Version=" ">
    <Macro Name="Find Window: notepad.exe" Expanded="True">
        <Action>
            Window.FindWindow(u'notepad.exe', None, None, None, u'Edit', 1, False, 0.0, 0)
        </Action>
        <Action>
            Window.BringToFront()
        </Action>
        <Action>
            EventGhost.PythonScript(u'del eg.lastFoundWindows[:]')
        </Action>
        <Action>
            Window.SendKeys(u'This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test', False, 2)
        </Action>
    </Macro>
</EventGhost>

The old mechanics of this method would send one key at a time and then wait for the notification to get processed by the receiving window. The user32.SendInput function allows you to send multiple keystrokes at a time. 

I have modified the code so it now sends all of the keystrokes in a single notification and then waits only once. The difference in performance is astonishing.

The xml below is a way of testing this. It is a very strange way you have to go about getting the SendKeys action to use this method for sending keystrokes. You need to use Find Window to locate the window. then you have to bring the window to the front (this gives it focus) then in a python script you need to delete the contents of eg.lastFoundWindow, then run the Send Keys action. This will send the keys using this method. 

I suggest using the code below on an unaltered version of EG so you can see the speed. the code below requires you to have notepad open as this is what it is going to send the keys to. The run the same test using this modified version.

    <?xml version="1.0" encoding="UTF-8" ?>
    <EventGhost Version="WIP-2018.05.25-17.43.19">
        <Macro Name="Find Window: notepad.exe" XML_Guid="{EF46CE14-DFF8-499C-9B35-840D5C348282}" Expanded="True">
            <Action XML_Guid="{15930DB5-AEB3-4F57-A0B0-840161990957}">
                Window.FindWindow(u'notepad.exe', None, None, None, u'Edit', 1, False, 0.0, 0)
            </Action>
            <Action XML_Guid="{0C9CF2AF-71AA-4992-8877-41DA592545BD}">
                Window.BringToFront()
            </Action>
            <Action XML_Guid="{CD0A4449-AA83-482B-A9D5-661DF8B0658F}">
                EventGhost.PythonScript(u'del eg.lastFoundWindows[:]')
            </Action>
            <Action XML_Guid="{63EC7FC7-320E-4602-A93F-BA3B9A6A032B}">
                Window.SendKeys(u'This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test', False, 2)
            </Action>
        </Macro>
    </EventGhost>
@kdschlosser kdschlosser added this to the v0.5.1 milestone Jun 7, 2018
@kdschlosser kdschlosser requested a review from topic2k June 7, 2018 05:45
I added a test of the window handle being passed to WinApi.SendKeys.__call__ against the window handle that has focus. if the 2 handles match or if there is no window handle passed SendRawCodes1 will get used.
(in most cases a user is going to want to send keys to the window they are looking at so this will be a nice addition especially if the text that is being input is lengthy. 

with how the code was before because of the use of the PostMessage function if a string of text is being sent to the same window that is currently in focus and the user types something you will end up with the text not being properly inputted.

The SendInput function inserts the events in the INPUT structures serially into the keyboard or mouse input stream. These events are not interspersed with other keyboard or mouse input events inserted either by the user (with the keyboard or mouse) or by calls to keybd_event, mouse_event, or other calls to SendInput. This is a much better mechanism to be able to send the keystrokes. 1 because of it's speed and 2 because it cannot be interfered with.
@kdschlosser
Copy link
Member Author

kdschlosser commented Jun 7, 2018

Ok I have added an auto detection of the window handle that is in focus vs the one that is passed into SendKeys. if they are the same then it will use the better mechanism to send the keystrokes. the way the original code was the better mechanism would only be used if the window handle passed into SendKeys was None.

I have created a new test macro. You can see both ways in action. one you will run when notepad is visible but do not make it active. the second is after you run the macro you will need to make notepad active. specifically the text entry area (the white box) simply clicking on the title bar or the button to make it visible will not active the faster mechanism. there is a 2 second stall on sending the keys from the time you run the macro

<?xml version="1.0" encoding="UTF-8" ?>
<EventGhost Version=" ">
    <Macro Name="Find Window: notepad.exe" Expanded="True">
        <Action>
            Window.FindWindow(u'notepad.exe', None, None, None, u'Edit', 1, False, 0.0, 0)
        </Action>
        <Action>
            EventGhost.Wait(2.0)
        </Action>
        <Action>
            Window.SendKeys(u'This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test{Enter}', False, 2)
        </Action>
    </Macro>
</EventGhost>

This fixes a possible issue of when sending a long string to an active window and you used FindWindow to make sure it was running before sending if the user started typing at the same time the string was being send the 2 would get all mixed up. The fixes that bug. SendInput cannot get mixed up with any other input whether it is from a Windows API call or from the user.

If keystrokes are being sent to a window that is not active it cannot get jumbled up by user input because it is not the active window. and because of how EG works you are not ale to send multiple message at the same time. So no worries there either.

This modification greatly improves the speed of sending keys to a background window. 
  
The calls to WaitForInputProcessed were done for each and every single key press. This is not necessary. There are 2 times when a wait needs to be performed

if the current key code block contains more then a single key code. or if the block that is next in line has more then a single key code. This test has to be performed  after a key press and after a key release. not for each key in a key code block. and not if there is only a single key.


there are 2 mechanisms at work. the sending of a key code and the setting of the keyboard state. when the shift key gets set in the keyboard state change it has a global affect so if a key code that has been sent has not been processed and a change to the keyboard state takes place then the  outcome of the key code once it does get processed will be different. This is the purpose for waiting until it does get processed before continuing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants