Jump to content

Detect when any and all windows have moved.


jaberwacky
 Share

Go to solution Solved by jaberwacky,

Recommended Posts

Is there a way to register my script to receive messages for when a window is moving or has been moved?  I've used _WinAPI_SetWindowsHookEx and all of the various options in many different combos.  Been at this for a couple of days now.  I've searched this forum and the net too.  Seems like I can only do this for specific programs that I specify?  If that is the case, then I guess I can register and unregister dynamically.  Any ideas?

Edit: guess I should state my intent.  I want to make a Snap feature that works in the middle of where monitors meet if there are more than one.  I know I can use the KB shortcuts and will if that's my only choice.  Here my code so far:

#AutoIt3Wrapper_Run_AU3Check=n
#AutoIt3Wrapper_AU3Check_Parameters=-w 1 -w 2 -w 3 -w 4 -w 6 -w 7 -d -q

#include <WinAPI.au3>

#include <Array.au3>

#include <WindowsConstants.au3>

Const $mouse_proc_callback = DllCallbackRegister(model_ll_mouse_proc, "long", "int;wparam;lparam")

Const $hook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($mouse_proc_callback), _WinAPI_GetModuleHandle(0))
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hook = ' & $hook & @CRLF) ;### Debug Console

Const $half_desktop_width = @DesktopWidth / 2

Const $range = 20

Global $window_focus

Global $left_snap = False

Global $right_snap = False

Global $active = False

Do
  Sleep(100)
Until False

Volatile Func model_ll_mouse_proc($code, $w_param, $l_param)
  Switch $code >= 0
    Case True
      Switch $w_param
        Case $wm_mousemove     
          Switch $active
            Case True
              With DllStructCreate($tagPOINT, $l_param)
                Select
                  Case (.X >= @DesktopWidth - $range) And (.X <= @DesktopWidth)
                    ;ConsoleWrite("Left: " & .X & @CRLF)

                    $left_snap = True
                    
                    $right_snap = False
                    
                  Case (.X >= @DesktopWidth) And (.X <= @DesktopWidth + $range)
                    ;ConsoleWrite("Right: " & .X & @CRLF)  

                    $right_snap = True
                    
                    $left_snap = False
                    
                  Case Else
                    $left_snap = False
                    
                    $right_snap = False
                EndSelect
              EndWith
          EndSwitch
          
        Case $wm_lbuttondown          
          $window_focus = _WinAPI_GetForegroundWindow()
          
          If GetRealParent($window_focus) Then
            
          EndIf
          
          $active = True
          
        Case $wm_lbuttonup   
          Select
            Case $left_snap
              WinMove($window_focus, "", $half_desktop_width, 0, $half_desktop_width, 1080)  

              $left_snap = False
              
              $right_snap = False
              
            Case $right_snap
              WinMove($window_focus, "", @DesktopWidth, $half_desktop_width, $half_desktop_width, 1024)

              $left_snap = False 
              
              $right_snap = False
          EndSelect
          
          $active = False
          
          $window_focus = Null
      EndSwitch
  EndSwitch

  Return _WinAPI_CallNextHookEx($hook, $code, $w_param, $l_param)
EndFunc

Func GetRealParent(Const $hwnd)
  ; http://stackoverflow.com/questions/16872126/the-correct-way-of-getting-the-parent-window
  
  Local Const $hParent = _WinAPI_GetAncestor($hwnd, $GA_PARENT)
  
  If (Not $hParent) Or ($hParent = _WinAPI_GetDesktopWindow()) Then
    Return True
  EndIf

  Return False
EndFunc

Here is the script I used to test setwindowshookex function:

#include <WinAPI.au3>

Const $wnd_proc_callback = DllCallbackRegister(_call_wnd_proc, "long", "int;wparam;lparam")

Const $hook = _WinAPI_SetWindowsHookEx($WH_CALLWNDPROC, DllCallbackGetPtr($wnd_proc_callback), _WinAPI_GetModuleHandle(0))

Do
  Sleep(100)
Until False

Func _call_wnd_proc($code, $w_param, $l_param)
  ConsoleWrite("Heeeeeyyyyy" & @CRLF)
  
  Return _WinAPI_CallNextHookEx($hook, $code, $w_param, $l_param)
EndFunc
Edited by jaberwacky
Link to comment
Share on other sites

I found this thread which does what I want but only for specific windows!  If anyone still knows how to make this happen for all present windows I would be much obliged.

'?do=embed' frameborder='0' data-embedContent>>

Here is the stripped down code from that thread that does what I want:

#include <WinAPI.au3>

Global Const $WM_USER = 0x400
Global Const $WM_MOVE = 0x0003
Global Const $UM_ADDMESSAGE = $WM_USER + 0x100

