'Window on top' button (SetWinEventHook)

Post your working scripts, libraries and tools
drozdman
Posts: 68
Joined: 05 Dec 2015, 01:07

'Window on top' button (SetWinEventHook)

Post by drozdman » 19 Jul 2018, 19:27

I used to use FileBox eXtender (an old discontinued program) mainly for the window-on-top button on Vista. Unfortunately it wasn't working for me on Win 7 nor on any newer version of Windows.
So in order to have a window-on-top button after being gradually forced off Vista (Firefox) I had to make this script using SetWinEventHook (EWinHook_SetWinEventHook function by cyruz) and +Owner option.
It's easier to do it with parent option (+Parent or Dllcall), child windows and ShellHook like in the Windows Explorer toolbar script, but in that case the button can't be on the title bar. So the more complicated option is necessary.

Weirdly, when searching regedit.exe, the script processor usage goes high. It's obviously the SetWinEventHook-related issue, but I'm not sure why exactly. So it may need some improvements.


● choose "On Top" switch icon with: onTopPinIcon:= (1 or 2)
● choose transparent: transpGUI:=1
● for slow starting windows click on title bar to show the "On Top" switch icon

Code: Select all


#SingleInstance force
#NoEnv
;#NoTrayIcon
/*  Widnow on top button
	● choose "On Top" switch icon with: onTopPinIcon:= (1 or 2) 
	● choose transparent: transpGUI:=1 	
	● for slow starting windows click on title bar to show the "On Top" switch icon	
*/

FileGetVersion, ver, wmploc.dll ;C:\WINDOWS\System32\wmploc.dll
RegExMatch(ver,"(\d+)\.\d+\.(\d+)", out) ;new 12.0.10240.16384 
new_wmploc:=(out1>=10 && out2>=8000) 
global onTop_ico:=new_wmploc ? 13 : 17
Menu, Tray, Icon, wmploc.dll, %onTop_ico%

Menu, Tray, Add, Exit , Exit 
Menu, Tray, Default, Exit 

Menu, ContextMenu, Add, Remove this, destroyGui
Menu, ContextMenu, Add, Info-this, Info_this
Menu, ContextMenu, Add,
Menu, ContextMenu, Add, Restart, Reload
Menu, ContextMenu, Add, Exit, Exit
Menu, ContextMenu, Icon, Exit, Shell32.dll, 132

SetWinDelay, -1
SetControlDelay, -1
SetBatchLines, -1


;========================

global onTopPinIcon:=2 ;pin: 1 or 2
global transpGUI:=0

global exclude_listClass:="#32770"  ; exclude class , on top button- NO 
global exclude_list:= "StikyNot.exe,regedit.exe" ; exclude processes, on top button - NO
global not_exclude_list:="taskmgr.exe,AU3_Spy.exe,SndVol.exe" ; on top button - YES ; processes from excluded class (Windows Task Manager)

;========================

global rel_X:=138, rel_Y:=4

if RegExMatch(A_OSVersion,"WIN_VISTA|WIN_7")
	rel_X:=130, rel_Y:=2


;=== click title bar
TCAPTION:=2	; title bar
WM_NCHITTEST := 0x0084
;===


EventHook := EWinHook_SetWinEventHook("EVENT_OBJECT_CREATE", "EVENT_OBJECT_LOCATIONCHANGE", 0, "WinProcCallback", 0, 0, "WINEVENT_OUTOFCONTEXT")
	
	OnExit(Func("ExitFunc").Bind(EventHook))

	global WS_CAPTION:=0x00C00000 , WS_BORDER:=0x00800000 , WS_SYSMENU:=0x00080000
	global WS_CHILD:=0x40000000 , WS_EX_TOOLWINDOW:=0x00000080 , WS_EX_TOPMOST := 0x00000008
	global WS_EX_NOACTIVATE:=0x08000000
	
global win_list:= Object()	
start()
;SetTimer, check_, 3000
OnMessage(0x201, "WM_LBUTTONDOWN") 
OnMessage(0x204, "WM_RBUTTONDOWN")
return


;========================================


