Key Stroke "Intercept" Concept

Technical support and scripting issues

Moderators: Dorian (MJT support), JRL

Post Reply
User avatar
JRL
Automation Wizard
Posts: 3497
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Key Stroke "Intercept" Concept

Post by JRL » Mon Oct 25, 2010 6:50 pm

For some time there has been at least one Macro Scheduler forum regular who has wanted to be able to intercept key strokes using Macro Scheduler scripting. As far as I know the best we users have been able to accomplish is to deal with the results of keystrokes, but not to actually intercept them. This morning while looking into a method to defeat Alt+Tab I ran into information about the Windows API function RegisterHotKey. I think I've found a way to use this API to intercept keystrokes so that they do not function for other applications, they only respond for Macro Scheduler scripts.

Could someone test the following code for me? If you run this script and then run any application that should respond to the Esc key, pressing the Esc key should fail to do anything in your other application. Instead it should bring up a Macro Scheduler dialog and place Success! in the edit field of the dialog. The "other" application must have focus when you press the Esc key.

I tested using Windows Display Properties screen (right click the desktop then select "Properties" from the menu). Normally, when Display Properties has focus, pressing the esc key closes the window. After opening Display Properties then running the following script, pressing the Esc key while Display Properties has focus does nothing to Display Properties but instead displays the script's dialog then types into the edit field.

Please be aware: I'm not sure what the potential ramifications of these library functions might be. The script might not be implementing properly or might not close some memory gizmo properly so be sure to not run this before saving that important paper you've been typing on all day.

The dialog in the following script requires Macro Scheduler version 12 or greater.

Code: Select all

//The key_down onevent provides the key a task to do in the script
//VK27,0 is the Esc key with no modifier.
OnEvent>key_down,vk27,0,Paste

//The dialog allows some place for the key task to occur and
//gives us control over the script
Dialog>Dialog1
object Dialog1: TForm
  Left = 465
  Top = 178
  HelpContext = 5000
  BorderIcons = [biSystemMenu]
  Caption = 'CustomDialog'
  ClientHeight = 223
  ClientWidth = 439
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = True
  ShowHint = True
  OnTaskBar = False
  PixelsPerInch = 96
  TextHeight = 13
  object Edit1: TEdit
    Left = 45
    Top = 40
    Width = 244
    Height = 21
    TabOrder = 8
  end
end
EndDialog>Dialog1

//A way to close the script
AddDialogHandler>Dialog1,,OnClose,Quit

//API function GlobalAddAtomA gives us an ID for the RegisterHotKey
//function's "ID" parameter.  "JRL" is just a text string.  For our
//purposes, it could be any alpha string shorter than 256 characters.
LibFunc>Kernel32,GlobalAddAtomA,GAAres,JRL

//API function RegisterHotKey required parameters. 1)A dialog handle
//2)An "ID" acquired using GlobalAddAtomA 3)A modifier key code allowing you to use
//ALT or CTRL or SHIFT with your key 4)The key's virtual key code
LibFunc>User32,RegisterHotKey,RHKres,dialog1.handle,GAAres,0,27

Label>Loop
Wait>0.01
Goto>Loop

SRT>Paste
  Show>dialog1
  SetDialogObjectFocus>dialog1,msEdit1
  Wait>1
  Send>Success!
END>Paste

SRT>Quit
  //API function GlobalDeleteAtom clears the atom we earlier created
  LibFunc>Kernel32,GlobalDeleteAtom,GDAres,GAAres
  Exit>0
END>Quit
Last edited by JRL on Fri Dec 03, 2010 6:28 pm, edited 3 times in total.

User avatar
jpuziano
Automation Wizard
Posts: 1085
Joined: Sat Oct 30, 2004 12:00 am

Post by jpuziano » Mon Oct 25, 2010 7:10 pm

Hi JRL,

I will admit to wanting this for quite some time. I have no time to test it now (crazy day in progress) but will definately give it a try and post results.

