ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

Post your working scripts, libraries and tools
lexikos
Posts: 6205
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

28 Sep 2015, 05:43

ScriptInfo(Command)

Returns the text that would have been shown in AutoHotkey's main window if you had called Command, but without actually showing or activating the window.

Command must be "ListLines", "ListVars", "ListHotkeys" or "KeyHistory".

Example usage:

Code: Select all

#InstallKeybdHook
InputBox t,, Press some keys to fill the key history.
MsgBox % ScriptInfo("KeyHistory")
ExitApp

Code: Select all

ScriptInfo(Command)
{
    static hEdit := 0, pfn, bkp
    if !hEdit {
        hEdit := DllCall("GetWindow", "ptr", A_ScriptHwnd, "uint", 5, "ptr")
        user32 := DllCall("GetModuleHandle", "str", "user32.dll", "ptr")
        pfn := [], bkp := []
        for i, fn in ["SetForegroundWindow", "ShowWindow"] {
            pfn[i] := DllCall("GetProcAddress", "ptr", user32, "astr", fn, "ptr")
            DllCall("VirtualProtect", "ptr", pfn[i], "ptr", 8, "uint", 0x40, "uint*", 0)
            bkp[i] := NumGet(pfn[i], 0, "int64")
        }
    }
 
    if (A_PtrSize=8) {  ; Disable SetForegroundWindow and ShowWindow.
        NumPut(0x0000C300000001B8, pfn[1], 0, "int64")  ; return TRUE
        NumPut(0x0000C300000001B8, pfn[2], 0, "int64")  ; return TRUE
    } else {
        NumPut(0x0004C200000001B8, pfn[1], 0, "int64")  ; return TRUE
        NumPut(0x0008C200000001B8, pfn[2], 0, "int64")  ; return TRUE
    }
 
    static cmds := {ListLines:65406, ListVars:65407, ListHotkeys:65408, KeyHistory:65409}
    cmds[Command] ? DllCall("SendMessage", "ptr", A_ScriptHwnd, "uint", 0x111, "ptr", cmds[Command], "ptr", 0) : 0
 
    NumPut(bkp[1], pfn[1], 0, "int64")  ; Enable SetForegroundWindow.
    NumPut(bkp[2], pfn[2], 0, "int64")  ; Enable ShowWindow.
 
    ControlGetText, text,, ahk_id %hEdit%
    return text
}
Caveats:
  • You have to parse the text yourself to extract whatever information you want.
  • If the main window is already visible, its content will be changed.
  • ListLines shows the lines inside ScriptInfo().
  • ListVars shows ScriptInfo()'s local variables (but also global variables).
It's based on ListGlobalVars.

I'm not interested enough to do more with it.
User avatar
joedf
Posts: 6605
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

28 Sep 2015, 13:50

Quite interesting! I remember of something i posted in the past where i suggested someone to go access ahk's ram (or using a hidden window of some kind) to get this kind of info.
And here it is! :+1:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 3200 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
User avatar
Nextron
Posts: 1274
Joined: 01 Oct 2013, 08:23
Location: Netherlands OS: Win7 x64 AHK: Unicode x32

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

28 Sep 2015, 21:02

Definitely something to keep around.

Until something better comes along, this brings some structure to the key history output:

Code: Select all

