With PowerShell, how to switch minimized application to normal state? - windows

Given a single-instance desktop application ccTest.exe, how can I use PowerShell (v6 or 7) to run the application in the Normal state if it is already running in Minimized state.
I need access to controls in the main window so it must be in Normal state. Get-Process works to check if the app is running. If not, then launch it with Start-Process and -WindowStyle Normal parameter. But Start-Process does not work if the the single-instance app is already running Minimized.
Due to timing constraints I'd rather not close ccTest.exe if minimized and then launch with Start-Process. Here is the test code.
if (Get-Process | Select MainWindowTitle, ProcessName, Id | where {$_.MainWindowTitle -like "ccTest*"})
{
if Minimized make it Normal
execute needed function
}
else
{
start-process "C:\Program Files (x86)\Test\ccTest.exe" -WindowStyle Normal
execute needed function
}
So how to change State/Style from Minimized to Normal to run the needed function?

This should do the trick.
Add-Type -AssemblyName UIAutomationClient
$MyProcess = Get-Process | where { $_.MainWindowTitle -like "ccTest*" }
if ($null -ne $MyProcess) {
# if Minimized make it Normal
$ae = [System.Windows.Automation.AutomationElement]::FromHandle($MyProcess.MainWindowHandle)
$wp = $ae.GetCurrentPattern([System.Windows.Automation.WindowPatternIdentifiers]::Pattern)
if ($wp.Current.WindowVisualState -eq 'Minimized') {
$wp.SetWindowVisualState('Normal')
}
# execute needed function
}
else {
start-process "C:\Program Files (x86)\Test\ccTest.exe" -WindowStyle Normal
# execute needed function
}
References:
Maximize window and bring it in front with powershell
Get window state of another process

Related

Launch Applications in WIndows using AppID and get the pid