If we can register a hot-key, the ability to un-register that hot-key would be nice... to make it available again for other apps, the OS, etc.

I am extreamely curious what Marcus thinks of this technique. Marcus, would you be willing to give us an MS command like:

RegisterHotKey>

The benefit of an actual MS command might be, when the macro ends (either a compiled or non-compiled macro) then it could take care of un-registering any hot-keys you might have registered. Note that if it could take care of this no matter "how" the MS macro ended i.e. cleanup if on normal exit or exit because of any error condition, that would be fantastic. I am hoping this would be possible because for most errors, an error handler must take control as it shows you what the error is. If it can do that, it should be able to automatically un-register any hot-keys that may have registered in your macro.

Very interesting JRL... thanks for sharing. :D
jpuziano

Note: If anyone else on the planet would find the following useful...
[Open] PlayWav command that plays from embedded script data
...then please add your thoughts/support at the above post - :-)

User avatar
JRL
Automation Wizard
Posts: 3497
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Mon Oct 25, 2010 7:16 pm

When you get around to testing you should find that the hot key is "unregistered" when the script closes. In the event of a script crash the thing that gets left behind is the "Global Atom" not the hot key. I'm not sure what the ramifications might be but I think that if you run the script again and close cleanly, the "Global Atom" is deleted.

I'll re-post the script above with some comments.

User avatar
JRL
Automation Wizard
Posts: 3497
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Mon Nov 01, 2010 10:15 pm

More on using the RegisterHotKey API to intercept key strokes before they are passed to any other Windows application.

First is a subroutine written so that it can be used as a function, much like using the OnEvent function.

Second is a sample script that demonstrates how to use the subroutine. It sets up two hot keys, the esc key and the F1 key. While the script is running esc and F1 will not perform their normal tasks for windows applications. When the script ends the hot keys go away.

Code: Select all

//The Microsoft documentation for RegisterHotKey states that the F12 key
//should not be used as a hot key.  It is reserved for the system debugger.

//Usage:
/*GoSub>SetHotKey,Unique Dialog name,Virtual Key Code,Modifier per M.S. list below,Action Subroutine name
0: No modifier key
1: SHIFT key must also be pressed
2: CONTROL key must also be pressed
3: ALT key must also be pressed
4: SHIFT + ALT keys must be pressed
5: CONTROL + ALT keys must be pressed
6  SHIFT + CONTROL keys must be pressed
7: CONTROL + ALT + SHIFT keys must also be pressed
8: Windows key must also be pressed
*/

SRT>SetHotKey
  Let>LOCALVARS=1
  Let>includeFlag=%SetHotKey_var_2%%SetHotKey_var_3%

  If>%SetHotKey_var_3%=1
    Let>SetHotKey_var_3=4
  EndIf
  If>%SetHotKey_var_3%=3
    Let>SetHotKey_var_3=1
  EndIf
  If>%SetHotKey_var_3%=4
    Let>SetHotKey_var_3=5
  EndIf
  If>%SetHotKey_var_3%=5
    Let>SetHotKey_var_3=3
  EndIf

  //A unique dialog needs to exist for each invoke of RegisterHotKey API
  DeleteFile>%temp_dir%dialoginclude.scp
  WriteLn>%temp_dir%dialoginclude.scp,wres,If>includeFlag=%SetHotKey_var_2%%SetHotKey_var_3%
  WriteLn>%temp_dir%dialoginclude.scp,wres,  Dialog>%SetHotKey_var_1%
  WriteLn>%temp_dir%dialoginclude.scp,wres,  EndDialog>%SetHotKey_var_1%
  WriteLn>%temp_dir%dialoginclude.scp,wres,EndIf
  WriteLn>%temp_dir%dialoginclude.scp,wres,If>includeFlag<>%SetHotKey_var_2%%SetHotKey_var_3%
  WriteLn>%temp_dir%dialoginclude.scp,wres,  Include>%temp_dir%dialoginclude.scp
  WriteLn>%temp_dir%dialoginclude.scp,wres,EndIf

  If>includeFlag=%SetHotKey_var_2%%SetHotKey_var_3%
    Include>%temp_dir%dialoginclude.scp
  EndIf

  //The key_down onevent provides the key a task to do in the script
  //VK27,0 is the Esc key with no modifier.
  OnEvent>key_down,vk%SetHotKey_var_2%,%SetHotKey_var_3%,%SetHotKey_var_4%

  //API function GlobalAddAtomA gives us an ID for the RegisterHotKey
  //function's "ID" parameter.  "JRL" is just a text string.  For our
  //purposes, it could be any alpha string shorter than 256 characters.
  LibFunc>Kernel32,GlobalAddAtomA,GAAres,JRL

  //API function RegisterHotKey required parameters. 1)A dialog handle
  //2)An "ID" acquired using GlobalAddAtomA 3)A modfier key code allowing you to use
  //ALT or CTRL or SHIFT with your key 4)The key's virtual key code
  LibFunc>User32,RegisterHotKey,RHKres,%SetHotKey_var_1%.handle,GAAres,%SetHotKey_var_3%,%SetHotKey_var_2%

  //API function GlobalDeleteAtom clears the atom we earlier created
  LibFunc>Kernel32,GlobalDeleteAtom,GDAres,GAAres
