Using SendKeys to Automate Repetitive Tasks

DanRollins
CERTIFIED EXPERT
Published:
Updated:
If you have ever found yourself doing a repetitive action with the mouse and keyboard, and if you have even a little programming experience, there is a good chance that you can use a text editor to whip together a sort of macro to automate the process.  

This article describes how to write short scripts that will let you "pull the strings" on an application program to make it dance to your will.  Examples will be in JavaScript code (.JS files) but the concepts here also apply to VBScript (.VBS files), in case BASIC is your language of choice.

Before running the SendKeys script...after double-clicking the toPng128.JS icon...After running the SendKeys scriptHere are some example usages:

You need to go through a directory of images files and convert them from BMP to JPG, using only MsPaint.
You need to look at each of a series of text files and for each one, you must decide to either keep it, delete it, or move it to a different directory.  Your viewing tool is Notepad.Exe
You need to copy some text from one program and paste it into another, again and again.
In short, this technique is handy anytime that you can accomplish some individual task with a few keystrokes but you would rather not spend the rest of the day stroking those particular keys over and over.

Important Notes:

The SendKeys function is often said to be somewhat "flaky" and "unreliable" and I would not recommend using it for any sort of mission-critical operation.
In particular, you must be careful to use AppActivate() to ensure that the desired target program receives the keystrokes.  And remember that if you try a long sequence of keystrokes (not recommended) the user or some other process might cause a different window to jump to the front -- and there is danger that sending the keystrokes to the wrong program could cause serious harm.
Nevertheless, once you understand the quirks of SendKeys, it does function as advertised in the documentation.  I consider it a useful tool for throwing together a quick tool to get something done.

Partial Automation
One thing I'd like to promote here is the idea of "partial automation" of a process.  As a programmer, I can always see  various ways to automate a task.  The trick is doing just the right amount of programming.  You can spend hours or days writing a program that will take care of every little detail (do the whole thing automatically), but writing the code might take longer than just doing the whole job manually!  

What I do is look for the part of the manual operation that is the most time-consuming and repetitive, and then automate just that portion.  SendKeys comes in handy in such cases.


Starter Kit
We'll be writing short programs in JavaScript.  Typical operations for this type of task include launching an application, feeding it some keystrokes, and (perhaps) popping up a message box.

I have a template script full of comments that I keep handy so that I don't need to constantly refer to the documentation.  It looks like this:
MyMacro.js
// Macro starter script -- basically a cheat-sheet of common commands
                      
                      var oShell = WScript.CreateObject("WScript.Shell");
                      
                      //oShell.AppActivate("Paint"); // Set focus to a program
                      //WScript.Sleep(500);          // Delay for 1/2 second
                      
                      //oShell.Run("notepad c:\\temp\\somefile.txt", 1, false);   // launch a program
                      //----------------------------- can add ,2 to run minimized, true=wait for exit
                      
                      //oShell.SendKeys("%F");       // Alt+F  ----- SendKeys strings to jog my memory
                      //oShell.SendKeys("^C");       // Ctrl+C
                      //oShell.SendKeys("{ENTER}");  // Send Enter key (also tilde {~} )
                      //oShell.SendKeys("{{TAB} 2"); // Send Tab key twice )
                      //oShell.SendKeys(" ");        // Send a space
                      
                      var nResult= oShell.Popup("Do you want to continue?", 0, "My Macro", 4+32 );
                      if ( nResult==6 ) { // 6=Yes, 7=No, 
                         WScript.Echo( "you said 'Yes!'" );	
                      }
                      WScript.Echo( "done!" );

Open in new window

Example #1:  Insert a string at the top of a text file.

This script will open a file in Notepad, insert a copyright notice at the top, and then save the file.

1

Copy the above template to a text file on your desktop.  Rename it to SetCopyright.js

2

Right-click that .JS file and select Edit

3

Replace the contents with this:
// Example #1:
                      // Insert copyright notice at the top of a file, 
                      // and save the file with the change
                      
                      var oShell = WScript.CreateObject("WScript.Shell");
                      oShell.AppActivate("Notepad"); // Set focus to program
                      WScript.Sleep(100);            // Delay for a bit
                      
                      oShell.SendKeys("^{HOME}");    // Cursor to top of file
                      WScript.Sleep(100);            // Delay for a bit
                      oShell.SendKeys("// Copyright {(}c{)} 2010, MyCompany");  
                      WScript.Sleep(100);          
                      oShell.SendKeys("{Enter}");    // line break
                      WScript.Sleep(100);          
                      oShell.SendKeys("^s");         // Ctrl+S saves the file
                      WScript.Echo( "done!" );

Open in new window

4

Save the file.

5

Double-click your SetCopyright.js icon in the desktop to run the script.The first line has been inserted by the scriptThe result is that whatever file is currently open in Notepad is altered, with the text...
// Copyright (c) 2010, MyCompany

Open in new window

....having been inserted at the top.

Here are a couple of items to note about the above script:
Notice the use of
   WScript.Sleep(100)