WM_LBUTTONDOWN(){
	global win_list
	;MouseGetPos,,,Win_id, control
	if(A_Gui){
		for k,v in win_list {
			if(win_list[k].button=A_Gui){
				ownerId:=k, ctrl:=win_list[k].ctrl
				windowOnTop(ownerId,A_Gui,ctrl)
				return 0
			}
		}
	}	
}


WM_RBUTTONDOWN(){
  global last_Gui
	if(A_Gui){
		Gosub, Menu_
    last_Gui:=A_Gui
  }
}


start(){
	global win_list
	WinGet, List_, List,,, Program Manager 
	Loop, %List_%  {
		id:=List_%A_Index%
		checkWindow(id)
	}
}

checkWindow(id){
		global win_list 
		id:=Format("0x{1:x}", id) 

		if !DllCall("IsWindowVisible", "UInt", id)
			return
		if !DllCall("IsWindow", "UInt", id)
			return
		
		if(win_list.HasKey(id))
			return
		WinGet, pname, ProcessName,ahk_id %id%  
		WinGetClass, class_, ahk_id %id%  
		;WinGetTitle, Title, ahk_id %id% 
		WinGet, Style, Style, ahk_id %id%
		WinGet, ExStyle, ExStyle, ahk_id %id%
		cap:=(Style & WS_CAPTION), border:=(Style & WS_BORDER), sysmen:=(Style & WS_SYSMENU)
		child:=(Style & WS_CHILD), toolwin:=(ExStyle & WS_EX_TOOLWINDOW)	
			if(!cap || !border || !sysmen)
				return 
			if(child || toolwin)
				return
			
			if((InStr(exclude_listClass,class_) || InStr(exclude_list,pname)) && !InStr(not_exclude_list,pname)) ; dialogs, 'Save As', msg box
				return	

		GuiHwnd:=make_Gui(id)	

}



make_Gui(ownerId){
	global win_list, rel_X, rel_Y
	Gui, New, +ToolWindow -caption 	+HwndGuiHwnd 
	static G_color:="999999" 
	Gui, %GuiHwnd%: Color,%G_color%	
	Gui, %GuiHwnd%: +Owner%ownerId%
	
	WinGet, ExStyle, ExStyle, ahk_id %ownerId%
	if(ExStyle & WS_EX_TOPMOST){
		Gui, %GuiHwnd%: +AlwaysOnTop
		Gui, %GuiHwnd%: Add, Picture, x1 y0 w16 h16  +HwndonTopHwnd BackgroundTrans Icon%onTop_ico% AltSubmit, wmploc.dll
	}else{
		Gui, %GuiHwnd%: Add, Picture, x2 y0 w16 h16  +HwndonTopHwnd BackgroundTrans Icon247 AltSubmit, shell32.dll 
	}
	

	WinGetPos, x1,y1,w1,h1, ahk_id %ownerId%
	x:= (w1>300) ?  x1 + w1-rel_X : x1 + w1-120
	;x:=x1+ w1-rel_X
	y:=y1+rel_Y
	if(x && y){
		;Gui, %GuiHwnd%: Show, x%x% y%y% h16 w18 NA, DrozdTool ;NA NoActivate
		;WinActivate, ahk_id %ownerId%	
	

		if(ExStyle & WS_EX_TOPMOST){
			DllCall("SetWindowPos", "UInt",GuiHwnd , "Int",-1, "Int",x, "Int",y, "Int",18, "Int",16, "UInt",0x0254 ) 		
			DllCall("ShowWindow", "UInt", GuiHwnd, "Int", 8)
		}else{
			DllCall("SetWindowPos", "UInt",GuiHwnd , "Int",-2, "Int",x, "Int",y, "Int",18, "Int",16, "UInt",0x0254 )
			DllCall("ShowWindow", "UInt", GuiHwnd, "Int", 8) ; SW_SHOWNOACTIVATE:=4, SW_SHOWNA:=8
		}
		;WinActivate, ahk_id %ownerId%	
	}
		
	WinSet, ExStyle, %WS_EX_NOACTIVATE%, ahk_id %GuiHwnd%		
	
	if(transpGUI){
		WinSet, TransColor, %G_color%, ahk_id %GuiHwnd% ; transparent Gui
	}else{
		Winset, Transparent,200, ahk_id %GuiHwnd%
	}
	;WinSet, Region, 0-0 18-0 18-14 16-16 2-16 0-14 , ahk_id %GuiHwnd% 
	
	win_list[ownerId]:=Object()
	win_list[ownerId].button:=GuiHwnd
	win_list[ownerId].ctrl:=onTopHwnd
	return GuiHwnd
}



