Send-Keys
Firstly, I am standing on the shoulders of giants. As the comments below state, the essential first function, Set-WindowState, is only slightly modified from the original form at
https://www.snip2code.com/Snippet/50118/Hide--Show--Minimize--Maximize--etc-wind/
Specifically, it now tests the current PID’s ParentProcessID. Much hair-pulling has shown that these calls only work if the PowerShell window is the one started from either a shortcut, the Start menu/screen, or Win+R. If the current PowerShell process is the child process of another shell, be it PowerShell or cmd.exe, then the calls to Set-WindowState fail silently.
####################
function Set-WindowState
####################
{
<#
.SYNOPSIS
Show, Hide Minimize, Maximize, or Restore the Powershell Console or other Window.
.DESCRIPTION
Show, Hide Minimize, Maximize, or Restore the Powershell Console or other Window.
Very slightly modified form of
https://www.snip2code.com/Snippet/50118/Hide--Show--Minimize--Maximize--etc-wind/
.PARAMETER WindowState
[string] The New Window state Mode.
May be one of the following:
Hide Hides the window and activates another window.
Normal Activates and displays a window. This is the Default. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.
ShowMinimized Activates the window and displays it as a minimized window.
Maximize Maximizes the specified window.
ShowNoActivate Displays a window in its most recent size and position. This value is similar to SW_SHOWNORMAL, except that the window is not activated.
Show Activates the window and displays it in its current size and position.
Minimize Minimizes the specified window and activates the next top-level window in the Z order.
ShowMinNoActive Displays the window as a minimized window. This value is similar to SW_SHOWMINIMIZED, except the window is not activated.
ShowNA Displays the window in its current size and position. This value is similar to SW_SHOW, except that the window is not activated.
Restore Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when restoring a minimized window.
ShowDefault Sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to the CreateProcess function by the program that started the application.
ForceMinimize Minimizes a window, even if the thread that owns the window is not responding. This flag should only be used when minimizing windows from a different thread.
.PARAMETER ID
[Int] The Process Identifier (PID) of the Target Window. If this parameter is not specified the Target window defaults to the current Powershell Console Window.
.INPUTS
[Int] $ID You can pipe Process Identifier (PID) of the Target Window.
.OUTPUTS
Returns $false if run in a subshell, returns $null otherwise.
.Example
PS C:\Users\User\Documents> Set-WindowState -WindowState Minimize
This will Minimize the Powershell Console Window.
.Link
https://www.snip2code.com/Snippet/50118/Hide--Show--Minimize--Maximize--etc-wind/
.notes
This only works in the original PSH window spawned off explorer.exe. It does not work in subshell, where PSH (or cmd.exe) calls PowerShell.exe.
#>
param (
[parameter(
Mandatory=$false,
ValuefromPipeline = $false
)]
[String] [ValidateSet(
"Hide",
"Normal",
"ShowMinimized",
"Maximize",
"ShowNoActivate",
"Show",
"Minimize",
"ShowMinNoActive",
"ShowNA",
"Restore",
"ShowDefault",
"ForceMinimize"
)] $WindowState = "Normal",
[parameter(
Mandatory=$false,
ValuefromPipeline=$true,
ValueFromPipeLineByPropertyName=$true
)]
[Int] $ID = $PID
);
#region check if we are a subshell or not. If we're a subshell, this won't work.
$parentProcessId = (
Get-WmiObject -Query "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId=$PID"
).ParentProcessId;
if ((Get-Process -Id $parentProcessId).ProcessName -ne 'explorer')
{
$message = "$($MyInvocation.MyCommand.Name) can only be run in a PowerShell window started from explorer. It cannot control windows when it is running in a subshell. Stopping.";
Write-Error -ErrorAction SilentlyContinue -Message $message;
Write-Warning -Message $message;
return $false;
break __outOfScript;
} # if ((Get-Process -Id $parentProcessId).ProcessName -ne 'explorer')
#endregion
#region set up the magic here
# this really should be an [Enum]
$windowStateHash = @{
Hide = 0;
Normal = 1;
ShowMinimized = 2;
Maximize = 3;
ShowNoActivate = 4;
Show = 5;
Minimize = 6;
ShowMinNoActive = 7;
ShowNA = 8;
Restore = 9;
ShowDefault = 10;
ForceMinimize = 11;
};
$showWindowAsync = Add-Type -MemberDefinition '
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
' -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru;
#endregion
# pull off the magic
$showWindowAsync::ShowWindowAsync((Get-Process -id $ID).MainWindowHandle, $windowStateHash.$WindowState) | Out-Null;
} # function Set-WindowState
####################
function Send-Keys
####################
{
<#
.Synopsis
Send keystrokes to another window.
.Description
A really brittle PSH implementation of the beloved and oft-abused SendKeys.SendWait().
If a window title is specified AND UNIQUE (i.e. there cannot be another window with the same title), then that window is given focus.
If a window title is not specified, the current PowerShell window will be minimized and whichever window previously had focus will be given focus.
Keystrokes are sent one-at-a-time, with a set delay between each.
.Parameter InputString
[string[]] Data to be sent.
.parameter WindowTitle
Title of window to be sent keystrokes. Must be unique of all windows on dekstop, opened or minimized.
.parameter Milliseconds
Delay between keystrokes. Default is 100ms (1/10 of a second). Note that the application that received incoming keystrokes may buffer them, so this only specifies the MAXIMUM keystrokes that can be sent per second.
.parameter NoActivate
Default behaviour is to restore the PowerShell window and give it focus. This leaves focus with the application that received the incoming keystrokes.
.link
Send-Keys (gc .\_vimrc) -WindowTitle 'Windows PowerShell ISE'
#>
param (
[parameter(ValueFromPipeline=$true)][string[]]$InputString = @(),
[string]$WindowTitle = $null,
[int]$Milliseconds = 100,
[switch]$NoActivate
);
begin
{
#region check if we are a subshell or not. If we're a subshell, this won't work.
$parentProcessId = (
Get-WmiObject -Query "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId=$PID"
).ParentProcessId;
if ((Get-Process -Id $parentProcessId).ProcessName -ne 'explorer')
{
$message = "$($MyInvocation.MyCommand.Name) can only be run in a PowerShell window started from explorer. It cannot control windows started in a subshell. Stopping.";
Write-Error -ErrorAction SilentlyContinue -Message $message;
Write-Warning -Message $message;
return $false;
break __outOfScript;
} # if ((Get-Process -Id $parentProcessId).ProcessName -ne 'explorer')
#endregion
#region determine which window to target (specified one, or one with last focus)
if ($WindowTitle)
{
$windowHandles = Get-Process |
? { $_.MainWindowTitle -eq "$WindowTitle"} |
Select-Object -ExpandProperty MainWindowHandle;
if (!$windowHandles)
{
$message = "$($MyInvocation.MyCommand.Name) -WindowTitle '$WindowTitle' not found. Stopping.";
Write-Error -ErrorAction SilentlyContinue -Message $message;
Write-Warning -Message $message;
return $false;
break __outOfScript;
} # (if !$windowHandles)
elseif ($windowHandles.Count -gt 2)
{
$message = "$($MyInvocation.MyCommand.Name) -WindowTitle '$WindowTitle' found $($windowHandles.Count) windows, expected to find only 1. Stopping.";
Write-Error -ErrorAction SilentlyContinue -Message $message;
Write-Warning -Message $message;
return $false;
break __outOfScript;
} # if (!$WindowsHandles) ... elseif
Add-Type '
using System;
using System.Runtime.InteropServices;
public class WindowControl {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
';
[WindowControl]::SetForegroundWindow(($windowHandles | Select-Object -First 1));
} # if ($WindowTitle)
else
{
Set-WindowState -ID $PID -WindowState Minimize;
} # if ($WindowTitle) ... else
#endregion
#region other initializations
#save the window size, because restoring focus may reset it
$windowSize = $Host.UI.RawUI.WindowSize
# counter for number of characters sent;
$i = 0;
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms");
#endregion
} # begin
process {
foreach ($line in $InputString)
{
foreach ($char in [char[]]$line)
{
if ($char -match "[\+\^\%\~\(\)\{\}\[\]]")
{
[string]$char = "{$char}";
} # if ($char -match "...
[System.Windows.Forms.SendKeys]::SendWait($char);
Start-Sleep -Milliseconds $Milliseconds;
$i += 1;
} # foreach ($char in [char[]]$line)
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}");
$i += 1;
} # foreach ($line in $InputString)
} # process
end
{
if (!$NoActivate)
{ # if -NoActivate specified, don't reopen the PSH window
# otherwise, set it back as it ever was.
Set-WindowState -ID $PID -WindowState Restore;
$Host.UI.RawUI.WindowSize = $windowSize;
} # if (!$NoActivate)
return $i;
} #end
} # function Send-Keys
Comments
- Anonymous
May 12, 2015
It's not that hard / not that easy. Once you have an IE COM object, you interact with it via the object methods. Toss $ieComObject | Get-Member to see all the methods. For example, $ieComObject.Browse($url) will point the browser to $url.