Global Const $hDll_hook = DllOpen("hook.dll") ; helper dll

Global Const $user32 = DllOpen("User32.dll")

Global Const $hWndLocal = GUICreate("") ; Needed to receive messages

Run("Calc")

WinWaitActive("Calculator")

Global Const $hWndTarget = WinGetHandle("Calculator")

Global Const $iThreadIdTarget = _GetWindowThreadProcessId($hWndTarget)

DllCall($hDll_hook, "int", "InstallFilterDLL", "long", $WH_CALLWNDPROC, "long", $iThreadIdTarget, "hwnd", $hWndTarget) ; 0 = Ok

DllCall($user32, "int", "SendMessage", "hwnd", $hWndTarget, "int", $UM_ADDMESSAGE, "int", $WM_MOVE, "hwnd", $hWndLocal)

GUIRegisterMsg($WM_MOVE, "_Filter")

; Keep running while target exists
While WinExists($hWndTarget)
  Sleep(250)
WEnd

DllCall($hDll_hook, "int", "UnInstallFilterDLL", "long", $iThreadIdTarget, "hwnd", $hWndTarget, "hwnd", $hWndLocal) ; 0 = ok

DllClose($hDll_hook)

DllClose($user32)

Func _Filter(Const $hWndGUI, Const $MsgID, Const $WParam, Const $LParam)
  Switch $MsgID      
    Case $WM_MOVE
      ConsoleWrite("MOVED" & @CRLF)
  EndSwitch
EndFunc

Func _GetWindowThreadProcessId(Const $hWnd)
  Local Const $aRval = DllCall($user32, "dword", "GetWindowThreadProcessId", "hwnd", $hWnd, "dword*", 0)[0]
 
  If @error Then
    Return SetError(@error, @extended, False)
  EndIf
  
  Return $aRval
EndFunc
Edited by jaberwacky
Link to comment
Share on other sites

I found a solution which works well enough for me!

#AutoIt3Wrapper_Run_AU3Check=n
#AutoIt3Wrapper_AU3Check_Parameters=-w 1 -w 2 -w 3 -w 4 -w 6 -w 7 -d -q

#include <WinAPI.au3>

#include <Array.au3>

#include <WindowsConstants.au3>

Global Const $UM_ADDMESSAGE = $WM_USER + 0x100

Global Const $h_dll_hook = DllOpen("hook.dll") ; helper dll

Global Const $user32 = DllOpen("User32.dll")

Global Const $h_wnd_local = GUICreate("") ; Needed to receive messages

Const $mouse_proc_callback = DllCallbackRegister(model_ll_mouse_proc, "long", "int;wparam;lparam")

Const $hook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($mouse_proc_callback), _WinAPI_GetModuleHandle(0))

Const $half_desktop_width = @DesktopWidth / 2

Const $range = 20

Global $id_target

Global $mouse_pos

Global $window_focus

Global $left_snap = False

Global $right_snap = False

OnAutoItExitRegister(_cleanup)

Do
  Sleep(100)
Until False

Volatile Func model_ll_mouse_proc($code, $w_param, $l_param)
  Switch $code >= 0
    Case True
      Switch $w_param
        Case $wm_mousemove         
          $mouse_pos = DllStructCreate($tagPOINT, $l_param)
          
          $mouse_pos.X = Abs($mouse_pos.X)       
          
          $mouse_pos.Y = Abs($mouse_pos.Y)
          
        Case $wm_lbuttondown          
          $window_focus = _WinAPI_GetForegroundWindow()
          
          If GetRealParent($window_focus) Then
            $id_target = _register_message($window_focus)
          EndIf
          
        Case $wm_lbuttonup   
          Select
            Case $left_snap
              WinMove($window_focus, "", $half_desktop_width, 0, $half_desktop_width, 1080)  
              $left_snap  = False              
              $right_snap = False
              
            Case $right_snap
              WinMove($window_focus, "", @DesktopWidth, $half_desktop_width, $half_desktop_width, 1024)
              $left_snap  = False               
              $right_snap = False
          EndSelect
          
          $window_focus = Null
      EndSwitch
  EndSwitch

  Return _WinAPI_CallNextHookEx($hook, $code, $w_param, $l_param)
EndFunc

Func _filter(Const $h_wnd_gui, Const $msg_id, Const $w_param, Const $l_param)
  Switch $msg_id      
    Case $WM_MOVE
      ConsoleWrite("MOVED" & @CRLF)
      
      _uninstall_filter($id_target, $window_focus)
      
    Case $WM_MOVING
      ConsoleWrite("MOVING" & @CRLF)
      
      If ($mouse_pos.X >= @DesktopWidth - $range) And ($mouse_pos.X <= @DesktopWidth) Then
        $left_snap  = True          
        $right_snap = False          
      ElseIf($mouse_pos.X >= @DesktopWidth) And ($mouse_pos.X <= @DesktopWidth + $range) Then
        $right_snap = True          
        $left_snap  = False          
      Else
        $left_snap  = False          
        $right_snap = False
      EndIf
  EndSwitch
