[How-To] Hide, minimize, or show a window (PowerShell hybrid)
Posted: 18 Oct 2020 12:46
Apparently Batch itself has little to no opportunities to interact with windows. Other scripting languages like PowerShell can support here and they can be merged into a Batch code. The below uses the macro syntax to define PowerShell code and inject conditions at runtime.
The comments and examples explain how to use it.
For those who are wondering why I heavily used platform invocation rather than the Get-Process cmdlet:
- It's all about windows. Enumerating the top-level windows rather than the processes (that may not even have a window) is much more logical.
- However, the main reason is that the .MainWindowHandle property is null for hidden windows. Thus, unhiding windows wouldn't have been supported using Get-Process.
Steffen
The comments and examples explain how to use it.
Code: Select all
@echo off &setlocal
:: define the show_hide_window macro variable
call :init_show_hide_window
:: create a PowerShell process with iconic window
:: pause to give you the opportunity to observe the icon on the task bar
:: maximize the window
start /min powershell.exe
pause
%show_hide_window% MAXIMIZE "$_.WINDOWTITLE -eq 'Windows PowerShell'"
echo %errorlevel%
:: create two Notepad processes with iconic window
:: pause to give you the opportunity to observe the icons on the task bar
:: maximize the first window and pause
:: hide the second window and pause (observe the disappeared icon on the task bar)
:: bring the second window to front in normal style
:: after a second minimize it to the task bar and pause
>"%temp%\test.txt" echo pick me first
start /min notepad.exe "%temp%\test.txt"
>"%temp%\another test.txt" echo leave me alone for a while
start /min notepad.exe "%temp%\another test.txt"
pause
del "%temp%\test.txt"
del "%temp%\another test.txt"
%show_hide_window% MAXIMIZE "$_.IMAGENAME -eq 'notepad.exe' -and $_.WINDOWTITLE -like 'test.txt - *'"
echo %errorlevel%
pause
%show_hide_window% HIDE "$_.IMAGENAME -eq 'notepad.exe' -and $_.WINDOWTITLE -like 'another test.txt - *'"
echo %errorlevel%
pause
%show_hide_window% SHOWNORMAL "$_.IMAGENAME -eq 'notepad.exe' -and $_.WINDOWTITLE -like 'another test.txt - *'"
echo %errorlevel%
timeout 1
%show_hide_window% MINIMIZE "$_.IMAGENAME -eq 'notepad.exe' -and $_.WINDOWTITLE -like 'another test.txt - *'"
echo %errorlevel%
pause
goto :eof
:init_show_hide_window
:: - BRIEF -
:: Hide, minimize, or bring a window into the foreground and activate it. The window appearance
:: is defined by an ID. Selection takes place by custom filters. If the filter matches more
:: than one window then the first found window will be selected.
:: - SYNTAX -
:: %show_hide_window% showcmd "filter"
:: showcmd Command for the new window appearance. Valid arguments:
:: HIDE
:: MAXIMIZE
:: MINIMIZE
:: RESTORE
:: SHOWDEFAULT
:: SHOWNORMAL
:: filter Conditional expression using at least one of these properties:
:: $_.PID
:: $_.IMAGENAME
:: $_.WINDOWTITLE
:: Image names and window titles have to be enclosed into single quotes.
:: Conditions are following the syntax of PowerShell comparisons:
:: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators
:: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logical_operators
:: - EXAMPLES -
:: Minimize the window with title "Windows PowerShell":
:: %show_hide_window% MINIMIZE "$_.WINDOWTITLE -eq 'Windows PowerShell'"
:: Hide the Notepad window whose title contains "foobar.txt":
:: %show_hide_window% HIDE "$_.IMAGENAME -eq 'notepad.exe' -and $_.WINDOWTITLE -like '*foobar.txt*'"
:: Maximize the main window of the process with process ID 1234:
:: %show_hide_window% MAXIMIZE "$_.PID -eq 1234"
setlocal DisableDelayedExpansion
set show_hide_window=for %%i in (1 2) do if %%i==2 (^
%=% for /f "tokens=1,2*" %%j in ("? ^^!arg^^!") do powershell.exe -nop -ep Bypass -c ^"try{Add-Type '^
%=% using System;using System.Text;using System.Collections.Generic;using System.Runtime.InteropServices;^
%=% public class Data{public IntPtr HWND;public int PID;public string IMAGENAME;public string WINDOWTITLE;}^
%=% public class Wnd{^
%=====% private delegate int CbPtr(IntPtr hWnd,IntPtr lPrm);^
%=====% private static List^^^^^^^<Data^^^^^^^> dataList=new List^^^^^^^<Data^^^^^^^>();^
%=====% [DllImport(\"user32.dll\")] static extern int EnumWindows(CbPtr func,IntPtr lPrm);^
%=====% [DllImport(\"user32.dll\")] static extern IntPtr GetWindow(IntPtr hWnd,int cmd);^
%=====% [DllImport(\"user32.dll\")] static extern int GetWindowText(IntPtr hWnd,StringBuilder str,int max);^
%=====% [DllImport(\"user32.dll\")] static extern int GetWindowThreadProcessId(IntPtr hWnd,ref int pid);^
%=====% [DllImport(\"kernel32.dll\")] static extern IntPtr OpenProcess(int access,int inherit,int pid);^
%=====% [DllImport(\"kernel32.dll\")] static extern int QueryFullProcessImageName(IntPtr hProc,int flags,StringBuilder exeName,ref int size);^
%=====% [DllImport(\"kernel32.dll\")] static extern int CloseHandle(IntPtr hObj);^
%=====% [DllImport(\"user32.dll\")] public static extern int ShowWindow(IntPtr hWnd,int show);^
%=====% [DllImport(\"user32.dll\")] public static extern int SetForegroundWindow(IntPtr hWnd);^
%=====% private static int CbProc(IntPtr hWnd,IntPtr lPrm){^
%=========% if (GetWindow(hWnd,4)==IntPtr.Zero){^
%=============% int pid=0,size=512;^
%=============% StringBuilder exe=new StringBuilder(size),title=new StringBuilder(size);^
%=============% int len=GetWindowText(hWnd,title,size);^
%=============% GetWindowThreadProcessId(hWnd,ref pid);^
%=============% IntPtr hProc=OpenProcess(4096,0,pid);^
%=============% QueryFullProcessImageName(hProc,0,exe,ref size);^
%=============% CloseHandle(hProc);^
%=============% if (len^^^^^^^>0^^^^^^^&^^^^^^^&size^^^^^^^>0)^
%=================% dataList.Add(new Data{HWND=hWnd,PID=pid,IMAGENAME=System.IO.Path.GetFileName(exe.ToString()),WINDOWTITLE=title.ToString()});^
%=========% }^
%=========% return 1;^
%=====% }^
%=====% public static List^^^^^^^<Data^^^^^^^> GetData(){^
%=========% EnumWindows(CbProc,IntPtr.Zero);^
%=========% return dataList;^
%=====% }^
%=% }';^
%=% $show=('HIDE','SHOWNORMAL','','MAXIMIZE','','','MINIMIZE','','','RESTORE','SHOWDEFAULT').IndexOf(\"%%~k\".ToUpper());^
%=% if(-1,2 -contains $show){exit 1;}^
%=% [Wnd]::GetData()^^^^^^^|?{%%~l;}^^^^^^^|%%{^
%=====% $e=[int]([Wnd]::ShowWindow($_.HWND,$show) -eq 0);^
%=====% if(0,6 -contains $show){exit $e;}^
%=====% exit [int]([Wnd]::SetForegroundWindow($_.HWND) -eq 0);^
%=% }}catch{exit 1;}exit 1;^"^&endlocal) else setlocal EnableDelayedExpansion^&set arg=
endlocal &set "show_hide_window=%show_hide_window%"
if !!# neq # set "show_hide_window=%show_hide_window:^^!=!%"
exit /b
- It's all about windows. Enumerating the top-level windows rather than the processes (that may not even have a window) is much more logical.
- However, the main reason is that the .MainWindowHandle property is null for hidden windows. Thus, unhiding windows wouldn't have been supported using Get-Process.
Steffen