destroyButton(hwnd){
	global win_list, show_temp
	GuiHwnd:=win_list[hwnd].button
	GuiHwnd:=Format("0x{1:x}", GuiHwnd)
	if(WinExist("ahk_id " GuiHwnd)){	
		show_temp:= "`nGuiHwnd= " GuiHwnd "`nIsWindow= " DllCall("IsWindow", "UInt", GuiHwnd) "`nWin= " hwnd
		Winset, AlwaysOnTop, Off, ahk_id %GuiHwnd%
		if(WinExist("ahk_id " GuiHwnd))
			Gui, %GuiHwnd%: Destroy		
		;WinClose, ahk_id %GuiHwnd%
	}
		win_list.Delete(hwnd)
}



;================================================

WinProcCallback(hWinEventHook, event, hwnd){
	Critical
	global win_list, rel_X,rel_Y
	static x0, y0
	if !hwnd
    return
	event:=Format("0x{1:x}",event) ; decimal to hexadecimal
	hwnd:=Format("0x{1:x}",hwnd)
	if event not in 0x8000,0x8001,0x800B  ;EVENT_OBJECT_CREATE:= 0x8000, EVENT_OBJECT_DESTROY:= 0x8001, EVENT_OBJECT_LOCATIONCHANGE:= 0x800B
     return

	if(event=0x8000){ ; new window
			fn:=Func("checkWindow").Bind(hwnd)
			SetTimer, %fn% , -600	
/*  			
			fn:=Func("checkWindow").Bind(hwnd)
			fn2:=Func("checkWindow").Bind(hwnd)
			SetTimer, %fn% , -600
			SetTimer, %fn2% , -5000	 ; re-check (slow starting windows)
			 */
	}else if(event=0x8001){ ; window closed
		 destroyButton(hwnd)
		
	}else if(event=0x800B){ ; move, re-size	

		if(win_list.HasKey(hwnd)){ 
			GuiHwnd:=win_list[hwnd].button
			WinGetPos, x1, y1,w1,h1, ahk_id %hwnd%

			y:=y1+rel_Y, x:= (w1>300) ?  x1 + w1-rel_X : x1 + w1-120
			if(x!=x0 || y!=y0){
				WinMove, ahk_id %GuiHwnd%,, %x%, %y%
			}
			x0:=y, y0:=x
		}	
	}		
}


;======================================

