jeeswg's documentation extension tutorial

Helpful script writing tricks and HowTo's
User avatar
Posts: 5132
Joined: 19 Dec 2016, 01:58
Location: UK

jeeswg's documentation extension tutorial

Post by jeeswg » 25 Jun 2017, 02:43

[updated: 2018-06-09]

This is part of a set of beginner's tutorials:
- beginner tutorial [to come]
- strings tutorial
- mathematics tutorial [to come]
- documentation extension tutorial

The documentation extension is:
- tips on commands/functions separated by category
- things I would add to (or highlight) in the documentation



[list every function/command/directive/variable/control flow statement]
list of every command/function/variable from across all versions - AutoHotkey Community ... 42#p131642

[chm to txt]
[C:\Program Files\AutoHotkey\AutoHotkey.chm to a txt file]
htm to txt, AutoHotkey Help (chm file) to txt - AutoHotkey Community

[chm to separate htm files]
[C:\Program Files\AutoHotkey\AutoHotkey.chm to separate htm files]
E.g. use 7-Zip.

[command/function list and parameter count/types]
conversion logic, v1 = -> v1 := -> v2, two-way compatibility - Page 5 - AutoHotkey Community ... 46#p140146

[command/function list and parameters]
list of every command/function/variable from across all versions - AutoHotkey Community ... 79#p130479

[how to create an old-style version of the latest AutoHotkey.chm file]
Suggestions on documentation improvements - Page 20 - AutoHotkey Community ... 83#p210683



[commands/functions, control flow statements, directives]
Alphabetical Command and Function Index
Alphabetical Function Index

[built-in variables, operators]
Variables and Expressions
Variables and Expressions

[auto-execute section, command-line parameters, comments, continuation sections, escape sequences]
AutoHotkey Scripts and Macros
AutoHotkey Scripts and Macros

Clipboard and ClipboardAll ... ipboardAll
Clipboard and ClipboardAll ... ipboardAll

[GUIs (AHK v1)]
GUI Control Types
ListView (GUI)
TreeView (GUI)

[GUIs (AHK v2)]
GuiCreate ... Create.htm
GUI Object
GuiControl Object ... ontrol.htm
GUI Control Types ... ntrols.htm
ListView (GUI)
TreeView (GUI)
MenuCreate ... Create.htm
MenuBarCreate ... Create.htm
Menu/MenuBar Object
OnCommand ... ommand.htm
OnEvent ... nEvent.htm
OnMessage ... essage.htm
OnNotify ... Notify.htm



>>>GUI: internal GUIs only
>>>GUI: internal/external GUIs, images
>>>GUI: notifications/dialogs
>>>interaction, files/folders, system
>>>binary data
>>>AutoHotkey control flow statements
>>>AutoHotkey main window menu items
>>>AutoHotkey modes/settings
>>>AutoHotkey itself (further commands/functions)
>>>(updated functions)


>>>GUI: internal GUIs only

[WARNING] Gui: If the Gui command is present in a script, or a script that it includes, that script becomes #Persistent. It can only be closed via ExitApp or by the user. I.e. this prevents scripts with no hotkeys from closing.
[LIMIT] Gui: The Gui command does not support specifying a custom class name: it must be 'AutoHotkeyGUI', although this can be edited in the AutoHotkey exe file or live in the virtual memory. Note: dialogs and windows can be created via dll functions, with any chosen class name, but these windows cannot be handled by the Gui command.
[LIMIT] Gui: The Gui command does not support specifying a window with no icon.
[LIMIT] Gui: There are no built-in commands/functions for use with toolbar controls.
[NOTE] Gui: In AHK v1, when labels handle GUI events the following variables can be useful: A_EventInfo, A_GuiXXX, A_ThisLabel.

Code: Select all

q:: ;create a simple GUI:
Gui, +HwndhWnd
Gui, Show, w300 h300
WinGetTitle, vWinTitle, % "ahk_id " hWnd
MsgBox, % vWinTitle

