log hotkeys, get extended hotkey info, interact with the *right* window (Hotkey command with FunctionObject example)

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

log hotkeys, get extended hotkey info, interact with the *right* window (Hotkey command with FunctionObject example)

23 Mar 2017, 14:25

This is a prototype for defining all hotkeys at the start of a script.

It also allows you to keep a log of all hotkeys. Live, within the script as it is running, and to a text file. You could have an OnExit function that outputs hotkey data to an ini file when the script exits, giving you usage data on hotkeys: first/last/total usage.

The script also allows you to get extended information for current and past hotkeys, far more than what is currently available in the 'A_' variables.

This is a prototype, in future, I might change some of the variable/object names to make them more convenient. I think now that I would want to use the name 'AX_' variables/objects, for something that I would want to be an 'A_' variable/object, but for which I don't want to call it that, in order to not cause confusion with standard 'A_' variables.

Here is some potential logic intended to make sure that hotkeys are always applied to the correct window:

Code: Select all

;e.g. AX_HotkeyCount is 50, when the 50th hotkey is pressed
;e.g. AX_HotkeyHWnd[50] would be the hWnd that matched the window criteria when the 50th hotkey was triggered

MyLabel:
if !(vMyLabelHKCount = "")
	return
vMyLabelHKCount := AX_HotkeyCount
hWnd := WinExist("A")
if (hWnd = AX_HotkeyHWnd[vMyLabelHKCount])
	MsgBox, % "OK to proceed"
else
	MsgBox, % "do nothing"
vMyLabelHKCount := ""
return
==================================================

full script:

Code: Select all

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

;jee log hotkeys.ahk by jeeswg
;[first released: 2017-03-23][updated: N/A]

;log hotkeys to a text file
;get extended information about hotkeys
;potentially prevent interacting with the wrong window

;uses 'Hotkey, If, % FunctionObject'
;see:
;Hotkey
;https://autohotkey.com/docs/commands/Hotkey.htm

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

#SingleInstance force
ListLines, Off
#KeyHistory 0
Menu, Tray, Click, 1
#NoEnv
AutoTrim, Off
#UseHook

SplitPath, A_ScriptName,,,, vScriptNameNoExt
Menu, Tray, Tip, % vScriptNameNoExt

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

DetectHiddenWindows, On

;'vJee'/'oJee' are the equivalent of AutoHotkey's 'A_'
global oJeeHotkey, vJeeHotkeyCount, vJeeHotkeyHWnd, vJeeHotkeyDelim, vJeePathHotkeyLog
vJeePathHotkeyLog := A_Desktop "\z jee log hotkeys list.txt"
vJeeHotkeyDelim := "|"

vList = ;continuation section
(
q|MyFunc|ahk_class Notepad
q|MyFunc|ahk_class WordPadClass
q|MyFunc|ahk_exe iexplore.exe
q|MyFunc|A
w|MyLabel|ahk_class Notepad
w|MyLabel|ahk_class WordPadClass
w|MyLabel|ahk_exe iexplore.exe
w|MyLabel|A
)

Loop, Parse, vList, `n
{
	vHotkeyCriteria := A_LoopField
	oHotkeyCriteria := StrSplit(vHotkeyCriteria, vJeeHotkeyDelim)
	vHotkey := oHotkeyCriteria[1]
	vHotkeyLabel := oHotkeyCriteria[2]
	fn := Func("JEE_HotkeyHandler").Bind(vHotkeyCriteria)
	Hotkey, If, % fn
	Hotkey, % vHotkey, % vHotkeyLabel
}
return

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

JEE_HotkeyHandler(vHotkeyCriteria, vHotkey)
{
	return JEE_HotkeyHandler2(vHotkeyCriteria, vHotkey)
}

JEE_HotkeyHandler2(vHotkeyCriteria, vHotkey)
{
	static
	oHotkeyCriteria := StrSplit(vHotkeyCriteria, vJeeHotkeyDelim)
	if !(hWnd := WinActive(oHotkeyCriteria[3]))
		return 0

	if !oJeeHotkey
		oJeeHotkey := {}

	DllCall("kernel32\GetSystemTimeAsFileTime", Int64P,vIntervalsUTC)
	DllCall("kernel32\FileTimeToLocalFileTime", Int64P,vIntervalsUTC, Int64P,vIntervalsLocal)
	vDateUTC := 1601
	vDateUTC += % Floor(vIntervalsUTC/10000000), Seconds
	vDateLocal := 1601
	vDateLocal += % Floor(vIntervalsLocal/10000000), Seconds

	WinGetClass, vWinClass, % "ahk_id " hWnd
	WinGet, vPName, ProcessName, % "ahk_id " hWnd
	vCount++
	vJeeHotkeyCount := vCount
	vJeeHotkeyHWnd := hWnd
	oJeeHotkey[vCount, "HWnd"] := hWnd
	oJeeHotkey[vCount, "Criteria"] := vHotkeyCriteria
	oJeeHotkey[vCount, "Hotkey"] := vHotkey
	oJeeHotkey[vCount, "HK"] := vHotkey
	oJeeHotkey[vCount, "Label"] := oHotkeyCriteria[2]
	oJeeHotkey[vCount, "WinCriteria"] := oHotkeyCriteria[3]
	oJeeHotkey[vCount, "WC"] := oHotkeyCriteria[3]
	oJeeHotkey[vCount, "HKWC"] := oHotkeyCriteria[1] "|" oHotkeyCriteria[3]
	oJeeHotkey[vCount, "Time"] := vDateLocal
	oJeeHotkey[vCount, "TimeUTC"] := vDateUTC
	oJeeHotkey[vCount, "MSec"] := Floor(vIntervalsLocal/10000)
	oJeeHotkey[vCount, "MSecUTC"] := Floor(vIntervalsUTC/10000)

	vOutput := vDateLocal "`t" vHotkeyCriteria "`t" vPName "`t" vWinClass "`r`n"
	FileAppend, % vOutput, % "*" vJeePathHotkeyLog, UTF-8
	return 1
}

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