EWinHook_SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwflags) {
		Critical
    Static S_OK                              := 0x00000000, S_FALSE                           := 0x00000001
         , RPC_E_CHANGED_MODE                := 0x80010106, E_INVALIDARG                      := 0x80070057
         , E_OUTOFMEMORY                     := 0x8007000E, E_UNEXPECTED                      := 0x8000FFFF
         , EVENT_MIN                         := 0x00000001, EVENT_MAX                         := 0x7FFFFFFF
         , EVENT_SYSTEM_SOUND                := 0x0001,     EVENT_SYSTEM_ALERT                := 0x0002
         , EVENT_SYSTEM_FOREGROUND           := 0x0003,     EVENT_SYSTEM_MENUSTART            := 0x0004
         , EVENT_SYSTEM_MENUEND              := 0x0005,     EVENT_SYSTEM_MENUPOPUPSTART       := 0x0006
         , EVENT_SYSTEM_MENUPOPUPEND         := 0x0007,     EVENT_SYSTEM_CAPTURESTART         := 0x0008
         , EVENT_SYSTEM_CAPTUREEND           := 0x0009,     EVENT_SYSTEM_MOVESIZESTART        := 0x000A
         , EVENT_SYSTEM_MOVESIZEEND          := 0x000B,     EVENT_SYSTEM_CONTEXTHELPSTART     := 0x000C
         , EVENT_SYSTEM_CONTEXTHELPEND       := 0x000D,     EVENT_SYSTEM_DRAGDROPSTART        := 0x000E
         , EVENT_SYSTEM_DRAGDROPEND          := 0x000F,     EVENT_SYSTEM_DIALOGSTART          := 0x0010
         , EVENT_SYSTEM_DIALOGEND            := 0x0011,     EVENT_SYSTEM_SCROLLINGSTART       := 0x0012
         , EVENT_SYSTEM_SCROLLINGEND         := 0x0013,     EVENT_SYSTEM_SWITCHSTART          := 0x0014
         , EVENT_SYSTEM_SWITCHEND            := 0x0015,     EVENT_SYSTEM_MINIMIZESTART        := 0x0016
         , EVENT_SYSTEM_MINIMIZEEND          := 0x0017,     EVENT_SYSTEM_DESKTOPSWITCH        := 0x0020
         , EVENT_SYSTEM_END                  := 0x00FF,     EVENT_OEM_DEFINED_START           := 0x0101
         , EVENT_OEM_DEFINED_END             := 0x01FF,     EVENT_UIA_EVENTID_START           := 0x4E00
         , EVENT_UIA_EVENTID_END             := 0x4EFF,     EVENT_UIA_PROPID_START            := 0x7500
         , EVENT_UIA_PROPID_END              := 0x75FF,     EVENT_CONSOLE_CARET               := 0x4001
         , EVENT_CONSOLE_UPDATE_REGION       := 0x4002,     EVENT_CONSOLE_UPDATE_SIMPLE       := 0x4003
         , EVENT_CONSOLE_UPDATE_SCROLL       := 0x4004,     EVENT_CONSOLE_LAYOUT              := 0x4005
         , EVENT_CONSOLE_START_APPLICATION   := 0x4006,     EVENT_CONSOLE_END_APPLICATION     := 0x4007
         , EVENT_CONSOLE_END                 := 0x40FF,     EVENT_OBJECT_CREATE               := 0x8000
         , EVENT_OBJECT_DESTROY              := 0x8001,     EVENT_OBJECT_SHOW                 := 0x8002
         , EVENT_OBJECT_HIDE                 := 0x8003,     EVENT_OBJECT_REORDER              := 0x8004
         , EVENT_OBJECT_FOCUS                := 0x8005,     EVENT_OBJECT_SELECTION            := 0x8006
         , EVENT_OBJECT_SELECTIONADD         := 0x8007,     EVENT_OBJECT_SELECTIONREMOVE      := 0x8008
         , EVENT_OBJECT_SELECTIONWITHIN      := 0x8009,     EVENT_OBJECT_STATECHANGE          := 0x800A
         , EVENT_OBJECT_LOCATIONCHANGE       := 0x800B,     EVENT_OBJECT_NAMECHANGE           := 0x800C
         , EVENT_OBJECT_DESCRIPTIONCHANGE    := 0x800D,     EVENT_OBJECT_VALUECHANGE          := 0x800E
         , EVENT_OBJECT_PARENTCHANGE         := 0x800F,     EVENT_OBJECT_HELPCHANGE           := 0x8010
         , EVENT_OBJECT_DEFACTIONCHANGE      := 0x8011,     EVENT_OBJECT_ACCELERATORCHANGE    := 0x8012
         , EVENT_OBJECT_INVOKED              := 0x8013,     EVENT_OBJECT_TEXTSELECTIONCHANGED := 0x8014
         , EVENT_OBJECT_CONTENTSCROLLED      := 0x8015,     EVENT_SYSTEM_ARRANGMENTPREVIEW    := 0x8016
         , EVENT_OBJECT_END                  := 0x80FF,     EVENT_AIA_START                   := 0xA000
         , EVENT_AIA_END                     := 0xAFFF,     WINEVENT_OUTOFCONTEXT             := 0x0000
         , WINEVENT_SKIPOWNTHREAD            := 0x0001,     WINEVENT_SKIPOWNPROCESS           := 0x0002
         , WINEVENT_INCONTEXT                := 0x0004 


    ; eventMin/eventMax check
    If ( !%eventMin% || !%eventMax% )
        Return 0
    ; dwflags check
    If ( !RegExMatch( dwflags
                    , "S)^\s*(WINEVENT_(?:INCONTEXT|OUTOFCONTEXT))\s*\|\s*(WINEVENT_SKIPOWN(?:PROCESS|"
                    . "THREAD))[^\S\n\r]*$|^\s*(WINEVENT_(?:INCONTEXT|OUTOFCONTEXT))[^\S\n\r]*$"
                    , dwfArray ) )
        Return 0
    dwflags := (dwfArray1 && dwfArray2) ? %dwfArray1% | %dwfArray2% : %dwfArray3%
        
    nCheck := DllCall( "CoInitialize", "Ptr",0       )
              DllCall( "SetLastError", "UInt",nCheck ) ; SetLastError in case of success/error
              
    If ( nCheck == E_INVALIDARG || nCheck == E_OUTOFMEMORY ||  nCheck == E_UNEXPECTED )
        Return -1
    
    If ( isFunc(lpfnWinEventProc) )
        lpfnWinEventProc := RegisterCallback(lpfnWinEventProc)
        
    hWinEventHook := DllCall( "SetWinEventHook", "UInt",%eventMin%, "UInt",%eventMax%, "Ptr",hmodWinEventProc
                                               , "Ptr",lpfnWinEventProc, "UInt",idProcess, "UInt",idThread, "UInt",dwflags )
    Return (hWinEventHook) ? hWinEventHook : 0
}
; https://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx
; https://autohotkey.com/boards/viewtopic.php?t=830 cyruz