>>>GUI: internal/external GUIs, images
>>(also: #IfWinActive)

[LIMIT] ProcessSetPriority: There is no 'ProcessGetPriority' function.
[LIMIT] ControlGetText: There is no support for external treeviews.
[LIMIT] StatusBarGetText/StatusBarWait: only work on a control of the form ClassNN 'msctls_statusbar321', i.e. not on a second or custom status bar.
[NOTE] WinGetTransColor/WinGetTransparent: unfortunately, it is not possible to make one colour partially transparent using these commands (there may or may not be alternative methods to achieve this).
[NOTE] #IfWinActive: For '#IfWinActive WinCriteria', where 'WinCriteria' is not blank, then 'hWnd := WinExist()' will contain the hWnd of the window that matched WinCriteria when the hotkey was triggered, the 'Last Found Window'.
[NOTE] ImageSearch: To perform an image search on image data in memory, e.g. from a file, rather than live on the screen, use the custom function: Gdip_ImageSearch.
[NOTE] ImageSearch: The ErrorLevel values for ImageSearch can be confusing, it is: '0=found,1=not found,2=problem' (it is *not*: '0=not found,1=found').
[NOTE] WinGet/ControlGet: To get the window/control under the cursor use MouseGetPos.
[NOTE] WinWait/WinWaitActive: If a matching window is found, you can use hWnd := WinExist() to retrieve its hWnd.
[NOTE] WinGetProcessName/WinGetProcessPath: The case of the name/path retrieved does not necessarily match the that of the exe. A_LoopFileLongPath can be used to correct the case of a path.

Code: Select all

;#IfWinActive and Last Found Window
#IfWinActive A ;matches any window (Last Found Window set)
#IfWinActive ;matches any window (Last Found Window not set)
hWnd := WinExist()
WinGetTitle, vWinTitle, % "ahk_id " hWnd
MsgBox, % vWinTitle
[Use the Acc library to get text from more control types:]
Acc library (MSAA) and AccViewer download links - AutoHotkey Community

Code: Select all

;e.g. LoadPicture:
hBitmap := LoadPicture(A_AhkPath) ;without type parameter, bitmap
hIcon := LoadPicture(A_AhkPath, "", vType) ;with type parameter, depends on the source

;e.g. ImageSearch:
ImageSearch, vPosX, vPosY, 0, 0, % A_ScreenWidth, % A_ScreenHeight, % "*84 HICON:*" hIcon

Code: Select all

q:: ;list visible/hidden windows
DetectHiddenWindows, On
WinGet, vWinList, List
vOutput := ""
Loop, % vWinList
	hWnd := vWinList%A_Index%
	WinGetTitle, vWinTitle, % "ahk_id " hWnd
	WinGetClass, vWinClass, % "ahk_id " hWnd
	WinGet, vPName, ProcessName, % "ahk_id " hWnd
	;WinGet, vPID, PID, % "ahk_id " hWnd
	vWinIsVisible := DllCall("user32\IsWindowVisible", Ptr,hWnd) ;1 = visible, 0 = not visible
	vWinVisibility := vWinIsVisible ? "V" : "H"
	vOutput .= vWinVisibility "|" hWnd "|" vWinTitle "|" vWinClass "|" vPName "`r`n"
	;alternatively to list hidden windows only:
	;if !vWinIsVisible
	;	vOutput .= hWnd "|" vWinTitle "|" vWinClass "|" vPName "`r`n"
MsgBox, % Clipboard := vOutput

Code: Select all

;q:: ;get a windows's coordinates
WinGet, hWnd, ID, A
WinGetPos, vWinX, vWinY, vWinW, vWinH, % "ahk_id " hWnd
vCoords1 := "X" vWinX ",Y" vWinY ",W" vWinW ",H" vWinH " (R" vWinX+vWinW ",B" vWinY+vWinH ")"

VarSetCapacity(RECT, 16, 0)
DllCall("user32\GetClientRect", Ptr,hWnd, Ptr,&RECT)
DllCall("user32\ClientToScreen", Ptr,hWnd, Ptr,&RECT)
vWinX := NumGet(RECT, 0, "Int"), vWinY := NumGet(RECT, 4, "Int")
vWinW := NumGet(RECT, 8, "Int"), vWinH := NumGet(RECT, 12, "Int")
vCoords2 := "X" vWinX ",Y" vWinY ",W" vWinW ",H" vWinH " (R" vWinX+vWinW ",B" vWinY+vWinH ")"

vOutput := ""
. "window: " vCoords1 "`r`n"
. "window's client area: " vCoords2
MsgBox, % vOutput

Code: Select all

q:: ;get a control's coordinates
WinGet, hWnd, ID, A
vCtlClassNN := "Edit1"
;vCtlClassNN := "DirectUIHWND3"
;vCtlClassNN := "SHELLDLL_DefView1"
ControlGet, hCtl, Hwnd,, % vCtlClassNN, A

;relative to screen
WinGetPos, vCtlX, vCtlY, vCtlW, vCtlH, % "ahk_id " hCtl
vCoords1 := "X" vCtlX ",Y" vCtlY ",W" vCtlW ",H" vCtlH " (R" vCtlX+vCtlW ",B" vCtlY+vCtlH ")"

;relative to window (specify ClassNN and hWnd)
ControlGetPos, vCtlX, vCtlY, vCtlW, vCtlH, % vCtlClassNN, A
vCoords2 := "X" vCtlX ",Y" vCtlY ",W" vCtlW ",H" vCtlH " (R" vCtlX+vCtlW ",B" vCtlY+vCtlH ")"

;relative to window (specify hCtl)
ControlGetPos, vCtlX, vCtlY, vCtlW, vCtlH,, % "ahk_id " hCtl
vCoords2X := "X" vCtlX ",Y" vCtlY ",W" vCtlW ",H" vCtlH " (R" vCtlX+vCtlW ",B" vCtlY+vCtlH ")"

;relative to window's client area
VarSetCapacity(RECT, 16, 0)
DllCall("GetWindowRect", Ptr,hCtl, Ptr,&RECT)
DllCall("MapWindowPoints", Ptr,0, Ptr,hWnd, Ptr,&RECT, UInt,2)
vCtlX := NumGet(RECT, 0, "Int"), vCtlY := NumGet(RECT, 4, "Int")
vCtlW := NumGet(RECT, 8, "Int")-vCtlX, vCtlH := NumGet(RECT, 12, "Int")-vCtlY
vCoords3 := "X" vCtlX ",Y" vCtlY ",W" vCtlW ",H" vCtlH " (R" vCtlX+vCtlW ",B" vCtlY+vCtlH ")"

vOutput := ""
. "relative to screen: " vCoords1 "`r`n"
. "relative to window: " vCoords2 "`r`n"
. "relative to window: " vCoords2X "`r`n"
. "relative to window's client area: " vCoords3
MsgBox, % vOutput
[e.g. repeated ImageSearch/PixelSearch:]
[repeated ImageSearch][ImageSearch: click image every time it's found]
My first project: How hard/easy it is to develop a script for this? - AutoHotkey Community
[repeated PixelSearch][PixelSearch: click pixel every time it's found]
after 4 hours i decided i should get help lol - AutoHotkey Community

For the full list of ahk_class/ahk_id/ahk_pid/ahk_exe/ahk_group see:
WinTitle & Last Found Window

colour names and RGB values (color names):


>>>GUI: notifications/dialogs
>>FileSelect/DirSelect (previously: FileSelectFile/FileSelectFolder)
>>(also previously: Progress/SplashImage/SplashTextOn)

[BACKGROUND] FileSelect: uses the Common File Dialog (pre-Windows Vista), and the Common Item Dialog (post-Windows Vista).
[LIMIT] FileSelect: has a different appearance depending on the operating system, you cannot choose the old style on a newer operating system.
[BACKGROUND] MsgBox: is based on the MessageBox dll function.
[BACKGROUND] InputBox: is based on the DialogBox dll function and a dialog resource inside the AutoHotkey exe (which can be inspected using Resource Hacker).
[LIMIT] MsgBox/InputBox: You cannot specify the font for MsgBox/InputBox commands. (The 'Font' parameter for InputBox is not implemented.)
[LIMIT] MsgBox: can cause a beep.
[NOTE] MsgBox: in AHK v2, the Text parameter has two defaults, 'Press OK to continue.', for MsgBox(), and a blank string otherwise, e.g. MsgBox(,, "").


>>>interaction, files/folders, system
>>ClipWait (also: Clipboard variable)
>>SoundXXX/DriveXXX/SysGet/MonitorXXX (also: SoundBeep, SoundPlay)
>>(also: A_OSVersion, A_AppData)

[LIMIT] FileRead: does not support UTF-16 BE ('Unicode big endian' on Notepad), use LCMapStringW (DllCall).
[LIMIT] FileGetTime/FileSetTime: do not handle milliseconds, and only handle local time, not UTC time.
[LIMIT] FileAppend/FileCopy/FileMove: only work if the specified directory already exists.
[LIMIT] FileRead: use *c to read the contents of a binary file, unfortunately you cannot do *c4 for example, to read the first 4 bytes. [Fixed in AHK v2.]
[WARNING] FileRead/FileAppend: do not support all of the encodings listed at: Code Page Identifiers (Windows)
[WARNING] FileAppend: uses 'End of line (EOL) translation' by default, (it replaces lone LFs with CRLFs), use * to prevent this.
[LIMIT] FileGetShortcut: cannot retrieve a lnk file's shortcut key. Although FileGetShortcut can specify a shortcut key.
[BACKGROUND] PostMessage/SendMessage: AHK has PostMessage/SendMessage commands, but PostMessage and SendMessage are also dll functions, and can be used with DllCall.
[NOTE] PostMessage/SendMessage: both send a message to a window/control, however, the SendMessage command waits until it receives a reply before moving on to the next line.
[WARNING] IniRead: 'To store a blank value (empty string), specify %A_Space%'.
[LIMIT] IniRead/IniWrite: use GetPrivateProfileString and WritePrivateProfileString which don't support UTF-8, although you could create an ANSI ini file, and convert the bytes to UTF-8 when reading/writing.
[NOTE] IniWrite: if the ini file does not already exist, it will create a UTF-16 LE/ANSI ini file depending on whether the AHK version is Unicode/ANSI.
[NOTE] IniRead/IniWrite: cannot handle all characters in a key name e.g. these 3: [=;
[LIMIT] RegWrite: There is no 'RegCreateKey' function to create empty registry keys (cf. RegWrite which creates registry values).
[NOTE] SoundBeep: can only play one sound at a time, even if you open multiple scripts.
[NOTE] Run: To keep a ComSpec window open after the task completes use switch: '/K'.
[LIMIT] A_AppData: There is no 'A_Recent' variable, for the Recent Items folder.
[NOTE] A_AppData: %A_AppData% is C:\Users\%A_UserName%\AppData\Roaming (%A_AppData% is not C:\Users\%A_UserName%\AppData) (e.g. on Windows 7).
[NOTE] A_OSVersion gives a friendly name in AHK v1, but gives a version number in AHK v2

value types, same order as they appear in Registry Editor (Windows 7):
REG_SZ String Value
REG_BINARY Binary Value
REG_DWORD DWORD (32-bit) Value
REG_QWORD QWORD (64-bit) Value
REG_MULTI_SZ Multi-String Value
REG_EXPAND_SZ Expandable String Value

root keys, same order as they appear in Registry Editor (Windows 7):

Code: Select all

q:: ;FileAppend/FileRead UTF-16 BE (in AHK Unicode versions)
vLE := Chr(8730) ;square root sign
vLE := Chr(65279) vLE ;prepend BOM
vChars := StrLen(vLE)
VarSetCapacity(vBE, vChars*2)
DllCall("kernel32\LCMapStringW", UInt,0, UInt,0x800, Str,vLE, Int,vChars, Str,vBE, Int,vChars)
vPath = %A_Desktop%\z %A_Now%.txt
FileAppend, % vBE, % "*" vPath, UTF-16-RAW

FileRead, vBE, % "*P1200 " vPath ;read as UTF-16 LE
FileGetSize, vSize, % vPath
vChars := Floor(vSize/2)
VarSetCapacity(vLE, vChars*2)
DllCall("kernel32\LCMapStringW", UInt,0, UInt,0x800, Str,vBE, Int,vChars, Str,vLE, Int,vChars)
if (SubStr(vLE, 1, 1) = Chr(65279))
	vLE := SubStr(vLE, 2)
MsgBox, % vLE

Code: Select all

q:: ;convert Unicode text to ANSI (lossy) (best-fit characters on/off)
vText := Chr(8730) ;square root sign

;FileAppend (ANSI) and FileRead (best-fit characters on)
vPath = %A_Desktop%\z %A_Now%.txt
FileAppend, % vText, % "*" vPath, CP1252
FileRead, vTextNew, % vPath
MsgBox, % vTextNew ;v (best-fit character for square root sign)

;StrGet (best-fit characters off)
VarSetCapacity(vTemp, StrPut(vText, "CP0")*2)
StrPut(vText, &vTemp, "CP0")
vTextNew := StrGet(&vTemp, "CP0")
MsgBox, % vTextNew ;?

;DllCall (best-fit characters on/off)
Loop, 2
	vBFC := (A_Index = 1)
	vFlags := vBFC ? 0 : 0x400

	;UTF-16 LE to ANSI
	vChars := StrLen(vText)+1
	VarSetCapacity(vTemp, vChars, 0)
 	DllCall("kernel32\WideCharToMultiByte", UInt,0, UInt,vFlags, Ptr,&vText, Int,vChars, Ptr,&vTemp, Int,vChars, Ptr,0, Ptr,0)

	;ANSI to UTF-16 LE
	VarSetCapacity(vTextNew, vChars*2, 0)
 	DllCall("kernel32\MultiByteToWideChar", UInt,0, UInt,0, Ptr,&vTemp, Int,vChars, Str,vTextNew, Int,vChars)
	MsgBox, % vTextNew

Code: Select all

q:: ;FileAppend/FileRead (different encodings)
vText := "hello world " Chr(8730) ;square root sign
vList := "CP1252,UTF-8,UTF-8-RAW,UTF-16,UTF-16-RAW"
Loop, Parse, vList, % ","
	vPath = %A_Desktop%\z enc %A_LoopField%.txt
	if !FileExist(vPath)
		FileAppend, % vText, % "*" vPath, % A_LoopField

;FileRead force opens a file with a UTF-8/UTF-16 BOM as UTF-8/UTF-16
vOutput := ""
Loop, Parse, vList, % ","
	vPath = %A_Desktop%\z enc %A_LoopField%.txt
	FileRead, vText1, % "*P1252 " vPath ;read as ANSI (CP-1252)
	FileRead, vText2, % "*P65001 " vPath ;read as UTF-8
	FileRead, vText3, % "*P1200 " vPath ;read as UTF-16 LE
	vOutput .= "- " A_LoopField "`r`n" vText1 "`r`n" vText2 "`r`n" vText3 "`r`n"
MsgBox, % vOutput

;FileRead alternative, ignore BOM
vOutput := ""
Loop, Parse, vList, % ","
	vPath = %A_Desktop%\z enc %A_LoopField%.txt
	FileGetSize, vSize, % vPath
	FileRead, vText, % "*c " vPath
	vText1 := StrGet(&vText, "CP1252")
	vText2 := StrGet(&vText, "UTF-8")
	vText3 := StrGet(&vText, "UTF-16")
	vOutput .= "- " A_LoopField "`r`n" vText1 "`r`n" vText2 "`r`n" vText3 "`r`n"
MsgBox, % vOutput

Code: Select all

q:: ;identify the encoding of a txt file
vPath := A_ScriptFullPath
vEnc := ""
if (oFile := FileOpen(vPath, "r"))
	vEnc := oFile.Encoding
	if !(SubStr(vEnc, 1, 3) = "UTF") ;e.g. CP1252/UTF-8/UTF-16
		if (oFile.ReadUShortType() = 65534) ;0xFFFE
			vEnc := "UTF-16 BE"
			vEnc := "ANSI"
MsgBox, % vEnc "`r`n" vPath

Code: Select all

q:: ;get Windows version number (e.g. Windows 7: 6.1.7601)
vVersion := DllCall("kernel32\GetVersion", UInt)
MsgBox, % Format("{}.{}.{:04}", vVersion & 0xFF, (vVersion >> 8) & 0xFF, (vVersion >> 16) & 0xFFFF)

Code: Select all

;get the output from a console application
MsgBox, % JEE_RunGetStdOut(ComSpec " /c set")

JEE_RunGetStdOut(vTarget, vSize:="")
	DetectHiddenWindows, On
	vComSpec := A_ComSpec ? A_ComSpec : ComSpec
	Run, % vComSpec,, Hide, vPID
	WinWait, % "ahk_pid " vPID
	DllCall("kernel32\AttachConsole", UInt,vPID)
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vTarget)
	vStdOut := ""
	if !(vSize = "")
		VarSetCapacity(vStdOut, vSize)
	while !oExec.StdOut.AtEndOfStream
		vStdOut := oExec.StdOut.ReadAll()
	Process, Close, % vPID
	return vStdOut


[NOTE] ObjAddRef/ObjRelease: Be careful with ObjAddRef and ObjRelease, and consider deleting objects when not needed e.g. oMyObj := "".
[LIMIT] Object: AHK objects do not have a Count method as standard.
[NOTE] Object: A key can have a value or child keys, but not both.
[LIMIT] Array: when defining an object via [] or Array(), it can have at most 255 keys (more keys can be added later) (StrSplit could be used instead)
[LIMIT] Object: when defining an object via {} or Object(), it can have at most 127 keys (more keys can be added later)

Code: Select all

;all 4 are equivalent:
oMyArray := Array(), oMyArray := []
oMyObj := Object(), oMyArray := {}

Code: Select all

;workarounds to get the object count:

	vCount := 0
	for vKey, vValue in oObj
	return vCount

;note: not guaranteed to work in future
	return NumGet(&oObj + 4*A_PtrSize)

Code: Select all

q:: ;increment via ++ doesn't work in the same way for object keys as variables, when applied to blank strings
var := 0
obj := {}, obj.a := 0
MsgBox, % var ;1
MsgBox, % obj.a ;1

var := ""
obj := {}, obj.a := ""
MsgBox, % var ;1
MsgBox, % obj.a ;(blank)

Code: Select all

q:: ;example of looping through an array
;this creates keys: '1,2,3,4' with values 'a,b,c,d' respectively
oArray := ["a","b","c","d"]

;now oArray[3] points to an object, instead of a string
;when it's value is requested, it will return blank
oArray.3 := ["e","f","g","h"]

vOutput := ""
for vKey, vValue in oArray
	vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

vOutput := ""
for vKey, vValue in oArray.3
	vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

>>Chr/Ord (previously: Asc)
>>(also: A_IsUnicode, if var in/contains list, if var is type)

[LIMIT] RegExMatch: In AHK Unicode, RegExMatch can only handle 2-byte pairs at 2-byte intervals, so is limited when handling ANSI/UTF-8/binary searches.
[NOTE] RegExMatch/RegExReplace: can be slow compared to other methods, e.g. sometimes using StrReplace multiple times can be faster than using RegExReplace.
[NOTE] RegExMatch/RegExReplace: 'Options)' is AHK-specific and is not a standard part of RegEx, although these options can usually be applied via standard RegEx instead.
[NOTE] RegExMatch/RegExReplace: There are some RegEx techniques that can be used that aren't in the AHK documentation, see: 'PCRE REGULAR EXPRESSION SYNTAX SUMMARY', at: pcresyntax specification
[LIMIT] StrReplace: does not have a case sensitive parameter (cf. InStr). Use StringCaseSense to set the case sensitive/insensitive mode, or use RegExReplace to do a case sensitive/insensitive text replace.
[WARNING] Sort: uses unstable sort: e.g. if two items are regarded as identical e.g. 'A' and 'a' in a case-insensitive sort, the items may be swapped (the relative positions are not maintained). In a stable sort, identical items remain in their original order.
[WARNING (AHK V2)] InStr/SubStr/RegExMatch/RegExReplace: handle negative offsets differently.
[WARNING] Chr/Asc/Ord: differ in the range 128-255 depending on whether the AHK version is Unicode/ANSI.
[NOTE] StringXXX: StringXXX commands are generally deprecated, although StringCaseSense and StringLower/StringUpper are not deprecated.
[WARNING] A_IsUnicode: A_IsUnicode's value is 1 or blank (not 1 or 0).
[WARNING] if var is [not] type: a blank string returns a positive result for some types e.g. it is considered as alphanumeric/alpha.
[WARNING] If you do Trim(var, "abc"), that is a list of individual characters to trim, e.g. Trim(var, "`r`n") will trim any CRs or LFs, not just consecutive blocks of CRLFs.

for 'if var between/contains/in/is'
the position of the word 'not' is inconsistent:
if var [not] between low and high
if var [not] contains list
if var [not] in list
if var is [not] type

Code: Select all

;e.g. SubStr get last n characters:
SubStr(vText, 1-n) ;AHK v1
SubStr(vText, -n) ;AHK v2
vIsV1 := !!SubStr(1,0)
SubStr(vText, vIsV1-n) ;two-way compatible

Code: Select all

;callouts retrieve every possible matching pattern
q:: ;RegEx - use a custom callout function to understand what RegExMatch/RegExReplace is doing
RegExMatch("abc", ".*", vMatch)
MsgBox, % vMatch

vOutput := ""
RegExMatch("abc", ".*(?CMyCallout)")
MsgBox, % SubStr(vOutput, 1, -1) ;abc,ab,a,,

MsgBox, % RegExReplace("abc", ".*", "")

vOutput := ""
RegExReplace("abc", ".*(?CMyCallout)")
MsgBox, % SubStr(vOutput, 1, -1) ;abc,ab,a,,

	global vOutput
	vOutput .= vMatch ","
	return 1

Code: Select all

q:: ;for each 'if var is type', does blank return positive
;note: 'number', not 'numeric'
vList := "integer,float,number,digit,xdigit,alpha,upper,lower,alnum,space,time"
vText := ""

Loop, Parse, vList, % ","
	vType := A_LoopField
	if vText is %vType%
		vOutput .= "y`t" vType "`r`n"
		vOutput .= "n`t" vType "`r`n"
MsgBox, % Clipboard := vOutput

;blank yields positive:

;blank yields negative:

Code: Select all

q:: ;for each 'if var is type', how many characters return positive
;note: 'number', not 'numeric'
vList := "integer,float,number,digit,xdigit,alpha,upper,lower,alnum,space,time"

vOutput := ""
VarSetCapacity(vOutput, 10000*2)
Loop, Parse, vList, % ","
	vType := A_LoopField
	vCount := 0
	vChars := ""
	VarSetCapacity(vChars, 10000*2)
	Loop, 65535
		vChar := Chr(A_Index)
		if vChar is %vType%
			vCount += 1, vChars .= vChar
	vOutput .= vCount "`t" vType "`t" vChars "`r`n"
MsgBox, % Clipboard := vOutput

;10	integer
;0	float
;10	number
;3328	digit
;5632	xdigit
;52	alpha
;26	upper
;26	lower
;62	alnum
;25	space
;0	time

Code: Select all

;understanding custom sort functions:

;if want a1 to be earlier in the new list return a negative number
;if want a2 to be earlier in the new list return a positive number
;if want a1 to be later in the new list return a positive number
;if want a2 to be later in the new list return a negative number
;if offset is negative, a2 was earlier in the original list
;if offset is positive, a1 was earlier in the original list

;to reverse order: return offset
;to maintain order: return -offset

;example from the documentation
MyVar = 1,2,3,4
Sort, MyVar, F ReverseDirection D,  ; Reverses the list so that it contains 4,3,2,1
ReverseDirection(a1, a2, offset)
    return offset  ; Offset is positive if a2 came after a1 in the original list; negative otherwise.

Code: Select all

;unstable v. stable sort:

;example from the documentation (unstable sort)
MyVar = def`nabc`nmno`nFGH`nco-op`ncoop`ncop`ncon`n
Sort, MyVar, F StringSort
StringSort(a1, a2)
    return a1 > a2 ? 1 : a1 < a2 ? -1 : 0  ; Sorts alphabetically based on the setting of StringCaseSense.

;example from the documentation (edited) (stable sort)
MyVar = def`nabc`nmno`nFGH`nco-op`ncoop`ncop`ncon`n
Sort, MyVar, F StringSort
StringSortStable(a1, a2, offset)
    return a1 > a2 ? 1 : a1 < a2 ? -1 : -offset  ; Sorts alphabetically based on the setting of StringCaseSense.

>>>binary data
>>(also: StrGet/StrPut)

[LIMIT] NumGet/NumPut: There is no recommended example function for moving memory, use RtlMoveMemory (DllCall).
[WARNING] NumGet/NumPut: For NumGet/NumPut the default parameter used to be UInt, it is now UPtr.
[NOTE] NumGet/NumPut: Read/write numbers in little endian form, e.g. 0x01020304 gets written as 0x04030201.

Code: Select all

q:: ;move memory
vData1 := "ABCDE"
vData2 := "abcde"
vSize := 4
;note: RtlMoveMemory parameter order is: 'dest, source' [NOT 'source, destination']
DllCall("kernel32\RtlMoveMemory", Ptr,&vData2, Ptr,&vData1, UPtr,vSize)
MsgBox, % vData2

Code: Select all

JEE_HexToBinData(vHex, ByRef vSize:="")
	vChars := StrLen(vHex)
	;CRYPT_STRING_HEXRAW := 0xC ;(not supported by Windows XP)
	DllCall("crypt32\CryptStringToBinary", Ptr,&vHex, UInt,vChars, UInt,0x4, Ptr,0, UIntP,vSize, Ptr,0, Ptr,0)
	VarSetCapacity(vData, vSize, 0)
	DllCall("crypt32\CryptStringToBinary", Ptr,&vHex, UInt,vChars, UInt,0x4, Ptr,&vData, UIntP,vSize, Ptr,0, Ptr,0)
	return &vData


JEE_BinDataToHex(vAddr, vSize)
	;CRYPT_STRING_HEX := 0x4 ;to return space/CRLF-separated text
	;CRYPT_STRING_HEXRAW := 0xC ;to return raw hex (not supported by Windows XP)
	DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0x4, Ptr,0, UIntP,vChars)
	VarSetCapacity(vHex, vChars*2, 0)
	DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0x4, Str,vHex, UIntP,vChars)
	vHex := StrReplace(vHex, "`r`n")
	vHex := StrReplace(vHex, " ")
	return vHex


JEE_BinDataToHex2(vAddr, vSize)
	;CRYPT_STRING_HEXRAW := 0xC ;to return raw hex (not supported by Windows XP)
	DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0xC, Ptr,0, UIntP,vChars)
	VarSetCapacity(vHex, vChars*2, 0)
	DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0xC, Str,vHex, UIntP,vChars)
	return vHex