on lines 7,10,12, and 14.  This is used to overcome a common problem when using SendKeys.  The script tends to run faster than the program can process the keystrokes, so delays are often required.  Sometimes, only trial-and-error will let you find out where the delays are needed.
Line 6 makes sure that the Notepad application window has the focus (it is absolutely critical that the desired program has the keyboard focus before using SendKeys).  Note that I used only the text "Notepad" in the AppActivate() function.  You can use either the full titlebar text OR you can use just the text after the dash.  But if you do that, beware!  If more than one instance of Notepad is open, you can get into trouble.  

There is a semi-cool trick, however, that lets you activate just a particular instance (without knowing the full titlebar text).  That is to use the Process ID instead of the application name.  If for some reason, you must have several instances of the application open, you can use Task Manager to obtain the PID of the instance you want to target.
In line 9, we want to position the cursor at the top of the file.  The keystroke to do that is Ctrl+[Home].  To send a Ctrl-shifted character, you must use the caret (^) in front of it, and to send special keys such as the Home key, you must use a predefined sequence, surrounded in curly braces -- in this case, {HOME}. Microsoft's SendKeys documentation has a nicely-formatted table listing these special keys, so I won't bore you with a copy of that table.

In line 11, note that the string we want to insert includes the text (c) but the code uses the sequence {(}c{)}.  That was needed because parenthesis characters have special meaning to the SendKeys function.  This is one of those quirks I mentioned earlier.  To have SendKeys emit certain characters, you need to surround those characters with curly braces. The special characters that need to be treated this way are:

     +  ^  %  ~  [  ]  (  )  {  }  

For instance, to send the percentage character,  use
    SendKeys( "{%}" );
If you forget to enclose the % in curly braces, SendKeys will take it to mean. "Make the next character an Alt-shifted keystroke."
The only reason to create that script would be to save you lots of repeated effort.  Let's say that you had forty files in a folder and you need to insert the copyright notice in each.   With the help of this script, the sequence becomes:

1) Open an Explorer window on the folder containing the files.
2) Open the first file in Notepad.
3) Double-click your SetCopyright.js icon.
4) Drag the next file from the Explorer window and drop it on the Notepad window.
5) Repeat steps 3-4 until done.

You will be finished before you know it!  

There are other, more sophisticated ways to do that same task, but we were able to create the macro in a few minutes that would save as much as an hour of tedious work. That's a clear win.  A more sophisticated script (for instance, one that opened each file, read it into memory and wrote it back out) would take longer to create and debug; enough longer that you might lose the cost/benefit advantage.


Example #2:  Resize a JPG image and save it as a PNG file.

Like the previous macro, this one feeds keystrokes into an application program, but this time it is MsPaint.  The "grunt work" task you've been given is to plow through a bunch of JPG files and resize them all to 128x128 and save the results as PNG files.  A tedious afternoon awaits you...

Or you can write a short script like:

ToPng128.js
// Assumes a JPG file is open in Paint.Exe
                      // Resizes it to 128x128
                      // Saves it with the same name but in PNG format and extension.
                      //
                      var oShell = WScript.CreateObject("WScript.Shell");
                      
                      oShell.AppActivate("Paint"); // Set focus to open MsPaint program
                      WScript.Sleep(500);          // Delay for 1/2 second
                      
                      //=== resize image to 128x128
                      oShell.SendKeys("%HRE");        // resize dialog box
                      oShell.SendKeys("%B{RIGHT} ");  // set radio button to pixels
                      oShell.SendKeys("%M");          // uncheck "Maintain aspect ratio"
                      oShell.SendKeys("%H128");       // horizontal
                      oShell.SendKeys("{TAB}128");    // vertical
                      oShell.SendKeys("{ENTER}");     // OK the dlg box
                      
                      oShell.SendKeys("%FA");      // File > Save As
                      WScript.Sleep(500);          // Delay for 1/2 second
                      oShell.SendKeys("%TP");      // Save as Type "PNG"
                      WScript.Sleep(500);          // Delay for 1/2 second
                      oShell.SendKeys("%S");       // save

Open in new window

Here again, I aimed for "partial automation."  Your task will be to drop files onto the Paint window, then double-click the .JS file.  The macro does the rest.

The sequence of steps looks confusing but it's all straightforward.  When we need to access a menu (er..., ribbon) function, we use a ALT-shifted letter, and then send some followup letters to select the command.  For instance, to bring up the Resize and Skew dialog, the script presses ALT+H (by sending %H) then the script types RE.

With that new-fangled ribbon, how do you know what keys to press?  The program shows you!   Here's what's displayed while you are pressing the Alt key.
Press ALT to see what keys to sendSo ALT+HRE brings up the resizing dialog:
Automate filling in this "Resize and Skew" dialogThe script needs to:
1) Set the Pixels radio button.
2) Clear (uncheck) the Maintain aspect ratio checkbox.
3) Fill in the Horizontal and Vertical values.
4) OK the dialog.

And it does all of that in lines 11-16.

Manipulating Dialog Box Controls
In some cases, you can use {TAB} to move from control to control, but it's usually best to use the ALT-key accelerator.  For instance, to get to the Horizontal text input box, the script presses ALT+H, then types in the desired size (128).  In this dialog, sending {Enter} is the same as a click of the [OK] button.

