[Class] WinHook

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

28 Dec 2018, 19:54

Updated on First Post

Update: 2018 12 28
  • Minor Change to make 0 the default for idProcess with WinHook.Event.Add
    0 means all Processes
Example:

Code: Select all

; When Foreground event occurs call func ForeGroundChange for any process (basically when any window comes to the foreground the func is called) 
WinHook.Event.Add(3, 3, "ForeGroundChange") ; 3 = EVENT_SYSTEM_FOREGROUND

ForeGroundChange(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
	WinGetClass, Class, ahk_id %hwnd%
	If (Class = "Notepad")			; was it Notepad window
		ToolTip, Notepad Window came to Foreground
	else
		ToolTip, non-Notepad Window came to Foreground (Notepad Window not to Foreground)  
}
You could do this before but you would have had to put a 0 in the fourth parameter slot.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: [Class] WinHook

29 Jan 2019, 14:03

You linked to this script in another post where we both answered and I put this page in my 'ahk link to look in more details' directory in my FF favorites (I've many similar ones) to see it... later - I was just looking at it - more in details so - now... and I would like to say that this script comes in handy, it is actually a nice and properly constructed script :thumbup: Thanks for sharing FanaticGuru.
my scripts
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: [Class] WinHook

12 Feb 2019, 14:07

The code below simply reclaims the architecture of your Hook.WinEvents nested class; it has the same build used for yours and reclaims the ideas which have been implemented in you code - in particular:
. use of a proxy hook function and of the map[hWinEventHook := this] trick to retrieve and call the appropriate delegate.
. upstream wintitle filtering, from within the proxy hook callback.
. auto-unhook on exit.

Code: Select all

; Usage example:
/*
#NoEnv
#SingleInstance force
#Warn

#Persistent
ControlGet, hEdit1, Hwnd,, Edit1, ahk_class Notepad
if not (hEdit1)
	ExitApp
WinEventHook.setForId(hEdit1, 0x800E, "f")
; WinEventHook.setForId(hEdit1, 0x800E, "f", 100) ; '100' means: "treat events separated by a period of less than 100ms as an influx of calls and buffer it as such"
return

!x::
	count := WinEventHook.unsetById(hEdit1)
	MsgBox % "ErrorLevel = " . ErrorLevel . "`nHook instance unset count = " . count
return


f(_params*) { ; _hWinEventHook, _event, _hwnd, _idObject, _idChild, _dwEventThread, _dwmsEventTime
	static OBJID_CLIENT := 0xFFFFFFFC ; https://docs.microsoft.com/fr-fr/windows/desktop/WinAuto/object-identifiers#OBJID_CLIENT
	local _idObject := _params.4
	if (_idObject = OBJID_CLIENT) ; actually, both the (edit child control) window's horizontal and the vertical scroll bars (OBJID_VSCROLL/OBJID_HSCROLL) could otherwise update the tooltip below
		ToolTip % Format("hWinEventHook = {}`nevent = {}`nhwnd = {}`nidObject = {}`nidChild = {}`ndwEventThread = {}`ndwmsEventTime = {}", _params*)
}
*/
; ========================================================

Class WinEventHook {

	/*
		Namespace: WinEventHook

		Environment: Windows 8.1 64 bit - Autohotkey v1.1.30.01 32-bit Unicode
		Credits:
			Fanatic Guru for WinHook - https://www.autohotkey.com/boards/viewtopic.php?t=59149&p=254920
		Related works:
			[LIB] EWinHook - SetWinEventHook implementation by cyruz - https://www.autohotkey.com/boards/viewtopic.php?p=176444
			Event Library by KuroiLight/klomb - https://www.autohotkey.com/boards/viewtopic.php?t=42657
	*/
	static _map := {}
	static _instances := {} ; _map and _instances are combined with a design conceived to allow cross-referencing while keeping track of instances
	static HANDLE_IDPROCESS := "" ; use internally to 'capture' events if a hWnd-filtering is set @._hookFunction. In any event, should not be 0 since the handle to the window that generates the event can be NULL - https://docs.microsoft.com/fr-fr/windows/desktop/api/winuser/nc-winuser-wineventproc#parameters
	static _callback := RegisterCallback("WinEventHook._hookFunction") ; we only need to call registercallback ones, we use a static variable - https://www.autohotkey.com/boards/viewtopic.php?t=59149&p=254920#profile249257

	Class _TimedCallback { ; nested class used internally to buffer an influx of calls as such, as influx

		static instances := {} ; keep track of instances
		_delegate := Func("Max") ; dummy variadic delegate
		_range := "Off"

		__New(_hWinEventHook) {
		return WinEventHook._TimedCallback.instances[ (this.ID:=_hWinEventHook) ] := this
		}
		__Call(_callee, _params*) { ; triggered by %this%(_hWinEventHook, _event, _hwnd, _idObject, _idChild, _dwEventThread, _dwmsEventTime)
			if (_callee = "") { ; for %fn%() or fn.() - https://www.autohotkey.com/docs/objects/Functor.htm#User-Defined
				this.params := _params
				SetTimer, % this, % -this._range
			}
		}
		__Delete() {
		this._delegate := "" ;  release the user function object
		}
		call() { ; triggered by this.call(_hWinEventHook, _event, _hwnd, _idObject, _idChild, _dwEventThread, _dwmsEventTime) yet  if the object is being used by SetTimer, only the call method is needed - https://www.autohotkey.com/docs/objects/Functor.htm#User-Defined
		return this._delegate.call(this.params*)
		}
		delegate {
			set {
				if not (IsObject(value) || value:=Func(value)) {
					throw Exception("Invalid callback.")
				return
				}
			return this._delegate := value
			}
			get {
			return this._delegate
			}
		}
		range {
			set {
				static USER_RANGE_MAXIMUM := 1000
				if value not between -1 and %USER_RANGE_MAXIMUM%
				{
					throw Exception("The property could not be set to a value lower or greater than the ranged value.",, (value = "") ? """""" : value)
				return
				}
			return this._range := (value = -1) ? "Off" : value
			}
			get {
			return (this._range = "Off") ? -1 : this._range
			}
		}

	}

	__New(_idProcess, _event, _delegate, _range, _hwnd, _offset:=-1) { ; designed to be used internally

		static _junkTimedCallback := (new WinEventHook._TimedCallback(0)) ; junk 'timed callback' used to check input parameters against the prerequisites
		static _junkFunc := Func("Func")
		local _hWinEventHook, _exception, _cbObject

		try { ; check input parameters against the prerequisites
			_junkTimedCallback.delegate := _delegate
			_junkTimedCallback.range := _range
		} catch _exception {
			throw Exception(_exception.message, _offset, _exception.extra)
		return
		} finally _junkTimedCallback.delegate := _junkFunc ; in any event, release the user function object

		if not (_hWinEventHook:=WinEventHook._map[_idProcess, _event]) { ; if the hook instance is not yet listed...
			_hWinEventHook := DllCall("SetWinEventHook"
									, "UInt", _event
									, "UInt", _event
									, "Ptr", 0x0
									, "Ptr", WinEventHook._callback
									, "UInt", _idProcess
									, "UInt", 0x0
									, "UInt", 0x0) ; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setwineventhook
			if not (_hWinEventHook) {
				throw Exception("Could not set the event hook function.", _offset)
			return
			}
			WinEventHook._map[_idProcess, _event] := _hWinEventHook
			WinEventHook._instances[_hWinEventHook] := {idProcess: _idProcess, event: _event} ; 'cross-referencing'
		}
		if (_range <> -1) { ; if influxes of calls need to be buffer as such, as influxes...
			_cbObject := new WinEventHook._TimedCallback(_hWinEventHook)
			_cbObject.delegate := _delegate
			_cbObject.range := _range
			WinEventHook._instances[_hWinEventHook].sieve[_hwnd] := _cbObject
		} else WinEventHook._instances[_hWinEventHook].sieve[_hwnd] := _delegate

	return _hWinEventHook
	}

	setForProcess(_idProcess, _event, _delegate, _range:=-1) {
		Process, Exist, % (_idProcess+0 <> "") ? _idProcess : 0 ; otherwise, if the PIDOrName parameter is blank, the script's own PID is retrieved - https://www.autohotkey.com/docs/commands/Process.htm#Exist
		if not (ErrorLevel) {
			throw Exception("The process could not be found.", -1, (_idProcess = "") ? """""" : _idProcess)
		return
		} else return new WinEventHook(_idProcess, _event, _delegate, _range, WinEventHook.HANDLE_IDPROCESS, -2)
	}
	setForId(_hwnd, _event, _delegate, _range:=-1) {
		local _idProcess := ""
		DllCall("User32.dll\GetWindowThreadProcessId", "Ptr", _hwnd, "UIntP", _idProcess, "UInt")
		if not (_idProcess) {
			throw Exception("Could not retrieve the identifier of the process that created the window.", -1)
		return
		} else return new WinEventHook(_idProcess, _event, _delegate, _range, Format("{1:u}", _hwnd), -2)
	}
	unset(_hWinEventHook) {
		local _unsetCount, _instance
		_unsetCount := (_instance:=WinEventHook._instances[_hWinEventHook]) ? WinEventHook._unset(_instance.idProcess, _instance.event) : 0
	return _unsetCount
	}

	unsetByProcess(_idProcess) {
		local _event, _hWinEventHook, _unsetCount
		for _event, _hWinEventHook in ObjClone(WinEventHook._map[_idProcess]), _unsetCount := 0
			ErrorLevel += !_unsetCount += WinEventHook._unset(_idProcess, _event)
		return _unsetCount
	}
	unsetById(_hwnd) {
		local _h, _hWinEventHook, _obj, _unsetCount, _sieve
		if not (_h:=Format("{1:u}", _hwnd)) { ; i.e. also if _hwnd = WinEventHook.HANDLE_IDPROCESS
			throw Exception("Invalid handle.", -1, (_hwnd = "") ? """""" : _hwnd)
		return
		}
		_hwnd := _h
		for _hWinEventHook, _obj in ObjClone(WinEventHook._instances), _unsetCount := 0 {
			if (ObjHasKey(_obj.sieve, _hwnd)) {
				_sieve := WinEventHook._instances[_hWinEventHook].sieve, ObjDelete(_sieve, _hwnd)
				if not (ObjCount(_sieve)) {
					ErrorLevel += !_unsetCount += WinEventHook._unset(_obj.idProcess, _obj.event)
				}
			}
		}
		return _unsetCount
	}
	unsetByEvent(_eventNum) {
		local _idProcess, _obj, _unsetCount, _event
		for _idProcess, _obj in ObjClone(WinEventHook._map), _unsetCount := 0 {
			for _event in _obj {
				if (_event = _eventNum) {
					ErrorLevel += !_unsetCount += WinEventHook._unset(_idProcess, _event)
				}
			}
		}
		return _unsetCount
	}

	unsetAll() {
		local _idProcess, _obj, _unsetCount, _event
		for _idProcess, _obj in ObjClone(WinEventHook._map), _unsetCount := 0
			for _event in _obj
				ErrorLevel += !_unsetCount += WinEventHook._unset(_idProcess, _event)
		return _unsetCount
	}
		_unset(_idProcess, _event) {
			local _hWinEventHook, _r
			_hWinEventHook := ObjDelete(WinEventHook._map[_idProcess], _event)
			(!ObjCount(WinEventHook._map[_idProcess]) && ObjDelete(WinEventHook._map, _idProcess))
			_r := DllCall("UnhookWinEvent", "Ptr", _hWinEventHook) ; https://docs.microsoft.com/fr-fr/windows/desktop/api/winuser/nf-winuser-unhookwinevent
			ObjDelete(WinEventHook._instances, _hWinEventHook)
			if (ObjHasKey(WinEventHook._TimedCallback.instances, _hWinEventHook)) ; if a '_TimedCallback' instance is associated with this hook instance...
				ObjDelete(WinEventHook._TimedCallback.instances, _hWinEventHook) ; release the function object (actually, the instance's own __Delete meta-function will be called)
		return _r
		}

		_dispose(_params*) {
		static _ := OnExit(ObjBindMethod(WinEventHook, "_dispose"))
		static _preventExit := 1
		return not _preventExit, WinEventHook.unsetAll()
		}

	_hookFunction(_event, _hwnd, _idObject, _idChild, _dwEventThread, _dwmsEventTime) {

		local _hWinEventHook, _enum, _handle, _delegate

		(_enum:=ObjNewEnum(WinEventHook._instances[ _hWinEventHook:=this ].sieve)).next(_handle, _delegate)
		if (_handle <> WinEventHook.HANDLE_IDPROCESS) { ; unsigned integers (i.e. hWnd, if any) necessarily appear before "" (HANDLE_IDPROCESS) in an enumeration
			Loop {
				if (_handle = _hwnd) {
					%_delegate%(_hWinEventHook, _event, _hwnd, _idObject, _idChild, _dwEventThread, _dwmsEventTime)
				return ;  'capture' the event: do not go all the way up on the process
				}
			} Until not (_enum[_handle, _delegate])
			if (_handle <> WinEventHook.HANDLE_IDPROCESS)
				return ;  'capture' the event: do not go all the way up on the process
		}
		%_delegate%(_hWinEventHook, _event, _hwnd, _idObject, _idChild, _dwEventThread, _dwmsEventTime)

	}

}
For my own purpose I actually needed to add:
. upstream influxes buffering for weighted delegate invocations.
. possibility of set/unset by hWnd/PID.
and to opt for:
. strict hWnd-filtering (vs. 'winTitle'-filtering).

Hoping creating this way a constructive atmosphere of sharing. Cheers
my scripts
User avatar
Thoughtfu1Tux
Posts: 125
Joined: 31 May 2018, 23:26

Re: [Class] WinHook

12 Feb 2019, 18:50

Awesome!! I love the Advanced Excel Example. I use custom chrome.css file to remove the top tabs within firefox since I use Tree Style tabs, but one of the annoyances is that with the latest update removing the top tabs also removes the exit button, minimize and maximize buttons. With this I can replace them with AutoHotKey!! Thank You!
User avatar
Scr1pter
Posts: 1271
Joined: 06 Aug 2017, 08:21
Location: Germany

Re: [Class] WinHook

17 Feb 2019, 15:13

Hello,

Thanks for that class!

I recognized the following problem:
When I run an application in fullscreen mode (e.g. Firefox, VLC Media Player, a game), the WinHook functionality doesn't work anymore.
I have three monitors and when I have a fullscreen application running on monitor 3 and switch applications on monitor 2, nothing happens.
If the application on monitor 3 is not fullscreen, everything works perfectly.

That's my script which works perfectly as long as there's no fullscreen application running:

Code: Select all

#include WinHook.ahk ; Include FanaticGuru's WinHook Class

WinHook.Shell.Add("Activated", , , "excel.exe", 4) ; Excel Window Activated
WinHook.Shell.Add("Activated", , , "notepad++.exe", 4) ; Notepad++ Window Activated
WinHook.Shell.Add("Activated", , , "", 4) ; Unknown Window Activated

Activated(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event) ; Specific window activated
{
  if (Win_Exe != "excel.exe") and (Win_Exe != "notepad++.exe") ; If it's neither Excel nor Notepad++:
  {
    picture = standard.jpg ; Set picture as default picture
  }
  else ; Otherwise:
  {
    StringTrimRight, titel, Win_Exe, 4 ; Trim .exe from window.exe, e.g. from excel.exe -> excel
    picture = %titel%.jpg ; Create picture name, e.g. Excel.jpg
  }
  MsgBox, %picture% ; Show picture
}
return
Maybe I will have to include an additional function in case a fullscreen application is running?
Thanks for any hint!

Cheers!
Please use [code][/code] when posting code!
Keyboard: Logitech G PRO - Mouse: Logitech G502 LS - OS: Windows 10 Pro 64 Bit - AHK version: 1.1.33.09
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: [Class] WinHook

17 Feb 2019, 16:37

Scr1pter wrote:
17 Feb 2019, 15:13
Hi Scr1pter,

Check this User's post.

I hope that helps.
my scripts
User avatar
Scr1pter
Posts: 1271
Joined: 06 Aug 2017, 08:21
Location: Germany

Re: [Class] WinHook

17 Feb 2019, 17:35

Thanks, that definitely solved my problem!
It didn't work with the if statement, though.

The "amateurish" version works perfectly

Code: Select all

#include WinHook.ahk ; Include FanaticGuru's WinHook Class

WinHook.Shell.Add("Activated", , , "excel.exe", 4) ; Excel Window Activated (Windowed)
WinHook.Shell.Add("Activated", , , "excel.exe", 32772) ; Excel Window Activated (Fullscreen)
WinHook.Shell.Add("Activated", , , "notepad++.exe", 4) ; Notepad++ Window Activated (Windowed)
WinHook.Shell.Add("Activated", , , "notepad++.exe", 32772) ; Notepad++ Window Activated (Fullscreen)
WinHook.Shell.Add("Activated", , , "", 4) ; Unknown Window Activated (Windowed)
WinHook.Shell.Add("Activated", , , "", 32772) ; Unknown Window Activated (Fullscreen)

Activated(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event) ; Specific window activated
{
  if (Win_Exe != "excel.exe") and (Win_Exe != "notepad++.exe") ; If it's neither Excel nor Notepad++:
  {
    picture = standard.jpg ; Set picture as default picture
  }
  else ; Otherwise:
  {
    StringTrimRight, titel, Win_Exe, 4 ; Trim .exe from window.exe, e.g. from excel.exe -> excel
    picture = %titel%.jpg ; Create picture name, e.g. Excel.jpg
  }
  MsgBox, %picture% ; Show picture
}
return
With the if statement it doesn't work.
I even changed the default parameter from 1 to 4 (inside of WinHook.ahk)

Code: Select all

#include WinHook.ahk ; Include FanaticGuru's WinHook Class

WinHook.Shell.Add("Activated", , , "excel.exe") ; Excel Window Activated (Fullscreen)
WinHook.Shell.Add("Activated", , , "notepad++.exe") ; Notepad++ Window Activated (Fullscreen)
WinHook.Shell.Add("Activated", , , "") ; Unknown Window Activated (Fullscreen)

Activated(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event) ; Specific window activated
{
  if ((Win_Event = 4) || (Win_Event = 32772)) ; If it's either windows or fullscreen:
  {
    if (Win_Exe != "excel.exe") and (Win_Exe != "notepad++.exe") ; If it's neither Excel nor Notepad++:
    {
      picture = standard.jpg ; Set picture as default picture
    }
    else ; Otherwise:
    {
      StringTrimRight, titel, Win_Exe, 4 ; Trim .exe from window.exe, e.g. from excel.exe -> excel
      picture = %titel%.jpg ; Create picture name, e.g. Excel.jpg
    }
    MsgBox, %picture% ; Show picture
  }
}
return
Anyway, it works, but if you can give me a final hint how to make it with an if statement, it will be great.

Thank you again!

Cheers!
Please use [code][/code] when posting code!
Keyboard: Logitech G PRO - Mouse: Logitech G502 LS - OS: Windows 10 Pro 64 Bit - AHK version: 1.1.33.09
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

18 Feb 2019, 15:01

Updated on First Post

Update: 2019 02 18
  • Updated comment help for HSHELL_HIGHBIT, HSHELL_RUDEAPPACTIVATED, and HSHELL_FLASH

Code: Select all

;				32768 = 0x8000 = HSHELL_HIGHBIT
;				32772 = 0x8000 + 4 = 0x8004 = HSHELL_RUDEAPPACTIVATED (HSHELL_HIGHBIT + HSHELL_WINDOWACTIVATED)
;				32774 = 0x8000 + 6 = 0x8006 = HSHELL_FLASH (HSHELL_HIGHBIT + HSHELL_REDRAW)
Calculated the decimal values of these events that need to be used with Shell Hook.

HSHELL_HIGHBIT is not a real event that can be monitored. It is a modifier that can be used with the HSHELL_WINDOWACTIVATED and HSHELL_REDRAW events.

Basically all the user needs to know is to use 32772 for HSHELL_RUDEAPPACTIVATED and 32774 for HSHELL_FLASH.

Some applications always do a HSHELL_RUDEAPPACTIVATED instead of a HSHELL_WINDOWACTIVATED others only do a HSHELL_RUDEAPPACTIVATED when the window is maximized.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

18 Feb 2019, 15:35

Scr1pter wrote:
17 Feb 2019, 17:35
With the if statement it doesn't work.
I even changed the default parameter from 1 to 4 (inside of WinHook.ahk)

Code: Select all

#include WinHook.ahk ; Include FanaticGuru's WinHook Class

WinHook.Shell.Add("Activated", , , "excel.exe") ; Excel Window Activated (Fullscreen)
WinHook.Shell.Add("Activated", , , "notepad++.exe") ; Notepad++ Window Activated (Fullscreen)
WinHook.Shell.Add("Activated", , , "") ; Unknown Window Activated (Fullscreen)

Activated(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event) ; Specific window activated
{
  if ((Win_Event = 4) || (Win_Event = 32772)) ; If it's either windows or fullscreen:
  {
    if (Win_Exe != "excel.exe") and (Win_Exe != "notepad++.exe") ; If it's neither Excel nor Notepad++:
    {
      picture = standard.jpg ; Set picture as default picture
    }
    else ; Otherwise:
    {
      StringTrimRight, titel, Win_Exe, 4 ; Trim .exe from window.exe, e.g. from excel.exe -> excel
      picture = %titel%.jpg ; Create picture name, e.g. Excel.jpg
    }
    MsgBox, %picture% ; Show picture
  }
}
return
WinHook.Shell.Add("Activated", , , "excel.exe") ; Excel Window Activated (Fullscreen) this does not do what you think. Leaving out the event does not default to all events. It defaults to 1 which is Created.

I might look at making an option for all events.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

18 Feb 2019, 16:23

Updated on First Post

Update: 2019 02 18 v2
  • Updated WinHook.Shell so that Event defaults to 0 which is all events
Here is a test function that might be useful.

Code: Select all

WinHook.Shell.Add("AllExcelEvents", , , "excel.exe")

AllExcelEvents(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event)
{
	static
	Str := Win_Event "`n" Str
	ToolTip % Str
}

Esc::ExitApp
This shows all the event codes in a tooltip as they are triggered by Excel windows.

I see a code 16 often for which I can find no documentation.

I have found this technique of just showing all or ranges of events useful for finding good numbers to use with both WinHook.Shell and WinHook.Event.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
Scr1pter
Posts: 1271
Joined: 06 Aug 2017, 08:21
Location: Germany

Re: [Class] WinHook

18 Feb 2019, 16:55

@FG:
Thanks for your engagement!
WinHook.Shell.Add("Activated", , , "excel.exe") ; Excel Window Activated (Fullscreen) this does not do what you think. Leaving out the event does not default to all events. It defaults to 1 which is Created.
I thought it defaulted to 4, because I changed it to 4?

Code: Select all

class WinHook
{
	class Shell
	{
		;Add(Func, wTitle:="", wClass:="", wExe:="", Event:=1) ; ORIG
		Add(Func, wTitle:="", wClass:="", wExe:="", Event:=4)
		{
			if !WinHook.Shell.Hooks
			{
				WinHook.Shell.Hooks := {}, WinHook.Shell.Events := {}
				DllCall("RegisterShellHookWindow", UInt, A_ScriptHwnd)
				MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
				OnMessage(MsgNum, ObjBindMethod(WinHook.Shell, "Message"))
			}
			.
			.
			.
Anyway, I overwrote the WinHook class with the new content you provided and changed the function call to:
WinHook.Shell.Add("Aktiviert", , , "excel.exe") ; Excel
The rest is all the same.

Well, I have honestly to say that I almost only understand train station (hehe, German English :) ), but I'm glad it works!
It even partially works for games!
Not a problem because for games I will keep the Logitech Gaming Software send virtual key presses which get caught by AHK.
For "normal applications" your shell functionality is 100% perfect :thumbup:

Cheers!
Please use [code][/code] when posting code!
Keyboard: Logitech G PRO - Mouse: Logitech G502 LS - OS: Windows 10 Pro 64 Bit - AHK version: 1.1.33.09
shiro_design
Posts: 14
Joined: 21 Apr 2019, 13:57

Re: [Class] WinHook

19 May 2019, 09:53

@FanaticGuru

Great post. I'm very new to autohotkey, not got much programming experience. I'm wanting to attach a gui to a window class and have it stay on top. How would i go about it? The window is 778 x 728. I want it just under the file menu
User avatar
megnatar
Posts: 92
Joined: 27 Oct 2014, 20:49
Location: The Netherlands
Contact:

Re: [Class] WinHook

24 Mar 2020, 04:15

I was about to write a shellhook class. You just saved me some work! Thank you for the you're fancy code and effort!!
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: [Class] WinHook

01 Sep 2021, 18:08

Is possible to intercept a window message and cancel it? For example:

Code: Select all

WinHook.Event.Add(0x0016, 0x0016, "Minimized", PID) 
And whenever the window receive the message 0x016 its not executed.
adrian88888888
Posts: 84
Joined: 13 Mar 2020, 22:51

Re: [Class] WinHook

21 Oct 2021, 17:47

The notepad example does not work :(
It used to work
Even here does not work when they try to activate notepad: https://youtu.be/ysZbOzuBb1Q?t=1614
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

21 Oct 2021, 19:36

adrian88888888 wrote:
21 Oct 2021, 17:47
The notepad example does not work :(
It used to work
Even here does not work when they try to activate notepad: https://youtu.be/ysZbOzuBb1Q?t=1614

I just downloaded the class and notepad example from the first page and they worked fine for me.

So I don't know. Maybe something changed about your system or you have another script or program running that it is causing a problem.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

21 Oct 2021, 21:34

fabricio234 wrote:
01 Sep 2021, 18:08
Is possible to intercept a window message and cancel it? For example:

Code: Select all

WinHook.Event.Add(0x0016, 0x0016, "Minimized", PID) 
And whenever the window receive the message 0x016 its not executed.

The techniques used in this class do not allow that. This class only gets messages when an event occurs. It can undo an event like restore a window as soon as it is minimized but it can not actually prevent it. It does not truly intercept and inject into the hook chain.

Injected into the hook chain means an event occurs, your procedure is called, your procedure takes an action, then your procedure either breaks the chain and cancels the default procedure of the event or send the event on down the chain using CallNextHookEx.

To truly intercept an event from another program and be injected into the hook chain requires a custom external DLL. This external DLL is then called with SetWindowsHookEx.

An AutoHotkey script can also intercept its own events with SetWindowsHookEx. SetWindowsHookEx must either call a DLL or a procedure in the process associated with the event.

So by using SetWindowsHookEx an AutoHotkey script could prevent its own windows from being minimized. But that would require different code than what this class provides.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
pv007
Posts: 93
Joined: 20 Jul 2020, 23:50

Re: [Class] WinHook

21 Jan 2022, 17:22

Possible to detect when a process doesnt exist anymore using SetWindowsHookEx?
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Class] WinHook

21 Jan 2022, 19:08

pv007 wrote:
21 Jan 2022, 17:22
Possible to detect when a process doesnt exist anymore using SetWindowsHookEx?

First this WinHook class uses SetWindowsHook not SetWindowsHookEx.
Second both only deal with windows not processes.

You could probably use something like RegisterWaitForSingleObject to set a trigger that calls a function when a process is terminated.

You could also just use Process with WaitClose to pauses and wait for a process to close.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
Groot
Posts: 22
Joined: 28 Feb 2019, 10:51

Strange behaviour with Calculator

25 Apr 2022, 12:21

Hello FanaticGuru and friends,

I am trying to catch the events when the windows on my system are created and destroyed.
So, to start with, I did some tests based on the simple example FanaticGuru gave in the first post.

It works perfectly with Notepad, but give me some very strange results wih Calculator.

This is the code I use to open Notepad and Calculator:

Code: Select all

#SingleInstance Force
#NoEnv

^j::
	run notepad.exe
    return
^k::
	run calc.exe
    return
And this is the code, based ont the example, that I use to track the creations and destruction of the windows:

Code: Select all

#SingleInstance Force
#NoEnv
#Include WinHook.ahk

WinHook.Shell.Add("Created",,, "notepad.exe",1)
WinHook.Shell.Add("Destroyed",,, "notepad.exe",2)

WinHook.Shell.Add("Created",,, "Calculator.exe",1)
WinHook.Shell.Add("Destroyed",,, "Calculator.exe",2)

Destroyed(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event)
{
	MsgBox %Win_Title% Destroyed
}
Created(Win_Hwnd, Win_Title, Win_Class, Win_Exe, Win_Event)
{
	MsgBox %Win_Title% Created
}
As I said, with Notepad, everything works great.
The problem I am facing appears on the closure of Calculator: I get the message "Calculator Created". (!)

I did some detailed tracking with the tool "ShellHook Messages" (https://www.autohotkey.com/board/topic/32628-tool-shellhook-messages/).
This are the results I get.
(I just added the "Title" column. The titles are in French.)
Error messages.png
Error messages.png (76.98 KiB) Viewed 2539 times
I am lost!

I have Windows 10 Pro 21H2 19044.1645.

Could anyone of you help me?
Any help appreciated!

Thank you!

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: Bodhi, gwarble, Spikea and 130 guests