;to try and ensure that we interact with the window
;that was active when the hotkey was triggered,
;and to ensure that that window has the required window criteria:
;we create a unique variable in each function/label,
;that records how many hotkeys have been triggered so far,
;we then get data for the nth hotkey when required

;e.g. 'HKWC' (hotkey and window criteria)
;if (oJeeHotkey[vMyFuncHC].HKWC = "q|ahk_class Notepad")
;	WinClose, % "ahk_id " oJeeHotkey[vMyFuncHC].HWnd
;if (oJeeHotkey[vMyFuncHC].HK = "q")
;	WinClose, % "ahk_id " oJeeHotkey[vMyFuncHC].HWnd
;if (oJeeHotkey[vMyFuncHC].WC = "ahk_class Notepad")
;	WinClose, % "ahk_id " oJeeHotkey[vMyFuncHC].HWnd

MyFunc()
{
	vMyFuncHC := vJeeHotkeyCount
	MsgBox, % JEE_HotkeyInfo(A_ThisFunc)
}

MyLabel:
	vMyLabelHC := vJeeHotkeyCount
	MsgBox, % JEE_HotkeyInfo(A_ThisLabel)
return

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

JEE_HotkeyInfo(vLabel)
{
	static
	vCount := vJeeHotkeyCount
	WinGetClass, vWinClass, % "ahk_id " oJeeHotkey[vCount].HWnd
	WinGet, vPName, ProcessName, % "ahk_id " oJeeHotkey[vCount].HWnd
	WinGet, vPID, PID, % "ahk_id " oJeeHotkey[vCount].HWnd

	vOutput := ""
	if !(oJeeHotkey[vCount].Hotkey = A_ThisHotkey)
		vOutput .= "[warning: hotkey mismatch: " oJeeHotkey[vCount] " " A_ThisHotkey "]`r`n"
	if !(oJeeHotkey[vCount].Label = vLabel)
		vOutput .= "[warning: label mismatch: " oJeeHotkey[vCount].Label " " vLabel "]`r`n"
	vOutput .= "==============================`r`n"
	vOutput .= "criteria (hotkey): " oJeeHotkey[vCount].Hotkey "`r`n"
	vOutput .= "criteria (label): " oJeeHotkey[vCount].Label "`r`n"
	vOutput .= "criteria (window): " oJeeHotkey[vCount].WinCriteria "`r`n"
	vOutput .= "class: " vWinClass "`r`n"
	vOutput .= "process: " vPName "`r`n"
	vOutput .= "hWnd: " oJeeHotkey[vCount].HWnd "`r`n"
	vOutput .= "PID: " vPID "`r`n"
	vOutput .= "time (local): " oJeeHotkey[vCount].Time "`r`n"
	vOutput .= "time (UTC): " oJeeHotkey[vCount].TimeUTC "`r`n"
	vOutput .= "system time (local): " oJeeHotkey[vCount].MSec "`r`n"
	vOutput .= "system time (UTC): " oJeeHotkey[vCount].MSecUTC "`r`n"
	vOutput .= "=============================="
	return vOutput
}

;==================================================
some basic code for experimenting with:

Code: Select all

vHotkey1 := "q"
vHotkeyLabel1 := "MyFunc"
vHotkey2 := "w"
vHotkeyLabel2 := "MyLabel"