EWinHook_UnhookWinEvent(hWinEventHook) {
    DllCall("UnhookWinEvent", "Ptr",hWinEventHook)
    DllCall("CoUninitialize")
}



;================================================


windowOnTop(window, button,ctrl){
	WinGet, ExStyle, ExStyle, ahk_id %window%
	if(ExStyle & WS_EX_TOPMOST){
		
/* 		
		Winset, AlwaysOnTop, off, ahk_id %window%		
		 */
		 
		DllCall("SetWindowPos", "UInt",window, "Int",-2, "Int",, "Int",, "Int",, "Int",, "UInt",0x0003 ) ;HWND_NOTOPMOST-2
		;DllCall("SetWindowPos", "UInt",button, "Int",-2, "Int",, "Int",, "Int",, "Int",, "UInt",0x0213 )
		
		GuiControl,%button%:, %ctrl%  ,*icon247 *w16 *h16 shell32.dll 		
		ControlMove,, 2, , ,, ahk_id %ctrl%
	}else{
		
/* 		WinSet, AlwaysOnTop, on, ahk_id %window%
			WinSet, AlwaysOnTop, on, ahk_id %button%
		 */
		DllCall("SetWindowPos", "UInt",window, "Int",-1, "Int",, "Int",, "Int",, "Int",, "UInt",0x0003 ) ;HWND_TOPMOST =-1
		DllCall("SetWindowPos", "UInt",button, "Int",-1, "Int",, "Int",, "Int",, "Int",, "UInt",0x0213 ) 
		

			
		if(onTopPinIcon=2){
			GuiControl,%button%:, %ctrl%  ,*icon%onTop_ico% *w16 *h16 wmploc.dll ; Icon%onTop_ico% AltSubmit, wmploc.dll
			ControlMove,,1, , ,, ahk_id %ctrl%
		}else{
			GuiControl,%button%:, %ctrl%  ,*icon248 *w16 *h16 shell32.dll ; 248 ontop
		}

	}
		;WinActivate, ahk_id %window%		
} 

		;SetWindowPos ;SWP_NOZORDER:=0x0004 , SWP_NOACTIVATE:=0x0010	, SWP_NOOWNERZORDER:=0x0200, SWP_SHOWWINDOW:=0x0040
		;SWP_NOMOVE =0x0002;SWP_NOSIZE=0x0001
;================================================



;=== click title bar
~LButton::
	MouseGetPos,x, y, WinID_, control
	
	if(InStr(control,"MSTaskListWClass")) ; Shell_TrayWnd MSTaskListWClass1
		return
	if(win_list.HasKey(WinID_))
		return
	
	SendMessage 0x0084 , 0, (y << 16) + x , ,ahk_id %WinID_% ; WM_NCHITTEST
	NCHI_:=ErrorLevel
	if(NCHI_= TCAPTION){
		checkWindow(WinID_)		
	}
