This page provides a complete overview of changes between AutoHotkey v2.0 and v2.1. For changes made after v2.1, see the changelog. For full technical details of changes, refer to GitHub.
Note: This is an overview of the AutoHotkey v2.1 specification and change history, not an independent guarantee that every item is implemented identically by Keysharp on every platform. Follow the links to the current API pages for Keysharp behavior and see Keysharp differences and limitations.
These changes may prevent v2.0 scripts from working as expected in the current release.
Note: Keysharp does not expose the native MNS_MODELESS style described in this subsection. Menu.Show waits by default; pass false for its Wait parameter to return immediately while the menu remains open.
The Tray menu and Menu.Show now use modeless menus to allow new threads to launch without freezing the menu. OS support for modeless menus has some bugs relating to Z-order and ability to click the first item in a submenu. Modeless menus are currently enabled by default, but this may be changed if workarounds are not found for these bugs.
Changed the default DPI awareness mode from system to per-monitor v2.
This change should affect scripts only in the scenarios described under OS DPI Scaling. Understanding the implications of the change requires a general understanding of DPI Scaling and more specifically two phenomena which affect system DPI aware scripts when there is a DPI mismatch:
Per-monitor DPI awareness disables DPI virtualization and bitmap-scaling, and therefore requires the program to react to DPI changes, such as by resizing the GUI and its controls. This is now done automatically by default unless the script disables DPIResize for the GUI or control. The built-in scaling is not expected to be perfect, and will require additional help from the script in some cases.
Note: On Windows, Keysharp implements DPIResize per control; the GUI option sets the default only for controls added afterward. Linux and macOS use toolkit-managed scaling, which individual controls cannot opt out of. The cross-platform DPIChange event can be used for custom reactions to a scale change.
Scripts which do not work well with the new default can restore the previous mode within a more limited context, as described in DPI Scaling - Mixed Settings.
Scripts which were designed to opt in to per-monitor mode should be relatively unaffected by this change. Such scripts might have previously observed the mode unexpectedly reverting under specific conditions, because the script's main window is created before the script sets the mode. See DPI Scaling - New Threads for details.
Hard-coded screen coordinates and offsets for secondary screens may need to be adjusted, but that would already be the case if display settings were changed.
Some changes to the script's behavior are enabled only if #Requires is used to specify a version requirement which cannot be met by "2.0.x". There are currently two modes: v2.0 mode and v2.1 mode. The mode can be set independently for each module and function. For details of how the directive is applied, see Compatibility Mode (#Requires).
v2.0 mode is used by default, but does not prohibit the use of features which require v2.1.
This term is used with property and return values. For v2.0 and when returned to a caller in v2.0 mode, the value is "" (an empty string). If an arrow function performs a tail call such as X() => Y(), a blank-unset result can be propagated without the maybe (?) operator. Otherwise, it is equivalent to unset.
Built-in functions return blank-unset if they lack a documented return value.
These return blank-unset under specific conditions where "" was previously returned:
When the end of a function body is reached or return is used without a parameter, the function may return "" (an empty string), unset or blank-unset.
"".unset if return is used with a parameter elsewhere within the function.If a function or property called from v2.0 mode returns blank-unset, it is translated to "" (an empty string).
See also: Default Return, blank-unset
These return unset under specific conditions instead of throwing an error:
Overall, unset is returned more consistently when an item or value is absent, whereas in v2.0 (and v2.0 mode) some methods return "" and some methods or properties throw UnsetItemError.
See also: Default Return, blank-unset
These changes should not prevent v2.0 scripts from working, but may affect the behaviour of the program. This includes some changes to behaviour which was previously undocumented (and therefore not something a script should have relied on), and some changes to documented behaviour which should generally not require changes to existing scripts.
InputHook: Removing an option from an SC-based key by name with KeyOpt now also removes the option from the corresponding VK (the reverse was already true).
ListVars now shows unset vars as "varname: unset" instead of showing string length and capacity.
MouseGetPos now throws OSError if the mouse position cannot be determined.
RegExReplace no longer truncates Replacement at the first null character (if any).
Showing the tray menu no longer stalls the current thread.
#Warn with no parameters now turns on VarUnset and Unreachable and turns off LocalSameAsGlobal for the current module, instead of enabling all warnings and setting to MsgBox mode (like #Warn All, MsgBox).
Added a warning for UTF-8 decoding errors in script files.
WinTitle parameters with two different ahk_id values now yield no match instead of arbitrarily ignoring one of the two values.
Added disableWindowFiltering to the manifest embedded in each EXE, allowing more windows to be detected.
Changed the following to use ASCII-only case insensitivity instead of locale case insensitivity: Tab control's UseTab method (excluding ExactMatch mode, which was already ASCII-only), Choose method and Text property; Menu's Add method.
Property setters defined in a class now allow the value parameter to be omitted (unset) so that assignments like x.y := unset can be handled. Setters which aren't designed to account for this may start executing the setter and throw UnsetError when value is evaluated, instead of throwing an Error immediately when the setter is called.
Some functions which previously always returned "" (without that being documented) now return a more useful value:
HotIf now returns the previous value of A_HotIf.
The ToggleCheck and ToggleEnable methods of Menu now return the new state.
Changed text input collection and dead key handling.
Changed InputHook handling of {dead key}{BS} so BS cancels out dead key.
Changed remapping to utilize left/right modifiers in Blind; e.g. >^a::b remaps RCtrl-A to B and LCtrl-RCtrl-A to LCtrl-B, instead of just B.
Changed Ctrl::Alt to not send an unsuppressed {Ctrl up}. This was originally intended to work around an issue with system handling of the modifier keys, but was superseded by a better workaround in v2.0.8.
Note: Keysharp does not currently implement DBGp or interactive script debugging. The changes in this subsection describe AutoHotkey only. See Debugging a Script for Keysharp's diagnostic facilities.
Improved DBGp property and context commands:
Changed the default DBGp max_children to 1000 and max_depth to 1.
Changed debugger to list properties defined by the Prototype for non-Object classes such as VarRef and ComValue, instead of a hard-coded (possibly empty) set of pseudo-properties.
Changed debugger classname attribute of Prototype objects to include the class name (e.g. "SomeClass.Prototype").
Changed debugger to never list the natively-defined __Class or Base properties.
Global, local and static variable declarations with multiple initializers are now evaluated as a single expression, so there is no need to step over each initializer individually while debugging.
Implemented basic support for per-monitor DPI awareness, including:
For details, see DPIResize (Gui).
These additions should generally not affect v2.0 scripts at all.
Added Modules, #Module, #Import and Export.
Added function definition expressions.
Added a !~= b, equivalent to !(a ~= b) (shorthand for "not RegExMatch").
Added typed properties (structures).
Added support for returning unset from functions and properties, and allowing or handling it with the maybe (?) and or-maybe (??) operators.
Added support for defining mandatory parameters to the right of optional parameters, such as Fn(a?, b).
Added optional chaining with the maybe operator (a?.b).
Added the maybe-assign operator (??=).
Improved load-time validation for unset, ? and ??.
Removed a length limitation for parameter default strings (but not expressions).
Added Any.Prototype.Props() and Props to enumerate own and inherited properties.
Implemented virtual references:
&x.y invokes x.__ref('y').Class.Call now accepts additional optional parameters to facilitate creating classes dynamically. In particular, this enables scripts to dynamically create Prototype objects which are based on native types other than Object, such as Map and Array.
Added support for left/right modifiers in {Blind}, as in {Blind>^}.
Added support for using WheelUp, WheelDown, WheelLeft and WheelRight on either side of a remapping.
Optimized auto-replace hotstrings to avoid retyping an identical leading part, where feasible.
Added InputHook H option to allow intercepting hotkeys.
ATan2: New function.
CallbackCreate: An array of parameter types can now be specified instead of a parameter count.
DllCall: CDecl can now be omitted regardless of which calling convention the function uses.
Edit: Added an optional Filename parameter.
FileCreateShortcut: The ShortcutKey parameter now permits the ^, + and ! modifiers.
ImageSearch: X and Y can now be omitted.
IsSet: Now accepts any unset expression. For example: IsSet(X?.Y)
PixelSearch: X and Y can now be omitted.
ProcessWait: ListLines now shows "STILL WAITING" if it was interrupted and resumed waiting, as it already did for WinWait.
RegExReplace: Replacement can now be a callback function instead of replacement text.
Throw: Now a function, for greater flexibility.
TraySetIcon: Now updates the icon of the script's main window.
Type: Now returns the string "unset" if Value is unset. For example: Type(MyVar?)
WinGetAlwaysOnTop: New function.
WinGetEnabled: New function.
Added A_KeybdHookInstalled and A_MouseHookInstalled.
Added A_HotIf.
Gui:
Menu:
ahk_class is now case-insensitive when not using the RegEx mode.
These improve performance but otherwise do not affect behaviour.
Optimized Pause to allow the OS-level thread to sleep until messages are received, instead of polling the pause state frequently. In testing, this reduced CPU usage from 0.04% to 0.00%.
Optimized ProcessWaitClose and RunWait to allow the OS-level thread to sleep until the process handle is signaled or messages are received. This reduced CPU usage (as above) and improved the response time.
Optimized dialog window class checks.
Optimized ternary by removing a redundant check for numericness.
Optimized window searches; see WinTitle - Performance for details.