END>SetHotKey

Code: Select all

//Winkey+Esc to end the script
OnEvent>key_down,vk27,8,Quit

//The Microsoft documentation for RegisterHotKey states that the F12 key
//should not be used as a hot key.  It is reserved for the system debugger.
GoSub>SetHotKey,EscDialog27,27,0,Action
GoSub>SetHotKey,F1Dialog112,112,0,Action

//Usage:
/*GoSub>SetHotKey,Unique Dialog name,Virtual Key Code,Modifier per M.S. list below,Action Subroutine name
0: No modifier key
1: SHIFT key must also be pressed
2: CONTROL key must also be pressed
3: ALT key must also be pressed
4: SHIFT + ALT keys must be pressed
5: CONTROL + ALT keys must be pressed
6  SHIFT + CONTROL keys must be pressed
7: CONTROL + ALT + SHIFT keys must also be pressed
8: Windows key must also be pressed
*/

SRT>SetHotKey
  Let>LOCALVARS=1
  Let>includeFlag=%SetHotKey_var_2%%SetHotKey_var_3%

  If>%SetHotKey_var_3%=1
    Let>SetHotKey_var_3=4
  EndIf
  If>%SetHotKey_var_3%=3
    Let>SetHotKey_var_3=1
  EndIf
  If>%SetHotKey_var_3%=4
    Let>SetHotKey_var_3=5
  EndIf
  If>%SetHotKey_var_3%=5
    Let>SetHotKey_var_3=3
  EndIf

  //A unique dialog needs to exist for each invoke of RegisterHotKey API
  DeleteFile>%temp_dir%dialoginclude.scp
  WriteLn>%temp_dir%dialoginclude.scp,wres,If>includeFlag=%SetHotKey_var_2%%SetHotKey_var_3%
  WriteLn>%temp_dir%dialoginclude.scp,wres,  Dialog>%SetHotKey_var_1%
  WriteLn>%temp_dir%dialoginclude.scp,wres,  EndDialog>%SetHotKey_var_1%
  WriteLn>%temp_dir%dialoginclude.scp,wres,EndIf
  WriteLn>%temp_dir%dialoginclude.scp,wres,If>includeFlag<>%SetHotKey_var_2%%SetHotKey_var_3%
  WriteLn>%temp_dir%dialoginclude.scp,wres,  Include>%temp_dir%dialoginclude.scp
  WriteLn>%temp_dir%dialoginclude.scp,wres,EndIf

  If>includeFlag=%SetHotKey_var_2%%SetHotKey_var_3%
    Include>%temp_dir%dialoginclude.scp
  EndIf

  //The key_down onevent provides the key a task to do in the script
  //VK27,0 is the Esc key with no modifier.
  OnEvent>key_down,vk%SetHotKey_var_2%,%SetHotKey_var_3%,%SetHotKey_var_4%

  //API function GlobalAddAtomA gives us an ID for the RegisterHotKey
  //function's "ID" parameter.  "JRL" is just a text string.  For our
  //purposes, it could be any alpha string shorter than 256 characters.
  LibFunc>Kernel32,GlobalAddAtomA,GAAres,JRL

  //API function RegisterHotKey required parameters. 1)A dialog handle
  //2)An "ID" acquired using GlobalAddAtomA 3)A modfier key code allowing you to use
  //ALT or CTRL or SHIFT with your key 4)The key's virtual key code
  LibFunc>User32,RegisterHotKey,RHKres,%SetHotKey_var_1%.handle,GAAres,%SetHotKey_var_3%,%SetHotKey_var_2%

  //API function GlobalDeleteAtom clears the atom we earlier created
  LibFunc>Kernel32,GlobalDeleteAtom,GDAres,GAAres