return

;================================================


check_:
	for k,v in win_list	{
	 if(!WinExist("ahk_id " k)){	
			GuiHwnd:=win_list[k].button
			if(WinExist("ahk_id " GuiHwnd)){
				Gui, %GuiHwnd%: Destroy
				ToolTip_("err",1)
			}
				win_list.Delete(k)			
		}
	}

return


restoreAll(){
	for k,v in win_list	{	
		Winset, AlwaysOnTop, Off, ahk_id %k%
	}
}

ExitFunc(hWinEventHook){
	EWinHook_UnhookWinEvent(hWinEventHook)
	;restoreAll()
}



;================================================
GuiContextMenu:
  Menu, ContextMenu, Show, %A_GuiX%, %A_GuiY%
return

Reload:
Reload
return

destroyGui:
	if(last_Gui){
		Gui, %last_Gui%: Destroy
		for k,v in win_list {
			if(win_list[k].button=last_Gui)
				Winset, AlwaysOnTop, Off, ahk_id %k%			
		}	
	}
return
		
Info_this:
	if(last_Gui){
		for k,v in win_list {
			if(win_list[k].button=last_Gui){
				WinGetTitle, Title, ahk_id %k% 
				WinGet, pname, ProcessName,ahk_id %k%
				WinGetClass, class_, ahk_id %k%  				
				MsgBox,,, %  pname ", id:"  Format("0x{1:x}", k) "`n" Title "`n" class_ "`n" A_Gui "`n" , 11  
			}
		}		
	}
return


Menu_:
;ToolTip_(A_Gui,t:=2)
return


;~+^h:: Gosub, test2
;~+^g:: Gosub, test


test2:
		WinGet, Win_id, ID, A
		WinGetClass, class_, ahk_id %Win_id%  
		WinGet, Style, Style, ahk_id %Win_id%
		WinGet, ExStyle, ExStyle, ahk_id %Win_id%
		cap:=(Style & WS_CAPTION), border:=(Style & WS_BORDER), sysmen:=(Style & WS_SYSMENU)
		child:=(Style & WS_CHILD), toolwin:=(ExStyle & WS_EX_TOOLWINDOW)
		MsgBox,,, % Style "`ntitle bar= " cap "`n" "border= " border  " , WS_SYSMENU= " sysmen "`nWS_CHILD= " child " , TOOLWINDOW= " toolwin  "`n" "on top= " (ExStyle & WS_EX_TOPMOST)
 
 		MsgBox,,, %	Win_id "`n" (!cap || !border || !sysmen)	"`n"		(child || toolwin)
	
return




test:
	WinGet , List_1, List, DrozdTool
	MsgBox,,, % "show_temp" show_temp "`n" List_1
	
	for k,v in win_list	{	
	WinGetTitle, Title, ahk_id %k% 
	WinGet, pname, ProcessName,ahk_id %k%  
	WinGetClass, class_, ahk_id %k%  	
	 ;MsgBox,,, % k " , " pname " , " class_ "`n" Title  "`n" ObjStr(v)
	 MsgBox,4100,, % "# " List_1 "`n" k " , " pname " , " class_ "`n" Title  "`n" ObjStr(v)
        IfMsgBox, No
            return		
/* 		if isObject(v){
			for k2,v2 in v
				MsgBox,,, % k "`n" k2 " = " v2
		}
		*/
	}
return



ObjStr(obj) {
 if(!isObject(obj))
  return false
  str:=""
 for k,v in obj
    str:=str  k " : " v ", "
 return str
}

ToolTip_(tekst,t:=2){
	GuiControlGet, Pos, Pos, edit_1
	tipX:= PosX+ 4, tipY:=PosY +2
	ToolTip, %tekst% ,%tipX%, %tipY%
	t:=t*1000
	Settimer, ToolTip_close , -%t%
}

ToolTip_close:
Settimer, ToolTip_close , Off
ToolTip
return


Exit:
;Esc::
ExitApp


Attachments
WinExplorerToolsDrozd.png
WinExplorerToolsDrozd.png (92.14 KiB) Viewed 161 times

Return to “Scripts and Functions”