Set Window Topmost (Powershell hybrids)
Posted: 11 Feb 2019 12:08
The macros in the code below expect one argument. Pass a 1 to set the window topmost or a 0 to undo this setting.
The xtopmost macro is very restrictive because it makes closing the window quite difficult. Users might feel annoyed. You should avoid using it unless you have a real good reason.
It's not too difficult to find the references of the API functions used, in case you want to understand the code. However, the numeric constants are not as self-explanatory as the C macro names. A little more information doesn't hurt ...
Steffen
The xtopmost macro is very restrictive because it makes closing the window quite difficult. Users might feel annoyed. You should avoid using it unless you have a real good reason.
Code: Select all
@echo off
setlocal DisableDelayedExpansion
:: setlocal EnableDelayedExpansion
call :init_topmost
call :init_xtopmost
echo(&echo set topmost
%topmost% 1
if errorlevel 1 (echo error) else echo success
pause
echo(&echo reset
%topmost% 0
if errorlevel 1 (echo error) else echo success
pause
echo(&echo set topmost (extended)
%xtopmost% 1
if errorlevel 1 (echo error) else echo success
pause
echo(&echo reset
%xtopmost% 0
if errorlevel 1 (echo error) else echo success
pause
exit /b
:: place the window above all windows and maintain this order
:init_topmost
setlocal DisableDelayedExpansion
set topmost=for %%i in (1 2) do if %%i==2 (^
%=% for /f %%j in ("^^!arg^^! x") do powershell -nop -ep Bypass -c ^"try{$c=Add-Type -Name pInv -PassThru -MemberDefinition '^
%=====% [DllImport(\"user32.dll\")] public static extern int SetWindowPos(IntPtr wnd,IntPtr insertAfter,int x,int y,int cx,int cy,uint flags);^
%=====% [DllImport(\"kernel32.dll\")] public static extern IntPtr GetConsoleWindow();';^
%=====% $flag=0;^
%=====% if(-not [Int32]::TryParse(\"%%~j\",[ref]$flag) -or ($flag -ne 0 -and $flag -ne 1)){^
%=========% [Console]::Error.WriteLine(\"Syntax Error`r`n Usage:`r`n%%topmost%% flag`r`n`tflag`t1 set topmost, 0 restore defaults\");^
%=========% exit 1;}^
%=====% exit ($c::SetWindowPos($c::GetConsoleWindow(),[IntPtr]($flag-2),0,0,0,0,3) -eq 0);}catch{exit 1;}^"^&endlocal^
%=% ) else setlocal EnableDelayedExpansion^&set arg=
endlocal&set "topmost=%topmost%"
if !!# neq # set "topmost=%topmost:^^=%"
exit /b
:: place the window above all windows and maintain this order; disable minimizing, maximizing, and closing of the window; no task bar icon
:init_xtopmost
setlocal DisableDelayedExpansion
set xtopmost=for %%i in (1 2) do if %%i==2 (^
%=% for /f %%j in ("^^!arg^^! x") do powershell -nop -ep Bypass -c ^"try{$c=Add-Type -Name pInv -PassThru -MemberDefinition '^
%=====% [DllImport(\"user32.dll\")] public static extern int SetWindowLong(IntPtr wnd,int idx,int newLong);^
%=====% [DllImport(\"user32.dll\")] public static extern Int64 SetWindowLongPtr(IntPtr wnd,int idx,Int64 newLong);^
%=====% [DllImport(\"user32.dll\")] public static extern int GetWindowLong(IntPtr wnd,int idx);^
%=====% [DllImport(\"user32.dll\")] public static extern Int64 GetWindowLongPtr(IntPtr wnd,int idx);^
%=====% [DllImport(\"user32.dll\")] public static extern int SetWindowPos(IntPtr wnd,IntPtr insertAfter,int x,int y,int cx,int cy,uint flags);^
%=====% [DllImport(\"user32.dll\")] public static extern IntPtr GetSystemMenu(IntPtr wnd,int revert);^
%=====% [DllImport(\"user32.dll\")] public static extern int DeleteMenu(IntPtr menu,uint id,uint flags);^
%=====% [DllImport(\"kernel32.dll\")] public static extern IntPtr GetConsoleWindow();';^
%=====% $flag=0;^
%=====% if(-not [Int32]::TryParse(\"%%~j\",[ref]$flag) -or ($flag -ne 0 -and $flag -ne 1)){^
%=========% [Console]::Error.WriteLine(\"Syntax Error`r`n Usage:`r`n%%xtopmost%% flag`r`n`tflag`t1 set topmost, 0 restore defaults\");^
%=========% exit 1;}^
%=====% $wnd=$c::GetConsoleWindow();^
%=====% if($flag -eq 1){^
%=========% if([IntPtr]::Size -eq 4){$null=$c::SetWindowLong($wnd,-16,$c::GetWindowLong($wnd,-16) -bAnd -bNot 131072 -bAnd -bNot 65536);^
%=============% $null=$c::SetWindowLong($wnd,-20,($c::GetWindowLong($wnd,-20) -bAnd -bNot 262144) -bOr 128);}^
%=========% else{$null=$c::SetWindowLongPtr($wnd,-16,$c::GetWindowLongPtr($wnd,-16) -bAnd -bNot [Int64]131072 -bAnd -bNot [Int64]65536);^
%=============% $null=$c::SetWindowLongPtr($wnd,-20,($c::GetWindowLongPtr($wnd,-20) -bAnd -bNot [Int64]262144) -bOr [Int64]128);}^
%=========% exit ($c::DeleteMenu($c::GetSystemMenu($wnd,0),61536,0) -eq 0 -or $c::SetWindowPos($wnd,[IntPtr]-1,0,0,0,0,3) -eq 0);}^
%=====% if([IntPtr]::Size -eq 4){$null=$c::SetWindowLong($wnd,-20,($c::GetWindowLong($wnd,-20) -bAnd -bNot 128) -bOr 262144);^
%=========% $null=$c::SetWindowLong($wnd,-16,$c::GetWindowLong($wnd,-16) -bOr 131072 -bOr 65536);}^
%=====% else{$null=$c::SetWindowLongPtr($wnd,-20,($c::GetWindowLongPtr($wnd,-20) -bAnd -bNot [Int64]128) -bOr [Int64]262144);^
%=========% $null=$c::SetWindowLongPtr($wnd,-16,$c::GetWindowLongPtr($wnd,-16) -bOr [Int64]131072 -bOr [Int64]65536);}^
%=====% exit ($c::GetSystemMenu($wnd,1) -ne [IntPtr]::Zero -or $c::SetWindowPos($wnd,[IntPtr]-2,0,0,0,0,3) -eq 0);}catch{exit 1;}^"^&endlocal^
%=% ) else setlocal EnableDelayedExpansion^&set arg=
endlocal&set "xtopmost=%xtopmost%"
if !!# neq # set "xtopmost=%xtopmost:^^=%"
exit /b
Code: Select all
:: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ magic numbers used in the code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
:: use Get/SetWindowLong for a 32 bit process, but Get/SetWindowLongPtr for a 64 bit process
:: 4 size of a memory address in a 32 bit process (it's 8 bytes for a 64 bit process), the size of IntPtr reflects this fact
::
:: window styles
:: -16 GWL_STYLE index used to get/set style information
:: 65536 WS_MAXIMIZEBOX used to show/hide the maximize button
:: 131072 WS_MINIMIZEBOX used to show/hide the minimize button
::
:: extended window styles
:: -20 GWL_EXSTYLE index used to get/set extended style information
:: 128 WS_EX_TOOLWINDOW does not appear in the task bar
:: 262144 WS_EX_APPWINDOW default appearance of a console window
::
:: ID to access the close menu item
:: 61536 SC_CLOSE used to delete the close item in the context menu and to gray the X button
::
:: Z order
:: [IntPtr]-1 HWND_TOPMOST place the window above all non-topmost windows and maintain this order
:: [IntPtr]-2 HWND_NOTOPMOST default behavior of a window regarding its Z order
:: 3 SWP_NOSIZE | SWP_NOMOVE do not resize or move the window when SetWindowPos is called