Learn about Windows 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.
I invite you to follow me on Twitter or Facebook. aSee you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
Hi, the SendKeys method (that is habitual in VB, .NET and a range of scripting technologies) is now superseded by a bunch of tools based on UIAutomaiton library: the library itself, UIAutomation PowerShell module (seen at PoshCode), UIAtomation and WASP cmdlets (at codeplex), white (also at CodePlex).
Sending keys is a very unreliable way to control the app, losing focus or even simply losing processor ticks on a highly loaded host may lead to losing or repeating characters if you put them into a text box...
@Alexander I respectfully disagree. First of all, the question was from a person who stated that he missed SendKeys, and wanted to know if there was a way to do that in Powershell ... there is. Second of all SendKeys, while sometimes dangerous, and often klugey, is extremely easy to use. In a controlled setting, there is nothing wrong with using it at all. The UIAutomation Library is very complex, and is not aimed at network administrators ... it is aimed at software testers. It certainly would be a good thing to learn, but is way beyond the scope for a person who simply needs to click the next button for some software without an automation install interface. In addition, all of the other projects (wasp on Codeplex and the UI Automation Module on PoshCode are the same project by the way) are not completed, and are more on the line of proof of concept. I appreciate your comments and suggestions for additional means of automating applications.
Okay, Ed, you are right, since no modules hide the lost SendKeys the admin asked for.
Worse, modules often require itself to be downloaded and installed.
However, things are not so bad as if the original UI Automation library from MS is used. Now, the Application Input code looks like (more lines of code, than in yours):
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Invoke-UIAButtonClick;
Get-UIAButton -Name Add | Invoke-UIAButtonClick;
Get-UIAButton -Name 1 | Invoke-UIAButtonClick;
Get-UIAButton -Name Equals | Invoke-UIAButtonClick;
Be careful when using double quotes in Powershell when using Sendkeys or other external scripting functions where operators or environmental variables in Powershell overlap with the desired function. In this case, some keys, such as CTRL, ALT, and SHIFT
are represented by the carat (^), percentage sign (%), and sum (+) operators, which are reserved in Powershell. So even though the examples in this article use double quotations, you will not get the desired outcome if you invoke this function using the aforementioned
To be on the safe side, just use single quotes when using Sendkeys with Powershell ;), unless you have a specific reason for not doing so.
How about the scenario where you start a new ComObject and need to get that Object's handle in order to AppActivate to bring it to the front?
I've read many of your posts and have found them very helpful so thank you for that. I am responding to this one because before my career as a data analyst I was working at a woodworking shop. I did flooring and moved up to working on custom architectural products
in addition to flooring. I really loved this work but found it hard to provide adequately for my family with such a variable income. I often give your introductory paragraphs a cursory glance and move on to the meat of the post but I was caught off guard by
this one; it feels good to know we have a common interest. Again, thanks for posting your expertise for all to learn from.