fn := Func("JEE_HotkeyHandler").Bind(vArg)
Hotkey, If, % fn
Hotkey, % vHotkey1, % vHotkeyLabel1
Hotkey, % vHotkey2, % vHotkeyLabel2
Hotkey, % "e", % "MyFunc"
Hotkey, r, MyLabel
return

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

JEE_HotkeyHandler(vArg, vHotkey)
{
	return JEE_HotkeyHandler2(vArg, vHotkey)
}

JEE_HotkeyHandler2(vArg, vHotkey)
{
	;if MyCriteria
		return 1
	;else
		return 0
}

MyFunc()
{
	MsgBox, % A_ThisFunc " " A_ThisHotkey
}

MyLabel:
	MsgBox, % A_ThisLabel " " A_ThisHotkey
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log hotkeys, get extended hotkey info, interact with the *right* window (Hotkey command with FunctionObject example)

05 May 2019, 19:36

Here's a basic example for the If, % FunctionObject functionality:

Code: Select all

;'If, % FunctionObject' example
;pressing q will give a different message if the active window is Notepad/WordPad/other

;note:
;If, % FunctionObject
;FunctionObject must be a single variable (not an expression)

;fn := Func("MyWinActive").Bind("ahk_class Notepad") ;also works
fn := Func("WinActive").Bind("ahk_class Notepad", "", "", "")
Hotkey, If, % fn
Hotkey, q, FuncQ_NP

;fn := Func("MyWinActive").Bind("ahk_class WordPadClass") ;also works
fn := Func("WinActive").Bind("ahk_class WordPadClass", "", "", "")
Hotkey, If, % fn
Hotkey, q, FuncQ_WP

Hotkey, If
Hotkey, q, FuncQ_All

MyWinActive(vWinTitle, vHotkey)
{
	return WinActive(vWinTitle)
}

FuncQ_All()
{
	MsgBox, % A_ThisFunc
}
FuncQ_NP()
{
	MsgBox, % A_ThisFunc
}
FuncQ_WP()
{
	MsgBox, % A_ThisFunc
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log hotkeys, get extended hotkey info, interact with the *right* window (Hotkey command with FunctionObject example)

05 May 2019, 19:59

Here's a basic AutoHotkey v2 script written in a C++ style. Everything is stored within functions, to avoid polluting the global namespace. And the use of hotkey labels has been avoided, instead, the Hotkey function is used.

Code: Select all

;AHK v2 script

#SingleInstance force

Main()

Main()
{
	MsgBox("hello world")
	MsgBox(Func1())
	Func2()

	Hotkey("If", Func("MyWinActive").Bind("ahk_class Notepad")) ;also works
	;Hotkey("If", Func("WinActive").Bind("ahk_class Notepad", "", "", ""))
	Hotkey("q", "FuncQ_NP")

	Hotkey("If", Func("MyWinActive").Bind("ahk_class WordPadClass")) ;also works
	;Hotkey("If", Func("WinActive").Bind("ahk_class WordPadClass", "", "", ""))
	Hotkey("q", "FuncQ_WP")

	Hotkey("If")
	Hotkey("q", "FuncQ_All")
}

MyWinActive(vWinTitle, vHotkey)
{
	return WinActive(vWinTitle)
}

FuncQ_All()
{
	MsgBox(A_ThisFunc)
}
FuncQ_NP()
{
	MsgBox(A_ThisFunc)
}
FuncQ_WP()
{
	MsgBox(A_ThisFunc)
}

Func1()
{
	return "hello 1"
}
Func2()
{
	MsgBox("hello 2")
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: log hotkeys, get extended hotkey info, interact with the *right* window (Hotkey command with FunctionObject example)

05 May 2019, 23:14

jeeswg wrote:
05 May 2019, 19:36
Here's a basic example for the If, % FunctionObject functionality:
Thanks jeeswg. ;)
FunctionObject must be a single variable (not an expression)
Unfortunately. As with SetTimer I would said. I remember having lost an hour debugging before finally realize that FunctionObject was an expression and not a single variable in my script... :facepalm: :headwall:
For some reason, IfWin... appear in the form of pairs while the If remains alone - I mean, there's no Hotkey, IfNot, functionobject. For boolean-like function like FileExist etc. you have to use either a wrapper or Hotkey, If, with a given #If !expression, at least if you want to check against the false.

I was working on hotkeys and context sensitivity for a script these days, which is intended to implement a 'IfNot', here's the relevant subset, this might be of some interest:
Spoiler

Code: Select all

fn := Func("WinActive").Bind("ahk_class Notepad", "", "", "")

I'm suprised it works. As I see it, this can only happen while an extra parameter is silently discarded. In this sense, I'd use instead Func("MyWinActive").Bind("ahk_class Notepad").
Cheers.
my scripts

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: MiM and 78 guests