>>(also: A_Now, A_NowUTC, A_MSec, A_TickCount)

[WARNING] FormatTime:

Code: Select all

FormatTime, vWDay, % vDate, WDay
gives 2 even when the date is invalid.
[NOTE] A_Now: The A_Now variable is of the form 'yyyyMMddHHmmss'.
[NOTE] DateAdd: add (or *subtract*) an amount of time from a date.
[NOTE] DateDiff: get the amount of time between 2 dates.
[NOTE] DateDiff: rounds down.

The Winapi uses 2 main time structures:
FILETIME, intervals since 01/01/1601, 1 interval = 100 nanoseconds = 0.1 microseconds. 1 second = 10000000 intervals. 1 millisecond = 10000 intervals.
SYSTEMTIME, 'friendly', Y/M/WDay/D/H/M/S/MS
GetLocalTime (local time as SYSTEMTIME)
GetSystemTime (UTC time as SYSTEMTIME)
(no handy function?) (local time as FILETIME)
GetSystemTimeAsFileTime (UTC time as FILETIME)
Time Functions (Windows) ... s.85).aspx

Code: Select all

q:: ;'vDate := A_Now A_MSec' is unreliable (as are any combination of individual AHK date/time variables e.g. A_YYYY, A_Sec)
vOutput := ""
vCount := 0
vCount2 := 0
	if (vCount = 3) || (vCount2 = 1000)
	vDate := A_Now " " A_MSec
	vPfx := (vDate < vDate2) ? ("x", vCount += 1) : ""
	if (vDate = vDate2)
	vOutput .= vPfx "`t" vDate "`r`n"
	vDate2 := vDate
