"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/globaldata.cpp" (8 May 2021, 44137 Bytes) of package /windows/misc/AutoHotkey_L-1.1.33.09.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "globaldata.cpp" see the Fossies "Dox" file reference documentation.

    1 /*
    2 AutoHotkey
    3 
    4 Copyright 2003-2009 Chris Mallett (support@autohotkey.com)
    5 
    6 This program is free software; you can redistribute it and/or
    7 modify it under the terms of the GNU General Public License
    8 as published by the Free Software Foundation; either version 2
    9 of the License, or (at your option) any later version.
   10 
   11 This program is distributed in the hope that it will be useful,
   12 but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 GNU General Public License for more details.
   15 */
   16 
   17 #include "stdafx.h" // pre-compiled headers
   18 // These includes should probably a superset of those in globaldata.h:
   19 #include "hook.h" // For KeyHistoryItem and probably other things.
   20 #include "clipboard.h"  // For the global clipboard object
   21 #include "script.h" // For the global script object and g_ErrorLevel
   22 #include "os_version.h" // For the global OS_Version object
   23 
   24 #include "Debugger.h"
   25 
   26 // Since at least some of some of these (e.g. g_modifiersLR_logical) should not
   27 // be kept in the struct since it's not correct to save and restore their
   28 // state, don't keep anything in the global_struct except those things
   29 // which are necessary to save and restore (even though it would clean
   30 // up the code and might make maintaining it easier):
   31 HINSTANCE g_hInstance = NULL; // Set by WinMain().
   32 DWORD g_MainThreadID = GetCurrentThreadId();
   33 DWORD g_HookThreadID; // Not initialized by design because 0 itself might be a valid thread ID.
   34 CRITICAL_SECTION g_CriticalRegExCache;
   35 
   36 UINT g_DefaultScriptCodepage = CP_ACP;
   37 
   38 bool g_DestroyWindowCalled = false;
   39 HWND g_hWnd = NULL;
   40 HWND g_hWndEdit = NULL;
   41 HWND g_hWndSplash = NULL;
   42 HFONT g_hFontEdit = NULL;
   43 HFONT g_hFontSplash = NULL;  // So that font can be deleted on program close.
   44 HACCEL g_hAccelTable = NULL;
   45 
   46 typedef int (WINAPI *StrCmpLogicalW_type)(LPCWSTR, LPCWSTR);
   47 StrCmpLogicalW_type g_StrCmpLogicalW = NULL;
   48 WNDPROC g_TabClassProc = NULL;
   49 
   50 modLR_type g_modifiersLR_logical = 0;
   51 modLR_type g_modifiersLR_logical_non_ignored = 0;
   52 modLR_type g_modifiersLR_physical = 0;
   53 modLR_type g_modifiersLR_numpad_mask = 0;
   54 modLR_type g_modifiersLR_ctrlaltdel_mask = 0;
   55 
   56 #ifdef FUTURE_USE_MOUSE_BUTTONS_LOGICAL
   57 WORD g_mouse_buttons_logical = 0;
   58 #endif
   59 
   60 // Used by the hook to track physical state of all virtual keys, since GetAsyncKeyState() does
   61 // not retrieve the physical state of a key.  Note that this array is sometimes used in a way that
   62 // requires its format to be the same as that returned from GetKeyboardState():
   63 BYTE g_PhysicalKeyState[VK_ARRAY_COUNT] = {0};
   64 bool g_BlockWinKeys = false;
   65 DWORD g_HookReceiptOfLControlMeansAltGr = 0; // In these cases, zero is used as a false value, any others are true.
   66 DWORD g_IgnoreNextLControlDown = 0;          //
   67 DWORD g_IgnoreNextLControlUp = 0;            //
   68 
   69 BYTE g_MenuMaskKeyVK = VK_CONTROL; // For #MenuMaskKey.
   70 USHORT g_MenuMaskKeySC = SC_LCONTROL;
   71 
   72 int g_HotkeyModifierTimeout = 50;  // Reduced from 100, which was a little too large for fast typists.
   73 int g_ClipboardTimeout = 1000; // v1.0.31
   74 
   75 HHOOK g_KeybdHook = NULL;
   76 HHOOK g_MouseHook = NULL;
   77 HHOOK g_PlaybackHook = NULL;
   78 bool g_ForceLaunch = false;
   79 bool g_WinActivateForce = false;
   80 bool g_RunStdIn = false;
   81 WarnMode g_Warn_UseUnsetLocal = WARNMODE_OFF;       // Used by #Warn directive.
   82 WarnMode g_Warn_UseUnsetGlobal = WARNMODE_OFF;      //
   83 WarnMode g_Warn_UseEnv = WARNMODE_OFF;              //
   84 WarnMode g_Warn_LocalSameAsGlobal = WARNMODE_OFF;   //
   85 WarnMode g_Warn_ClassOverwrite = WARNMODE_OFF;      //
   86 WarnMode g_Warn_Unreachable = WARNMODE_OFF;
   87 SingleInstanceType g_AllowOnlyOneInstance = ALLOW_MULTI_INSTANCE;
   88 bool g_persistent = false;  // Whether the script should stay running even after the auto-exec section finishes.
   89 bool g_NoTrayIcon = false;
   90 #ifdef AUTOHOTKEYSC
   91     bool g_AllowMainWindow = false;
   92 #endif
   93 bool g_MainTimerExists = false;
   94 bool g_AutoExecTimerExists = false;
   95 bool g_InputTimerExists = false;
   96 bool g_DerefTimerExists = false;
   97 bool g_SoundWasPlayed = false;
   98 bool g_IsSuspended = false;  // Make this separate from g_AllowInterruption since that is frequently turned off & on.
   99 bool g_DeferMessagesForUnderlyingPump = false;
  100 BOOL g_WriteCacheDisabledInt64 = FALSE;  // BOOL vs. bool might improve performance a little for
  101 BOOL g_WriteCacheDisabledDouble = FALSE; // frequently-accessed variables (it has helped performance in
  102 BOOL g_NoEnv = FALSE;                    // ExpandExpression(), but didn't seem to help performance in g_NoEnv.
  103 BOOL g_AllowInterruption = TRUE;         //
  104 int g_nLayersNeedingTimer = 0;
  105 int g_nThreads = 0;
  106 int g_nPausedThreads = 0;
  107 int g_MaxHistoryKeys = 40;
  108 DWORD g_InputTimeoutAt = 0;
  109 
  110 // g_MaxVarCapacity is used to prevent a buggy script from consuming all available system RAM. It is defined
  111 // as the maximum memory size of a variable, including the string's zero terminator.
  112 // The chosen default seems big enough to be flexible, yet small enough to not be a problem on 99% of systems:
  113 VarSizeType g_MaxVarCapacity = 64 * 1024 * 1024;
  114 UCHAR g_MaxThreadsPerHotkey = 1;
  115 int g_MaxThreadsTotal = MAX_THREADS_DEFAULT;
  116 // On my system, the repeat-rate (which is probably set to XP's default) is such that between 20
  117 // and 25 keys are generated per second.  Therefore, 50 in 2000ms seems like it should allow the
  118 // key auto-repeat feature to work on most systems without triggering the warning dialog.
  119 // In any case, using auto-repeat with a hotkey is pretty rare for most people, so it's best
  120 // to keep these values conservative:
  121 int g_MaxHotkeysPerInterval = 70; // Increased to 70 because 60 was still causing the warning dialog for repeating keys sometimes.  Increased from 50 to 60 for v1.0.31.02 since 50 would be triggered by keyboard auto-repeat when it is set to its fastest.
  122 int g_HotkeyThrottleInterval = 2000; // Milliseconds.
  123 bool g_MaxThreadsBuffer = false;  // This feature usually does more harm than good, so it defaults to OFF.
  124 SendLevelType g_InputLevel = 0;
  125 HotkeyCriterion *g_HotCriterion = NULL; // The current criterion for new hotkeys.
  126 HotkeyCriterion *g_FirstHotCriterion = NULL, *g_LastHotCriterion = NULL;
  127 
  128 // Global variables for #if (expression).
  129 UINT g_HotExprTimeout = 1000; // Timeout for #if (expression) evaluation, in milliseconds.
  130 HWND g_HotExprLFW = NULL; // Last Found Window of last #if expression.
  131 HotkeyCriterion *g_FirstHotExpr = NULL, *g_LastHotExpr = NULL;
  132 
  133 static int GetScreenDPI()
  134 {
  135     // The DPI setting can be different for each screen axis, but
  136     // apparently it is such a rare situation that it is not worth
  137     // supporting it. So we just retrieve the X axis DPI.
  138 
  139     HDC hdc = GetDC(NULL);
  140     int dpi = GetDeviceCaps(hdc, LOGPIXELSX);
  141     ReleaseDC(NULL, hdc);
  142     return dpi;
  143 }
  144 
  145 int g_ScreenDPI = GetScreenDPI();
  146 MenuTypeType g_MenuIsVisible = MENU_TYPE_NONE;
  147 int g_nMessageBoxes = 0;
  148 int g_nInputBoxes = 0;
  149 int g_nFileDialogs = 0;
  150 int g_nFolderDialogs = 0;
  151 InputBoxType g_InputBox[MAX_INPUTBOXES];
  152 SplashType g_Progress[MAX_PROGRESS_WINDOWS] = {{0}};
  153 SplashType g_SplashImage[MAX_SPLASHIMAGE_WINDOWS] = {{0}};
  154 GuiType **g_gui = NULL;
  155 int g_guiCount = 0, g_guiCountMax = 0;
  156 HWND g_hWndToolTip[MAX_TOOLTIPS] = {NULL};
  157 MsgMonitorList g_MsgMonitor;
  158 
  159 // Init not needed for these:
  160 UCHAR g_SortCaseSensitive;
  161 bool g_SortNumeric;
  162 bool g_SortReverse;
  163 int g_SortColumnOffset;
  164 Func *g_SortFunc;
  165 
  166 TCHAR g_delimiter = ',';
  167 TCHAR g_DerefChar = '%';
  168 TCHAR g_EscapeChar = '`';
  169 
  170 // Hot-string vars (initialized when ResetHook() is first called):
  171 TCHAR g_HSBuf[HS_BUF_SIZE];
  172 int g_HSBufLength;
  173 HWND g_HShwnd;
  174 
  175 // Hot-string global settings:
  176 int g_HSPriority = 0;  // default priority is always 0
  177 int g_HSKeyDelay = 0;  // Fast sends are much nicer for auto-replace and auto-backspace.
  178 SendModes g_HSSendMode = SM_INPUT; // v1.0.43: New default for more reliable hotstrings.
  179 SendRawType g_HSSendRaw = SCM_NOT_RAW;
  180 bool g_HSCaseSensitive = false;
  181 bool g_HSConformToCase = true;
  182 bool g_HSDoBackspace = true;
  183 bool g_HSOmitEndChar = false;
  184 bool g_HSEndCharRequired = true;
  185 bool g_HSDetectWhenInsideWord = false;
  186 bool g_HSDoReset = false;
  187 bool g_HSResetUponMouseClick = true;
  188 bool g_HSSameLineAction = false;
  189 TCHAR g_EndChars[HS_MAX_END_CHARS + 1] = _T("-()[]{}:;'\"/\\,.?!\n \t");  // Hotstring default end chars, including a space.
  190 // The following were considered but seemed too rare and/or too likely to result in undesirable replacements
  191 // (such as while programming or scripting, or in usernames or passwords): <>*+=_%^&|@#$|
  192 // Although dash/hyphen is used for multiple purposes, it seems to me that it is best (on average) to include it.
  193 // Jay D. Novak suggested ([{/ for things such as fl/nj or fl(nj) which might resolve to USA state names.
  194 // i.e. word(synonym) and/or word/synonym
  195 
  196 // Global objects:
  197 Var *g_ErrorLevel = NULL; // Allows us (in addition to the user) to set this var to indicate success/failure.
  198 input_type *g_input = NULL;
  199 Script g_script;
  200 // This made global for performance reasons (determining size of clipboard data then
  201 // copying contents in or out without having to close & reopen the clipboard in between):
  202 Clipboard g_clip;
  203 OS_Version g_os;  // OS version object, courtesy of AutoIt3.
  204 
  205 HICON g_IconSmall;
  206 HICON g_IconLarge;
  207 
  208 DWORD g_OriginalTimeout;
  209 
  210 global_struct g_default, g_startup, *g_array;
  211 global_struct *g = &g_startup; // g_startup provides a non-NULL placeholder during script loading. Afterward it's replaced with an array.
  212 
  213 // I considered maintaining this on a per-quasi-thread basis (i.e. in global_struct), but the overhead
  214 // of having to check and restore the working directory when a suspended thread is resumed (especially
  215 // when the script has many high-frequency timers), and possibly changing the working directory
  216 // whenever a new thread is launched, doesn't seem worth it.  This is because the need to change
  217 // the working directory is comparatively rare:
  218 CString g_WorkingDir;
  219 LPTSTR g_WorkingDirOrig = NULL;  // Assigned a value in WinMain().
  220 
  221 bool g_ContinuationLTrim = false;
  222 bool g_ForceKeybdHook = false;
  223 ToggleValueType g_ForceNumLock = NEUTRAL;
  224 ToggleValueType g_ForceCapsLock = NEUTRAL;
  225 ToggleValueType g_ForceScrollLock = NEUTRAL;
  226 
  227 ToggleValueType g_BlockInputMode = TOGGLE_DEFAULT;
  228 bool g_BlockInput = false;
  229 bool g_BlockMouseMove = false;
  230 
  231 // The order of initialization here must match the order in the enum contained in script.h
  232 // It's in there rather than in globaldata.h so that the action-type constants can be referred
  233 // to without having access to the global array itself (i.e. it avoids having to include
  234 // globaldata.h in modules that only need access to the enum's constants, which in turn prevents
  235 // many mutual dependency problems between modules).  Note: Action names must not contain any
  236 // spaces or tabs because within a script, those characters can be used in lieu of a delimiter
  237 // to separate the action-type-name from the first parameter.
  238 // Note about the sub-array: Since the parent array is global, it would be automatically
  239 // zero-filled if we didn't provide specific initialization.  But since we do, I'm not sure
  240 // what value the unused elements in the NumericParams subarray will have.  Therefore, it seems
  241 // safest to always terminate these subarrays with an explicit zero, below.
  242 
  243 // STEPS TO ADD A NEW COMMAND:
  244 // 1) Add an entry to the command enum in script.h.
  245 // 2) Add an entry to the below array (it's position here MUST exactly match that in the enum).
  246 //    The first item is the command name, the second is the minimum number of parameters (e.g.
  247 //    if you enter 3, the first 3 args are mandatory) and the third is the maximum number of
  248 //    parameters (the user need not escape commas within the last parameter).
  249 //    The subarray should indicate the param numbers that must be numeric (first param is numbered 1,
  250 //    not zero).  That subarray should be terminated with an explicit zero to be safe and
  251 //    so that the compiler will complain if the sub-array size needs to be increased to
  252 //    accommodate all the elements in the new sub-array, including room for its 0 terminator.
  253 //    Note: If you use a value for MinParams than is greater than zero, remember than any params
  254 //    beneath that threshold will also be required to be non-blank (i.e. user can't omit them even
  255 //    if later, non-blank params are provided).  UPDATE: For a parameter to recognize an expression
  256 //    such as x+100, it must be listed in the sub-array as a pure numeric parameter.
  257 // 3) If the new command has any params that are output or input vars, change Line::ArgIsVar().
  258 // 4) Add any desired load-time validation in Script::AddLine() in an syntax-checking section.
  259 // 5) Implement the command in Line::Perform() or Line::EvaluateCondition (if it's an IF).
  260 //    If the command waits for anything (e.g. calls MsgSleep()), be sure to make a local
  261 //    copy of any ARG values that are needed during the wait period, because if another hotkey
  262 //    subroutine suspends the current one while its waiting, it could also overwrite the ARG
  263 //    deref buffer with its own values.
  264 
  265 // v1.0.45 The following macro sets the high-bit for those commands that require overlap-checking of their
  266 // input/output variables during runtime (commands that don't have an output variable never need this byte
  267 // set, and runtime performance is improved even for them).  Some of commands are given the high-bit even
  268 // though they might not strictly require it because rarity/performance/maintainability say it's best to do
  269 // so when in doubt.  Search on "MaxParamsAu2WithHighBit" for more details.
  270 #define H |(char)0x80
  271 
  272 Action g_act[] =
  273 {
  274     {_T(""), 0, 0, 0, NULL}  // ACT_INVALID.
  275 
  276     // ACT_ASSIGN, ACT_ADD/SUB/MULT/DIV: Give them names for display purposes.
  277     // Note: Line::ToText() relies on the below names being the correct symbols for the operation:
  278     // 1st param is the target, 2nd (optional) is the value:
  279     , {_T("="), 1, 2, 2 H, NULL}  // Omitting the second param sets the var to be empty. "H" (high-bit) is probably needed for those cases when PerformAssign() must call ExpandArgs() or similar.
  280     , {_T(":="), 1, 2, 2, {2, 0}} // Same, though param #2 is flagged as numeric so that expression detection is automatic.  "H" (high-bit) doesn't appear to be needed even when ACT_ASSIGNEXPR calls AssignBinaryClip() because that AssignBinaryClip() checks for source==dest.
  281 
  282     // ACT_EXPRESSION, which is a stand-alone expression outside of any IF or assignment-command;
  283     // e.g. fn1(123, fn2(y)) or x&=3
  284     // Its name should be "" so that Line::ToText() will properly display it.
  285     , {_T(""), 1, 1, 1, {1, 0}}
  286 
  287     , {_T("+="), 2, 3, 3, {2, 0}}
  288     , {_T("-="), 1, 3, 3, {2, 0}} // Subtraction (but not addition) allows 2nd to be blank due to 3rd param.
  289     , {_T("*="), 2, 2, 2, {2, 0}}
  290     , {_T("/="), 2, 2, 2, {2, 0}}
  291 
  292     , {_T("Static"), 1, 1, 1, {1, 0}} // ACT_STATIC (used only at load time).
  293 
  294     , {_T("in"), 2, 2, 2, NULL}, {_T("not in"), 2, 2, 2, NULL}
  295     , {_T("contains"), 2, 2, 2, NULL}, {_T("not contains"), 2, 2, 2, NULL}  // Very similar to "in" and "not in"
  296     , {_T("is"), 2, 2, 2, NULL}, {_T("is not"), 2, 2, 2, NULL}
  297     , {_T("between"), 1, 3, 3, NULL}, {_T("not between"), 1, 3, 3, NULL}  // Min 1 to allow #2 and #3 to be the empty string.
  298     , {_T(""), 1, 1, 1, {1, 0}} // ACT_IFEXPR's name should be "" so that Line::ToText() will properly display it.
  299 
  300     // Comparison operators take 1 param (if they're being compared to blank) or 2.
  301     // For example, it's okay (though probably useless) to compare a string to the empty
  302     // string this way: "If var1 >=".  Note: Line::ToText() relies on the below names:
  303     , {_T("="), 1, 2, 2, NULL}, {_T("<>"), 1, 2, 2, NULL}, {_T(">"), 1, 2, 2, NULL}
  304     , {_T(">="), 1, 2, 2, NULL}, {_T("<"), 1, 2, 2, NULL}, {_T("<="), 1, 2, 2, NULL}
  305 
  306     // For these, allow a minimum of zero, otherwise, the first param (WinTitle) would
  307     // be considered mandatory-non-blank by default.  It's easier to make all the params
  308     // optional and validate elsewhere that at least one of the four isn't blank.
  309     // Also, All the IFs must be physically adjacent to each other in this array
  310     // so that ACT_FIRST_IF and ACT_LAST_IF can be used to detect if a command is an IF:
  311     , {_T("IfWinExist"), 0, 4, 4, NULL}, {_T("IfWinNotExist"), 0, 4, 4, NULL}  // Title, text, exclude-title, exclude-text
  312     // Passing zero params results in activating the LastUsed window:
  313     , {_T("IfWinActive"), 0, 4, 4, NULL}, {_T("IfWinNotActive"), 0, 4, 4, NULL} // same
  314     , {_T("IfInString"), 2, 2, 2, NULL} // String var, search string
  315     , {_T("IfNotInString"), 2, 2, 2, NULL} // String var, search string
  316     , {_T("IfExist"), 1, 1, 1, NULL} // File or directory.
  317     , {_T("IfNotExist"), 1, 1, 1, NULL} // File or directory.
  318     // IfMsgBox must be physically adjacent to the other IFs in this array:
  319     , {_T("IfMsgBox"), 1, 1, 1, NULL} // MsgBox result (e.g. OK, YES, NO)
  320 
  321     , {_T("Else"), 0, 0, 0, NULL} // No args; it has special handling to support same-line ELSE-actions (e.g. "else if").
  322 
  323     , {_T("MsgBox"), 0, 4, 3, NULL} // Text (if only 1 param) or: Mode-flag, Title, Text, Timeout.
  324     , {_T("InputBox"), 1, 11, 11 H, {5, 6, 7, 8, 10, 0}} // Output var, title, prompt, hide-text (e.g. passwords), width, height, X, Y, Locale, Timeout, Default
  325     , {_T("SplashTextOn"), 0, 4, 4, {1, 2, 0}} // Width, height, title, text
  326     , {_T("SplashTextOff"), 0, 0, 0, NULL}
  327     , {_T("Progress"), 0, 6, 6, NULL}  // Off|Percent|Options, SubText, MainText, Title, Font, FutureUse
  328     , {_T("SplashImage"), 0, 7, 7, NULL}  // Off|ImageFile, |Options, SubText, MainText, Title, Font, FutureUse
  329     , {_T("ToolTip"), 0, 4, 4, {2, 3, 4, 0}}  // Text, X, Y, ID.  If Text is omitted, the Tooltip is turned off.
  330     , {_T("TrayTip"), 0, 4, 4, {3, 4, 0}}  // Title, Text, Timeout, Options
  331 
  332     , {_T("Input"), 0, 4, 4 H, NULL}  // OutputVar, Options, EndKeys, MatchList.
  333 
  334     , {_T("Transform"), 2, 4, 4 H, NULL}  // output var, operation, value1, value2
  335 
  336     , {_T("StringLeft"), 3, 3, 3, {3, 0}}  // output var, input var, number of chars to extract
  337     , {_T("StringRight"), 3, 3, 3, {3, 0}} // same
  338     , {_T("StringMid"), 3, 5, 5, {3, 4, 0}} // Output Variable, Input Variable, Start char, Number of chars to extract, L
  339     , {_T("StringTrimLeft"), 3, 3, 3, {3, 0}}  // output var, input var, number of chars to trim
  340     , {_T("StringTrimRight"), 3, 3, 3, {3, 0}} // same
  341     , {_T("StringLower"), 2, 3, 3, NULL} // output var, input var, T = Title Case
  342     , {_T("StringUpper"), 2, 3, 3, NULL} // output var, input var, T = Title Case
  343     , {_T("StringLen"), 2, 2, 2, NULL} // output var, input var
  344     , {_T("StringGetPos"), 3, 5, 3, {5, 0}}  // Output Variable, Input Variable, Search Text, R or Right (from right), Offset
  345     , {_T("StringReplace"), 3, 5, 4, NULL} // Output Variable, Input Variable, Search String, Replace String, do-all.
  346     , {_T("StringSplit"), 2, 5, 5, NULL} // Output Array, Input Variable, Delimiter List (optional), Omit List, Future Use
  347     , {_T("SplitPath"), 1, 6, 6 H, NULL} // InputFilespec, OutName, OutDir, OutExt, OutNameNoExt, OutDrive
  348     , {_T("Sort"), 1, 2, 2, NULL} // OutputVar (it's also the input var), Options
  349 
  350     , {_T("EnvGet"), 2, 2, 2 H, NULL} // OutputVar, EnvVar
  351     , {_T("EnvSet"), 1, 2, 2, NULL} // EnvVar, Value
  352     , {_T("EnvUpdate"), 0, 0, 0, NULL}
  353 
  354     , {_T("RunAs"), 0, 3, 3, NULL} // user, pass, domain (0 params can be passed to disable the feature)
  355     , {_T("Run"), 1, 4, 4 H, NULL}      // TargetFile, Working Dir, WinShow-Mode/UseErrorLevel, OutputVarPID
  356     , {_T("RunWait"), 1, 4, 4 H, NULL}  // TargetFile, Working Dir, WinShow-Mode/UseErrorLevel, OutputVarPID
  357     , {_T("URLDownloadToFile"), 2, 2, 2, NULL} // URL, save-as-filename
  358 
  359     , {_T("GetKeyState"), 2, 3, 3 H, NULL} // OutputVar, key name, mode (optional) P = Physical, T = Toggle
  360     , {_T("Send"), 1, 1, 1, NULL}         // But that first param can validly be a deref that resolves to a blank param.
  361     , {_T("SendRaw"), 1, 1, 1, NULL}      //
  362     , {_T("SendInput"), 1, 1, 1, NULL}    //
  363     , {_T("SendPlay"), 1, 1, 1, NULL}     //
  364     , {_T("SendEvent"), 1, 1, 1, NULL}    // (due to rarity, there is no raw counterpart for this one)
  365 
  366     // For these, the "control" param can be blank.  The window's first visible control will
  367     // be used.  For this first one, allow a minimum of zero, otherwise, the first param (control)
  368     // would be considered mandatory-non-blank by default.  It's easier to make all the params
  369     // optional and validate elsewhere that the 2nd one specifically isn't blank:
  370     , {_T("ControlSend"), 0, 6, 6, NULL} // Control, Chars-to-Send, std. 4 window params.
  371     , {_T("ControlSendRaw"), 0, 6, 6, NULL} // Control, Chars-to-Send, std. 4 window params.
  372     , {_T("ControlClick"), 0, 8, 8, {5, 0}} // Control, WinTitle, WinText, WhichButton, ClickCount, Hold/Release, ExcludeTitle, ExcludeText
  373     , {_T("ControlMove"), 0, 9, 9, {2, 3, 4, 5, 0}} // Control, x, y, w, h, WinTitle, WinText, ExcludeTitle, ExcludeText
  374     , {_T("ControlGetPos"), 0, 9, 9 H, NULL} // Four optional output vars: xpos, ypos, width, height, control, std. 4 window params.
  375     , {_T("ControlFocus"), 0, 5, 5, NULL}     // Control, std. 4 window params
  376     , {_T("ControlGetFocus"), 1, 5, 5 H, NULL}  // OutputVar, std. 4 window params
  377     , {_T("ControlSetText"), 0, 6, 6, NULL}   // Control, new text, std. 4 window params
  378     , {_T("ControlGetText"), 1, 6, 6 H, NULL}   // Output-var, Control, std. 4 window params
  379     , {_T("Control"), 1, 7, 7, NULL}   // Command, Value, Control, std. 4 window params
  380     , {_T("ControlGet"), 2, 8, 8 H, NULL}   // Output-var, Command, Value, Control, std. 4 window params
  381 
  382     , {_T("SendMode"), 1, 1, 1, NULL}
  383     , {_T("SendLevel"), 1, 1, 1, {1, 0}}
  384     , {_T("CoordMode"), 1, 2, 2, NULL} // Attribute, screen|relative
  385     , {_T("SetDefaultMouseSpeed"), 1, 1, 1, {1, 0}} // speed (numeric)
  386     , {_T("Click"), 0, 1, 1, NULL} // Flex-list of options.
  387     , {_T("MouseMove"), 2, 4, 4, {1, 2, 3, 0}} // x, y, speed, option
  388     , {_T("MouseClick"), 0, 7, 7, {2, 3, 4, 5, 0}} // which-button, x, y, ClickCount, speed, d=hold-down/u=release, Relative
  389     , {_T("MouseClickDrag"), 1, 7, 7, {2, 3, 4, 5, 6, 0}} // which-button, x1, y1, x2, y2, speed, Relative
  390     , {_T("MouseGetPos"), 0, 5, 5 H, {5, 0}} // 4 optional output vars: xpos, ypos, WindowID, ControlName. Finally: Mode. MinParams must be 0.
  391 
  392     , {_T("StatusBarGetText"), 1, 6, 6 H, {2, 0}} // Output-var, part# (numeric), std. 4 window params
  393     , {_T("StatusBarWait"), 0, 8, 8, {2, 3, 6, 0}} // Wait-text(blank ok),seconds,part#,title,text,interval,exclude-title,exclude-text
  394     , {_T("ClipWait"), 0, 2, 2, {1, 2, 0}} // Seconds-to-wait (0 = 500ms), 1|0: Wait for any format, not just text/files
  395     , {_T("KeyWait"), 1, 2, 2, NULL} // KeyName, Options
  396 
  397     , {_T("Sleep"), 1, 1, 1, {1, 0}} // Sleep time in ms (numeric)
  398     , {_T("Random"), 0, 3, 3, {2, 3, 0}} // Output var, Min, Max (Note: MinParams is 1 so that param2 can be blank).
  399 
  400     , {_T("Goto"), 1, 1, 1, NULL}
  401     , {_T("Gosub"), 1, 1, 1, NULL}   // Label (or dereference that resolves to a label).
  402     , {_T("OnExit"), 0, 2, 2, NULL}  // Optional label, future use (since labels are allowed to contain commas)
  403     , {_T("Hotkey"), 1, 3, 3, NULL}  // Mod+Keys, Label/Action (blank to avoid changing curr. label), Options
  404     , {_T("SetTimer"), 0, 3, 3, {3, 0}}  // Label (or dereference that resolves to a label), period (or ON/OFF), Priority
  405     , {_T("Critical"), 0, 1, 1, NULL}  // On|Off
  406     , {_T("Thread"), 1, 3, 3, {2, 3, 0}}  // Command, value1 (can be blank for interrupt), value2
  407     , {_T("Return"), 0, 1, 1, {1, 0}}
  408     , {_T("Exit"), 0, 1, 1, {1, 0}} // ExitCode
  409     , {_T("Loop"), 0, 4, 4, NULL} // Iteration Count or FilePattern or root key name [,subkey name], FileLoopMode, Recurse? (custom validation for these last two)
  410     , {_T("For"), 1, 3, 3, {3, 0}}  // For var [,var] in expression
  411     , {_T("While"), 1, 1, 1, {1, 0}} // LoopCondition.  v1.0.48: Lexikos: Added g_act entry for ACT_WHILE.
  412     , {_T("Until"), 1, 1, 1, {1, 0}} // Until expression (follows a Loop)
  413     , {_T("Break"), 0, 1, 1, NULL}, {_T("Continue"), 0, 1, 1, NULL}
  414     , {_T("Try"), 0, 0, 0, NULL}
  415     , {_T("Catch"), 0, 1, 0, NULL} // fincs: seems best to allow catch without a parameter
  416     , {_T("Finally"), 0, 0, 0, NULL}
  417     , {_T("Throw"), 0, 1, 1, {1, 0}}
  418     , {_T("Switch"), 0, 1, 1, {1, 0}}
  419     , {_T("Case"), 1, MAX_ARGS, MAX_ARGS, NULL}
  420     , {_T("{"), 0, 0, 0, NULL}, {_T("}"), 0, 0, 0, NULL}
  421 
  422     , {_T("WinActivate"), 0, 4, 2, NULL} // Passing zero params results in activating the LastUsed window.
  423     , {_T("WinActivateBottom"), 0, 4, 4, NULL} // Min. 0 so that 1st params can be blank and later ones not blank.
  424 
  425     // These all use Title, Text, Timeout (in seconds not ms), Exclude-title, Exclude-text.
  426     // See above for why zero is the minimum number of params for each:
  427     , {_T("WinWait"), 0, 5, 5, {3, 0}}, {_T("WinWaitClose"), 0, 5, 5, {3, 0}}
  428     , {_T("WinWaitActive"), 0, 5, 5, {3, 0}}, {_T("WinWaitNotActive"), 0, 5, 5, {3, 0}}
  429 
  430     , {_T("WinMinimize"), 0, 4, 2, NULL}, {_T("WinMaximize"), 0, 4, 2, NULL}, {_T("WinRestore"), 0, 4, 2, NULL} // std. 4 params
  431     , {_T("WinHide"), 0, 4, 2, NULL}, {_T("WinShow"), 0, 4, 2, NULL} // std. 4 params
  432     , {_T("WinMinimizeAll"), 0, 0, 0, NULL}, {_T("WinMinimizeAllUndo"), 0, 0, 0, NULL}
  433     , {_T("WinClose"), 0, 5, 2, {3, 0}} // title, text, time-to-wait-for-close (0 = 500ms), exclude title/text
  434     , {_T("WinKill"), 0, 5, 2, {3, 0}} // same as WinClose.
  435     , {_T("WinMove"), 0, 8, 8, {1, 2, 3, 4, 5, 6, 0}} // title, text, xpos, ypos, width, height, exclude-title, exclude_text
  436     // Note for WinMove: title/text are marked as numeric because in two-param mode, they are the X/Y params.
  437     // This helps speed up loading expression-detection.  Also, xpos/ypos/width/height can be the string "default",
  438     // but that is explicitly checked for, even though it is required it to be numeric in the definition here.
  439     , {_T("WinMenuSelectItem"), 0, 11, 11, NULL} // WinTitle, WinText, Menu name, 6 optional sub-menu names, ExcludeTitle/Text
  440 
  441     , {_T("Process"), 1, 3, 3, NULL}  // Sub-cmd, PID/name, Param3 (use minimum of 1 param so that 2nd can be blank)
  442 
  443     , {_T("WinSet"), 1, 6, 6, NULL} // attribute, setting, title, text, exclude-title, exclude-text
  444     // WinSetTitle: Allow a minimum of zero params so that title isn't forced to be non-blank.
  445     // Also, if the user passes only one param, the title of the "last used" window will be
  446     // set to the string in the first param:
  447     , {_T("WinSetTitle"), 0, 5, 3, NULL} // title, text, newtitle, exclude-title, exclude-text
  448     , {_T("WinGetTitle"), 1, 5, 3 H, NULL} // Output-var, std. 4 window params
  449     , {_T("WinGetClass"), 1, 5, 5 H, NULL} // Output-var, std. 4 window params
  450     , {_T("WinGet"), 1, 6, 6 H, NULL} // Output-var/array, cmd (if omitted, defaults to ID), std. 4 window params
  451     , {_T("WinGetPos"), 0, 8, 8 H, NULL} // Four optional output vars: xpos, ypos, width, height.  Std. 4 window params.
  452     , {_T("WinGetText"), 1, 5, 5 H, NULL} // Output var, std 4 window params.
  453 
  454     , {_T("SysGet"), 2, 4, 4 H, NULL} // Output-var/array, sub-cmd or sys-metrics-number, input-value1, future-use
  455 
  456     , {_T("PostMessage"), 1, 8, 8, {1, 2, 3, 0}}  // msg, wParam, lParam, Control, WinTitle, WinText, ExcludeTitle, ExcludeText
  457     , {_T("SendMessage"), 1, 9, 9, {1, 2, 3, 9, 0}}  // msg, wParam, lParam, Control, WinTitle, WinText, ExcludeTitle, ExcludeText, Timeout
  458 
  459     , {_T("PixelGetColor"), 3, 4, 4 H, {2, 3, 0}} // OutputVar, X-coord, Y-coord [, RGB]
  460     , {_T("PixelSearch"), 0, 9, 9 H, {3, 4, 5, 6, 7, 8, 0}} // OutputX, OutputY, left, top, right, bottom, Color, Variation [, RGB]
  461     , {_T("ImageSearch"), 0, 7, 7 H, {3, 4, 5, 6, 0}} // OutputX, OutputY, left, top, right, bottom, ImageFile
  462     // NOTE FOR THE ABOVE: 0 min args so that the output vars can be optional.
  463 
  464     // See above for why minimum is 1 vs. 2:
  465     , {_T("GroupAdd"), 1, 6, 6, NULL} // Group name, WinTitle, WinText, Label, exclude-title/text
  466     , {_T("GroupActivate"), 1, 2, 2, NULL}
  467     , {_T("GroupDeactivate"), 1, 2, 2, NULL}
  468     , {_T("GroupClose"), 1, 2, 2, NULL}
  469 
  470     , {_T("DriveSpaceFree"), 2, 2, 2 H, NULL} // Output-var, path (e.g. c:\)
  471     , {_T("Drive"), 1, 3, 3, NULL} // Sub-command, Value1 (can be blank for Eject), Value2
  472     , {_T("DriveGet"), 0, 3, 3 H, NULL} // Output-var (optional in at least one case), Command, Value
  473 
  474     , {_T("SoundGet"), 1, 4, 4 H, {4, 0}} // OutputVar, ComponentType (default=master), ControlType (default=vol), Mixer/Device Number
  475     , {_T("SoundSet"), 1, 4, 4, {1, 4, 0}} // Volume percent-level (0-100), ComponentType, ControlType (default=vol), Mixer/Device Number
  476     , {_T("SoundGetWaveVolume"), 1, 2, 2 H, {2, 0}} // OutputVar, Mixer/Device Number
  477     , {_T("SoundSetWaveVolume"), 1, 2, 2, {1, 2, 0}} // Volume percent-level (0-100), Device Number (1 is the first)
  478     , {_T("SoundBeep"), 0, 2, 2, {1, 2, 0}} // Frequency, Duration.
  479     , {_T("SoundPlay"), 1, 2, 2, NULL} // Filename [, wait]
  480 
  481     , {_T("FileAppend"), 0, 3, 3, NULL} // text, filename (which can be omitted in a read-file loop). Update: Text can be omitted too, to create an empty file or alter the timestamp of an existing file.
  482     , {_T("FileRead"), 2, 2, 2 H, NULL} // Output variable, filename
  483     , {_T("FileReadLine"), 3, 3, 3 H, {3, 0}} // Output variable, filename, line-number
  484     , {_T("FileDelete"), 1, 1, 1, NULL} // filename or pattern
  485     , {_T("FileRecycle"), 1, 1, 1, NULL} // filename or pattern
  486     , {_T("FileRecycleEmpty"), 0, 1, 1, NULL} // optional drive letter (all bins will be emptied if absent.
  487     , {_T("FileInstall"), 2, 3, 3, {3, 0}} // source, dest, flag (1/0, where 1=overwrite)
  488     , {_T("FileCopy"), 2, 3, 3, {3, 0}} // source, dest, flag
  489     , {_T("FileMove"), 2, 3, 3, {3, 0}} // source, dest, flag
  490     , {_T("FileCopyDir"), 2, 3, 3, {3, 0}} // source, dest, flag
  491     , {_T("FileMoveDir"), 2, 3, 3, NULL} // source, dest, flag (which can be non-numeric in this case)
  492     , {_T("FileCreateDir"), 1, 1, 1, NULL} // dir name
  493     , {_T("FileRemoveDir"), 1, 2, 1, {2, 0}} // dir name, flag
  494 
  495     , {_T("FileGetAttrib"), 1, 2, 2 H, NULL} // OutputVar, Filespec (if blank, uses loop's current file)
  496     , {_T("FileSetAttrib"), 1, 4, 4, {3, 4, 0}} // Attribute(s), FilePattern, OperateOnFolders?, Recurse? (custom validation for these last two)
  497     , {_T("FileGetTime"), 1, 3, 3 H, NULL} // OutputVar, Filespec, WhichTime (modified/created/accessed)
  498     , {_T("FileSetTime"), 0, 5, 5, {1, 4, 5, 0}} // datetime (YYYYMMDDHH24MISS), FilePattern, WhichTime, OperateOnFolders?, Recurse?
  499     , {_T("FileGetSize"), 1, 3, 3 H, NULL} // OutputVar, Filespec, B|K|M (bytes, kb, or mb)
  500     , {_T("FileGetVersion"), 1, 2, 2 H, NULL} // OutputVar, Filespec
  501 
  502     , {_T("SetWorkingDir"), 1, 1, 1, NULL} // New path
  503     , {_T("FileSelectFile"), 1, 5, 3 H, NULL} // output var, options, working dir, greeting, filter
  504     , {_T("FileSelectFolder"), 1, 4, 4 H, {3, 0}} // output var, root directory, options, greeting
  505 
  506     , {_T("FileGetShortcut"), 1, 8, 8 H, NULL} // Filespec, OutTarget, OutDir, OutArg, OutDescrip, OutIcon, OutIconIndex, OutShowState.
  507     , {_T("FileCreateShortcut"), 2, 9, 9, {8, 9, 0}} // file, lnk [, workdir, args, desc, icon, hotkey, icon_number, run_state]
  508 
  509     , {_T("IniRead"), 2, 5, 4 H, NULL}   // OutputVar, Filespec, Section, Key, Default (value to return if key not found)
  510     , {_T("IniWrite"), 3, 4, 4, NULL}  // Value, Filespec, Section, Key
  511     , {_T("IniDelete"), 2, 3, 3, NULL} // Filespec, Section, Key
  512 
  513     // These require so few parameters due to registry loops, which provide the missing parameter values
  514     // automatically:
  515     , {_T("RegRead"), 1, 4, 4 H, NULL} // OutputVar, RegKey, RegSubkey, ValueName
  516     , {_T("RegWrite"), 0, 5, 5, NULL} // ValueType, RegKey, RegSubKey, ValueName, Value (set to blank if omitted?)
  517     , {_T("RegDelete"), 0, 3, 3, NULL} // RegKey, RegSubKey, ValueName
  518     , {_T("SetRegView"), 1, 1, 1, NULL}
  519 
  520     , {_T("OutputDebug"), 1, 1, 1, NULL}
  521 
  522     , {_T("SetKeyDelay"), 0, 3, 3, {1, 2, 0}} // Delay in ms (numeric, negative allowed), PressDuration [, Play]
  523     , {_T("SetMouseDelay"), 1, 2, 2, {1, 0}} // Delay in ms (numeric, negative allowed) [, Play]
  524     , {_T("SetWinDelay"), 1, 1, 1, {1, 0}} // Delay in ms (numeric, negative allowed)
  525     , {_T("SetControlDelay"), 1, 1, 1, {1, 0}} // Delay in ms (numeric, negative allowed)
  526     , {_T("SetBatchLines"), 1, 1, 1, NULL} // Can be non-numeric, such as 15ms, or a number (to indicate line count).
  527     , {_T("SetTitleMatchMode"), 1, 1, 1, NULL} // Allowed values: 1, 2, slow, fast
  528     , {_T("SetFormat"), 2, 2, 2, NULL} // Float|Integer, FormatString (for float) or H|D (for int)
  529     , {_T("FormatTime"), 1, 3, 3 H, NULL} // OutputVar, YYYYMMDDHH24MISS, Format (format is last to avoid having to escape commas in it).
  530 
  531     , {_T("Suspend"), 0, 1, 1, NULL} // On/Off/Toggle/Permit/Blank (blank is the same as toggle)
  532     , {_T("Pause"), 0, 2, 2, NULL} // On/Off/Toggle/Blank (blank is the same as toggle), AlwaysAffectUnderlying
  533     , {_T("AutoTrim"), 1, 1, 1, NULL} // On/Off
  534     , {_T("StringCaseSense"), 1, 1, 1, NULL} // On/Off/Locale
  535     , {_T("DetectHiddenWindows"), 1, 1, 1, NULL} // On/Off
  536     , {_T("DetectHiddenText"), 1, 1, 1, NULL} // On/Off
  537     , {_T("BlockInput"), 1, 1, 1, NULL} // On/Off
  538 
  539     , {_T("SetNumlockState"), 0, 1, 1, NULL} // On/Off/AlwaysOn/AlwaysOff or blank (unspecified) to return to normal.
  540     , {_T("SetScrollLockState"), 0, 1, 1, NULL} // same
  541     , {_T("SetCapslockState"), 0, 1, 1, NULL} // same
  542     , {_T("SetStoreCapslockMode"), 1, 1, 1, NULL} // On/Off
  543 
  544     , {_T("KeyHistory"), 0, 2, 2, NULL}, {_T("ListLines"), 0, 1, 1, NULL}
  545     , {_T("ListVars"), 0, 0, 0, NULL}, {_T("ListHotkeys"), 0, 0, 0, NULL}
  546 
  547     , {_T("Edit"), 0, 0, 0, NULL}
  548     , {_T("Reload"), 0, 0, 0, NULL}
  549     , {_T("Menu"), 2, 6, 6, NULL}  // tray, add, name, label, options, future use
  550     , {_T("Gui"), 1, 4, 4, NULL}  // Cmd/Add, ControlType, Options, Text
  551     , {_T("GuiControl"), 0, 3, 3 H, NULL} // Sub-cmd (defaults to "contents"), ControlName/ID, Text
  552     , {_T("GuiControlGet"), 1, 4, 4, NULL} // OutputVar, Sub-cmd (defaults to "contents"), ControlName/ID (defaults to control assoc. with OutputVar), Text/FutureUse
  553 
  554     , {_T("ExitApp"), 0, 1, 1, {1, 0}}  // Optional exit-code. v1.0.48.01: Allow an expression like ACT_EXIT does.
  555     , {_T("Shutdown"), 1, 1, 1, {1, 0}} // Seems best to make the first param (the flag/code) mandatory.
  556 
  557     , {_T("FileEncoding"), 0, 1, 1, NULL}
  558 
  559     , {_T("#If"), 0, 1, 1, {1, 0}}
  560 };
  561 // Below is the most maintainable way to determine the actual count?
  562 // Due to C++ lang. restrictions, can't easily make this a const because constants
  563 // automatically get static (internal) linkage, thus such a var could never be
  564 // used outside this module:
  565 int g_ActionCount = _countof(g_act);
  566 
  567 
  568 
  569 Action g_old_act[] =
  570 {
  571     {_T(""), 0, 0, 0, NULL}  // OLD_INVALID.
  572     , {_T("SetEnv"), 1, 2, 2, NULL}
  573     , {_T("EnvAdd"), 2, 3, 3, {2, 0}}, {_T("EnvSub"), 1, 3, 3, {2, 0}} // EnvSub (but not Add) allow 2nd to be blank due to 3rd param.
  574     , {_T("EnvMult"), 2, 2, 2, {2, 0}}, {_T("EnvDiv"), 2, 2, 2, {2, 0}}
  575     , {_T("IfEqual"), 1, 2, 2, NULL}, {_T("IfNotEqual"), 1, 2, 2, NULL}
  576     , {_T("IfGreater"), 1, 2, 2, NULL}, {_T("IfGreaterOrEqual"), 1, 2, 2, NULL}
  577     , {_T("IfLess"), 1, 2, 2, NULL}, {_T("IfLessOrEqual"), 1, 2, 2, NULL}
  578     , {_T("WinGetActiveTitle"), 1, 1, 1, NULL} // <Title Var>
  579     , {_T("WinGetActiveStats"), 5, 5, 5, NULL} // <Title Var>, <Width Var>, <Height Var>, <Xpos Var>, <Ypos Var>
  580 };
  581 int g_OldActionCount = _countof(g_old_act);
  582 
  583 
  584 key_to_vk_type g_key_to_vk[] =
  585 { {_T("Numpad0"), VK_NUMPAD0}
  586 , {_T("Numpad1"), VK_NUMPAD1}
  587 , {_T("Numpad2"), VK_NUMPAD2}
  588 , {_T("Numpad3"), VK_NUMPAD3}
  589 , {_T("Numpad4"), VK_NUMPAD4}
  590 , {_T("Numpad5"), VK_NUMPAD5}
  591 , {_T("Numpad6"), VK_NUMPAD6}
  592 , {_T("Numpad7"), VK_NUMPAD7}
  593 , {_T("Numpad8"), VK_NUMPAD8}
  594 , {_T("Numpad9"), VK_NUMPAD9}
  595 , {_T("NumpadMult"), VK_MULTIPLY}
  596 , {_T("NumpadDiv"), VK_DIVIDE}
  597 , {_T("NumpadAdd"), VK_ADD}
  598 , {_T("NumpadSub"), VK_SUBTRACT}
  599 // , {_T("NumpadEnter"), VK_RETURN}  // Must do this one via scan code, see below for explanation.
  600 , {_T("NumpadDot"), VK_DECIMAL}
  601 , {_T("Numlock"), VK_NUMLOCK}
  602 , {_T("ScrollLock"), VK_SCROLL}
  603 , {_T("CapsLock"), VK_CAPITAL}
  604 
  605 , {_T("Escape"), VK_ESCAPE}  // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  606 , {_T("Esc"), VK_ESCAPE}
  607 , {_T("Tab"), VK_TAB}
  608 , {_T("Space"), VK_SPACE}
  609 , {_T("Backspace"), VK_BACK} // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  610 , {_T("BS"), VK_BACK}
  611 
  612 // These keys each have a counterpart on the number pad with the same VK.  Use the VK for these,
  613 // since they are probably more likely to be assigned to hotkeys (thus minimizing the use of the
  614 // keyboard hook, and use the scan code (SC) for their counterparts.  UPDATE: To support handling
  615 // these keys with the hook (i.e. the sc_takes_precedence flag in the hook), do them by scan code
  616 // instead.  This allows Numpad keys such as Numpad7 to be differentiated from NumpadHome, which
  617 // would otherwise be impossible since both of them share the same scan code (i.e. if the
  618 // sc_takes_precedence flag is set for the scan code of NumpadHome, that will effectively prevent
  619 // the hook from telling the difference between it and Numpad7 since the hook is currently set
  620 // to handle an incoming key by either vk or sc, but not both.
  621 
  622 // Even though ENTER is probably less likely to be assigned than NumpadEnter, must have ENTER as
  623 // the primary vk because otherwise, if the user configures only naked-NumPadEnter to do something,
  624 // RegisterHotkey() would register that vk and ENTER would also be configured to do the same thing.
  625 , {_T("Enter"), VK_RETURN}  // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  626 , {_T("Return"), VK_RETURN}
  627 
  628 , {_T("NumpadDel"), VK_DELETE}
  629 , {_T("NumpadIns"), VK_INSERT}
  630 , {_T("NumpadClear"), VK_CLEAR}  // same physical key as Numpad5 on most keyboards?
  631 , {_T("NumpadUp"), VK_UP}
  632 , {_T("NumpadDown"), VK_DOWN}
  633 , {_T("NumpadLeft"), VK_LEFT}
  634 , {_T("NumpadRight"), VK_RIGHT}
  635 , {_T("NumpadHome"), VK_HOME}
  636 , {_T("NumpadEnd"), VK_END}
  637 , {_T("NumpadPgUp"), VK_PRIOR}
  638 , {_T("NumpadPgDn"), VK_NEXT}
  639 
  640 , {_T("PrintScreen"), VK_SNAPSHOT}
  641 , {_T("CtrlBreak"), VK_CANCEL}  // Might want to verify this, and whether it has any peculiarities.
  642 , {_T("Pause"), VK_PAUSE} // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  643 , {_T("Break"), VK_PAUSE} // Not really meaningful, but kept for as a synonym of Pause for backward compatibility.  See CtrlBreak.
  644 , {_T("Help"), VK_HELP}  // VK_HELP is probably not the extended HELP key.  Not sure what this one is.
  645 , {_T("Sleep"), VK_SLEEP}
  646 
  647 , {_T("AppsKey"), VK_APPS}
  648 
  649 // UPDATE: For the NT/2k/XP version, now doing these by VK since it's likely to be
  650 // more compatible with non-standard or non-English keyboards:
  651 , {_T("LControl"), VK_LCONTROL} // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  652 , {_T("RControl"), VK_RCONTROL} // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  653 , {_T("LCtrl"), VK_LCONTROL} // Abbreviated versions of the above.
  654 , {_T("RCtrl"), VK_RCONTROL} //
  655 , {_T("LShift"), VK_LSHIFT}
  656 , {_T("RShift"), VK_RSHIFT}
  657 , {_T("LAlt"), VK_LMENU}
  658 , {_T("RAlt"), VK_RMENU}
  659 // These two are always left/right centric and I think their vk's are always supported by the various
  660 // Windows API calls, unlike VK_RSHIFT, etc. (which are seldom supported):
  661 , {_T("LWin"), VK_LWIN}
  662 , {_T("RWin"), VK_RWIN}
  663 
  664 // The left/right versions of these are handled elsewhere since their virtual keys aren't fully API-supported:
  665 , {_T("Control"), VK_CONTROL} // So that VKtoKeyName() delivers consistent results, always have the preferred name first.
  666 , {_T("Ctrl"), VK_CONTROL}  // An alternate for convenience.
  667 , {_T("Alt"), VK_MENU}
  668 , {_T("Shift"), VK_SHIFT}
  669 /*
  670 These were used to confirm the fact that you can't use RegisterHotkey() on VK_LSHIFT, even if the shift
  671 modifier is specified along with it:
  672 , {_T("LShift"), VK_LSHIFT}
  673 , {_T("RShift"), VK_RSHIFT}
  674 */
  675 , {_T("F1"), VK_F1}
  676 , {_T("F2"), VK_F2}
  677 , {_T("F3"), VK_F3}
  678 , {_T("F4"), VK_F4}
  679 , {_T("F5"), VK_F5}
  680 , {_T("F6"), VK_F6}
  681 , {_T("F7"), VK_F7}
  682 , {_T("F8"), VK_F8}
  683 , {_T("F9"), VK_F9}
  684 , {_T("F10"), VK_F10}
  685 , {_T("F11"), VK_F11}
  686 , {_T("F12"), VK_F12}
  687 , {_T("F13"), VK_F13}
  688 , {_T("F14"), VK_F14}
  689 , {_T("F15"), VK_F15}
  690 , {_T("F16"), VK_F16}
  691 , {_T("F17"), VK_F17}
  692 , {_T("F18"), VK_F18}
  693 , {_T("F19"), VK_F19}
  694 , {_T("F20"), VK_F20}
  695 , {_T("F21"), VK_F21}
  696 , {_T("F22"), VK_F22}
  697 , {_T("F23"), VK_F23}
  698 , {_T("F24"), VK_F24}
  699 
  700 // Mouse buttons:
  701 , {_T("LButton"), VK_LBUTTON}
  702 , {_T("RButton"), VK_RBUTTON}
  703 , {_T("MButton"), VK_MBUTTON}
  704 // Supported in only in Win2k and beyond:
  705 , {_T("XButton1"), VK_XBUTTON1}
  706 , {_T("XButton2"), VK_XBUTTON2}
  707 // Custom/fake VKs for use by the mouse hook (supported only in WinNT SP3 and beyond?):
  708 , {_T("WheelDown"), VK_WHEEL_DOWN}
  709 , {_T("WheelUp"), VK_WHEEL_UP}
  710 // Lexikos: Added fake VKs for support for horizontal scrolling in Windows Vista and later.
  711 , {_T("WheelLeft"), VK_WHEEL_LEFT}
  712 , {_T("WheelRight"), VK_WHEEL_RIGHT}
  713 
  714 , {_T("Browser_Back"), VK_BROWSER_BACK}
  715 , {_T("Browser_Forward"), VK_BROWSER_FORWARD}
  716 , {_T("Browser_Refresh"), VK_BROWSER_REFRESH}
  717 , {_T("Browser_Stop"), VK_BROWSER_STOP}
  718 , {_T("Browser_Search"), VK_BROWSER_SEARCH}
  719 , {_T("Browser_Favorites"), VK_BROWSER_FAVORITES}
  720 , {_T("Browser_Home"), VK_BROWSER_HOME}
  721 , {_T("Volume_Mute"), VK_VOLUME_MUTE}
  722 , {_T("Volume_Down"), VK_VOLUME_DOWN}
  723 , {_T("Volume_Up"), VK_VOLUME_UP}
  724 , {_T("Media_Next"), VK_MEDIA_NEXT_TRACK}
  725 , {_T("Media_Prev"), VK_MEDIA_PREV_TRACK}
  726 , {_T("Media_Stop"), VK_MEDIA_STOP}
  727 , {_T("Media_Play_Pause"), VK_MEDIA_PLAY_PAUSE}
  728 , {_T("Launch_Mail"), VK_LAUNCH_MAIL}
  729 , {_T("Launch_Media"), VK_LAUNCH_MEDIA_SELECT}
  730 , {_T("Launch_App1"), VK_LAUNCH_APP1}
  731 , {_T("Launch_App2"), VK_LAUNCH_APP2}
  732 
  733 // Probably safest to terminate it this way, with a flag value.  (plus this makes it a little easier
  734 // to code some loops, maybe).  Can also calculate how many elements are in the array using sizeof(array)
  735 // divided by sizeof(element).  UPDATE: Decided not to do this in case ever decide to sort this array; don't
  736 // want to rely on the fact that this will wind up in the right position after the sort (even though it
  737 // should):
  738 //, {_T(""), 0}
  739 };
  740 
  741 
  742 
  743 key_to_sc_type g_key_to_sc[] =
  744 // Even though ENTER is probably less likely to be assigned than NumpadEnter, must have ENTER as
  745 // the primary vk because otherwise, if the user configures only naked-NumPadEnter to do something,
  746 // RegisterHotkey() would register that vk and ENTER would also be configured to do the same thing.
  747 { {_T("NumpadEnter"), SC_NUMPADENTER}
  748 
  749 , {_T("Delete"), SC_DELETE}
  750 , {_T("Del"), SC_DELETE}
  751 , {_T("Insert"), SC_INSERT}
  752 , {_T("Ins"), SC_INSERT}
  753 // , {_T("Clear"), SC_CLEAR}  // Seems unnecessary because there is no counterpart to the Numpad5 clear key?
  754 , {_T("Up"), SC_UP}
  755 , {_T("Down"), SC_DOWN}
  756 , {_T("Left"), SC_LEFT}
  757 , {_T("Right"), SC_RIGHT}
  758 , {_T("Home"), SC_HOME}
  759 , {_T("End"), SC_END}
  760 , {_T("PgUp"), SC_PGUP}
  761 , {_T("PgDn"), SC_PGDN}
  762 
  763 // If user specified left or right, must use scan code to distinguish *both* halves of the pair since
  764 // each half has same vk *and* since their generic counterparts (e.g. CONTROL vs. L/RCONTROL) are
  765 // already handled by vk.  Note: RWIN and LWIN don't need to be handled here because they each have
  766 // their own virtual keys.
  767 // UPDATE: For the NT/2k/XP version, now doing these by VK since it's likely to be
  768 // more compatible with non-standard or non-English keyboards:
  769 /*
  770 , {_T("LControl"), SC_LCONTROL}
  771 , {_T("RControl"), SC_RCONTROL}
  772 , {_T("LShift"), SC_LSHIFT}
  773 , {_T("RShift"), SC_RSHIFT}
  774 , {_T("LAlt"), SC_LALT}
  775 , {_T("RAlt"), SC_RALT}
  776 */
  777 };
  778 
  779 
  780 // Can calc the counts only after the arrays are initialized above:
  781 int g_key_to_vk_count = _countof(g_key_to_vk);
  782 int g_key_to_sc_count = _countof(g_key_to_sc);
  783 
  784 KeyHistoryItem *g_KeyHistory = NULL; // Array is allocated during startup.
  785 int g_KeyHistoryNext = 0;
  786 
  787 #ifdef ENABLE_KEY_HISTORY_FILE
  788 bool g_KeyHistoryToFile = false;
  789 #endif
  790 
  791 // These must be global also, since both the keyboard and mouse hook functions,
  792 // in addition to KeyEvent() when it's logging keys with only the mouse hook installed,
  793 // MUST refer to the same variables.  Otherwise, the elapsed time between keyboard and
  794 // and mouse events will be wrong:
  795 DWORD g_HistoryTickNow = 0;
  796 DWORD g_HistoryTickPrev = GetTickCount();  // So that the first logged key doesn't have a huge elapsed time.
  797 HWND g_HistoryHwndPrev = NULL;
  798 
  799 // Also hook related:
  800 DWORD g_TimeLastInputPhysical = GetTickCount();
  801 DWORD g_TimeLastInputKeyboard = g_TimeLastInputPhysical;
  802 DWORD g_TimeLastInputMouse = g_TimeLastInputPhysical;