END>SetHotKey

Label>Loop
Wait>0.01
Goto>Loop

SRT>Action
  MDL>Action processed
END>Action

SRT>Quit
  Exit>0
END>Quit

User avatar
jpuziano
Automation Wizard
Posts: 1085
Joined: Sat Oct 30, 2004 12:00 am

Post by jpuziano » Tue Nov 02, 2010 1:45 am

Hi JRL,

I tested the first script you posted in this thread and it seems to work fine. On seeing this in your later scripts posted in this thread...
  • //A unique dialog needs to exist for each invoke of RegisterHotKey API
...I am wondering, does this technique absolutely require us to use a dialog in the macro?

What if I wanted a simple macro that sets up two programmed hotkeys using these API calls, one for F1 and one for ESC and there are no dialogs... the macro just runs in the background and if you hit F1, it uses an MDL> command to display a message saying that you hit F1. At that point, could you also hit ESC and would it be able to respond by using an MDL> command to display a message that you hit ESC? Or, would you have to click OK on the first MDL before the second ESC hotkey would be able to respond?

:?:
jpuziano

Note: If anyone else on the planet would find the following useful...
[Open] PlayWav command that plays from embedded script data
...then please add your thoughts/support at the above post - :-)

User avatar
JRL
Automation Wizard
Posts: 3497
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Tue Nov 02, 2010 4:49 am

See THIS SITE for everything you ever wanted to know about the RegisterHotKey API.
...I am wondering, does this technique absolutely require us to use a dialog in the macro?
Nope.... But you said and I agree
If we can register a hot-key, the ability to un-register that hot-key would be nice...
If the hot key is registered to a window or a dialog, when the dialog closes, the hotkey is terminated. Therefore when the script ends, (no matter how) the dialog no longer exists and neither does the hot key. If, on the other hand, the hot key is registered to the thread that the script creates, one must also run the "UnregisterHotKey" function. If the script crashes before running UnregisterHotKey, you will be stuck with the hot key until you restart your computer.

Notice that the dialogs created by this subroutine have no objects nor properties set and the dialogs are never shown. There are hundreds of dialogs on your computer at any given time and you usually don't know about them. (Run "View System Windows" and start counting.)

The main reason I posted the subroutine is to provide an easy and safe way to create a hot key. Maybe in release 13 or 14 or ... a new function will be added. Until then using the Window's API works.
What if I wanted a simple macro that sets up two programmed hotkeys using these API calls, one for F1 and one for ESC and there are no dialogs...
Use the subroutine and there will always be dialogs.
...the macro just runs in the background and if you hit F1, it uses an MDL> command to display a message saying that you hit F1. At that point, could you also hit ESC and would it be able to respond by using an MDL> command to display a message that you hit ESC? Or, would you have to click OK on the first MDL before the second ESC hotkey would be able to respond?
The hot key does not actually do anything. The RegisterHotKey function just causes the key press to be "registered" to the script. Only the script can detect the press. Therefore the script either does something when the key is pressed or does nothing when the key is pressed. The subroutine sets up an OnEvent key press handler to allow the script to accomplish something when the key is pressed. Since a script halts when you display a Modal Message, the second key press will be intercepted by the RegisterHotKey API, but not acted upon by the halted script. So... yes, you would need to close the message before seeing further responses by the script for other key presses. If you want to be able to act on multiple hot keys simultaneously, you either need to avoid modal situations or run multiple scripts each set up to react to separate hot keys.

