Is there a way to minimize all windows except one? - powershell

I need to Take a screenshot with a script when a process has an error.
is there a way to minimize all windows, except the window of the process that got the error?
I know the way to minimize all:
$shell = new-object -com shell.application
$shell.MinimizeAll()
But is there a way to minimize everything except one window?
Thanks!

use API Windows
$Win32ShowWindowAsync = Add-Type –memberDefinition #”
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
“# -name “Win32ShowWindowAsync” -namespace Win32Functions –passThru
$titletowindow="TODO.csv - Bloc-notes"
get-process |
where mainwindowhandle -ne 0 |
%{if ($_.MainWindowTitle -eq $titletowindow) { $Win32ShowWindowAsync::ShowWindowAsync($_.MainWindowHandle, 3) | Out-Null} else { $Win32ShowWindowAsync::ShowWindowAsync($_.MainWindowHandle, 6) | Out-Null} }

Related

How to minimize the window for foo.exe after bar.exe opens

I'm re-writting a batch file that combined powershell and cmd commands to be strictly a powershell script, and I'm looking for a way to minimize/close the API confirmation window which pops up in the user's web browser after the application the script calls on starts.
My original batch file code looked like this using a shell object to minimize everything before moving on to the next command:
cd /d "%userprofile%\Desktop\streamblastoff\libraries\binaries\Snip"
start Snip.exe
timeout /t 1 /nobreak
powershell -command "(new-object -com shell.application).minimizeall()"
And this is what I have so far:
Push-Location -Path $PSScriptRoot\libraries\binaries\snip
Start-Process .\Snip.exe; (new-object -com shell.application).minimizeall()
Pop-Location
It doesn't work though, everything minimizes before the browser window appears ... which I guess I should have seen coming. Ideally I'd like to do it all a bit cleaner in my new script, and be able to close/minimize the specific tab once the window pops up in the users default browser, but I'm not sure if that'd be possible ...
needing to minimize a users default browser window after the first application starts
# First application.
$bar = "powershell"
# First application starts.
Start-Process -FilePath $bar
# Get Default Browser
$foo = Get-ItemPropertyValue -Path "Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice" -Name 'ProgId' |
ForEach-Object {
Get-ItemPropertyValue -Path "Registry::HKEY_CLASSES_ROOT\$_\shell\open\command" -Name '(default)'
} |
ForEach-Object {
$_ -match '^"([^"]+\.exe)"' | Out-Null
$matches[1]
} |
ForEach-Object {
Get-Item -Path $_
} |
ForEach-Object {
$_.BaseName
}
# End Get Default Browser
Function minimizeProcess
{
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
HelpMessage="Input a System.Diagnostics.Process object.")]
[System.Diagnostics.Process] $Process
)
Begin
{
# P/Invoke Definition
$signature = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
# Compile new class.
$myType = Add-Type -MemberDefinition $signature -Name MyClass -Namespace MyNamespace -PassThru
}
Process
{
# Minimize the window.
# For different display actions see:
# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
$myType::ShowWindowAsync($Process.MainWindowHandle, 7) | Out-Null
}
}
# Minimize $foo if $bar is running.
if ((Get-Process | ForEach-Object { $_.Name }) -contains $bar)
{
Get-Process -Name $foo |
ForEach-Object {
$process = Get-CimInstance Win32_Process -Filter "ProcessID = '$($_.Id)'"
$owner = Invoke-CimMethod -InputObject $process -MethodName GetOwner
Add-Member -InputObject $_ -NotePropertyName Owner -NotePropertyValue $owner.User
$_
} |
# Only minimize processes that belong to the user.
Where-Object { $_.Owner -like $env:USERNAME } |
ForEach-Object {
minimizeProcess $_
# foo minimizes.
}
}
PowerShell cmdlet to open a URL in the user's default browser. · GitHub
How to find the default browser via the registry on Windows 10 - Stack Overflow
PowerShell P/Invoke Walkthrough | Precision Computing
Add-Type (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs
Process.WaitForInputIdle Method (System.Diagnostics) | Microsoft Docs
Process.MainWindowHandle Property (System.Diagnostics) | Microsoft Docs
ShowWindowAsync function (winuser.h) - Win32 apps | Microsoft Docs
powershell - Get-Process and Process Owner - Stack Overflow
.net - How do I determine the owner of a process in C#? - Stack Overflow
everything minimizes before the browser window appears
The error you were making was you did not copy the sleep.
Push-Location -Path $PSScriptRoot\libraries\binaries\snip
Start-Process .\Snip.exe
# timeout /t 1 /nobreak
Start-Sleep 1
(new-object -com shell.application).minimizeall()
Pop-Location
When creating macros, you have to insert sleeps to accommodate delays in the graphical user interface.
Start-Sleep (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs
Timeout - delay in seconds - Windows CMD - SS64.com
be able to close/minimize the specific tab
SeleniumHQ Browser Automation

Simple way to determine when an process has an open window not just running with powershell?

I am attempting to work with an exe via powershell. I have to call the open application and pass some keyboard shortcuts to it because it does not have built in switches unfortunately. I am using start-process to kick it off and using a do until loop to wait till the app is open to pass the keyboard shortcuts to it. when I use just get-process "ProcessName" it exits the loop like 5 seconds before the application window is actually. I could work around this by using start-sleep for for another 5 or so seconds but am not sure if it will only take that long on other machines. Is there a property I can use from get-process that will only return a value when an application window is actually open?
I have tried
start-process -FilePath "Path to exe"
Do{
write-Output "test..."
}
until ((Get-Process -Name "ProcessName" -ErrorAction SilentlyContinue).Responding -eq "True")
I have also tried:
start-process -FilePath "Path to exe"
Do{
write-Output "test..."
}
until ((Get-Process -Name "ProcessName" -ErrorAction SilentlyContinue).TotalProcessorTime.Milliseconds -gt "600")
The totalprocessor time works when testing but I don't know that number will be accurate on other machines.
You can use user32.dll!EnumDesktopWindows to loop through all windows and then call user32.dll!GetWindowThreadProcessId to determine whether any window belongs to the process in question:
Add-Type -TypeDefinition #'
using System;
using System.Runtime.InteropServices;
public class ProcessWindowFinder
{
private delegate bool WindowDelegate(IntPtr hWnd, int lParam);
[DllImport("user32.dll", SetLastError=true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
private static extern bool EnumDesktopWindows(IntPtr hDesktop, WindowDelegate lpfn, IntPtr lParam);
public static bool FindByPID(uint pid)
{
bool found = false;
ProcessWindowFinder.WindowDelegate filter = delegate(IntPtr hWnd, int lParam)
{
if(!found)
{
// test whether this window belongs to the target process
// if we haven't already found a windows belonging to it
uint _pid;
GetWindowThreadProcessId(hWnd, out _pid);
if(pid == _pid)
found = true;
}
return true;
};
EnumDesktopWindows(IntPtr.Zero, filter, IntPtr.Zero);
return found;
}
}
'#
Now you can use it to test whether your process has any desktop windows:
# launch file.exe, grab the process object
$process = Start-Process ".\path\to\file.exe" -PassThru
# Wait up to 15 seconds for window
$waitFor = 15
do {
Write-Host "Waiting for process... "
Start-Sleep -Seconds 1
} until ((--$waitFor -lt 0) -or [ProcessWindowFinder]::FindByPID($process.Id) -or $process.HasExited)
if($process.HasExited){
# process already exited
}
elseif($waitFor -lt 0){
# timed out waiting for window
}
else{
# we got a window!
}
$myshell = New-Object -com "Wscript.Shell"
$process = start-process -FilePath "Path to file" -PassThru
do{
start-sleep -Milliseconds 500
}
until($myshell.appactivate("ProcessName") -eq "True")
$myshell.appactivate("ProcessName")
$myshell.sendkeys("^{ }")

Empty recycle bin with Powershell V2

Is there a way to empty the recycle bin using Powershell 2.0.
I do not want to update Powershell.
You could clear recycle bin via com object. Like so:
$Shell= New-Object -ComObject Shell.Application
$Bin = $Shell.NameSpace(10)
foreach ($Item in #($Bin.Items())){Remove-item $Item.Path -Force}
You could also directly call SHEmptyRecycleBin Win32 function:
$definition = #'
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
public static extern uint SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, uint dwFlags);
'#
$winApi = Add-Type -MemberDefinition $definition -Name WinAPI -Namespace Extern -PassThru
$winApi::SHEmptyRecycleBin(0, $null, 7)
All recycle bins are deleted, no confirmation message is shown, no progress bar, no sound.

Maximizing Lync Window Using Powershell?

I've created a script that will automatically initiate a video call with a user of my choice.
When ran, the script leaves the video call docked, with the lync video call window flashing.
How would I be able to get this window to maximize and go to full screen when the script is ran?
Thank you so much for your help.
Below is my code
$assemblyPath = “C:\Program Files (x86)\Microsoft Office 2013\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”
Import-Module $assemblyPath
$LyncClient = [Microsoft.Lync.Model.LyncClient]::GetClient()
$StartVideoCallMethod = {
$Conversation = $this.ConversationManager.AddConversation();
$contact = $LyncClient.ContactManager.GetContactByUri("useremailhere")
[void]$Conversation.AddParticipant($contact);
[void]$Conversation.Modalities['AudioVideo'].BeginConnect({}, 0);
};
Add-Member -InputObject $LyncClient -MemberType ScriptMethod -Name StartVideoCall -Value $StartVideoCallMethod -Force;
# Initiate the video call
$Conversation = $LyncClient.StartVideoCall();
I don't have Lync, but something like this should work. I'm using the process name (or what I'm guessing it is) to get the MainWindowHandle for the Lync window, then sending that a command to maximize (cmd=3, see here for the full list of values: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx).
This code may break if more than one process matches by name, but it should get you started; if you can get the PID or some other, better unique identifier, use that. Just mess around with the output of Get-Process and you should see a number of options, and remember you can always use a Where clause to filter the output. Or of course if there's some way to get the MainWindowHandle directly from $LyncClient, even better.
$w = Get-Process -Name "Lync"
$Win32ShowWindowAsync = Add-Type –memberDefinition `
'[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);' `
-name “Win32ShowWindowAsync” -namespace Win32Functions –passThru
$Win32ShowWindowAsync::ShowWindowAsync($w.MainWindowHandle,3) | Out-Null
Here the code that I have so far.
Still needs some tweaks to perfect it but it does the job.
Tweaks would be specifying which window to maximize as it will sometimes maximize the lync contacts window.
Code
$assemblyPath = “C:\Program Files (x86)\Microsoft Office 2013\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”
Import-Module $assemblyPath
$exePath = "C:\Program Files\Microsoft Office 15\root\office15\lync.exe"
if(!(get-process | ?{$_.path -eq $exePath})){
Start-Process -FilePath $exePath -WindowStyle Maximized
Start-Sleep -s 10
}
$LyncClient = [Microsoft.Lync.Model.LyncClient]::GetClient()
$StartVideoCallMethod = {
$Conversation = $this.ConversationManager.AddConversation();
$contact = $LyncClient.ContactManager.GetContactByUri("ernesto.gomila#quirchfoods.com")
[void]$Conversation.AddParticipant($contact);
[void]$Conversation.Modalities['AudioVideo'].BeginConnect({}, 0);
};
Add-Member -InputObject $LyncClient -MemberType ScriptMethod -Name StartVideoCall -Value $StartVideoCallMethod -Force;
# Initiate the video call
$Conversation = $LyncClient.StartVideoCall();
#Maximize window
$w = Get-Process -Name "lync"
$Win32ShowWindowAsync = Add-Type –memberDefinition #"
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"# -name “Win32ShowWindowAsync” -namespace Win32Functions –passThru
Start-Sleep -s 2
$Win32ShowWindowAsync::ShowWindowAsync($w.MainWindowHandle,3) | Out-Null

Can this be made to work on a remote computer?

I've been killing myself trying to get this to work on a remote computer, is it even possible? If so, can someone point me in the right direction?
Here's the code:
Function Lock-WorkStation {
#Requires -Version 2.0
$signature = #"
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
"#
$LockWorkStation = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru
$LockWorkStation::LockWorkStation() | Out-Null
}
I can't test here, but for me it can NOT work because, as you can read in Microsoft documentation, the LockWorkStation function is callable only by processes running on the interactive desktop. In addition, the user must be logged on.
So when you connect to a remote computer using PSSession as far as I understand you are not in the interactive session.
Nothing to do with this, but it can help in Windows Vista/7 2008/R2, you can use the command tsdiscon.exe to lock a Remote Desktop session or your workstation.
Here is a sample where, logged as adminstrator domain on my computer, I first list, then lock the console session on my server.
PS> query session /server:WM2008R2ENT
SESSION UTILISATEUR ID ÉTAT TYPE PÉRIPHÉRIQUE
services 0 Déco
console jpb 2 Actif
PS> tsdiscon 2 /server:WM2008R2ENT
It's possible. But you need a workaround to connect to the interactive session.
Download the PowerShellPack and install it. You only need one module called "TaskScheduler".
I've tested the following code:
Function Lock-Workstation
{
param(
$Computername,
$Credential
)
if(!(get-module taskscheduler)){Import-Module TaskScheduler}
New-task -ComputerName $Computername -credential:$Credential |
Add-TaskTrigger -In (New-TimeSpan -Seconds 30) |
Add-TaskAction -Script `
{
$signature = #"
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
"#
$LockWorkStation = Add-Type -memberDefinition $signature `
-name "Win32LockWorkStation" `
-namespace Win32Functions `
-passthru
$LockWorkStation::LockWorkStation() | Out-Null
} | Register-ScheduledTask TestTask -ComputerName $Computername `
-credential:$Credential
}
You can use it like this:
Lock-Workstation "NameOfTheComputer" (Get-Credential)
or like this:
Lock-Workstation "NameOfTheComputer"
If you receive an error in Connect-ToTaskScheduler when specifying a credential, it's because there is a typo in the module (edit Connect-ToTaskScheduler.ps1 and replace "$NetworkCredentail.Domain," with "$NetworkCredential.Domain,"