Swap windows between monitors - Windows 11 bug [Workaround found]

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
AntoineBK
Posts: 27
Joined: 12 Feb 2022, 10:48

Swap windows between monitors - Windows 11 bug [Workaround found]

18 Oct 2022, 09:47

I am using the following script (and see below) to swap windows between 2 monitors.

It worked fine on Windows 10, but I can't make it work properly on Windows 11.

I think the problem comes from the WinMove function (last line of script) : It fails to swap some windows only when they are maximized (no problem for windows that are not fullscreen). For example, it does not work with Adobe Acrobat, notepad, Firefox, file explorer when they are maximized. It works fine with a lot of other softwares.

The window is moved to the other screen, but only the border is visible (the inside is either "full" or "transparent" and we can't interact with the software that has been swapped, until we swap it again to its previous location).

Code: Select all

; MonSwap - Swaps all the application windows from one monitor to another.
; v1.0.2
; Author: Alan Henager
;
; v1.0.1 - xenrik - Updated to use relative screen size when swapping
; v1.0.2 - boiler, masgo - exclude Windows 10 secondary monitor taskbar from being swapped

;#NoTrayIcon

SetWinDelay, 0 ; This switching should be instant

; Set this key combination to whatever.
^#Numpad0::
SwapAll:
{
  DetectHiddenWindows, Off ; I think this is default, but just for safety's sake...
  WinGet, WinArray, List ; , , , Sharp
  ; Enable the above commented out portion if you are running SharpE

  i := WinArray
  Loop, %i% {
     WinID := WinArray%A_Index%
	 WinGetClass, ThisClass, ahk_id %WinID%
	 if (ThisClass = "Shell_SecondaryTrayWnd") ; do not swap the secondary monitor taskbar
	 	continue
     WinGetTitle, CurWin, ahk_id %WinID%
     If (CurWin = ) ; For some reason, CurWin <> didn't seem to work.
     {}
     else
     {
        WinGet, IsMin, MinMax, ahk_id %WinID% ; The window will re-locate even if it's minimized
        If (IsMin = -1) {
           ;WinRestore, ahk_id %WinID%
           ;SwapMon(WinID)
           ;WinMinimize, ahk_id %WinID%
        } else {
           SwapMon(WinID)
        }
     }
  }
  return
}

SwapMon(WinID) ; Swaps window with and ID of WinID onto the other monitor
{
  SysGet, Mon1, Monitor, 1
  Mon1Width := Mon1Right - Mon1Left
  Mon1Height := Mon1Bottom - Mon1Top

  SysGet, Mon2, Monitor, 2
  Mon2Width := Mon2Right - Mon2Left
  Mon2Height := Mon2Bottom - Mon2Top

  WinGetPos, WinX, WinY, WinWidth, WinHeight, ahk_id %WinID%
  WinCenter := WinX + (WinWidth / 2)
  if (WinCenter >= Mon1Left and WinCenter <= Mon1Right) {

	NewX := (WinX - Mon1Left) / Mon1Width
    NewX := Mon2Left + (Mon2Width * NewX)

    NewWidth := WinWidth / Mon1Width
    NewWidth := Mon2Width * NewWidth

    NewY := (WinY - Mon1Top) / Mon1Height
    NewY := Mon2Top + (Mon2Height * NewY)

    NewHeight := WinHeight / Mon1Height
    NewHeight := Mon2Height * NewHeight

 } else {
    NewX := (WinX - Mon2Left) / Mon2Width
    NewX := Mon1Left + (Mon1Width * NewX)

    NewWidth := WinWidth / Mon2Width
    NewWidth := Mon1Width * NewWidth

    NewY := (WinY - Mon2Top) / Mon2Height
    NewY := Mon1Top + (Mon1Height * NewY)

    NewHeight := WinHeight / Mon2Height
    NewHeight := Mon1Height * NewHeight
  }

  WinMove, ahk_id %WinID%, , %NewX%, %NewY%, %NewWidth%, %NewHeight%
  return
}
EDIT : Here is what I get when I swap notepad or a file explorer window :

Image (it looks empty but at the top right you can see the window icons).
Last edited by AntoineBK on 19 Oct 2022, 07:48, edited 1 time in total.
AntoineBK
Posts: 27
Joined: 12 Feb 2022, 10:48

Re: Swap windows between monitors - Windows 11 bug

18 Oct 2022, 11:28

Ok, I found a *dirty* workaround, but it works fine. If a window is maximized, I just do the following : WinRestore > WinMove > WinMaximize (instead of only WinMove which does not work properly sometimes) :angel:

At the beginning of the script (after line 8 for example), declare :

Code: Select all

global isMin
And at the end of the script (line 82), replace the WinMove line by the following :

Code: Select all

   If (IsMin = 1) {
      WinRestore, ahk_id %WinID%
      WinMove, ahk_id %WinID%, , %NewX%, %NewY%, %NewWidth%, %NewHeight%
      WinMaximize, ahk_id %WinID%
   } else {
      WinMove, ahk_id %WinID%, , %NewX%, %NewY%, %NewWidth%, %NewHeight%
   }
If someone has a more elegant solution, please let me know !

Please find below the script with the aforementioned modifications :

Code: Select all

; MonSwap - Swaps all the application windows from one monitor to another.
; v1.0.2
; Author: Alan Henager
;
; v1.0.1 - xenrik - Updated to use relative screen size when swapping
; v1.0.2 - boiler, masgo - exclude Windows 10 secondary monitor taskbar from being swapped

;#NoTrayIcon

SetWinDelay, 0 ; This switching should be instant
global isMin

; Set this key combination to whatever.
^#Numpad0::
SwapAll:
{
  DetectHiddenWindows, Off ; I think this is default, but just for safety's sake...
  WinGet, WinArray, List ; , , , Sharp
  ; Enable the above commented out portion if you are running SharpE

  i := WinArray
  Loop, %i% {
     WinID := WinArray%A_Index%
	 WinGetClass, ThisClass, ahk_id %WinID%
	 if (ThisClass = "Shell_SecondaryTrayWnd") ; do not swap the secondary monitor taskbar
	 	continue
     WinGetTitle, CurWin, ahk_id %WinID%
     If (CurWin = ) ; For some reason, CurWin <> didn't seem to work.
     {}
     else
     {
        WinGet, IsMin, MinMax, ahk_id %WinID% ; The window will re-locate even if it's minimized
        If (IsMin = -1) {
           ;WinRestore, ahk_id %WinID%
           ;SwapMon(WinID)
           ;WinMinimize, ahk_id %WinID%
        } else {
           SwapMon(WinID)
        }
     }
  }
  return
}

SwapMon(WinID) ; Swaps window with and ID of WinID onto the other monitor
{
  SysGet, Mon1, Monitor, 1
  Mon1Width := Mon1Right - Mon1Left
  Mon1Height := Mon1Bottom - Mon1Top

  SysGet, Mon2, Monitor, 2
  Mon2Width := Mon2Right - Mon2Left
  Mon2Height := Mon2Bottom - Mon2Top

  WinGetPos, WinX, WinY, WinWidth, WinHeight, ahk_id %WinID%
  WinCenter := WinX + (WinWidth / 2)
  if (WinCenter >= Mon1Left and WinCenter <= Mon1Right) {

	NewX := (WinX - Mon1Left) / Mon1Width
    NewX := Mon2Left + (Mon2Width * NewX)

    NewWidth := WinWidth / Mon1Width
    NewWidth := Mon2Width * NewWidth

    NewY := (WinY - Mon1Top) / Mon1Height
    NewY := Mon2Top + (Mon2Height * NewY)

    NewHeight := WinHeight / Mon1Height
    NewHeight := Mon2Height * NewHeight

 } else {
    NewX := (WinX - Mon2Left) / Mon2Width
    NewX := Mon1Left + (Mon1Width * NewX)

    NewWidth := WinWidth / Mon2Width
    NewWidth := Mon1Width * NewWidth

    NewY := (WinY - Mon2Top) / Mon2Height
    NewY := Mon1Top + (Mon1Height * NewY)

    NewHeight := WinHeight / Mon2Height
    NewHeight := Mon1Height * NewHeight
  }

   If (IsMin = 1) {
      WinRestore, ahk_id %WinID%
      WinMove, ahk_id %WinID%, , %NewX%, %NewY%, %NewWidth%, %NewHeight%
      WinMaximize, ahk_id %WinID%
   } else {
      WinMove, ahk_id %WinID%, , %NewX%, %NewY%, %NewWidth%, %NewHeight%
   }


return

}
:!: Beware :!: Compared with the original script, I commented the lines 34 to 36, because I don't need them ! So pay attention if you need it.

The only inconvenient with this workaround is that the original script (with lines 34 to 36 commented) made the swap instantly. Now, we see the windows moving, even if it's subtle. Hence, one may want to find a more elegant solution.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], Rohwedder, sachalamp and 95 guests