MsgBox, % "errors: " vCount
MsgBox, % Clipboard := vOutput


w:: ;'vDate := A_Now A_MSec' is unreliable (as are any combination of individual AHK date/time variables e.g. A_YYYY, A_Sec)
;in this example, A_MSec is retrieved before A_Now is
vOutput := ""
vCount := 0
vCount2 := 0
	if (vCount = 3) || (vCount2 = 1000)
	vMSec := A_MSec
	vDate := A_Now " " vMSec
	vPfx := (vDate < vDate2) ? ("x", vCount += 1) : ""
	if (vDate = vDate2)
	vOutput .= vPfx "`t" vDate "`r`n"
	vDate2 := vDate
MsgBox, % "errors: " vCount
MsgBox, % Clipboard := vOutput

Code: Select all

q:: ;reliable alternative to 'vDate := A_Now A_MSec'
DllCall("kernel32\GetSystemTimeAsFileTime", Int64P,vIntervalsUTC)
vDateUTC := 1601
vDateUTC += % Floor(vIntervalsUTC/10000000), Seconds
vMSec := Mod(Floor(vIntervalsUTC/10000),1000)

;DllCall("kernel32\GetSystemTimeAsFileTime", Int64P,vIntervalsUTC)
DllCall("kernel32\FileTimeToLocalFileTime", Int64P,vIntervalsUTC, Int64P,vIntervalsLocal)
vDateLocal := 1601
vDateLocal += % Floor(vIntervalsLocal/10000000), Seconds
vMSec := Mod(Floor(vIntervalsLocal/10000),1000)