ParseKeyHistory(KeyHistory:="",ParseStringEnumerations:=1){
	/*
	Parses the text from AutoHotkey's Key History into an associative array:
	Header:
	KeyHistory[0]	["Window"]              	String
					["K-hook"]              	Bool
					["M-hook"]              	Bool
					["TimersEnabled"]       	Int
					["TimersTotal"]         	Int
					["Timers"]              	String OR Array 	[i] String
					["ThreadsInterrupted"]  	Int
					["ThreadsPaused"]       	Int
					["ThreadsTotal"]        	Int
					["ThreadsLayers"]       	Int
					["PrefixKey"]           	Bool
					["ModifiersGetKeyState"]	|String OR Array	["LAlt"]   Bool
					["ModifiersLogical"]    	|					["LCtrl"]  Bool
					["ModifiersPhysical"]   	|					["LShift"] Bool
																	["LWin"]   Bool
																	["RAlt"]   Bool
																	["RCtrl"]  Bool
																	["RShift"] Bool
																	["RWin"]   Bool
	Body:
	KeyHistory[i]	["VK"]     	String [:xdigit:]{2}
					["SC"]     	String [:xdigit:]{3}
					["Type"]   	Char [ hsia#U]
					["UpDn"]   	Bool (0=up 1=down)
					["Elapsed"]	Float
					["Key"]    	String
					["Window"] 	String
	*/
	
	
	If !(KeyHistory) && IsFunc("ScriptInfo")
		KeyHistory:=ScriptInfo("KeyHistory")

	RegExMatch(KeyHistory,"sm)(?P<Head>.*?)\s*^NOTE:.*-{109}\s*(?P<Body>.*)\s+Press \[F5] to refresh\.",KeyHistory_)
	KeyHistory:=[]

	RegExMatch(KeyHistory_Head,"Window: (.*)\s+Keybd hook: (.*)\s+Mouse hook: (.*)\s+Enabled Timers: (\d+) of (\d+) \((.*)\)\s+Interrupted threads: (.*)\s+Paused threads: (\d+) of (\d+) \((\d+) layers\)\s+Modifiers \(GetKeyState\(\) now\) = (.*)\s+Modifiers \(Hook's Logical\) = (.*)\s+Modifiers \(Hook's Physical\) = (.*)\s+Prefix key is down: (.*)",Re)

	KeyHistory[0]:={"Window": Re1, "K-hook": (Re2="yes"), "M-hook": (Re3="yes"), "TimersEnabled": Re4, "TimersTotal": Re5, "Timers": Re6, "ThreadsInterrupted": Re7, "ThreadsPaused": Re8, "ThreadsTotal": Re9, "ThreadsLayers": Re10, "ModifiersGetKeyState": Re11, "ModifiersLogical": Re12, "ModifiersPhysical": Re13, "PrefixKey": (Re14="yes")}

	If (ParseStringEnumerations){
		Loop,Parse,% "ModifiersGetKeyState,ModifiersLogical,ModifiersPhysical",CSV
		{
			i:=A_Loopfield
			k:=KeyHistory[0][i]
			KeyHistory[0][i]:={}
			Loop,Parse,% "LWin,LShift,LCtrl,LAlt,RWin,RShift,RCtrl,RAlt",CSV
				KeyHistory[0][i][A_LoopField]:=Instr(k,A_Loopfield)
		}
		
		k:=KeyHistory[0]["Timers"]
		KeyHistory[0]["Timers"]:=[]
		Loop,Parse,k,%A_Space%
			KeyHistory[0]["Timers"].Push(A_Loopfield)
	}

	Loop,Parse,KeyHistory_Body,`n,`r
	{
		RegExMatch(A_Loopfield,"(\w+) {2}(\w+)\t([ hsia#U])\t([du])\t(\S+)\t(\S*) *\t(.*)",Re)
		KeyHistory.Push({"VK": Re1, "SC": Re2, "Type": Re3, "UpDn": (Re4="D"), "Elapsed": Re5, "Key": Re6, "Window": Re7})
	}
	
	Return KeyHistory
}
For example:

Code: Select all

$F1::
	KeyHistory:=ParseKeyHistory()
	MsgBox % "Time since last keypress: " KeyHistory[KeyHistory.MaxIndex()]["Elapsed"] " s."
return
F2::
	MsgBox % "The keyboard hook is " (ParseKeyHistory()[0]["K-hook"] ? "enabled." : "disabled.")
	Hotkey,$F1,Off
	MsgBox % "The keyboard hook is " (ParseKeyHistory()[0]["K-hook"] ? "enabled." : "disabled.")
	Hotkey,$F1,On
return
just me
Posts: 5578
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

29 Sep 2015, 01:52

IMO, it would be much better to have it as a built-in function without the need to overwrite the contents of user32.dll (and without the example how this can be done).
lexikos
Posts: 6205
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

29 Sep 2015, 03:34

overwrite the contents of user32.dll
Perhaps you're being lazy with words; I'm fairly certain you understand that it is not overwriting a DLL file. It does not even affect processes other than the script's own process, since AutoHotkey 1.1 does not run on Win9x...
(and without the example how this can be done).
What do you mean? Assuming you are referring to "the example" of disabling a dll function within the script's memory space, AutoHotkey.exe and the documentation is and always will be "without the example". The example has been posted in several places over the years, and will stay regardless of whether this function is built-in.


