"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/AutoHotkey.cpp" (8 May 2021, 17466 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 "AutoHotkey.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 #include "globaldata.h" // for access to many global vars
   19 #include "application.h" // for MsgSleep()
   20 #include "window.h" // For MsgBox() & SetForegroundLockTimeout()
   21 #include "TextIO.h"
   22 
   23 // General note:
   24 // The use of Sleep() should be avoided *anywhere* in the code.  Instead, call MsgSleep().
   25 // The reason for this is that if the keyboard or mouse hook is installed, a straight call
   26 // to Sleep() will cause user keystrokes & mouse events to lag because the message pump
   27 // (GetMessage() or PeekMessage()) is the only means by which events are ever sent to the
   28 // hook functions.
   29 
   30 
   31 int MainExecuteScript();
   32 
   33 
   34 int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
   35 {
   36     // Init any globals not in "struct g" that need it:
   37     g_hInstance = hInstance;
   38     InitializeCriticalSection(&g_CriticalRegExCache); // v1.0.45.04: Must be done early so that it's unconditional, so that DeleteCriticalSection() in the script destructor can also be unconditional.
   39 
   40     // v1.1.22+: This is done unconditionally, on startup, so that any attempts to read a drive
   41     // that has no media (and possibly other errors) won't cause the system to display an error
   42     // dialog that the script can't suppress.  This is known to affect floppy drives and some
   43     // but not all CD/DVD drives.  MSDN says: "Best practice is that all applications call the
   44     // process-wide SetErrorMode function with a parameter of SEM_FAILCRITICALERRORS at startup."
   45     // Note that in previous versions, this was done by the Drive/DriveGet commands and not
   46     // reverted afterward, so it affected all subsequent commands.
   47     SetErrorMode(SEM_FAILCRITICALERRORS);
   48 
   49     UpdateWorkingDir(); // Needed for the FileSelectFile() workaround.
   50     g_WorkingDirOrig = SimpleHeap::Malloc(const_cast<LPTSTR>(g_WorkingDir.GetString())); // Needed by the Reload command.
   51 
   52     // Set defaults, to be overridden by command line args we receive:
   53     bool restart_mode = false;
   54 
   55 #ifndef AUTOHOTKEYSC
   56     #ifdef _DEBUG
   57         TCHAR *script_filespec = _T("Test\\Test.ahk");
   58     #else
   59         TCHAR *script_filespec = NULL; // Set default as "unspecified/omitted".
   60     #endif
   61 #endif
   62 
   63     // The problem of some command line parameters such as /r being "reserved" is a design flaw (one that
   64     // can't be fixed without breaking existing scripts).  Fortunately, I think it affects only compiled
   65     // scripts because running a script via AutoHotkey.exe should avoid treating anything after the
   66     // filename as switches. This flaw probably occurred because when this part of the program was designed,
   67     // there was no plan to have compiled scripts.
   68     // 
   69     // Examine command line args.  Rules:
   70     // Any special flags (e.g. /force and /restart) must appear prior to the script filespec.
   71     // The script filespec (if present) must be the first non-backslash arg.
   72     // All args that appear after the filespec are considered to be parameters for the script
   73     // and will be added as variables %1% %2% etc.
   74     // The above rules effectively make it impossible to autostart AutoHotkey.ini with parameters
   75     // unless the filename is explicitly given (shouldn't be an issue for 99.9% of people).
   76     TCHAR var_name[32], *param; // Small size since only numbers will be used (e.g. %1%, %2%).
   77     Var *var;
   78     bool switch_processing_is_complete = false;
   79     int script_param_num = 1;
   80     int first_script_param = __argc;
   81 
   82     for (int i = 1; i < __argc; ++i) // Start at 1 because 0 contains the program name.
   83     {
   84         param = __targv[i]; // For performance and convenience.
   85         if (switch_processing_is_complete) // All args are now considered to be input parameters for the script.
   86         {
   87             if (   !(var = g_script.FindOrAddVar(var_name, _stprintf(var_name, _T("%d"), script_param_num)))   )
   88                 return CRITICAL_ERROR;  // Realistically should never happen.
   89             var->Assign(param);
   90             ++script_param_num;
   91         }
   92         // Insist that switches be an exact match for the allowed values to cut down on ambiguity.
   93         // For example, if the user runs "CompiledScript.exe /find", we want /find to be considered
   94         // an input parameter for the script rather than a switch:
   95         else if (!_tcsicmp(param, _T("/R")) || !_tcsicmp(param, _T("/restart")))
   96             restart_mode = true;
   97         else if (!_tcsicmp(param, _T("/F")) || !_tcsicmp(param, _T("/force")))
   98             g_ForceLaunch = true;
   99         else if (!_tcsnicmp(param, _T("/ErrorStdOut"), 12))
  100             g_script.SetErrorStdOut(param[12] == '=' ? param + 13 : NULL);
  101 #ifndef AUTOHOTKEYSC // i.e. the following switch is recognized only by AutoHotkey.exe (especially since recognizing new switches in compiled scripts can break them, unlike AutoHotkey.exe).
  102         else if (!_tcsicmp(param, _T("/iLib"))) // v1.0.47: Build an include-file so that ahk2exe can include library functions called by the script.
  103         {
  104             ++i; // Consume the next parameter too, because it's associated with this one.
  105             if (i >= __argc) // Missing the expected filename parameter.
  106                 return CRITICAL_ERROR;
  107             // For performance and simplicity, open/create the file unconditionally and keep it open until exit.
  108             g_script.mIncludeLibraryFunctionsThenExit = new TextFile;
  109             if (!g_script.mIncludeLibraryFunctionsThenExit->Open(__targv[i], TextStream::WRITE | TextStream::EOL_CRLF | TextStream::BOM_UTF8, CP_UTF8)) // Can't open the temp file.
  110                 return CRITICAL_ERROR;
  111         }
  112         else if (!_tcsnicmp(param, _T("/CP"), 3)) // /CPnnn
  113         {
  114             // Default codepage for the script file, NOT the default for commands used by it.
  115             g_DefaultScriptCodepage = ATOU(param + 3);
  116         }
  117 #endif
  118 #ifdef CONFIG_DEBUGGER
  119         // Allow a debug session to be initiated by command-line.
  120         else if (!g_Debugger.IsConnected() && !_tcsnicmp(param, _T("/Debug"), 6) && (param[6] == '\0' || param[6] == '='))
  121         {
  122             if (param[6] == '=')
  123             {
  124                 param += 7;
  125 
  126                 LPTSTR c = _tcsrchr(param, ':');
  127 
  128                 if (c)
  129                 {
  130                     StringTCharToChar(param, g_DebuggerHost, (int)(c-param));
  131                     StringTCharToChar(c + 1, g_DebuggerPort);
  132                 }
  133                 else
  134                 {
  135                     StringTCharToChar(param, g_DebuggerHost);
  136                     g_DebuggerPort = "9000";
  137                 }
  138             }
  139             else
  140             {
  141                 g_DebuggerHost = "localhost";
  142                 g_DebuggerPort = "9000";
  143             }
  144             // The actual debug session is initiated after the script is successfully parsed.
  145         }
  146 #endif
  147         else // since this is not a recognized switch, the end of the [Switches] section has been reached (by design).
  148         {
  149             switch_processing_is_complete = true;  // No more switches allowed after this point.
  150 #ifdef AUTOHOTKEYSC
  151             --i; // Make the loop process this item again so that it will be treated as a script param.
  152 #else
  153             script_filespec = param;  // The first unrecognized switch must be the script filespec, by design.
  154 #endif
  155             first_script_param = i + 1;
  156         }
  157     }
  158 
  159     // Like AutoIt2, store the number of script parameters in the script variable %0%, even if it's zero:
  160     if (   !(var = g_script.FindOrAddVar(_T("0")))   )
  161         return CRITICAL_ERROR;  // Realistically should never happen.
  162     var->Assign(script_param_num - 1);
  163 
  164     if (Var *var = g_script.FindOrAddVar(_T("A_Args"), 6, VAR_DECLARE_SUPER_GLOBAL))
  165     {
  166         // Store the remaining args in an array and assign it to "Args".
  167         // If there are no args, assign an empty array.
  168         Object *args = Object::CreateFromArgV(__targv + first_script_param, __argc - first_script_param);
  169         if (!args)
  170             return CRITICAL_ERROR;  // Realistically should never happen.
  171         var->AssignSkipAddRef(args);
  172     }
  173     else
  174         return CRITICAL_ERROR;
  175 
  176     global_init(*g);  // Set defaults.
  177 
  178 // Set up the basics of the script:
  179 #ifdef AUTOHOTKEYSC
  180     if (g_script.Init(*g, _T(""), restart_mode) != OK) 
  181 #else
  182     if (g_script.Init(*g, script_filespec, restart_mode) != OK)  // Set up the basics of the script, using the above.
  183 #endif
  184         return CRITICAL_ERROR;
  185 
  186     // Set g_default now, reflecting any changes made to "g" above, in case AutoExecSection(), below,
  187     // never returns, perhaps because it contains an infinite loop (intentional or not):
  188     CopyMemory(&g_default, g, sizeof(global_struct));
  189 
  190     // Could use CreateMutex() but that seems pointless because we have to discover the
  191     // hWnd of the existing process so that we can close or restart it, so we would have
  192     // to do this check anyway, which serves both purposes.  Alt method is this:
  193     // Even if a 2nd instance is run with the /force switch and then a 3rd instance
  194     // is run without it, that 3rd instance should still be blocked because the
  195     // second created a 2nd handle to the mutex that won't be closed until the 2nd
  196     // instance terminates, so it should work ok:
  197     //CreateMutex(NULL, FALSE, script_filespec); // script_filespec seems a good choice for uniqueness.
  198     //if (!g_ForceLaunch && !restart_mode && GetLastError() == ERROR_ALREADY_EXISTS)
  199 
  200     UINT load_result = g_script.LoadFromFile();
  201     if (load_result == LOADING_FAILED) // Error during load (was already displayed by the function call).
  202     {
  203 #ifndef AUTOHOTKEYSC
  204         if (g_script.mIncludeLibraryFunctionsThenExit)
  205             g_script.mIncludeLibraryFunctionsThenExit->Close(); // Flush its buffer to disk.
  206 #endif
  207         return CRITICAL_ERROR;  // Should return this value because PostQuitMessage() also uses it.
  208     }
  209     if (!load_result) // LoadFromFile() relies upon us to do this check.  No script was loaded or we're in /iLib mode, so nothing more to do.
  210         return 0;
  211 
  212     // Unless explicitly set to be non-SingleInstance via SINGLE_INSTANCE_OFF or a special kind of
  213     // SingleInstance such as SINGLE_INSTANCE_REPLACE and SINGLE_INSTANCE_IGNORE, persistent scripts
  214     // and those that contain hotkeys/hotstrings are automatically SINGLE_INSTANCE_PROMPT as of v1.0.16:
  215     if (g_AllowOnlyOneInstance == ALLOW_MULTI_INSTANCE && IS_PERSISTENT)
  216         g_AllowOnlyOneInstance = SINGLE_INSTANCE_PROMPT;
  217 
  218     HWND w_existing = NULL;
  219     UserMessages reason_to_close_prior = (UserMessages)0;
  220     if (g_AllowOnlyOneInstance && g_AllowOnlyOneInstance != SINGLE_INSTANCE_OFF && !restart_mode && !g_ForceLaunch)
  221     {
  222         // Note: the title below must be constructed the same was as is done by our
  223         // CreateWindows(), which is why it's standardized in g_script.mMainWindowTitle:
  224         if (w_existing = FindWindow(WINDOW_CLASS_MAIN, g_script.mMainWindowTitle))
  225         {
  226             if (g_AllowOnlyOneInstance == SINGLE_INSTANCE_IGNORE)
  227                 return 0;
  228             if (g_AllowOnlyOneInstance != SINGLE_INSTANCE_REPLACE)
  229                 if (MsgBox(_T("An older instance of this script is already running.  Replace it with this")
  230                     _T(" instance?\nNote: To avoid this message, see #SingleInstance in the help file.")
  231                     , MB_YESNO, g_script.mFileName) == IDNO)
  232                     return 0;
  233             // Otherwise:
  234             reason_to_close_prior = AHK_EXIT_BY_SINGLEINSTANCE;
  235         }
  236     }
  237     if (!reason_to_close_prior && restart_mode)
  238         if (w_existing = FindWindow(WINDOW_CLASS_MAIN, g_script.mMainWindowTitle))
  239             reason_to_close_prior = AHK_EXIT_BY_RELOAD;
  240     if (reason_to_close_prior)
  241     {
  242         // Now that the script has been validated and is ready to run, close the prior instance.
  243         // We wait until now to do this so that the prior instance's "restart" hotkey will still
  244         // be available to use again after the user has fixed the script.  UPDATE: We now inform
  245         // the prior instance of why it is being asked to close so that it can make that reason
  246         // available to the OnExit subroutine via a built-in variable:
  247         ASK_INSTANCE_TO_CLOSE(w_existing, reason_to_close_prior);
  248         //PostMessage(w_existing, WM_CLOSE, 0, 0);
  249 
  250         // Wait for it to close before we continue, so that it will deinstall any
  251         // hooks and unregister any hotkeys it has:
  252         int interval_count;
  253         for (interval_count = 0; ; ++interval_count)
  254         {
  255             Sleep(20);  // No need to use MsgSleep() in this case.
  256             if (!IsWindow(w_existing))
  257                 break;  // done waiting.
  258             if (interval_count == 100)
  259             {
  260                 // This can happen if the previous instance has an OnExit subroutine that takes a long
  261                 // time to finish, or if it's waiting for a network drive to timeout or some other
  262                 // operation in which it's thread is occupied.
  263                 if (MsgBox(_T("Could not close the previous instance of this script.  Keep waiting?"), 4) == IDNO)
  264                     return CRITICAL_ERROR;
  265                 interval_count = 0;
  266             }
  267         }
  268         // Give it a small amount of additional time to completely terminate, even though
  269         // its main window has already been destroyed:
  270         Sleep(100);
  271     }
  272 
  273     // Call this only after closing any existing instance of the program,
  274     // because otherwise the change to the "focus stealing" setting would never be undone:
  275     SetForegroundLockTimeout();
  276 
  277     // Create all our windows and the tray icon.  This is done after all other chances
  278     // to return early due to an error have passed, above.
  279     if (g_script.CreateWindows() != OK)
  280         return CRITICAL_ERROR;
  281 
  282     // At this point, it is nearly certain that the script will be executed.
  283 
  284     // v1.0.48.04: Turn off buffering on stdout so that "FileAppend, Text, *" will write text immediately
  285     // rather than lazily. This helps debugging, IPC, and other uses, probably with relatively little
  286     // impact on performance given the OS's built-in caching.  I looked at the source code for setvbuf()
  287     // and it seems like it should execute very quickly.  Code size seems to be about 75 bytes.
  288     setvbuf(stdout, NULL, _IONBF, 0); // Must be done PRIOR to writing anything to stdout.
  289 
  290     if (g_MaxHistoryKeys && (g_KeyHistory = (KeyHistoryItem *)malloc(g_MaxHistoryKeys * sizeof(KeyHistoryItem))))
  291         ZeroMemory(g_KeyHistory, g_MaxHistoryKeys * sizeof(KeyHistoryItem)); // Must be zeroed.
  292     //else leave it NULL as it was initialized in globaldata.
  293 
  294     // MSDN: "Windows XP: If a manifest is used, InitCommonControlsEx is not required."
  295     // Therefore, in case it's a high overhead call, it's not done on XP or later:
  296     if (!g_os.IsWinXPorLater())
  297     {
  298         // Since InitCommonControls() is apparently incapable of initializing DateTime and MonthCal
  299         // controls, InitCommonControlsEx() must be called.
  300         INITCOMMONCONTROLSEX icce;
  301         icce.dwSize = sizeof(INITCOMMONCONTROLSEX);
  302         icce.dwICC = ICC_WIN95_CLASSES | ICC_DATE_CLASSES; // ICC_WIN95_CLASSES is equivalent to calling InitCommonControls().
  303         InitCommonControlsEx(&icce);
  304     }
  305 
  306 #ifdef CONFIG_DEBUGGER
  307     // Initiate debug session now if applicable.
  308     if (!g_DebuggerHost.IsEmpty() && g_Debugger.Connect(g_DebuggerHost, g_DebuggerPort) == DEBUGGER_E_OK)
  309     {
  310         g_Debugger.Break();
  311     }
  312 #endif
  313 
  314     // Activate the hotkeys, hotstrings, and any hooks that are required prior to executing the
  315     // top part (the auto-execute part) of the script so that they will be in effect even if the
  316     // top part is something that's very involved and requires user interaction:
  317     Hotkey::ManifestAllHotkeysHotstringsHooks(); // We want these active now in case auto-execute never returns (e.g. loop)
  318     g_script.mIsReadyToExecute = true; // This is done only after the above to support error reporting in Hotkey.cpp.
  319     g_HSSameLineAction = false; // `#Hotstring X` should not affect Hotstring().
  320 
  321     Var *clipboard_var = g_script.FindOrAddVar(_T("Clipboard")); // Add it if it doesn't exist, in case the script accesses "Clipboard" via a dynamic variable.
  322     if (clipboard_var)
  323         // This is done here rather than upon variable creation speed up runtime/dynamic variable creation.
  324         // Since the clipboard can be changed by activity outside the program, don't read-cache its contents.
  325         // Since other applications and the user should see any changes the program makes to the clipboard,
  326         // don't write-cache it either.
  327         clipboard_var->DisableCache();
  328 
  329     return MainExecuteScript();
  330 }
  331 
  332 
  333 int MainExecuteScript()
  334 {
  335     
  336 #ifndef _DEBUG
  337     __try
  338 #endif
  339     {
  340         // Run the auto-execute part at the top of the script (this call might never return):
  341         if (!g_script.AutoExecSection()) // Can't run script at all. Due to rarity, just abort.
  342             return CRITICAL_ERROR;
  343         // REMEMBER: The call above will never return if one of the following happens:
  344         // 1) The AutoExec section never finishes (e.g. infinite loop).
  345         // 2) The AutoExec function uses the Exit or ExitApp command to terminate the script.
  346         // 3) The script isn't persistent and its last line is reached (in which case an ExitApp is implicit).
  347 
  348         // Call it in this special mode to kick off the main event loop.
  349         // Be sure to pass something >0 for the first param or it will
  350         // return (and we never want this to return):
  351         MsgSleep(SLEEP_INTERVAL, WAIT_FOR_MESSAGES);
  352     }
  353 #ifndef _DEBUG
  354     __except (EXCEPTION_EXECUTE_HANDLER)
  355     {
  356         LPCTSTR msg;
  357         auto ecode = GetExceptionCode();
  358         switch (ecode)
  359         {
  360         // Having specific messages for the most common exceptions seems worth the added code size.
  361         // The term "stack overflow" is not used because it is probably less easily understood by
  362         // the average user, and is not useful as a search term due to stackoverflow.com.
  363         case EXCEPTION_STACK_OVERFLOW: msg = _T("Function recursion limit exceeded."); break;
  364         case EXCEPTION_ACCESS_VIOLATION: msg = _T("Invalid memory read/write."); break;
  365         default: msg = _T("System exception 0x%X."); break;
  366         }
  367         TCHAR buf[127];
  368         sntprintf(buf, _countof(buf), msg, ecode);
  369         g_script.CriticalError(buf);
  370         return ecode;
  371     }
  372 #endif 
  373     return 0; // Never executed; avoids compiler warning.
  374 }