MsgBox, % vDateUTC " " vMSec "`r`n" vDateLocal " " vMSec "`r`n`r`n" A_NowUTC " " A_MSec "`r`n" A_Now " " A_MSec

Code: Select all

q:: ;DateDiff rounds down
MsgBox, % DateDiff(19990101, 19990101, "D") ;0
MsgBox, % DateDiff(1999010103, 19990101, "D") ;0
MsgBox, % DateDiff(1999010121, 19990101, "D") ;0
MsgBox, % DateDiff(19990102, 19990101, "D") ;1

MsgBox, % DateDiff(19990101, 19990101, "H") ;0
MsgBox, % DateDiff(199901010003, 19990101, "H") ;0
MsgBox, % DateDiff(199901010057, 19990101, "H") ;0
MsgBox, % DateDiff(1999010101, 19990101, "H") ;1

Code: Select all

q:: ;get weekday and month in English
;note: AutoHotkey uses 1-7 for Sun-Mon (not 0-6)
oMonth := StrSplit("January,February,March,April,May,June,July,August,September,October,November,December", ",")
oMonth3 := StrSplit("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ",")
oDay := StrSplit("Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", ",")
oDay3 := StrSplit("Sun,Mon,Tue,Wed,Thu,Fri,Sat", ",")

MsgBox, % oMonth[A_MM] "`r`n" oMonth3[A_MM]
MsgBox, % oDay[A_WDay] "`r`n" oDay3[A_WDay]

vDate := A_Now
FormatTime, vWDay, % vDate, WDay
FormatTime, vMM, % vDate, MM
FormatTime, vDate, % vDate, % "'" oDay3[vWDay] "' dd '" oMonth3[vMM] "' yyyy HH:mm:ss"
MsgBox, % vDate

>>Download (previously: UrlDownloadToFile)


[WARNING] Mod: In theory the Mod function could be used to truncate a number to n decimal places, however it uses a curious definition of Mod.
[NOTE] Mod: If the dividend is negative, the output is negative.
[NOTE] Random: To return a random integer, both range values should be integers, otherwise at least one range value should have a decimal point.

Mod function returning wrong value? - AutoHotkey Community ... 14&t=29762

Code: Select all

q:: ;Dec2Hex and Hex2Dec
;e.g. Dec2Hex:
vNum := 255
MsgBox, % vHex := Format("{:X}", 255) ;FF
MsgBox, % vHex := Format("0x{:X}", 255) ;0xFF

;e.g. Hex2Dec
vHex := 0xFF
MsgBox, % vNum := vHex + 0 ;255

vHex := "FF"
vHex := "0x" vHex
MsgBox, % vNum := vHex + 0 ;255

vHex := "FF"
MsgBox, % vHex := Format("{:i}", "0x" vHex) ;255
jeeswg's characters tutorial - AutoHotkey Community


>>BlockInput/SendXXX (also: ControlClick, ControlSend)
>>Click/MouseXXX (also: A_CaretX/A_CaretY, A_Cursor)
>>(also: CoordMode, A_CoordModeXXX, SetXXX)

[WARNING] A_CaretX/A_CaretY: don't work correctly when multiple characters are selected.
[LIMIT] Hotkey: Not all hotkeys can be defined: Fn key, reserved Windows hotkeys, three-key hotkeys, not every key can act as a modifier key. LAlt can cause issues e.g. with menu bars.
[LIMIT] Hotkey: There is no 'Hotstring' command to create hotstrings dynamically (cf. Hotkey command).
[LIMIT] Hotkey: Not all hotstrings containing a colon (in the abbreviation/output) are possible.
[NOTE] Hotkey: A semicolon hotkey: '`;::' or 'SC027::', a ctrl+semicolon hotkey: '^;::' or '^SC027::'.
[NOTE] Click: Click's parameters can make it awkward to use.
[LIMIT] ControlSend: is a bit unreliable.
[NOTE] ControlSend: If ControlSend by itself doesn't work, sometimes doing a prior ControlFocus, before the ControlSend, does work. Note: using ControlFocus on an inactive window, does not mean that the current active window will lose focus.
[WARNING] ControlClick: is a bit unreliable, e.g. test on MS Paint, it can send clicks to all points between the current cursor position and the specified point, like dragging the mouse would.
[NOTE] SendXXX: Be aware of the {Raw} option, to send text, and avoid treating special characters like +^#!{} differently.
[NOTE] SendXXX: Be aware that sending CRLF (i.e. '`r`n' in AutoHotkey) will send 2 enters, not one.

Code: Select all

;e.g. click without moving the cursor
q:: ;4 methods to send a click
CoordMode, Mouse, Screen
vPosX := A_ScreenWidth/2, vPosY := A_ScreenHeight/2

;method 1 (moves cursor)
MouseClick,, % vPosX, % vPosY

;method 2 (moves cursor)
Click, % vPosX ", " vPosY ;moves mouse

;method 3 (doesn't move cursor)
WinGetPos, vWinX, vWinY,,, A
ControlClick, % "x" -vWinX+vPosX " y" -vWinY+vPosY, A

;method 4 (doesn't move cursor)
;if !hWnd := DllCall("WindowFromPoint", Int,vPosX, Int,vPosY, Ptr)
if !hWnd := DllCall("WindowFromPoint", UInt64,(vPosX&0xFFFFFFFF)|(vPosY<<32), Ptr)
PostMessage, 0x201, 0, % (vPosX & 0xFFFF)|(vPosY<<16),, % "ahk_id " hWnd ;WM_LBUTTONDOWN := 0x201
PostMessage, 0x202, 0, % (vPosX & 0xFFFF)|(vPosY<<16),, % "ahk_id " hWnd ;WM_LBUTTONUP := 0x202

Code: Select all

;e.g. hover without moving the cursor
Home:: ;start menu (recent items menu) - focus menu item
End:: ;start menu (recent items menu) - focus menu item
if !(hWnd := WinExist("ahk_class BaseBar"))
if InStr(A_ThisHotkey, "Home")
	PostMessage, 0x200, 0, % (10 & 0xFFFF)|(10<<16), ToolbarWindow321, % "ahk_id " hWnd ;WM_MOUSEMOVE := 0x200
	WinGetPos,,,, vPosH, % "ahk_id " hWnd
	PostMessage, 0x200, 0, % (10 & 0xFFFF)|((vPosH-10)<<16), ToolbarWindow321, % "ahk_id " hWnd ;WM_MOUSEMOVE := 0x200

>>>AutoHotkey control flow statements

[LIMIT] Break/Continue/Return: You cannot use Break/Continue/Return in a multi-statement expression, e.g. not possible: oMyObj := "", return
[WARNING] Loop: File loops do not handle long filenames.
[LIMIT] Loop: Registry loops return keys and values in reverse alphabetical order.
[LIMIT] Loop: There is no recommended example in the documentation for a traditional For loop function.

[traditional for loop via functions][for i = a to b (step c)]
Traditional For loop (i.e., step through a sequence) - Suggestions - AutoHotkey Community ... -sequence/

[traditional for loop via objects][for i = a to b (step c)]
For loop question - Ask for Help - AutoHotkey Community ... ntry423515
enum type and while loop - Ask for Help - AutoHotkey Community ... hile-loop/


>>>AutoHotkey main window menu items

[LIMIT] ListXXX/KeyHistory: The 'ListXXX'/'KeyHistory' information cannot be retrieved programmatically:
[WARNING] Reload: When the Reload command is used, lines underneath it can be still be executed (unlike with return/Exit/ExitApp), therefore it is recommended to put a 'return' line underneath it.

[AutoHotkey script information direct to variable]
ScriptInfo(): Get ListLines/ListVars/ListHotkeys/KeyHistory text - AutoHotkey Community
[get variable text from other scripts]
DebugVars - AutoHotkey Community
[get ScriptInfo() from other scripts]
test whether another script's hotkeys are still working (trigger hotkeys in another script) - AutoHotkey Community


>>>AutoHotkey modes/settings
>>(also: #NoTrayIcon, #UseHook, A_IsAdmin)

[WARNING/LIMIT] DetectHiddenWindows/StringCaseSense: In functions, if you change the setting for DetectHiddenWindows or StringCaseSense, you should change it back again before the function returns.
[WARNING] DetectHiddenWindows: 'bug'.
[NOTE] SetRegView: Be aware of the differences between the x64/x32 versions of the registry. Basically on x64 PCs, there are sometimes special x32 registry keys called Wow6432Node, for use with x32 programs.
[NOTE] #UseHook: makes some hotkeys that would work on Admin windows, no longer work.

DetectHiddenWindows bug? - AutoHotkey Community ... 14&t=30010


>>>AutoHotkey itself (further commands/functions)
>>(also: #Include, #Persistent, #SingleInstance, A_Args)

[LIMIT] #Include: has limited flexibility e.g. you cannot use A_Desktop. [Fixed in - February 11, 2018]
[NOTE] Exit: On a 'return' control flow statement, the script will return to the last sub (if any) that it came from, but with an 'Exit' command, the thread is ended immediately.
[NOTE] OutputDebug: One way to use OutputDebug: download DebugView, run Dbgview.exe, any time an OutputDebug line is executed, the text appears in the DebugView window.
[NOTE] EnvAdd/EnvSub: the var/value parameters should be numbers, but they are flexible: if they *start* with a number, subsequent characters are ignored.

#SingleInstance options:
- #SingleInstance [force|ignore|off]
- force (default AHK v2): terminate existing instance, start new instance
- ignore: if another instance exists, do nothing, else, start new instance (e.g. useful to avoid interrupting downloads)
- off: start new instance (alongside other existing instances)
- (blank) (default AHK v1): if another instance exists, user is prompted to 'force' OR 'ignore', else, start new instance (in AHK v2, the 'blank' option no longer exists)

#Persistent: ways to invoke persistence other than via #Persistent:
- hotkeys
- hotstrings
- Gui (even if not executed)
- OnMessage (even if not executed)
- #InstallKeybdHook
- #InstallMouseHook
- BlockInput: MouseMove subcommand (if the script is not already persistent: causes 'temporary persistence' once executed, until Suspend/Hotkey commands)
- Input (if the script is not already persistent: causes persistence once executed)

Code: Select all

;a function that will run on script startup (due to 'static')
	static vDummy := MyFunc()
	MsgBox, % "hello world"

;also mentioned here:
;GeekDude's tips and tricks - AutoHotkey Community
;Static init functions (was: Goto Eof) - AutoHotkey Community

>>>(A_ variables)

A_ variables are essentially functions, and can be a little slow to retrieve information, so if repeatedly using an A_ variable whose contents you do not expect to change, it may be faster to retrieve the contents once and store it in a custom variable.
E.g. very slow: A_IPAddress1/2/3/4.
E.g. quite slow: A_IsAdmin, A_UserName.
There are more details here:
jeeswg's benchmark tests - Page 3 - AutoHotkey Community ... 02#p221802


>>>(updated functions)
(functions that have had major changes since AHK v1.0, 'AutoHotkey Basic')

(ahk_exe): specify process name/path, cf. ahk_class/ahk_id/ahk_pid/ahk_group
(A_OSVersion): more OSes added, but it is deprecated
(Icon Support): 'Negative icon numbers may be used to identify a group icon resource within an executable file'
CoordMode: 'Window' preferred over 'Relative' for coordinates relative to the active window.
DllCall: WStr/AStr/UPtr/Ptr introduced ('Str' depends on whether AHK version is Unicode/ANSI)
IniRead/IniWrite/IniDelete: 'Read, write or delete entire sections, or retrieve a list of all section names'
InStr: 'Searches for a given occurrence of a string, from the left or the right'
Loop: Files ('FDR' style) [cf. 'Loop, FilePattern']
Loop: Reg ('RootKey\SubKey' style) [cf. 'Loop, RootKey']
NumGet/NumPut: default type parameter is now UPtr [cf. UInt]
RegRead/RegWrite/RegDelete: 'RootKey\SubKey' style
SoundSet/SoundGet: 'improved support for Windows Vista and later'
SoundSetWaveVolume/SoundGetWaveVolume: 'improved support for Windows Vista and later'
WinGet: get process path via 'ProcessPath'

affected by whether AHK version is Unicode/ANSI:
Chr/Asc/Ord, FileRead/FileAppend, Transform

see also:
AutoHotkey_L New Features
Script Compatibility


- a custom function can have at most 255 parameters
- when defining an object via [] or Array(), it can have at most 255 keys (more keys can be added later) (StrSplit could be used instead)
- when defining an object via {} or Object(), it can have at most 127 keys (more keys can be added later)

see also:
[Variable Capacity and Memory]
Variables and Expressions

Code: Select all

q:: ;range -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF

MsgBox, % 0x7FFFFFFFFFFFFFFF+0 ;9223372036854775807 ;max
MsgBox, % 0x7FFFFFFFFFFFFFFF+1 ;-9223372036854775808 ;wraps around

MsgBox, % -0x8000000000000000+0 ;-9223372036854775808 ;min
MsgBox, % -0x8000000000000000-1 ;9223372036854775807 ;wraps around

>>>(AutoHotkey v2)
- ClipWait: the function returns 0 if it timed out, otherwise 1.

Return to “Tutorials”