I'm not very interested in using the information this function provides, as I indicated in the top post. Now that the information is easy to access (with this function), there is opportunity for users to prove that the information is actually useful, which might increase my motivation to provide some built-in way to access it.
User avatar
Trogluddite
Posts: 8
Joined: 23 Mar 2018, 06:42

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

23 Mar 2018, 07:15

Apologies for re-animating such an old thread (and with my first post too :roll: ), but this was exactly the function that I've been trying to work out for myself for about a week now!
Thankyou very much Lexicos! :D
there is opportunity for users to prove that the information is actually usefu
Now that I have this function to work from, I shall take you up on that! :mrgreen:
SAbboushi
Posts: 137
Joined: 08 Dec 2014, 22:13

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

02 Aug 2018, 21:21

Thanks! Needed this for AHK V2. Posting here in case anyone else needs it

Code: Select all

;====================================================================================================
; NOTES
;====================================================================================================

/*
	8/2/2018 1:37PM:
	SA modified for AHK V2
*/

$F1::
	KeyHistory:=ParseKeyHistory()
	MsgBox  "Time since last keypress: " KeyHistory[KeyHistory.MaxIndex()]["Elapsed"] " s."
return
F2::
	MsgBox "The keyboard hook is " (ParseKeyHistory()[0]["K-hook"] ? "enabled." : "disabled.")
	Hotkey "$F1", "Off"
	MsgBox "The keyboard hook is " (ParseKeyHistory()[0]["K-hook"] ? "enabled." : "disabled.")
	Hotkey "$F1", "On"
return

