Toggle Scroll Lock on or off - powershell

I am using PowerShell in my script to check the status of various keys like NumLock and CapsLock.
powershell.exe -Command [Console]::CapsLock
powershell.exe -Command [Console]::NumberLock
But I found no way to check the status of ScrollLock through PowerShell console command. Can you guys tell me why powershell.exe -Command [Console]::ScrollLock does not work and what is needed to be done?

You can get the ScrollLock key state with the GetKeyState() function from the user32.dll native Windows API:
Add-Type -MemberDefinition #'
[DllImport("user32.dll")]
public static extern short GetKeyState(int nVirtKey);
'# -Name keyboardfuncs -Namespace user32
# 0x91 = 145, the virtual key code for the Scroll Lock key
# see http://www.foreui.com/articles/Key_Code_Table.htm
if([user32.keyboardfuncs]::GetKeyState(0x91) -eq 0){
# Scroll Lock is off
}
else {
# Scroll Lock is on
}

You can also get it by running:
[System.Windows.Forms.Control]::IsKeyLocked('Scroll')

Related

What is the Windows terminal command to check if a program is running?

I would like to create a Windows script that checks if a program "anyprogram.exe" is running. If the condition is true, it ends the process of that program.
How can I do this?
Use the Get-Process cmdlet in combination with Stop-Process:
# Note: Do NOT include ".exe" in the process name.
Get-Process -Name anyprogram -ErrorAction Ignore | Stop-Process
Note that Stop-Process forcefully stops (kills) a process, without giving it a chance to shut down gracefully.
GitHub issue #13664 discusses how graceful termination could be implemented as well.
If you need graceful termination - which may or may not be honored by the target process, however - use the standard Windows taskkill.exe utility (whose /f switch can be used to request forceful termination):
Get-Process -Name anyprogram -ErrorAction Ignore |
ForEach-Object { taskkill /pid $_.Id }
If - potentially forceful - termination must be guaranteed, try graceful termination first, then - after a timeout - fall back to forceful termination - see this answer.
(Get-Process | Select-Object ProcessName | Where { $_.ProcessName -eq "anyprogram" }).Count -gt 0
Gets a list of processes running, filters on the name, counts the number of programs running with that name. If that number is greater than zero then the return value is true. If that number is zero then the return value is false.
A word of caution: omit the .exe extension in the comparison $_.ProcessName -eq "anyprogram" because Get-Process omits the ".exe" extension from results.
I've done things within this realm years ago but I've been so heavily into my Mac world now that I'm hoping this is up to date enough or helpful.
Closing an app "gracefully" with WM_CLOSE:
$src = #'
using System;
using System.Runtime.InteropServices;
public static class Win32 {
public static uint WM_CLOSE = 0x10;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
'#
Add-Type -TypeDefinition $src
$zero = [IntPtr]::Zero
$p = #(Get-Process Notepad)[0]
[Win32]::SendMessage($p.MainWindowHandle, [Win32]::WM_CLOSE, $zero, $zero) > $null
Also, I found this post I had bookmarked, which just might be helpful in the sense of the "checking to see" if the app is running.
Stack Overflow: Continuously check if a process is running

Get foreground windows of windows explorer to retrieve the url

I think am close to the solution, or in the general ball park, but the way am calling the powershell might be part of the issue am calling the ps1 from a batch file, to
I want to have the user click a button and then have powershell get the url of the active window (EXPLORER.exe) - i did try to but the start/sleep command to check in the ISE but could get it to work :( could you help be out ?
Thank You
Set-ExecutionPolicy RemoteSigned
Param(
)
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class UserWindows {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
try {
$ActiveHandle = [UserWindows]::GetForegroundWindow()
$Process | Select LocationURL| select -ExpandProperty LocationURL -First 1 |
Tee-Object -Variable "dirvar"
} catch {
Write-Error "Failed to get active Window details. More Info: $_"
}
$dirvar1 = "$dirvar" -replace 'file:///', ''
echo "$dirvar1"
Start-Process "Z:\30_Sysadmin\ADB_LOADER_DIR\ADBDIR.bat" "$dirvar1"
You are going about it in a difficult way. There are helper functions. This lists all shell windows (Internet Explorer and Explorer windows). The object is an Internet Explorer object. See https://msdn.microsoft.com/en-us/library/aa752084(v=vs.85).aspx.
This is VBScript but COM calls are identical across all languages.
Set objShell = CreateObject("Shell.Application")
Set AllWindows = objShell.Windows
For Each window in AllWindows
window.Navigate "c:\"
msgbox window.locationURL
Next

Opening PowerShell Script and hide Command Prompt, but not the GUI

I currently open my PowerShell script through a .bat file. The script has a GUI. I tried to put this in my script but it hid the GUI as well and also kept looping because I wanted my GUI to continuously loop:
powershell.exe -WindowStyle Hidden -file c:\script.ps1
How do we run a script without Command Promp, but also not hide the gui? And is that the same if we run the script using a .bat file? Thank you!
If you want to hide the Console behind the GUI I've had success with the following native functions:
# .Net methods for hiding/showing the console in the background
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
function Show-Console
{
$consolePtr = [Console.Window]::GetConsoleWindow()
# Hide = 0,
# ShowNormal = 1,
# ShowMinimized = 2,
# ShowMaximized = 3,
# Maximize = 3,
# ShowNormalNoActivate = 4,
# Show = 5,
# Minimize = 6,
# ShowMinNoActivate = 7,
# ShowNoActivate = 8,
# Restore = 9,
# ShowDefault = 10,
# ForceMinimized = 11
[Console.Window]::ShowWindow($consolePtr, 4)
}
function Hide-Console
{
$consolePtr = [Console.Window]::GetConsoleWindow()
#0 hide
[Console.Window]::ShowWindow($consolePtr, 0)
}
Once the above functions have been added to your Form, simply call the Hide-Console function in your Form_Load event:
$OnFormLoad =
{
Hide-Console
}
If you need to show the Console, perhaps for debugging, you can easily show the console again by calling the Show-Console function:
$OnButtonClick =
{
Show-Console
}
For more information on the numbers you can pass to ShowWindow you can check out the ShowWindow documentation on MSDN here
Update based on comment
Thanks for this code. I tried to use it in my script, but where exactly am I suppose to put Hide-Console? My form load looks like this $objForm.Add_Shown({$objForm.Activate()}) [void] $objForm.ShowDialog()
To hide the console with this code, all you need to do is call Hide-Console. Since you already have code in the Shown event ($objForm.Add_Shown) we can simply add another line to call the command:
Change this:
$objForm.Add_Shown({$objForm.Activate()})
To this:
$objForm.Add_Shown({
$objForm.Activate()
Hide-Console
})
When your form is Shown the console will be hidden (You can still call Show-Console if you want to see it later).
If you run PowerShell from a shortcut with the window set to minimized, it will briefly flash a cmd icon in the taskbar but you barely notice it. Yet, it will start your PowerShell GUI without having a console window behind it.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -windowstyle Hidden -file "C:\path\whatever.ps1"
If you want to start a second GUI console window from whatever.ps1 without it stopping the processing on whatever.ps1 you need to use start-process. However, start-process with -WindowStyle hidden prevents the GUI from showing up. Removing -WindowStyle shows a command window behind your GUI. However, if you start-process with cmd.exe /k, it does work.
$argumentlist = "/c powershell.exe -file `"c:\path\whatever2.ps1`" -param1 `"paramstring`""
Start-Process cmd.exe -WindowStyle Hidden -ArgumentList $argumentlist
As a bonus, if you start whatever2.ps1 with a param() statement, you can pass named, required arguments. Just be sure it's the very first thing in the ps1 file, before assemblies even.
param (
[Parameter(Mandatory=$true)]
[string]$var1
)
$argumentlist = "/c powershell.exe -file `"C:\path\whatever2.ps1`" -param1 `"param1string`""
In the solution proposed posted in a comment (*
Put it in a shortcut instead of in a batch file. Right click a blank
spot on desktop or in a folder window, New, Shortcut, paste your line
in, Next, name it, Finish
*.) to hide definitely the Command Prompt, I set, in the properties of the shortcut Run=minimized in General.
Simplified the function for show hide a bit
function Show-Console
{
param ([Switch]$Show,[Switch]$Hide)
if (-not ("Console.Window" -as [type])) {
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
}
if ($Show)
{
$consolePtr = [Console.Window]::GetConsoleWindow()
# Hide = 0,
# ShowNormal = 1,
# ShowMinimized = 2,
# ShowMaximized = 3,
# Maximize = 3,
# ShowNormalNoActivate = 4,
# Show = 5,
# Minimize = 6,
# ShowMinNoActivate = 7,
# ShowNoActivate = 8,
# Restore = 9,
# ShowDefault = 10,
# ForceMinimized = 11
$null = [Console.Window]::ShowWindow($consolePtr, 5)
}
if ($Hide)
{
$consolePtr = [Console.Window]::GetConsoleWindow()
#0 hide
$null = [Console.Window]::ShowWindow($consolePtr, 0)
}
}
show-console -show
show-console -hide

How to execute a powershell script without stealing focus?

I made a powershell script that uses GetForegroundWindow() to identify which Window is the currently focused Window. Unfortunately, when the powershell script is executed (via Windows Task Scheduler or a hotkey), the script steals focus of the current foreground app; incorrectly using the Powershell window itself as the Foreground app, instead of the intended foreground app.
I even tried creating an EXE using PS2EXE "-noconsole" setting; however, the script still doesn't doesn't work.
Could someone please suggest a way to execute this script without changing focus of the current foreground Window?
My script code works during tests (ONLY if I add "Start-Sleep -s 5"; and, manually steal back the focus with alt-tab for the script to identify the correct foreground Window.
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class Tricks {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
function Set-WindowStyle {
param(
[Parameter()]
[ValidateSet('FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE',
'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED',
'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL')]
$Style = 'SHOW',
[Parameter()]
$MainWindowHandle = (Get-Process -Id $pid).MainWindowHandle
)
$WindowStates = #{
FORCEMINIMIZE = 11; HIDE = 0
MAXIMIZE = 3; MINIMIZE = 6
RESTORE = 9; SHOW = 5
SHOWDEFAULT = 10; SHOWMAXIMIZED = 3
SHOWMINIMIZED = 2; SHOWMINNOACTIVE = 7
SHOWNA = 8; SHOWNOACTIVATE = 4
SHOWNORMAL = 1
}
Write-Verbose ("Set Window Style {1} on handle {0}" -f $MainWindowHandle, $($WindowStates[$style]))
$Win32ShowWindowAsync = Add-Type -memberDefinition #"
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"# -name "Win32ShowWindowAsync" -namespace Win32Functions -passThru
$Win32ShowWindowAsync::ShowWindowAsync($MainWindowHandle, $WindowStates[$Style]) | Out-Null
}
$a = [tricks]::GetForegroundWindow()
$title = get-process | ? { $_.mainwindowhandle -eq $a }
$title2 = $title | select -ExpandProperty ProcessName
if ($title2 -eq 'Kodi'){
Set-WindowStyle MINIMIZE $a;
if (Get-Process -Name Yatse2) {(Get-Process -Name Yatse2).MainWindowHandle | foreach { Set-WindowStyle MINIMIZE $_ }}
} ELSE {
$title.CloseMainWindow()
}
I know this question is old, but I've spent quite some time trying to figure out how to not lose focus from the current window for myself and found some information, so hopefully this will help future readers.
The easiest solution is just to literally simulate an Alt+Tab keypress from within your Powershell script instead of having to do it yourself. The following code comes from this StackOverflow answer:
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
[System.Windows.Forms.SendKeys]::SendWait("%{TAB}")
# Retrieve the window handles...
...where % represents the Alt modifier and {TAB} is interpreted as the tab key.
User homersimpson's answer works, but using reflection is a bit slow. You can speed things up by adding the Windows.Forms assembly directly...
Add-Type -AssemblyName System.Windows.Forms
...other imports
#Return focus to the original window.
[System.Windows.Forms.SendKeys]::SendWait("%{TAB}")
...Your Code Here
Again, the % represents Alt and you know what TAB is. You are effectively Alt-Tabbing the new window away, returning focus to your desired one.
Create task that can be run on demand in Task Scheduler and executes your powershell script
Make sure it is set to be run whether user is logged on or not
Create a shortcut that starts the task
That allows your script to be run without visible window

Powershell test for noninteractive mode

I have a script that may be run manually or may be run by a scheduled task. I need to programmatically determine if I'm running in -noninteractive mode (which is set when run via scheduled task) or normal mode. I've googled around and the best I can find is to add a command line parameter, but I don't have any feasible way of doing that with the scheduled tasks nor can I reasonably expect the users to add the parameter when they run it manually.
Does noninteractive mode set some kind of variable or something I could check for in my script?
Edit:
I actually inadvertently answered my own question but I'm leaving it here for posterity.
I stuck a read-host in the script to ask the user for something and when it ran in noninteractive mode, boom, terminating error. Stuck it in a try/catch block and do stuff based on what mode I'm in.
Not the prettiest code structure, but it works. If anyone else has a better way please add it!
I didn't like any of the other answers as a complete solution. [Environment]::UserInteractive reports whether the user is interactive, not specifically if the process is interactive. The api is useful for detecting if you are running inside a service. Here's my solution to handle both cases:
function Assert-IsNonInteractiveShell {
# Test each Arg for match of abbreviated '-NonInteractive' command.
$NonInteractive = [Environment]::GetCommandLineArgs() | Where-Object{ $_ -like '-NonI*' }
if ([Environment]::UserInteractive -and -not $NonInteractive) {
# We are in an interactive shell.
return $false
}
return $true
}
You can check how powershell was called using Get-WmiObject for WMI objects:
(gwmi win32_process | ? { $_.processname -eq "powershell.exe" }) | select commandline
#commandline
#-----------
#"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe" -noprofile -NonInteractive
UPDATE: 2020-10-08
Starting in PowerShell 3.0, this cmdlet has been superseded by Get-CimInstance
(Get-CimInstance win32_process -Filter "ProcessID=$PID" | ? { $_.processname -eq "pwsh.exe" }) | select commandline
#commandline
#-----------
#"C:\Program Files\PowerShell\6\pwsh.exe"
I think the question needs a more thorough evaluation.
"interactive" means the shell is running as REPL - a continuous read-execute-print loop.
"non-interactive" means the shell is executing a script, command, or script block and terminates after execution.
If PowerShell is run with any of the options -Command, -EncodedCommand, or -File, it is non-interactive. Unfortunately, you can also run a script without options (pwsh script.ps1), so there is no bullet-proof way of detecting whether the shell is interactive.
So are we out of luck then? No, fortunately PowerShell does automatically add options that we can test if PowerShell runs a script block or is run via ssh to execute commands (ssh user#host command).
function IsInteractive {
# not including `-NonInteractive` since it apparently does nothing
# "Does not present an interactive prompt to the user" - no, it does present!
$non_interactive = '-command', '-c', '-encodedcommand', '-e', '-ec', '-file', '-f'
# alternatively `$non_interactive [-contains|-eq] $PSItem`
-not ([Environment]::GetCommandLineArgs() | Where-Object -FilterScript {$PSItem -in $non_interactive})
}
Now test in your PowerShell profile whether this is in interactive mode, so the profile is not run when you execute a script, command or script block (you still have to remember to run pwsh -f script.ps1 - not pwsh script.ps1)
if (-not (IsInteractive)) {
exit
}
Testing for interactivity should probably take both the process and the user into account. Looking for the -NonInteractive (minimally -noni) powershell switch to determine process interactivity (very similar to #VertigoRay's script) can be done using a simple filter with a lightweight -like condition:
function Test-Interactive
{
<#
.Synopsis
Determines whether both the user and process are interactive.
#>
[CmdletBinding()] Param()
[Environment]::UserInteractive -and
!([Environment]::GetCommandLineArgs() |? {$_ -ilike '-NonI*'})
}
This avoids the overhead of WMI, process exploration, imperative clutter, double negative naming, and even a full regex.
I wanted to put an updated answer here because it seems that [Environment]::UserInteractive doesn't behave the same between a .NET Core (container running microsoft/nanoserver) and .NET Full (container running microsoft/windowsservercore).
While [Environment]::UserInteractive will return True or False in 'regular' Windows, it will return $null in 'nanoserver'.
If you want a way to check interactive mode regardless of the value, add this check to your script:
($null -eq [Environment]::UserInteractive -or [Environment]::UserInteractive)
EDIT: To answer the comment of why not just check the truthiness, consider the following truth table that assumes such:
left | right | result
=======================
$null | $true | $false
$null | $false | $true (!) <--- not what you intended
This will return a Boolean when the -Noninteractive switch is used to launch the PowerShell prompt.
[Environment]::GetCommandLineArgs().Contains('-NonInteractive')
I came up with a posh port of existing and proven C# code that uses a fair bit of P/Invoke to determine all the corner cases. This code is used in my PowerShell Build Script that coordinates several build tasks around Visual Studio projects.
# Some code can be better expressed in C#...
#
Add-Type #'
using System;
using System.Runtime.InteropServices;
public class Utils
{
[DllImport("kernel32.dll")]
private static extern uint GetFileType(IntPtr hFile);
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
public static bool IsInteractiveAndVisible
{
get
{
return Environment.UserInteractive &&
GetConsoleWindow() != IntPtr.Zero &&
IsWindowVisible(GetConsoleWindow()) &&
GetFileType(GetStdHandle(-10)) == 2 && // STD_INPUT_HANDLE is FILE_TYPE_CHAR
GetFileType(GetStdHandle(-11)) == 2 && // STD_OUTPUT_HANDLE
GetFileType(GetStdHandle(-12)) == 2; // STD_ERROR_HANDLE
}
}
}
'#
# Use the interactivity check somewhere:
if (![Utils]::IsInteractiveAndVisible)
{
return
}
C:\> powershell -NoProfile -NoLogo -NonInteractive -Command "[Environment]::GetCommandLineArgs()"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
-NoProfile
-NoLogo
-NonInteractive
-Command
[Environment]::GetCommandLineArgs()
Implement two scripts, one core.ps1 to be manually launched, and one scheduled.ps1 that launches core.ps1 with a parameter.
powerShell -NonInteractive { Get-WmiObject Win32_Process -Filter "Name like '%powershell%'" | select-Object CommandLine }
powershell -Command { Get-WmiObject Win32_Process -Filter "Name like '%powershell%'" | select-Object CommandLine }
In the first case, you'll get the "-NonInteractive" param. In the latter you won't.
Script: IsNonInteractive.ps1
function Test-IsNonInteractive()
{
#ref: http://www.powershellmagazine.com/2013/05/13/pstip-detecting-if-the-console-is-in-interactive-mode/
#powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
return [bool]([Environment]::GetCommandLineArgs() -Contains '-NonInteractive')
}
Test-IsNonInteractive
Example Usage (from command prompt)
pushd c:\My\Powershell\Scripts\Directory
::run in non-interactive mode
powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
::run in interactive mode
powershell -File .\IsNonInteractive.ps1
popd
More Involved Example Powershell Script
#script options
$promptForCredentialsInInteractive = $true
#script starts here
function Test-IsNonInteractive()
{
#ref: http://www.powershellmagazine.com/2013/05/13/pstip-detecting-if-the-console-is-in-interactive-mode/
#powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
return [bool]([Environment]::GetCommandLineArgs() -Contains '-NonInteractive')
}
function Get-CurrentUserCredentials()
{
return [System.Net.CredentialCache]::DefaultCredentials
}
function Get-CurrentUserName()
{
return ("{0}\{1}" -f $env:USERDOMAIN,$env:USERNAME)
}
$cred = $null
$user = Get-CurrentUserName
if (Test-IsNonInteractive)
{
$msg = 'non interactive'
$cred = Get-CurrentUserCredentials
}
else
{
$msg = 'interactive'
if ($promptForCredentialsInInteractive)
{
$cred = (get-credential -UserName $user -Message "Please enter the credentials you wish this script to use when accessing network resources")
$user = $cred.UserName
}
else
{
$cred = Get-CurrentUserCredentials
}
}
$msg = ("Running as user '{0}' in '{1}' mode" -f $user,$msg)
write-output $msg