I'm trying to launch Windows applications using their AppID such as Microsoft.WindowsCalculator_8wekyb3d8bbwe!App which I get by calling Get-StartApps
Currently I can launch the applications but can't get the correct PID
cmd = exec.Command("powershell", "start", `shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App`)
err := cmd.Start()
fmt.Println(cmd.Process.Pid)
This returns the PID of powershell
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe start shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App
Is there a way to launch the application by the AppID and still get the correct PID?
tl;dr
// Make PowerShell not only launch Calculator, but also
// determine and output its PID, as described in the next section.
out, _ :=
exec.Command(
`powershell.exe`,
`-NoProfile`,
`-Command`,
`Start-Process -ErrorAction Stop calculator: ; (Get-Process Calculator | Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID`,
).Output()
// Parse stdout output, which contains the PID, into an int
var pid int
fmt.Sscanf(string(out), "%d\n", &pid)
In principle, you can pass -PassThru to PowerShell's Start-Process (start) cmd, which returns a process-info object that has an .Id property containing the launched process' PID, and output the latter.
Unfortunately, with UWP / AppX applications specifically, such as Calculator, this does not work, which is a problem that exists in the underlying .NET APIs, up to at least .NET 6.0 - see GitHub issue #10996.
You can try the following workaround:
Launch the AppX application with Start-Process, which indirectly creates a process whose name is Calculator (Windows 10) / CalculatorApp (Windows 11).
You can identify this name yourself if you run (Get-Process *calc*).Name after launching Calculator. Get-Process *calc* | Select-Object Name, Path would show the executable path too, but note that this executable should be considered an implementation detail and can not be invoked directly.
Return the ID of that Calculator / CalculatorApp process. The fact that Calculator only ever creates one such process in a given user session actually makes identifying that process easy.
Note that this means that the PID of a preexisting Calculator process may be returned, which, however, is the correct one, because the transient process launched by Start-Process simply delegates creation of a new Calculator window to an existing process.
If you wanted to identify the newly created window, more work would be required: You'd have to enumerate the process' windows and identify the one with the highest z-order.
PowerShell code (note: in Windows 11, replace Calculator with CalculatorApp):
# Launch Calculator - which may reuse an existing instance and
# merely create a new *window* - and report the PID.
Start-Process -ErrorAction Stop calculator:
(Get-Process Calculator | Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID
Note that I've used the URL scheme calculator: as a simpler way to launch Calculator.
Note:
The Where-Object SessionId -eq (Get-Process -ID $PID).SessionId guards against mistakenly considering potential Calculator processes created by other users in their own sessions (Get-Process returns all processes running on the local machine, across all user sessions). Filtering by .SessionID, i.e. by the active user session (window station), prevents this problem.
As a PowerShell CLI call:
powershell.exe -NoProfile -Command "Start-Process -ErrorAction Stop calculator: ; (Get-Process Calculator | Where-Object SessionId -eq (Get-Process -ID $PID).SessionId).ID"

How to close other PowerShell windows from script

I have a PowerShell script which has the following steps:
Opens another PowerShell window
Navigates to an angular projects directory
Runs a command to serve the project
Is there a way that I can close all other running PowerShell windows, but keep the currently running script and it's newly created window open? Following the changes, I would like it to behave like this:
Close all other PowerShell windows
Opens another PowerShell window
Navigates to an angular projects directory
Runs a command to serve the project
You could use Get-Process to enumerate all running powershell processes, then filter out the current one by comparing with the value of $PID, before piping the rest to Stop-Process:
Get-Process powershell |? Id -ne $PID |Stop-Process -Force
You can include the child process by ID if necessary:
$childProcess = Start-Process powershell $childProcArgs -PassThru
Get-Process powershell |? Id -notin #($PID;$childProcess.Id) |Stop-Process -Force
... although I would suggest simply killing all other powershell instances first, and then launch the child process after
Is there a way that I can close all other running PowerShell windows,
but keep the currently running script and it's newly created window
open?
$Previous = Get-process -Name *Powershell*;
{YOUR SCRIPT}
Stop-process $previous

Running powershell script to perform automated webpage login from task schedular behaves differently from running the script manually

My university requires all computers to perform a web-based login in order to get-access to the internet, and claims that all users will log-off automatically in the mid-night (sounds strange, but it is true), so I am trying to write a powershell script (in Windows 10) to perform automatic login at mid-night.
My script is list here. It opens an IE process in the background (in a nonvisible way), fill in the username and password, login, and kills the IE process.
# If there are existing Internet Explorer processes, close it
$IE_Process = Get-Process iexplore -ErrorAction Ignore
if ($IE_Process) {
$IE_Close = Foreach-Object { $IE_Process.CloseMainWindow() }
}
Stop-Process -Name "iexplore" -ErrorAction Ignore
# Login Information
$url = "http://xxx.xxx.xxx.xxx/"
$username = "xxxxxxxx"
$password = "xxxxxxxx"
# Open an IE process
$ie = New-Object -com internetexplorer.application;
$ie.silent = $true
$ie.navigate($url);
while ($ie.Busy -eq $true)
{
Start-Sleep -s 1;
}
# The stupid webpage needs to submit twice
$ie.Document.getElementById("loginname").value = $username
$ie.Document.getElementByID("password").value = $password
$ie.Document.getElementById("button").Click()
Start-Sleep -s 1;
$ie.Document.getElementById("loginname").value = $username
$ie.Document.getElementByID("password").value = $password
$ie.Document.getElementById("button").Click()
# Close the IE process
$IE_Process = Get-Process iexplore -ErrorAction Ignore
if ($IE_Process) {
$IE_Close = Foreach-Object { $IE_Process.CloseMainWindow() }
}
Stop-Process -Name "iexplore" -ErrorAction Ignore
Remove-Variable -Name ie,username,password,url,IE_Process -ErrorAction Ignore
The script is saved as "login_IE.ps1". It may be poorly written as I am new to powershell, but it works. If I open an cmd window and execute the following command, I am logged in.
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy RemoteSigned -File C:\Users\MyName\Documents\Powershell\login_IE.ps1
However, if I create a scheduled task in windows task scheduler executing this script, it doesn't work. I fill the "Program/script:" as:
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
and fill the "Add arguments (optional):" as:
-ExecutionPolicy RemoteSigned -File C:\Users\MyName\Documents\Powershell\login_IE.ps1
The scheduled task is run under my account (I am the only user of this computer).
If I run the scheduled task manually, in the task manager I can see two IE process opened in the "Background process", communicate with the internet, and then get killed, so I am pretty sure that the script has actually been executed. But I found I am not logged in since I don't have internet access, where could the problem be?
Any advice is really appreciated. Thanks in advance.
Similar type of issue I had, when trying to run the script directly from Powershell window it works as expected, but from the task scheduler or command line both not getting the desired results.
Commenting and adding lines like below in my script, help me to run the script from command line and task scheduler as well
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
[Net.ServicePointManager]::SecurityProtocol =
[Net.SecurityProtocolType]::Tls12
Hope this helps anyone else.

Start a detached background process in PowerShell

I have a Java program which I would like to launch as a background process from a PowerShell script, similar to the way a daemon runs on Linux. The PowerShell script needs to do a couple of things:
Run the program as a separate and detached process in the background, meaning the parent window can be closed and the process keeps running.
Redirect the program's standard output and standard error to files.
Save the PID of the background process to a file so it can be terminated later by another script.
I have a shell script on Linux which starts the program like so:
$ java -jar MyProgram.jar >console.out 2>console.err &
I'm hoping to replicate the same behavior on Windows using a PowerShell script. I have tried using Start-Process with various combinations of options, as well as creating System.Diagnostics.ProcessStartInfo and System.Diagnostics.Process objects, but so far I am not having any luck. PowerShell starts the program as a background process, but the program abruptly terminates when the DOS window which started the PowerShell session is closed. I would like it to start in the background and be independent of the command window which started it.
The output redirection has also been troublesome, as it seems that the output and error streams can only be redirected in the process is being run in the same window (e.g., using -NoNewWindow).
Is this sort of thing possible in PowerShell?
Use jobs for this:
Start-Job -ScriptBlock {
& java -jar MyProgram.jar >console.out 2>console.err
}
Another option would be Start-Process:
Start-Process java -ArgumentList '-jar', 'MyProgram.jar' `
-RedirectStandardOutput '.\console.out' -RedirectStandardError '.\console.err'
Consider using the task scheduler for this. Define a task and set it without any triggers. That will allow you to simply "Run" (manually trigger) the task.
You can set up and/or trigger scheduled tasks using the ScheduledTasks powershell module, or you can use the GUI.
This is an old post but since I have it working fine thought it might help to share. Its the call to 'java' instead of 'javaw' that is likely your issue. Ran it out myself using my JEdit java program through powershell to launch it.
#Requires -Version 3.0
$MyDriveRoot = (Get-Location).Drive.Root
$JEditDir = $($mydriveroot + "jEdit") ;# Should be C:\jEdit or wherever you want. JEdit is a sub-directory.
$jEdit = $($JEditDir + "\jedit.jar" )
$jEditSettings = $($JEditDir + "\settings")
$JEditLogs = $($JEditDir + "\logs")
Start-Process -FilePath javaw -ArgumentList ( '-jar',"$jEdit", '-settings="$JEditSettings"' ) -RedirectStandardOutput "$JEditLogs\console.out" -RedirectStandardError "$JEditLogs\console.err"
Which you can turn into a little function and then an alias to make it easy to launch in Powershell.
If ( ( Test-Path $jedit) ) {
Function Start-JEdit() {
Start-Process -FilePath javaw -ArgumentList ( '-jar',"$jEdit", '-settings="$($mydriveroot + "jEdit\settings")"' ) -RedirectStandardOutput "$JEditLogs\console.out" -RedirectStandardError "$JEditLogs\console.err"
}
New-Alias -Name jedit -Force Start-JEdit -Description "Start JEdit programmers text editor"
}
Try this with PowerShell:
Start-Process cmd -Args /c,"java -jar MyProgram.jar" `
-WindowStyle Hidden -RSI console.out -RSE console.err
OR
Start-Process cmd -Args /c,"java -jar MyProgram.jar >console.out 2>console.err" `
-WindowStyle Hidden
This will start a detached cmd window that is hidden, and will redirect the std streams accordingly.
Old question, but since I had the same goal, I used answer from #use to acheive it.
So here is my code :)
$NAME_TASK = "myTask"
$NAME_TASKPATH = "\myPath\"
if ($args[0] -eq "-task") {
# Code to be run "detached" here...
Unregister-ScheduledTask -TaskName $NAME_TASK -TaskPath $NAME_TASKPATH -Confirm:$False
Exit
}
$Task = (Get-ScheduledTask -TaskName $NAME_TASK -TaskPath $NAME_TASKPATH -ErrorAction 'SilentlyContinue')
if ($Task) {
Write-Host "ERR: Task already in progress"
Exit 1
}
$A = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy bypass -NoProfile -Command ""$PSCommandPath -task $args"""
Register-ScheduledTask -TaskName $NAME_TASK -TaskPath $NAME_TASKPATH -Action $A | Start-ScheduledTask
The solution is to combine Start-Process with nohup:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.3#example-9-create-a-detached-process-on-linux
(Note: This is NOT for Windows.)

Powershell script only runs in certain scenarios

I have a functions that only works on some scensarios.
It works on Powershell ISE, but when I save the same thing in a .Ps1 file and run it, it doest not work.
I have the function as part of a big script. It works and open the Window when I run it on Windows 7 but doest not run on Windows Server 2008 R2.
Why?
The script with the function and its calling is:
Function Get-SaveFileTxt($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.initialDirectory = $initialDirectory
$SaveFileDialog.AddExtension = $true
$SaveFileDialog.DefaultExt = "txt"
$SaveFileDialog.filter = "Text Files (*.txt)| *.txt"
$SaveFileDialog.ShowDialog() | Out-Null
$SaveFileDialog.filename
}
Get-SaveFileTxt
Not sure if this is the case here but I remember a bug that the opened dialog doesn't take focus and appear behind other opened windows. Can you confirm?
UPDATE:
Set the ShowHelp property to $true.
$SaveFileDialog.ShowHelp = $true
It works in the ISE because ISE's apartment state is STA by default and your powershell mode is MTA, you can check it with:
[System.Threading.Thread]::CurrentThread.ApartmentState
Your code will work if you open powershell in STA mode:
powershell.exe -STA

Resources