ParseKeyHistory(KeyHistory:="",ParseStringEnumerations:=1){
	/*
	Parses the text from AutoHotkey's Key History into an associative array:
	Header:
	KeyHistory[0]	["Window"]              	String
					["K-hook"]              	Bool
					["M-hook"]              	Bool
					["TimersEnabled"]       	Int
					["TimersTotal"]         	Int
					["Timers"]              	String OR Array 	[i] String
					["ThreadsInterrupted"]  	Int
					["ThreadsPaused"]       	Int
					["ThreadsTotal"]        	Int
					["ThreadsLayers"]       	Int
					["PrefixKey"]           	Bool
					["ModifiersGetKeyState"]	|String OR Array	["LAlt"]   Bool
					["ModifiersLogical"]    	|					["LCtrl"]  Bool
					["ModifiersPhysical"]   	|					["LShift"] Bool
																	["LWin"]   Bool
																	["RAlt"]   Bool
																	["RCtrl"]  Bool
																	["RShift"] Bool
																	["RWin"]   Bool
	Body:
	KeyHistory[i]	["VK"]     	String [:xdigit:]{2}
					["SC"]     	String [:xdigit:]{3}
					["Type"]   	Char [ hsia#U]
					["UpDn"]   	Bool (0=up 1=down)
					["Elapsed"]	Float
					["Key"]    	String
					["Window"] 	String
	*/
	
	
	If !(KeyHistory) && IsFunc("ScriptInfo")
		KeyHistory:=ScriptInfo("KeyHistory")

	RegExMatch(KeyHistory,"sm)(?P<Head>.*?)\s*^NOTE:.*-{109}\s*(?P<Body>.*)\s+Press \[F5] to refresh\.",KeyHistory_)
	
		/*
			"sm)": "s" = "." matches newline; "m" = treats strings as multilines and "^" and "$" 
				will match beginning and end of each line
				
			(?P<name>group) captures the match of group into the backreference "name".
			
			"(?P<Head>.*?)\s*^NOTE:.*-{109}": Match string up until the whitespace ("\s") before
				"^NOTE" which, after some characters, is followed by 109 instances of "-" (i.e.
				the underlines preceding the key history section)
				
			"(?P<Body>.*)\s+Press \[F5] to refresh\.": backreference "Body" starts after whitespace
				after the 109 "-" until the whitepace before "Press [F5]..."
			
			
			KeyHistory_: match object where [0] is entire match, [1] is "Head" group and [2] is 
				"body" group	
			
			
		*/
	
	xx:=keyhistory = keyhistory_
	
	KeyHistory:=[]

	RegExMatch(KeyHistory_[1],"Window: (.*)\s+Keybd hook: (.*)\s+Mouse hook: (.*)\s+Enabled Timers: (\d+) of (\d+) \((.*)\)\s+Interrupted threads: (.*)\s+Paused threads: (\d+) of (\d+) \((\d+) layers\)\s+Modifiers \(GetKeyState\(\) now\) = (.*)\s+Modifiers \(Hook's Logical\) = (.*)\s+Modifiers \(Hook's Physical\) = (.*)\s+Prefix key is down: (.*)",Re)

	;KeyHistory[0]:={"Window": Re1, "K-hook": (Re2="yes"), "M-hook": (Re3="yes"), "TimersEnabled": Re4, "TimersTotal": Re5, "Timers": Re6, "ThreadsInterrupted": Re7, "ThreadsPaused": Re8, "ThreadsTotal": Re9, "ThreadsLayers": Re10, "ModifiersGetKeyState": Re11, "ModifiersLogical": Re12, "ModifiersPhysical": Re13, "PrefixKey": (Re14="yes")}
	
	KeyHistory[0]:= {"Window": Re[1],
 	                "K-hook": (Re[2]="yes"),
                    "M-hook": (Re[3]="yes"),
                    "TimersEnabled": Re[4],
                    "TimersTotal": Re[5],
                    "Timers": Re[6],
                    "ThreadsInterrupted": Re[7],
                    "ThreadsPaused": Re[8],
                    "ThreadsTotal": Re[9],
                    "ThreadsLayers": Re[10],
                    "ModifiersGetKeyState": Re[11],
                    "ModifiersLogical": Re[12],
                    "ModifiersPhysical": Re[13],
                    "PrefixKey": (Re[14]="yes")}	

	If (ParseStringEnumerations){
		Loop Parse "ModifiersGetKeyState,ModifiersLogical,ModifiersPhysical", "CSV"
		{
			i:=A_Loopfield
			k:=KeyHistory[0][i]
			KeyHistory[0][i]:={}
			Loop Parse "LWin,LShift,LCtrl,LAlt,RWin,RShift,RCtrl,RAlt", "CSV"
				KeyHistory[0][i][A_LoopField]:=Instr(k,A_Loopfield)
		}
		
		k:=KeyHistory[0]["Timers"]
		KeyHistory[0]["Timers"]:=[]
		Loop Parse k, A_Space
			KeyHistory[0]["Timers"].Push(A_Loopfield)
	}

	Loop Parse KeyHistory_[2],"`n","`r"
	{
		RegExMatch(A_Loopfield,"(\w+) {2}(\w+)\t([ hsia#U])\t([du])\t(\S+)\t(\S*) *\t(.*)",Re)
		;KeyHistory.Push({"VK": Re1, "SC": Re2, "Type": Re3, "UpDn": (Re4="D"), "Elapsed": Re5, "Key": Re6, "Window": Re7})
		
		KeyHistory.Push({"VK": Re[1],
                     "SC": Re[2],
                     "Type": Re[3],
                     "UpDn": (Re[4]="D"),
                     "Elapsed": Re[5],
                     "Key": Re[6],
                     "Window": Re[7]})
	}
	
	Return KeyHistory
}
Sam_
Posts: 61
Joined: 20 Mar 2014, 20:24

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

27 Sep 2018, 21:41

On a potentially semi-related topic, is it possible to retrieve the callstack upon encountering an exception?
lexikos
Posts: 6205
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

28 Sep 2018, 19:41

You can obtain the call stack whenever you want by calling Exception in a loop as demonstrated elsewhere.

But when do you "encounter an exception"? If you throw the exception, you can get the call stack and attach it to the exception before throwing. If you catch an exception, the stack has already unwound to your handler, so getting it at that point might not be useful. For unhandled exceptions, you can register a callback with the new OnError function. The callback is called before the stack unwinds.

Improvements to exception handling are being planned for v2.
Sam_
Posts: 61
Joined: 20 Mar 2014, 20:24

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

28 Sep 2018, 19:49

You can obtain the call stack whenever you want by calling Exception in a loop as demonstrated elsewhere.

