Provide Input to Applications with PowerShell
Summary: Use SendKeys to provide input to graphical applications with Windows PowerShell.
Hey, Scripting Guy! I am almost embarrassed to admit this but you know what I miss most about VBScript? I miss the SendKeys function. I do not know why Windows PowerShell did not include a New-SendKeys cmdlet. I know all about the risks, about losing focus, and that, depending on what a user has open on their computer, things may go amiss but the simple fact is, that sometimes I must be able to send a keystroke to an application. A perfect example is a recent software update from Microsoft Update—the Live Essentials update. It stops processing until someone checks OK and presses Enter. This is really difficult to manage when a computer is downloading 17 updates that total 186 Megabytes. One does not know whether the long pause is because the “Internet is slow” or because some dialog box that is prompting for user interaction is hidden behind a forest of windows. Sorry for the rant, but as you might suspect, I was just burned by this.
Hello MB, Microsoft Scripting Guy Ed Wilson here. Remember, tomorrow is Scripting Guys Day. I was out in my woodworking shop yesterday making some hand-cut dovetails for a box I am building out of walnut, when it dawned on me that tomorrow is Scripting Guys Day, and I did not even buy myself a present. Well that is what is so great about the Internet, I can easily correct that oversight. Hmm, maybe a nice 15-inch Jack Plane…
MB, the old VBScript SendKeys function was one of those things that people loved to hate. VBScripters used it because there was no other option. In Windows PowerShell, there are actually two other options for GUI Automation. There is a good MSDN article that discusses UI Automation for program testing. The second option is a codeplex project named WASP (Windows Automation Snapin for PowerShell).
There is nothing wrong with using the VBScript SendKeys function inside Windows PowerShell. In fact, I talked about this in a Hey, Scripting Guy! post last year in a discussion about passwords. A .NET Framework SendKeys class can also be utilized.
When you use SendKeys (either the VBScript function, or the .NET Framework class) it is important to call the target application to the foreground. If you do not do this, whatever application has the foreground will receive the keystrokes. With just a bit of imagination I am sure that you could devise your own worst case scenarios for self-inflicted disaster!
I actually ran across the following problem while writing todays script. In my first script, I did not include a start-sleep command to pause the script execution. The reason for pausing the script is to allow the application (Calculator in this example) to fully initialize. Calculator is a very lightweight application, and it loads very quickly. Unfortunately, it does not load instantly. When I first ran my script, the appactivate method failed because Calculator was not in memory when the line of code was called. Therefore, my Windows PowerShell ISE was still in the foreground when SendKeys added some “new code” to my script. If I had not been paying attention, and if I had saved the script on exit, my script would have been hosed. The results of this are seen in the following figure.
Because Calculator loads quickly, I added a 500 millisecond pause to the script. The revised SendKeys.ps1 script is seen here.
add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
start-sleep -Milliseconds 500
The two .NET Framework classes that are used in the SendKeys.ps1 script are not loaded by default. To provide access to these, it is necessary to load the assemblies. In Windows PowerShell 2.0, it is easiest to use the Add-Type Windows PowerShell cmdlet to load assemblies. In Windows PowerShell 1.0 it was common to use the LoadWithPartialName static method from the System.Reflection.Assembly .NET class. However, that method is obsolete and therefore it should not be used.
After the two assemblies are loaded, I call the calc executable to load Calculator. Because I am not doing anything other than launching Calculator, there is no need to use the Start-Process Windows PowerShell cmdlet to start the calc process.
As mentioned earlier, I have to provide a pause to allow the Calculator application to launch. The 500 millisecond timing works on my computer but it might not work on your computer! The start-sleep Windows PowerShell cmdlet will accept a pause value in seconds also. For example, to pause the script for 2 seconds, the command would appear as indicated here.
Start-Sleep –Seconds 2
If you are working with a larger application or with a variety of computers with different processing capabilities, it would be best to modify the script to use an event. I have written a number of Hey, Scripting Guy! posts that talk about consuming events from Windows PowerShell scripts.
When the script runs, Calculator will appear, and the numbers sent via the SendWait method will update. This is seen in the following figure.
MB, that is all there is to using SendKeys to provide input to an application. Input week will continue tomorrow.
Ed Wilson, Microsoft Scripting Guy