Hope this makes sense.

P.S.
This is working great for me in Windows XP. Has anyone tried it using Vista or Win7?

User avatar
jpuziano
Automation Wizard
Posts: 1085
Joined: Sat Oct 30, 2004 12:00 am

Post by jpuziano » Wed Dec 15, 2010 12:21 am

JRL wrote:The main reason I posted the subroutine is to provide an easy and safe way to create a hot key. Maybe in release 13 or 14 or ... a new function will be added. Until then using the Window's API works.
Hi JRL,

Just a quick note to say thanks. I am using a modified form of your subroutine method to register a hot-key and it is working well.

To any others who may try it, if you are going to use it inside a compiled macro, when you compile it (under Tools/Create Exe) you'll see the Options and by default, "Compile Includes" has a check-mark beside it i.e. it is turned on. JRL's subroutine method uses an Include> statement that must be able to pull in "include" code from a text file that the macro itself writes when it first runs... so if you leave this checked, it will try to resolve those Include> lines when it compiles... however it can't as the lines haven't been written yet (because the macro has not actually run yet, we're just compiling it) so the hot-key will not work.

The fix is just to remove the check-mark by the "Compile Includes" option... then compile it. That will leave resolution of the included lines to run-time (when they will actually exist) and the code will work.

Yes this is substantially more complicated than just a simple RegisterHotKey> and UnRegisterHotKey> command... and I hope one day those commands exist in Macro Scheduler... but this does work, its fast and it is RESPONSIVE.

Thank you so much JRL for posting this method... much appreciated! :D
jpuziano

Note: If anyone else on the planet would find the following useful...
[Open] PlayWav command that plays from embedded script data
...then please add your thoughts/support at the above post - :-)

User avatar
PepsiHog
Automation Wizard
Posts: 511
Joined: Wed Apr 08, 2009 4:19 pm
Location: Florida

Re: Key Stroke "Intercept" Concept

Post by PepsiHog » Tue Jul 14, 2020 3:41 pm

I copied the last posted version of this script.
I pasted it.
I ran it.
I pressed F1.
I got absolutely nothing.
The only key that works is esc, to end the macro.

Can someone tell me what I'm missing here, please?

Would like to use this, but I can not get the dang thing to work,
PepsiHog
Windows 7

PepsiHog. Yep! I drink LOTS of Pepsi (still..in 2021) AND enjoy programming. (That's my little piece of heaven!)

The immensity of the scope of possibilities within Macro Scheduler pushes the user beyond just macros!

User avatar
PepsiHog
Automation Wizard
Posts: 511
Joined: Wed Apr 08, 2009 4:19 pm
Location: Florida

Re: Key Stroke "Intercept" Concept

Post by PepsiHog » Tue Jul 14, 2020 3:50 pm

Ok.....I get it. It's a hotkey for when another window is in focus. Well why didn't someone just say that?

I can think of possibilities.

Thanks JRL,
PepsiHog

[edit]- Someone is going to point out that "it was said", not realizing that I was (not sure what the word is to describe my being, but the only word I can think of is) being rhetorical. This whole thing has become exceedingly complicated! Just know that I know and you don't need to tell me.
Windows 7

PepsiHog. Yep! I drink LOTS of Pepsi (still..in 2021) AND enjoy programming. (That's my little piece of heaven!)

The immensity of the scope of possibilities within Macro Scheduler pushes the user beyond just macros!

Post Reply
Sign up to our newsletter for free automation tips, tricks & discounts