Even a complex sequence like this is pretty easy to automate.  Just do it manually, one step at a time, taking notes as you go.  Then convert the keystrokes into SendKeys() commands.  And if you can't walk on water, you'll need to test the sequence on a "scratch file" at least a few times.  I hardly ever get everything right the first time.

After the Resize and Skew dialog box is OKed, Paint immediately resizes the image as requested.

The final steps (lines 18-22) save the file using the "Save As..." command.  To complicate the scenario, we need to jump down to the Save as type control (just send ALT+T) then select PNG format (press P) and finally Alt+S save the file and close the dialog.

As soon as one file is finished, you drag another and drop it onto Paint, double-click the script icon, and repeat.

Summary:
========

In this article, we saw how to partially automate some repetitive tasks using JavaScript, the WScript.Shell object, and its SendKeys() function.

JavaScript (and VBScript) are full-featured programming languages, so it is possible to use scripts to do a lot more than just save yourself a few keystrokes.  But quite often, you can actually finish a large task sooner by slapping together a quick script to automate just a portion of the task.


Additional Notes:
I've had you double-clicking the .JS file on the desktop to execute it.  But is is also possible to set up a system-wide "hot key" for it.  Just create a shortcut to it and set its Properties.  Set a value for the Shortcut Key (for instance, set it to Ctrl+Shift+Z).
There are often alternative ways to do things that do not use SendKeys.  For instance, rather than ALT+FO (and then sending a filename to the File / Open dialog), you are nearly always better off by starting the application and passing it a document filespec in the command line.  

Likewise, if you need to automate a web browser operation, it's usually much better to use the browser DOM, rather than trying to use SendKeys.  See Browser Bot -- Automate Browsing Sequences

And of course, if you have a tool that has a built-in macro capability (such as Office Word or Excel), by all means use that rather than this roll-your-own SendKeys technique.
In these examples, we manually started an operation then automated the rest.  But it's possible to have the script start an operation and then pause, to let you (for example) select some text, then let the script continue with additional operations.  You can use the Shell.Popup() function to put a full-stop pause into the sequence.  Just be sure to use AppActivate() after the Popup() command, to make sure that the target window has the input focus when the script resumes.
If a script is feeding keystrokes too quickly you can consider sending the keys one at a time in a loop, delaying between each keystroke.  For instance:function SendText(s) { for( var j=0; j<s.length; j++ ) { oShell.SendKeys( s.substr(j,1) ); WScript.Sleep(10); } }But keep in mind that SendKeys expects to see special sequences such {TAB} or %F or {+} as a contiguous group.  So such a looping operation will work only for strings of simple text.
The scripts here were tested on a Windows 7 system.  I can verify that the same techniques (if not the same exact scripts) work well on Windows XP systems.  However, I've read reports that SendKeys is either non-functional or "extra wonky" (whatever that means) when running Vista.

References:

SendKeys Method
http://msdn.microsoft.com/en-us/library/8c6yea83(VS.85).aspx

Windows Script Host Reference
http://msdn.microsoft.com/en-us/library/98591fh7(VS.85).aspx

JScript Language Reference (Windows Scripting - JScript)
http://msdn.microsoft.com/en-us/library/yek4tbz0(VS.85).aspx

WScript Object
http://msdn.microsoft.com/en-us/library/at5ydy31(VS.85).aspx

Run Method (Windows Script Host)
http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx

AppActivate Method
http://msdn.microsoft.com/en-us/library/wzcddbek(VS.85).aspx

WshShell Object
http://msdn.microsoft.com/en-us/library/aew9yb99(VS.85).aspx

Popup Method
http://msdn.microsoft.com/en-us/library/x83z1d9f(VS.85).aspx

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you liked this article and want to see more from this author, please click the Yes button near the:
      Was this article helpful?
label that is just below and to the right of this text.   Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
9
41,582 Views
DanRollins
CERTIFIED EXPERT

Comments (3)

aikimarkGet vaccinated; Social distance; Wear a mask
CERTIFIED EXPERT
Top Expert 2014

Commented:
Great article, Dan (got my Yes vote)

Automating tasks with SendKeys has spawned some freeware/opensource solutions for the less-DIY crowd:

AutoIt:
http://www.autoitscript.com/autoit3/index.shtml

Project Sikuli (out of MIT) addresses some of the graphical limitations:
http://groups.csail.mit.edu/uid/sikuli/

====================
I did a SendKeys solution for a client, in VB of course, for automating their Adobe Printer prompt:
https://www.experts-exchange.com/questions/24641450/Set-file-name-for-Adobe-Printer.html#25236171
CERTIFIED EXPERT

Commented:
Thanks for writing.

Commented:
I like Perfect Macro Recorder - does the trick without the code

http://www.perfectiontools.com/index.html?fra=http://www.perfectiontools.com/downloads.html

It's not free, but I got it for free from GiveawayOfTheDay, so anyone who's not in a rush can keep checking their site 'til it comes around again.

http://www.giveawayoftheday.com/

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.