Copyright 2003-2006 Chris Mallett (support@autohotkey.com)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "stdafx.h" // pre-compiled headers
#include "globaldata.h" // for access to many global vars
#include "application.h" // for MsgSleep()
#include "window.h" // For MsgBox() & SetForegroundLockTimeout()
// General note:
// The use of Sleep() should be avoided *anywhere* in the code. Instead, call MsgSleep().
// The reason for this is that if the keyboard or mouse hook is installed, a straight call
// to Sleep() will cause user keystrokes & mouse events to lag because the message pump
// (GetMessage() or PeekMessage()) is the only means by which events are ever sent to the
// hook functions.
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Init any globals not in "struct g" that need it:
g_hInstance = hInstance;
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 (deleting when never initialized can crash, at least on Win 9x).
if (!GetCurrentDirectory(sizeof(g_WorkingDir), g_WorkingDir)) // Needed for the FileSelectFile() workaround.
*g_WorkingDir = '\0';
// Unlike the below, the above must not be Malloc'd because the contents can later change to something
// as large as MAX_PATH by means of the SetWorkingDir command.
g_WorkingDirOrig = SimpleHeap::Malloc(g_WorkingDir); // Needed by the Reload command.
// Set defaults, to be overridden by command line args we receive:
//char *script_filespec = "C:\\A-Source\\AutoHotkey\\Test\\New Text Document.ahk";
#else
char *script_filespec = NULL; // Set default as "unspecified/omitted".
#endif
#endif
// The problem of some command line parameters such as /r being "reserved" is a design flaw (one that
// can't be fixed without breaking existing scripts). Fortunately, I think it affects only compiled
// scripts because running a script via AutoHotkey.exe should avoid treating anything after the
// filename as switches. This flaw probably occurred because when this part of the program was designed,
// there was no plan to have compiled scripts.
//
// Examine command line args. Rules:
// Any special flags (e.g. /force and /restart) must appear prior to the script filespec.
// The script filespec (if present) must be the first non-backslash arg.
// All args that appear after the filespec are considered to be parameters for the script
// and will be added as variables %1% %2% etc.
// The above rules effectively make it impossible to autostart AutoHotkey.ini with parameters
// unless the filename is explicitly given (shouldn't be an issue for 99.9% of people).
char var_name[32], *param; // Small size since only numbers will be used (e.g. %1%, %2%).
Var *var;
bool switch_processing_is_complete = false;
int script_param_num = 1;
for (int i = 1; i < __argc; ++i) // Start at 1 because 0 contains the program name.
{
param = __argv[i]; // For performance and convenience.
if (switch_processing_is_complete) // All args are now considered to be input parameters for the script.
{
if ( !(var = g_script.FindOrAddVar(var_name, sprintf(var_name, "%d", script_param_num))) )
return CRITICAL_ERROR; // Realistically should never happen.
var->Assign(param);
++script_param_num;
}
// Insist that switches be an exact match for the allowed values to cut down on ambiguity.
// For example, if the user runs "CompiledScript.exe /find", we want /find to be considered
// an input parameter for the script rather than a switch:
else if (!stricmp(param, "/R") || !stricmp(param, "/restart"))
restart_mode = true;
else if (!stricmp(param, "/F") || !stricmp(param, "/force"))
g_ForceLaunch = true;
else if (!stricmp(param, "/ErrorStdOut"))
g_script.mErrorStdOut = true;
#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).
else if (!stricmp(param, "/iLib")) // v1.0.47: Build an include-file so that ahk2exe can include library functions called by the script.
{
++i; // Consume the next parameter too, because it's associated with this one.
if (i >= __argc) // Missing the expected filename parameter.
return CRITICAL_ERROR;
// For performance and simplicity, open/crease the file unconditionally and keep it open until exit.
if ( !(g_script.mIncludeLibraryFunctionsThenExit = fopen(__argv[i], "w")) ) // Can't open the temp file.
return CRITICAL_ERROR;
}
#endif
else // since this is not a recognized switch, the end of the [Switches] section has been reached (by design).
{
switch_processing_is_complete = true; // No more switches allowed after this point.
#ifdef AUTOHOTKEYSC
--i; // Make the loop process this item again so that it will be treated as a script param.
#else
script_filespec = param; // The first unrecognized switch must be the script filespec, by design.
#endif
}
}
#ifndef AUTOHOTKEYSC
if (script_filespec)// Script filename was explicitly specified, so check if it has the special conversion flag.
// The below is done even if AutoExecSectionTimeout() already set the values once.
// This is because when the AutoExecute section finally does finish, by definition it's
// supposed to store the global settings that are currently in effect as the default values.
// In other words, the only purpose of AutoExecSectionTimeout() is to handle cases where
// the AutoExecute section takes a long time to complete, or never completes (perhaps because
// it is being used by the script as a "backround thread" of sorts):
// Save the values of KeyDelay, WinDelay etc. in case they were changed by the auto-execute part
// of the script. These new defaults will be put into effect whenever a new hotkey subroutine
// is launched. Each launched subroutine may then change the values for its own purposes without
// affecting the settings for other subroutines:
global_clear_state(g); // Start with a "clean slate" in both g and g_default.
CopyMemory(&g_default, &g, sizeof(global_struct)); // Above has set g.IsPaused==false in case it's ever possible that it's true as a result of AutoExecSection().
// After this point, the values in g_default should never be changed.
// It seems best to set ErrorLevel to NONE after the auto-execute part of the script is done.
// However, we do not set it to NONE right before launching each new hotkey subroutine because
// it's more flexible that way (i.e. the user may want one hotkey subroutine to use the value of
// ErrorLevel set by another). This reset was also done by LoadFromFile(), but we do it again
// here in case the auto-exectute section changed it:
g_ErrorLevel->Assign(ERRORLEVEL_NONE);
// Since we're about to enter the script's idle state, set the "idle thread" to
// be minimum priority so that it can always be "interrupted" (though technically,
// there is no actual idle quasi-thread, so it can't really be interrupted):
g.Priority = PRIORITY_MINIMUM;
g.ThreadIsCritical = false; // v1.0.38.04: Prevent the idle thread from being seen as uninterruptible.
g.AllowTimers = true; // v1.0.40.01: Similar to above.
g.AllowThreadToBeInterrupted = true; // This is the primary line, the one above is not strictly necessary (just for maintainability).
// Call it in this special mode to kick off the main event loop.
// Be sure to pass something >0 for the first param or it will
// return (and we never want this to return):
MsgSleep(SLEEP_INTERVAL, WAIT_FOR_MESSAGES);
return 0; // Never executed; avoids compiler warning.