I'm working on a dual pc stream setup and would like to use my elgato stream deck on my gaming pc for some specific functions. The only problem is, no obs control. I was combing through reddit and came across the suggestion to use SendKeys in powershell scripts to perform hotkey functions. I am able to connect the two computers through PSSession and run my script, but nothing happens. I have the application focused. Even when I just open a text file and run the script, nothing comes up. I don't really think this should be that hard should it? Any help would be appreciated.
$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys(']')
Continuing from my comment...
Sendkeys is a thing but can be really finicky, focus, timing issues, etc. GUI automation is not really PowerShell's strong suit. Custom tools like AutoIT, Selenium, etc., are better options.
With Sendkeys, you often must set delays to ensure focus before calling keystrokes.
YOu can also avoid the use of ...
New-Object -ComObject wscript.shell
... and use this...
Add-Type -AssemblyName System.Windows.Forms
Here are a few examples you can try.
# pops the WinKey Start Menu
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait('^{ESC}')
Or this way...
# Initialize a variable with the .Net namespace, then use it.
$SendKeys = [System.Windows.Forms.SendKeys]
$SendKeys::SendWait('^{ESC}')
# SendKeys to hold down keys
$SendKeys::SendWait('q(+%) + (+)q')
# Send commnad results to notepad - Note the sleep to wait for notepad to open, paste the content then select the file menu
Get-NetIPConfiguration | clip | notepad
Sleep -Seconds 1
$SendKeys::SendWait('^V')
Sleep -Seconds 2
$SendKeys::SendWait('%F')
Update as per our comments below:
SendKeys requires a GUI up and running. A GUI requires a logged-on, interactive user.
Related
In my script, I start a third-party non-GUI application. I'm sort of trying to run this embedded in my script itself, so I will be able to change the icon and the windows caption.
I have two restriction:
I have to use & 'application.exe' to start the application. I tested Start-Process -NoNewWindow, but that breaks the functionality of application.exe.
The application.exe needs to be running in my script. I can only change the icon when I compile my script with PS1 to Exe afterwards.
The challenge I'm now facing is related to the first restriction. I need to change the caption a-synchronously. The $host.ui.RawUI.WindowTitle = “New Title” is not working, because application.exe changes the caption right after execution. So I need to change it by using functions like SetWindowText(). This is working in VB.NET, but I'm looking for a way to start this function in parallel with the & 'application.exe'. When I use &, the application is executed and the script waits until it terminates. So I need to do the SetWindowText() in parallel.
Visual Basic/C has a BackgroundWorker functions for such cases. Is something like that also available in PowerShell?
Thanks for any help in advance!
Kind regards,
Eric
Everybody thank you very much for your help!
The solution proved to be a lot easier that I thought. You don't have to keep on renaming the window. You just have to start the cmd window, wait a bit (in the background it's doing something with conhost.exe) and then rename it once. Here's the code I used:
$titletext = "My New CMD Window Title"
# Start a thread job to change the window title to $titletext
$null = Start-ThreadJob { param( $rawUI, $windowTitle )
Start-Sleep -s 2 #Wait until cmd.exe is started
if ( $rawUI.WindowTitle -ne $windowTitle ) {
$rawUI.WindowTitle = $windowTitle
}
} -ArgumentList $host.ui.RawUI, $titletext
& 'c:\windows\system32\cmd.exe'
Kind regards,
Eric
I'm very new to Powershell. I wrote a simple script basically to automate grinding in a video game when I'm away from my computer. I wanted to know if there's a way instead to 'target' the button presses to a particular window/application/etc. while still working on the same machine in other windows. As it stands, I have to have the game window focused for the script to work so it's only really useful if I'm AFK. But if I could have it running in the background while working on other things that would be useful.
$WShell = New-Object -Com Wscript.Shell
while (1) {sleep 1; [System.Windows.Forms.SendKeys]::SendWait("{LEFT}"*80);
[System.Windows.Forms.SendKeys]::SendWait("{RIGHT}"*80);sleep 1;
[System.Windows.Forms.SendKeys]::SendWait("{C}"*20)}
Since I'm new to Powershell I'm not sure if this is doable or not. If not, it's no problem, this is a pretty frivolous use case, I was just curious.
Think about what you're currently doing. . .it's almost the same as tying the shoe laces on a pair of shoes, just to put on different ones;) You're not using the $wShell anywhere in your script.
. . .but, you're almost there already. We can start by loading the required assemblies, and then have it point to the application which you want to interact with:
Add-Type -AssemblyName Microsoft.VisualBasic
Add-Type -AssemblyName System.Windows.Forms
[Microsoft.VisualBasic.Interaction]::AppActivate('Notepad')
Start-Sleep -Milliseconds 1000
[System.Windows.Forms.SendKeys]::SendWait("H")
Start-Sleep -Milliseconds 100
[System.Windows.Forms.SendKeys]::SendWait("E")
Start-Sleep -Milliseconds 100
[System.Windows.Forms.SendKeys]::SendWait("L")
Start-Sleep -Milliseconds 100
[System.Windows.Forms.SendKeys]::SendWait("L")
Start-Sleep -Milliseconds 100
[System.Windows.Forms.SendKeys]::SendWait("O")
Take note of notepad in [Microsoft.VisualBasic.Interaction]::AppActivate('Notepad'), as it will be what ever program you're trying to interact with. In this case, you're just sending "HELLO" to the notepad that's already on started.
Would you like to:
[1] Accept the certificate for this session
Please input your selection (The default selection is [1]): 1
I have to enter number one here, how it can be done through power shell script
Read-Host would be the way to do it from the console while using Write-Host/Write-Output to present the option information. You would of course need to check the contents of the variable to see if you got an expected result.
If you're looking for a simple yes/no answer and are OK with popups, use a .NET messagebox:
[System.Windows.Forms.MessageBox]::Show('message', 'titlemessage')
There are tons of overloads for this which include giving you the ability to control the buttons displayed and the icon (warning, error, informational, etc.)
EDIT: This SO post shows how to accomplish SendKeys functionality with both a COM object and a .NET object: How to perform keystroke inside powershell?
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('title of the application window')
Sleep 1
$wshell.SendKeys(1)
and
add-type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait(1)
or
add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
[Microsoft.VisualBasic.Interaction]::AppActivate(“Calc”)
[System.Windows.Forms.SendKeys]::SendWait(“1{ADD}1=”)
from: https://blogs.technet.microsoft.com/heyscriptingguy/2011/01/10/provide-input-to-applications-with-powershell/
Recently I ran into an issue with a laptop that had NumLock disabled automatically at certain times (such as when coming out of sleep mode). This prompted me to look for ways to programmatically check if NumLock was off, and if so, turn it on.
I'm looking for the best way to accomplish this. I want to run the script when certain events occur, such as when logging on to the laptop. I plan to do this with a scheduled task, and I'd prefer to use PowerShell over VBScript, but I'd be happy to use whatever works.
In PowerShell, [console]::NumberLock is a Read Only property that will evaluate to true if NumLock is enabled.
The script I ended up writing is as follows:
if(-not [console]::NumberLock){
$w = New-Object -ComObject WScript.Shell;
$w.SendKeys('{NUMLOCK}');
}
Building on Thriggle's answer above, you can ensure NumLock never gets turned off by adding a loop. Note that this is pretty hard on CPU:
While($true){
if(-not [console]::NumberLock){
$w = New-Object -ComObject WScript.Shell;
$w.SendKeys('{NUMLOCK}');
}
}
I have ps1 script to grab some information from the vmware cluster environment.
In some place of ps1 script requires the ENTER button keystroke.
So, How to do that ?
-Thanks
If I understand correctly, you want PowerShell to send the ENTER keystroke to some interactive application?
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('title of the application window')
Sleep 1
$wshell.SendKeys('~')
If that interactive application is a PowerShell script, just use whatever is in the title bar of the PowerShell window as the argument to AppActivate (by default, the path to powershell.exe). To avoid ambiguity, you can have your script retitle its own window by using the title 'new window title' command.
A few notes:
The tilde (~) represents the ENTER keystroke. You can also use {ENTER}, though they're not identical - that's the keypad's ENTER key. A complete list is available here: http://msdn.microsoft.com/en-us/library/office/aa202943%28v=office.10%29.aspx.
The reason for the Sleep 1 statement is to wait 1 second because it takes a moment for the window to activate, and if you invoke SendKeys immediately, it'll send the keys to the PowerShell window, or to nowhere.
Be aware that this can be tripped up, if you type anything or click the mouse during the second that it's waiting, preventing to window you activate with AppActivate from being active. You can experiment with reducing the amount of time to find the minimum that's reliably sufficient on your system (Sleep accepts decimals, so you could try .5 for half a second). I find that on my 2.6 GHz Core i7 Win7 laptop, anything less than .8 seconds has a significant failure rate. I use 1 second to be safe.
IMPORTANT WARNING: Be extra careful if you're using this method to send a password, because activating a different window between invoking AppActivate and invoking SendKeys will cause the password to be sent to that different window in plain text!
Sometimes wscript.shell's SendKeys method can be a little quirky, so if you run into problems, replace the fourth line above with this:
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait('~');
function Do-SendKeys {
param (
$SENDKEYS,
$WINDOWTITLE
)
$wshell = New-Object -ComObject wscript.shell;
IF ($WINDOWTITLE) {$wshell.AppActivate($WINDOWTITLE)}
Sleep 1
IF ($SENDKEYS) {$wshell.SendKeys($SENDKEYS)}
}
Do-SendKeys -WINDOWTITLE Print -SENDKEYS '{TAB}{TAB}'
Do-SendKeys -WINDOWTITLE Print
Do-SendKeys -SENDKEYS '%{f4}'
Send "Enter" key to an App, for example for pressing "OK". Works great:
Add-Type -AssemblyName microsoft.VisualBasic
Add-Type -AssemblyName System.Windows.Forms
# Get the desired process:
$ProcessName = Get-Process -Name Calculator
Start-Sleep -Seconds 1
# If the process is actually running, bring it to front:
If ($ProcessName)
{
(New-Object -ComObject.Wscript.Shell).AppActivate((Get-Process $ProcessName -ErrorAction SilentlyContinue).MainWindowTitle)
}
# Send "Enter" key to the app:
[Microsoft.VisualBasic.Interaction]::AppActivate($ProcessName.ProcessName)
[System.Windows.Forms.SendKeys]::SendWait({'~'})
Also the $wshell = New-Object -ComObject wscript.shell; helped a script that was running in the background, it worked fine with just but adding $wshell. fixed it from running as background! [Microsoft.VisualBasic.Interaction]::AppActivate("App Name")