EndFunc

Func GetRealParent(Const $hwnd)
  ; http://stackoverflow.com/questions/16872126/the-correct-way-of-getting-the-parent-window
  
  Local Const $hParent = _WinAPI_GetAncestor($hwnd, $GA_PARENT)
  
  If (Not $hParent) Or ($hParent = _WinAPI_GetDesktopWindow()) Then
    Return True
  EndIf

  Return False
EndFunc

Func _register_message(Const $h_target)
  Local Const $thread_id_target = _GetWindowThreadProcessId($h_target)

  DllCall($h_dll_hook, "int", "InstallFilterDLL", "long", $WH_CALLWNDPROC, "long", $thread_id_target, "hwnd", $h_target)

  _SendMessage($h_target, $UM_ADDMESSAGE, $WM_MOVE, $h_wnd_local)

  _SendMessage($h_target, $UM_ADDMESSAGE, $WM_MOVING, $h_wnd_local)

  GUIRegisterMsg($WM_MOVE, "_filter")

  GUIRegisterMsg($WM_MOVING, "_filter")
  
  Return $thread_id_target
EndFunc

Func _GetWindowThreadProcessId(Const $h_wnd)
  Local Const $ret = DllCall($user32, "dword", "GetWindowThreadProcessId", "hwnd", $h_wnd, "dword*", 0)[0]
 
  If @error Then
    Return SetError(@error, @extended, False)
  EndIf
  
  Return $ret
EndFunc

Func _uninstall_filter(Const $thread_id_target, Const $h_wnd_target)
  DllCall($h_dll_hook, "int", "UnInstallFilterDLL", "long", $thread_id_target, "hwnd", $h_wnd_target, "hwnd", $h_wnd_local)
EndFunc

Func _cleanup()
  _uninstall_filter($id_target, $window_focus)

  DllClose($h_dll_hook)

  DllClose($user32)
EndFunc
Edited by jaberwacky
Link to comment
Share on other sites

Sad to say that this remains unsolved.  This code will get the move and moving info for some windows but not all.  If anybody knows a better solution, I'd love to hear it.

#AutoIt3Wrapper_Run_AU3Check=n
#AutoIt3Wrapper_AU3Check_Parameters=-w 1 -w 2 -w 3 -w 4 -w 6 -w 7 -d -q

#include <WinAPI.au3>

#include <Array.au3>

#include <WindowsConstants.au3>

#include <BorderConstants.au3>

Const $UM_ADDMESSAGE = $WM_USER + 0x100

Const $h_dll_hook = DllOpen("hook.dll") ; helper dll

Const $user32 = DllOpen("User32.dll")

Const $h_wnd_local = GUICreate("") ; Needed to receive messages

Const $mouse_proc_callback = DllCallbackRegister(_ll_mouse_proc, "long", "int;wparam;lparam")

Const $hook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($mouse_proc_callback), _WinAPI_GetModuleHandle(0))

Const $half_desktop_width = @DesktopWidth / 2

Const $h_dc = _WinAPI_GetWindowDC(0)
Const $h_pen = _WinAPI_CreatePen($PS_SOLID, $half_desktop_width, 0x0000FF)
Const $o_orig = _WinAPI_SelectObject($h_dc, $h_pen)

Local $s_rect = DllStructCreate($tagRECT)

Const $range = 20

Global $id_target

Global $mouse_pos

Global $window_focus

Global $left_snap = False

Global $right_snap = False

OnAutoItExitRegister(_cleanup)

Do
  Sleep(100)
Until False

Volatile Func _ll_mouse_proc($code, $w_param, $l_param)
  Switch $code >= 0
    Case True
      Switch $w_param
        Case $wm_lbuttondown          
          $window_focus = _WinAPI_GetForegroundWindow()
          ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $window_focus = ' & $window_focus & @CRLF) ;### Debug Console
          
          If _get_real_parent($window_focus) Then
            $id_target = _register_message($window_focus)
          EndIf        
      EndSwitch
  EndSwitch

  Return _WinAPI_CallNextHookEx($hook, $code, $w_param, $l_param)
EndFunc