But when do you "encounter an exception"? If you throw the exception, you can get the call stack and attach it to the exception before throwing. If you catch an exception, the stack has already unwound to your handler, so getting it at that point might not be useful. For unhandled exceptions, you can register a callback with the new OnError function. The callback is called before the stack unwinds.

Improvements to exception handling are being planned for v2.
I'm sad to say this is news to me. Would you mind pointing me to some examples?
SAbboushi
Posts: 137
Joined: 08 Dec 2014, 22:13

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

17 Oct 2018, 14:18

ScriptInfo was working fine in AutoHotkeyV2 - 2.0-a096-2ad11cb after I changed one line:

Code: Select all

;Text := ControlGetText(, "ahk_id" hEdit)
    Text := ControlGetText(,"ahk_id" hEdit)
Just downloaded AutoHotkey_2.0-a100-52515e2.zip. ScriptInfo() now gives error:
Error: Missing a required parameter.

Line#
...
---> 049: Text := ControlGetText(,"ahk_id" hEdit)
...

Thoughts?
SAbboushi
Posts: 137
Joined: 08 Dec 2014, 22:13

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

17 Oct 2018, 14:45

Works fine on AutoHotkey_2.0-a099-ca386dc.zip
lexikos
Posts: 6205
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

18 Oct 2018, 20:39

The script is not compatible with a100, because the Control parameter is now mandatory.

Just change ,"ahk_id" hEdit to hEdit (remove the comma and string).
SAbboushi
Posts: 137
Joined: 08 Dec 2014, 22:13

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

05 Nov 2018, 11:16

there is opportunity for users to prove that the information is actually useful, which might increase my motivation to provide some built-in way to access it.
Just to reiterate that I find your script to be very useful when debugging (I use SciTE4AutoHotkey).

When I use KeyHistory/ListHotkeys... in my script, I get annoyed because their windows are not particularly accessible when the script is paused by a breakpoint / while single stepping through code i.e. I cannot activate their windows (except when eventually Windows flags them as "Not responding") and when they're "Not responding", I don't have access to the menus/hotkeys to switch between KeyHistory/ListHotkeys...

So getting the info I need through ScriptInfo is not unpleasant, especially with Nextron's ParseKeyHistory() which I am grateful for. Eventually one of us will get around to creating ParseListHotkeys and the others.

Bottom line: I'm certainly in favor of being able to access this info as objects via built-in functions in AHK V2
toralf
Posts: 511
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

06 Nov 2018, 15:02

You can obtain the call stack whenever you want by calling Exception in a loop as demonstrated elsewhere.
I'm sad to say this is news to me. Would you mind pointing me to some examples?
Here is the post for v2 . It also contains a link too the post for v1
https://autohotkey.com/boards/viewtopic.php?f=6&t=58700
ciao
toralf
Sam_
Posts: 61
Joined: 20 Mar 2014, 20:24

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

12 Nov 2018, 16:20

You can obtain the call stack whenever you want by calling Exception in a loop as demonstrated elsewhere.
I'm sad to say this is news to me. Would you mind pointing me to some examples?
Here is the post for v2 . It also contains a link too the post for v1
https://autohotkey.com/boards/viewtopic.php?f=6&t=58700
Thanks! I have played around with this a bit more and asked some additional questions in this topic. If you have any suggestions on how I could circumvent the limitations I have encountered, maybe we could continue the conversation in that topic?
SAbboushi
Posts: 137
Joined: 08 Dec 2014, 22:13

Re: ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text

15 Nov 2018, 10:14

there is opportunity for users to prove that the information is actually useful, which might increase my motivation to provide some built-in way to access it.
...When I use KeyHistory/ListHotkeys... in my script, I get annoyed because their windows are not particularly accessible when the script is paused by a breakpoint / while single stepping through code i.e. I cannot activate their windows (except when eventually Windows flags them as "Not responding") and when they're "Not responding", I don't have access to the menus/hotkeys to switch between KeyHistory/ListHotkeys...
My bad... seems I didn't prove anything other than I didn't do enough due-diligence: just saw the documentation on Interactive Debugging which clearly says to use "pause" after e.g. KeyHistory...

Nevertheless, I find ScriptInfo.ahk to be a little more... conventient :)

Return to “Scripts and Functions”

Who is online

Users browsing this forum: gwarble and 63 guests