Func _filter(Const $h_wnd_gui, Const $msg_id, Const $w_param, Const $l_param)
  Switch $msg_id      
    Case $WM_MOVE      
      ConsoleWrite("Move" & @CRLF)
      
      Select
        Case $left_snap
          WinMove($window_focus, "", $half_desktop_width, 0, $half_desktop_width, 1080) 
          
        Case $right_snap
          WinMove($window_focus, "", @DesktopWidth, $half_desktop_width, $half_desktop_width, 1024)
      EndSelect
      
      $left_snap  = False          
      $right_snap = False
      
      $window_focus = Null
      
      _uninstall_filter($id_target, $window_focus)
      
    Case $WM_MOVING  
      ConsoleWrite("Moving" & @CRLF)
      
      Local Const $mouse_pos = MouseGetPos(0)
      
      Select
        Case ($mouse_pos >= (@DesktopWidth - $range)) And ($mouse_pos <= @DesktopWidth)
          ConsoleWrite("Left" & @CRLF)
          
          With $s_rect
            .Left   = $half_desktop_width
            .Right  = 0
            .Top    = $half_desktop_width
            .Bottom = 1080
          EndWith
          
          _WinAPI_DrawEdge($h_dc, DllStructGetPtr($s_rect), $BDR_RAISEDINNER, $BF_RECT)

          $left_snap  = True          
          $right_snap = False  
        
        Case ($mouse_pos >= @DesktopWidth) And ($mouse_pos <= (@DesktopWidth + $range))
          ConsoleWrite("Right" & @CRLF)
          
          $right_snap = True          
          $left_snap  = False  
          
        Case Else
          $left_snap  = False          
          $right_snap = False
      EndSelect
  EndSwitch
EndFunc

Func _get_real_parent(Const $hwnd)
  ; http://stackoverflow.com/questions/16872126/the-correct-way-of-getting-the-parent-window
  
  Local Const $hParent = _WinAPI_GetAncestor($hwnd, $GA_PARENT)
  
  If (Not $hParent) Or ($hParent = _WinAPI_GetDesktopWindow()) Then
    Return True
  EndIf

  Return False
EndFunc

Func _register_message(Const $h_target)
  Local Const $thread_id_target = DllCall($user32, "dword", "GetWindowThreadProcessId", "hwnd", $h_target, "dword*", 0)[0]

  DllCall($h_dll_hook, "int", "InstallFilterDLL", "long", $WH_CALLWNDPROC, "long", $thread_id_target, "hwnd", $h_target)

  _SendMessage($h_target, $UM_ADDMESSAGE, $WM_MOVE, $h_wnd_local)

  _SendMessage($h_target, $UM_ADDMESSAGE, $WM_MOVING, $h_wnd_local)

  GUIRegisterMsg($WM_MOVE, "_filter")

  GUIRegisterMsg($WM_MOVING, "_filter")
  
  Return $thread_id_target
EndFunc

Func _uninstall_filter(Const $thread_id_target, Const $h_wnd_target)
  DllCall($h_dll_hook, "int", "UnInstallFilterDLL", "long", $thread_id_target, "hwnd", $h_wnd_target, "hwnd", $h_wnd_local)
EndFunc

Func _cleanup()
  _uninstall_filter($id_target, $window_focus)

  _WinAPI_SelectObject($h_dc, $o_orig)
  _WinAPI_DeleteObject($h_pen)
  _WinAPI_ReleaseDC(0, $h_dc)

  DllClose($h_dll_hook)

  DllClose($user32)
EndFunc
Edited by jaberwacky
Link to comment
Share on other sites

  • Solution

Ok!  Yes.  I think this will work. Thank you everybody.

#include <APISysConstants.au3>
#include <GUIMenu.au3>
#include <WinAPIProc.au3>
#include <WinAPISys.au3>
#include <WindowsConstants.au3>

Global $hEventProc = DllCallbackRegister(_EventProc, "none", "ptr;dword;hwnd;long;long;dword;dword")

Global $hEventHook = _WinAPI_SetWinEventHook($EVENT_MIN, $EVENT_MAX, DllCallbackGetPtr($hEventProc))

OnAutoItExitRegister(OnAutoItExit)

Global $iPid = Run(@SystemDir & "\notepad.exe")

While ProcessExists($iPid)
  Sleep(1000)
WEnd

Func OnAutoItExit()
  _WinAPI_UnhookWinEvent($hEventHook)

  DllCallbackFree($hEventProc)
EndFunc

Func _EventProc($hEventHook, $iEvent, $hWnd, $iObjectID, $iChildID, $iThreadId, $iEventTime)
  #forceref $hEventHook, $iObjectID, $iChildID, $iThreadId, $iEventTime

  Switch $iEvent
    Case $EVENT_SYSTEM_MOVESIZEEND
      ConsoleWrite("End" & @CRLF)
      
    Case $EVENT_SYSTEM_MOVESIZESTART
      ConsoleWrite("Start" & @CRLF)
  EndSwitch
EndFunc
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...