"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/window.cpp" (8 May 2021, 97871 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 "window.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 "window.h"
   19 #include "util.h" // for strlcpy()
   20 #include "application.h" // for MsgSleep()
   21 #include "psapi.h" // for ahk_exe
   22 #include <dwmapi.h>
   23 
   24 
   25 HWND WinActivate(global_struct &aSettings, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText
   26     , bool aFindLastMatch, HWND aAlreadyVisited[], int aAlreadyVisitedCount)
   27 {
   28     // If window is already active, be sure to leave it that way rather than activating some
   29     // other window that may match title & text also.  NOTE: An explicit check is done
   30     // for this rather than just relying on EnumWindows() to obey the z-order because
   31     // EnumWindows() is *not* guaranteed to enumerate windows in z-order, thus the currently
   32     // active window, even if it's an exact match, might become overlapped by another matching
   33     // window.  Also, use the USE_FOREGROUND_WINDOW vs. IF_USE_FOREGROUND_WINDOW macro for
   34     // this because the active window can sometimes be NULL (i.e. if it's a hidden window
   35     // and DetectHiddenWindows is off):
   36     HWND target_window;
   37     if (USE_FOREGROUND_WINDOW(aTitle, aText, aExcludeTitle, aExcludeText))
   38     {
   39         // User asked us to activate the "active" window, which by definition already is.
   40         SET_TARGET_TO_ALLOWABLE_FOREGROUND(aSettings.DetectHiddenWindows)
   41         // The documented behavior is "If a matching window is already active, that window
   42         // will be kept active"; however, if the active (foreground) window is hidden and
   43         // DetectHiddenWindows is off, that window isn't a match and so we should search
   44         // for a matching window.
   45         if (target_window) // Added in v1.1.20.
   46         {
   47             // The window is already active, but might be minimized.  The behavior in
   48             // v1.1.19 and earlier was to leave it minimized, but it seems more useful
   49             // and intuitive to restore it.  The documentation says "If the window is
   50             // minimized, it is automatically restored prior to being activated."
   51             if (IsIconic(target_window))
   52                 ShowWindow(target_window, SW_RESTORE);
   53             return target_window;
   54         }
   55     }
   56 
   57     if (!aFindLastMatch && !*aTitle && !*aText && !*aExcludeTitle && !*aExcludeText)
   58     {
   59         // User passed no params, so use the window most recently found by WinExist():
   60         if (   !(target_window = GetValidLastUsedWindow(aSettings))   )
   61             return NULL;
   62     }
   63     else
   64     {
   65         /*
   66         // Might not help avg. performance any?
   67         if (!aFindLastMatch) // Else even if the windows is already active, we want the bottommost one.
   68             if (hwnd = WinActive(aTitle, aText, aExcludeTitle, aExcludeText)) // Already active.
   69                 return target_window;
   70         */
   71         // Don't activate in this case, because the top-most window might be an
   72         // always-on-top but not-meant-to-be-activated window such as AutoIt's
   73         // splash text:
   74         if (   !(target_window = WinExist(aSettings, aTitle, aText, aExcludeTitle, aExcludeText, aFindLastMatch
   75             , false, aAlreadyVisited, aAlreadyVisitedCount))   )
   76             return NULL;
   77     }
   78     // Above has ensured that target_window is non-NULL, that it is a valid window, and that
   79     // it is eligible due to g->DetectHiddenWindows being true or the window not being hidden
   80     // (or being one of the script's GUI windows).
   81     return SetForegroundWindowEx(target_window);
   82 }
   83 
   84 
   85 
   86 #ifdef _DEBUG_WINACTIVATE
   87 #define LOGF "c:\\AutoHotkey SetForegroundWindowEx.txt"
   88 HWND AttemptSetForeground(HWND aTargetWindow, HWND aForeWindow, LPTSTR aTargetTitle)
   89 #else
   90 HWND AttemptSetForeground(HWND aTargetWindow, HWND aForeWindow)
   91 #endif
   92 // Returns NULL if aTargetWindow or its owned-window couldn't be brought to the foreground.
   93 // Otherwise, on success, it returns either aTargetWindow or an HWND owned by aTargetWindow.
   94 {
   95     // Probably best not to trust its return value.  It's been shown to be unreliable at times.
   96     // Example: I've confirmed that SetForegroundWindow() sometimes (perhaps about 10% of the time)
   97     // indicates failure even though it succeeds.  So we specifically check to see if it worked,
   98     // which helps to avoid using the keystroke (2-alts) method, because that may disrupt the
   99     // desired state of the keys or disturb any menus that the user may have displayed.
  100     // Also: I think the 2-alts last-resort may fire when the system is lagging a bit
  101     // (i.e. a drive spinning up) and the window hasn't actually become active yet,
  102     // even though it will soon become active on its own.  Also, SetForegroundWindow() sometimes
  103     // indicates failure even though it succeeded, usually because the window didn't become
  104     // active immediately -- perhaps because the system was under load -- but did soon become
  105     // active on its own (after, say, 50ms or so).  UPDATE: If SetForegroundWindow() is called
  106     // on a hung window, at least when AttachThreadInput is in effect and that window has
  107     // a modal dialog (such as MSIE's find dialog), this call might never return, locking up
  108     // our thread.  So now we do this fast-check for whether the window is hung first (and
  109     // this call is indeed very fast: its worst case is at least 30x faster than the worst-case
  110     // performance of the ABORT-IF-HUNG method used with SendMessageTimeout.
  111     // UPDATE for v1.0.42.03: To avoid a very rare crashing issue, IsWindowHung() is no longer called
  112     // here, but instead by our caller.  Search on "v1.0.42.03" for more comments.
  113     BOOL result = SetForegroundWindow(aTargetWindow);
  114     // Note: Increasing the sleep time below did not help with occurrences of "indicated success
  115     // even though it failed", at least with metapad.exe being activated while command prompt
  116     // and/or AutoIt2's InputBox were active or present on the screen:
  117     SLEEP_WITHOUT_INTERRUPTION(SLEEP_INTERVAL); // Specify param so that it will try to specifically sleep that long.
  118     HWND new_fore_window = GetForegroundWindow();
  119     if (new_fore_window == aTargetWindow)
  120     {
  121 #ifdef _DEBUG_WINACTIVATE
  122         if (!result)
  123         {
  124             FileAppend(LOGF, _T("SetForegroundWindow() indicated failure even though it succeeded: "), false);
  125             FileAppend(LOGF, aTargetTitle);
  126         }
  127 #endif
  128         return aTargetWindow;
  129     }
  130     if (new_fore_window != aForeWindow && aTargetWindow == GetWindow(new_fore_window, GW_OWNER))
  131         // The window we're trying to get to the foreground is the owner of the new foreground window.
  132         // This is considered to be a success because a window that owns other windows can never be
  133         // made the foreground window, at least if the windows it owns are visible.
  134         return new_fore_window;
  135     // Otherwise, failure:
  136 #ifdef _DEBUG_WINACTIVATE
  137     if (result)
  138     {
  139         FileAppend(LOGF, _T("SetForegroundWindow() indicated success even though it failed: "), false);
  140         FileAppend(LOGF, aTargetTitle);
  141     }
  142 #endif
  143     return NULL;
  144 }
  145 
  146 
  147 
  148 HWND SetForegroundWindowEx(HWND aTargetWindow)
  149 // Caller must have ensured that aTargetWindow is a valid window or NULL, since we
  150 // don't call IsWindow() here.
  151 {
  152     if (!aTargetWindow)
  153         return NULL;  // When called this way (as it is sometimes), do nothing.
  154 
  155     // v1.0.42.03: Calling IsWindowHung() once here rather than potentially more than once in AttemptSetForeground()
  156     // solves a crash that is not fully understood, nor is it easily reproduced (it occurs only in release mode,
  157     // not debug mode).  It's likely a bug in the API's IsHungAppWindow(), but that is far from confirmed.
  158     DWORD target_thread = GetWindowThreadProcessId(aTargetWindow, NULL);
  159     if (target_thread != g_MainThreadID && IsWindowHung(aTargetWindow)) // Calls to IsWindowHung should probably be avoided if the window belongs to our thread.  Relies upon short-circuit boolean order.
  160         return NULL;
  161 
  162 #ifdef _DEBUG_WINACTIVATE
  163     TCHAR win_name[64];
  164     GetWindowText(aTargetWindow, win_name, _countof(win_name));
  165 #endif
  166 
  167     HWND orig_foreground_wnd = GetForegroundWindow();
  168     // AutoIt3: If there is not any foreground window, then input focus is on the TaskBar.
  169     // MY: It is definitely possible for GetForegroundWindow() to return NULL, even on XP.
  170     // Lexikos: It can be easily reproduced by calling GetForegroundWindow() in a tight
  171     //  loop on Windows 10 and switching windows with Alt+Esc.  In such cases the taskbar
  172     //  is definitely NOT active, and if our caller requested activation of the taskbar,
  173     //  it should not be skipped just because !orig_foreground_wnd.
  174     // MSDN: "The foreground window can be NULL in certain circumstances, such as when a
  175     //  window is losing activation."
  176     //if (!orig_foreground_wnd)
  177     //  orig_foreground_wnd = FindWindow(_T("Shell_TrayWnd"), NULL);
  178 
  179     // Fix for v1.1.28.02: Restore the window *before* checking if it is already active.
  180     // This was supposed to be done in v1.1.20, but was only done for WinTitle = "A".
  181     // See "IsIconic" in WinActivate() for comments.
  182     if (IsIconic(aTargetWindow))
  183         // This might never return if aTargetWindow is a hung window.  But it seems better
  184         // to do it this way than to use the PostMessage() method, which might not work
  185         // reliably with apps that don't handle such messages in a standard way.
  186         // A minimized window must be restored or else SetForegroundWindow() always(?)
  187         // won't work on it.  UPDATE: ShowWindowAsync() would prevent a hang, but
  188         // probably shouldn't use it because we rely on the fact that the message
  189         // has been acted on prior to trying to activate the window (and all Async()
  190         // does is post a message to its queue):
  191         ShowWindow(aTargetWindow, SW_RESTORE);
  192 
  193     if (aTargetWindow == orig_foreground_wnd) // It's already the active window.
  194         return aTargetWindow;
  195 
  196     // This causes more trouble than it's worth.  In fact, the AutoIt author said that
  197     // he didn't think it even helped with the IE 5.5 related issue it was originally
  198     // intended for, so it seems a good idea to NOT to this, especially since I'm 80%
  199     // sure it messes up the Z-order in certain circumstances, causing an unexpected
  200     // window to pop to the foreground immediately after a modal dialog is dismissed:
  201     //BringWindowToTop(aTargetWindow); // AutoIt3: IE 5.5 related hack.
  202 
  203     HWND new_foreground_wnd;
  204 
  205     if (!g_WinActivateForce)
  206         // Try a simple approach first:
  207 #ifdef _DEBUG_WINACTIVATE
  208 #define IF_ATTEMPT_SET_FORE if (new_foreground_wnd = AttemptSetForeground(aTargetWindow, orig_foreground_wnd, win_name))
  209 #else
  210 #define IF_ATTEMPT_SET_FORE if (new_foreground_wnd = AttemptSetForeground(aTargetWindow, orig_foreground_wnd))
  211 #endif
  212         IF_ATTEMPT_SET_FORE
  213             return new_foreground_wnd;
  214         // Otherwise continue with the more drastic methods below.
  215 
  216     // MY: The AttachThreadInput method, when used by itself, seems to always
  217     // work the first time on my XP system, seemingly regardless of whether the
  218     // "allow focus steal" change has been made via SystemParametersInfo()
  219     // (but it seems a good idea to keep the SystemParametersInfo() in effect
  220     // in case Win2k or Win98 needs it, or in case it really does help in rare cases).
  221     // In many cases, this avoids the two SetForegroundWindow() attempts that
  222     // would otherwise be needed; and those two attempts cause some windows
  223     // to flash in the taskbar, such as Metapad and Excel (less frequently) whenever
  224     // you quickly activate another window after activating it first (e.g. via hotkeys).
  225     // So for now, it seems best just to use this method by itself.  The
  226     // "two-alts" case never seems to fire on my system?  Maybe it will
  227     // on Win98 sometimes.
  228     // Note: In addition to the "taskbar button flashing" annoyance mentioned above
  229     // any SetForegroundWindow() attempt made prior to the one below will,
  230     // as a side-effect, sometimes trigger the need for the "two-alts" case
  231     // below.  So that's another reason to just keep it simple and do it this way
  232     // only.
  233 
  234 #ifdef _DEBUG_WINACTIVATE
  235     TCHAR buf[1024];
  236 #endif
  237 
  238     bool is_attached_my_to_fore = false, is_attached_fore_to_target = false;
  239     DWORD fore_thread;
  240     if (orig_foreground_wnd) // Might be NULL from above.
  241     {
  242         // Based on MSDN docs, these calls should always succeed due to the other
  243         // checks done above (e.g. that none of the HWND's are NULL):
  244         fore_thread = GetWindowThreadProcessId(orig_foreground_wnd, NULL);
  245 
  246         // MY: Normally, it's suggested that you only need to attach the thread of the
  247         // foreground window to our thread.  However, I've confirmed that doing all three
  248         // attaches below makes the attempt much more likely to succeed.  In fact, it
  249         // almost always succeeds whereas the one-attach method hardly ever succeeds the first
  250         // time (resulting in a flashing taskbar button due to having to invoke a second attempt)
  251         // when one window is quickly activated after another was just activated.
  252         // AutoIt3: Attach all our input threads, will cause SetForeground to work under 98/Me.
  253         // MSDN docs: The AttachThreadInput function fails if either of the specified threads
  254         // does not have a message queue (My: ok here, since any window's thread MUST have a
  255         // message queue).  [It] also fails if a journal record hook is installed.  ... Note
  256         // that key state, which can be ascertained by calls to the GetKeyState or
  257         // GetKeyboardState function, is reset after a call to AttachThreadInput.  You cannot
  258         // attach a thread to a thread in another desktop.  A thread cannot attach to itself.
  259         // Therefore, idAttachTo cannot equal idAttach.  Update: It appears that of the three,
  260         // this first call does not offer any additional benefit, at least on XP, so not
  261         // using it for now:
  262         //if (g_MainThreadID != target_thread) // Don't attempt the call otherwise.
  263         //  AttachThreadInput(g_MainThreadID, target_thread, TRUE);
  264         if (fore_thread && g_MainThreadID != fore_thread && !IsWindowHung(orig_foreground_wnd))
  265             is_attached_my_to_fore = AttachThreadInput(g_MainThreadID, fore_thread, TRUE) != 0;
  266         if (fore_thread && target_thread && fore_thread != target_thread) // IsWindowHung(aTargetWindow) was called earlier.
  267             is_attached_fore_to_target = AttachThreadInput(fore_thread, target_thread, TRUE) != 0;
  268     }
  269 
  270     // The log showed that it never seemed to need more than two tries.  But there's
  271     // not much harm in trying a few extra times.  The number of tries needed might
  272     // vary depending on how fast the CPU is:
  273     for (int i = 0; i < 5; ++i)
  274     {
  275         IF_ATTEMPT_SET_FORE
  276         {
  277 #ifdef _DEBUG_WINACTIVATE
  278             if (i > 0) // More than one attempt was needed.
  279             {
  280                 sntprintf(buf, _countof(buf), _T("AttachThreadInput attempt #%d indicated success: %s")
  281                     , i + 1, win_name);
  282                 FileAppend(LOGF, buf);
  283             }
  284 #endif
  285             break;
  286         }
  287     }
  288 
  289     // I decided to avoid the quick minimize + restore method of activation.  It's
  290     // not that much more effective (if at all), and there are some significant
  291     // disadvantages:
  292     // - This call will often hang our thread if aTargetWindow is a hung window: ShowWindow(aTargetWindow, SW_MINIMIZE)
  293     // - Using SW_FORCEMINIMIZE instead of SW_MINIMIZE has at least one (and probably more)
  294     // side effect: When the window is restored, at least via SW_RESTORE, it is no longer
  295     // maximized even if it was before the minimize.  So don't use it.
  296     if (!new_foreground_wnd) // Not successful yet.
  297     {
  298         // Some apps may be intentionally blocking us by having called the API function
  299         // LockSetForegroundWindow(), for which MSDN says "The system automatically enables
  300         // calls to SetForegroundWindow if the user presses the ALT key or takes some action
  301         // that causes the system itself to change the foreground window (for example,
  302         // clicking a background window)."  Also, it's probably best to avoid doing
  303         // the 2-alts method except as a last resort, because I think it may mess up
  304         // the state of menus the user had displayed.  And of course if the foreground
  305         // app has special handling for alt-key events, it might get confused.
  306         // My original note: "The 2-alts case seems to mess up on rare occasions,
  307         // perhaps due to menu weirdness triggered by the alt key."
  308         // AutoIt3: OK, this is not funny - bring out the extreme measures (usually for 2000/XP).
  309         // Simulate two single ALT keystrokes.  UPDATE: This hardly ever succeeds.  Usually when
  310         // it fails, the foreground window is NULL (none).  I'm going to try an Win-tab instead,
  311         // which selects a task bar button.  This seems less invasive than doing an alt-tab
  312         // because not only doesn't it activate some other window first, it also doesn't appear
  313         // to change the Z-order, which is good because we don't want the alt-tab order
  314         // that the user sees to be affected by this.  UPDATE: Win-tab isn't doing it, so try
  315         // Alt-tab.  Alt-tab doesn't do it either.  The window itself (metapad.exe is the only
  316         // culprit window I've found so far) seems to resist being brought to the foreground,
  317         // but later, after the hotkey is released, it can be.  So perhaps this is being
  318         // caused by the fact that the user has keys held down (logically or physically?)
  319         // Releasing those keys with a key-up event might help, so try that sometime:
  320         KeyEvent(KEYDOWNANDUP, VK_MENU);
  321         KeyEvent(KEYDOWNANDUP, VK_MENU);
  322         //KeyEvent(KEYDOWN, VK_LWIN);
  323         //KeyEvent(KEYDOWN, VK_TAB);
  324         //KeyEvent(KEYUP, VK_TAB);
  325         //KeyEvent(KEYUP, VK_LWIN);
  326         //KeyEvent(KEYDOWN, VK_MENU);
  327         //KeyEvent(KEYDOWN, VK_TAB);
  328         //KeyEvent(KEYUP, VK_TAB);
  329         //KeyEvent(KEYUP, VK_MENU);
  330         // Also replacing "2-alts" with "alt-tab" below, for now:
  331 
  332 #ifndef _DEBUG_WINACTIVATE
  333         new_foreground_wnd = AttemptSetForeground(aTargetWindow, orig_foreground_wnd);
  334 #else // debug mode
  335         IF_ATTEMPT_SET_FORE
  336             FileAppend(LOGF, _T("2-alts ok: "), false);
  337         else
  338         {
  339             FileAppend(LOGF, _T("2-alts (which is the last resort) failed.  "), false);
  340             HWND h = GetForegroundWindow();
  341             if (h)
  342             {
  343                 TCHAR fore_name[64];
  344                 GetWindowText(h, fore_name, _countof(fore_name));
  345                 FileAppend(LOGF, _T("Foreground: "), false);
  346                 FileAppend(LOGF, fore_name, false);
  347             }
  348             FileAppend(LOGF, _T(".  Was trying to activate: "), false);
  349         }
  350         FileAppend(LOGF, win_name);
  351 #endif
  352     } // if()
  353 
  354     // Very important to detach any threads whose inputs were attached above,
  355     // prior to returning, otherwise the next attempt to attach thread inputs
  356     // for these particular windows may result in a hung thread or other
  357     // undesirable effect:
  358     if (is_attached_my_to_fore)
  359         AttachThreadInput(g_MainThreadID, fore_thread, FALSE);
  360     if (is_attached_fore_to_target)
  361         AttachThreadInput(fore_thread, target_thread, FALSE);
  362 
  363     // Finally.  This one works, solving the problem of the MessageBox window
  364     // having the input focus and being the foreground window, but not actually
  365     // being visible (even though IsVisible() and IsIconic() say it is)!  It may
  366     // help with other conditions under which this function would otherwise fail.
  367     // Here's the way the repeat the failure to test how the absence of this line
  368     // affects things, at least on my XP SP1 system:
  369     // y::MsgBox, test
  370     // #e::(some hotkey that activates Windows Explorer)
  371     // Now: Activate explorer with the hotkey, then invoke the MsgBox.  It will
  372     // usually be activated but invisible.  Also: Whenever this invisible problem
  373     // is about to occur, with or without this fix, it appears that the OS's z-order
  374     // is a bit messed up, because when you dismiss the MessageBox, an unexpected
  375     // window (probably the one two levels down) becomes active rather than the
  376     // window that's only 1 level down in the z-order:
  377     if (new_foreground_wnd) // success.
  378     {
  379         // Even though this is already done for the IE 5.5 "hack" above, must at
  380         // a minimum do it here: The above one may be optional, not sure (safest
  381         // to leave it unless someone can test with IE 5.5).
  382         // Note: I suspect the two lines below achieve the same thing.  They may
  383         // even be functionally identical.  UPDATE: This may no longer be needed
  384         // now that the first BringWindowToTop(), above, has been disabled due to
  385         // its causing more trouble than it's worth.  But seems safer to leave
  386         // this one enabled in case it does resolve IE 5.5 related issues and
  387         // possible other issues:
  388         BringWindowToTop(aTargetWindow);
  389         //SetWindowPos(aTargetWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
  390         return new_foreground_wnd; // Return this rather than aTargetWindow because it's more appropriate.
  391     }
  392     else
  393         return NULL;
  394 }
  395 
  396 
  397 
  398 HWND WinClose(global_struct &aSettings, LPTSTR aTitle, LPTSTR aText, int aTimeToWaitForClose
  399     , LPTSTR aExcludeTitle, LPTSTR aExcludeText, bool aKillIfHung)
  400 // Return the HWND of any found-window to the caller so that it has the option of waiting
  401 // for it to become an invalid (closed) window.
  402 {
  403     HWND target_window;
  404     IF_USE_FOREGROUND_WINDOW(aSettings.DetectHiddenWindows, aTitle, aText, aExcludeTitle, aExcludeText)
  405         // Close topmost (better than !F4 since that uses the alt key, effectively resetting
  406         // its status to UP if it was down before.  Use WM_CLOSE rather than WM_EXIT because
  407         // I think that's what Alt-F4 sends (and otherwise, app may quit without offering
  408         // a chance to save).
  409         // DON'T DISPLAY a MsgBox (e.g. debugging) before trying to close foreground window.
  410         // Otherwise, it may close the owner of the dialog window (this app), perhaps due to
  411         // split-second timing issues.
  412     else if (*aTitle || *aText || *aExcludeTitle || *aExcludeText)
  413     {
  414         // Since EnumWindows() is *not* guaranteed to start proceed in z-order from topmost to
  415         // bottommost (though it almost certainly does), do it this way to ensure that the
  416         // topmost window is closed in preference to any other windows with the same <aTitle>
  417         // and <aText>:
  418         if (   !(target_window = WinActive(aSettings, aTitle, aText, aExcludeTitle, aExcludeText))   )
  419             if (   !(target_window = WinExist(aSettings, aTitle, aText, aExcludeTitle, aExcludeText))   )
  420                 return NULL;
  421     }
  422     else
  423         target_window = GetValidLastUsedWindow(aSettings);
  424 
  425     return target_window ? WinClose(target_window, aTimeToWaitForClose, aKillIfHung) : NULL;
  426 }
  427 
  428 
  429 
  430 HWND WinClose(HWND aWnd, int aTimeToWaitForClose, bool aKillIfHung)
  431 {
  432     if (aKillIfHung) // This part is based on the AutoIt3 source.
  433         // Update: Another reason not to wait a long time with the below is that WinKill
  434         // is normally only used when the target window is suspected of being hung.  It
  435         // seems bad to wait something like 2 seconds in such a case, when the caller
  436         // probably already knows it's hung.
  437         // Obsolete in light of dedicated hook thread: Because this app is much more sensitive to being
  438         // in a "not-pumping-messages" state, due to the keyboard & mouse hooks, it seems better to wait
  439         // for only 200 ms (e.g. in case the user is gaming and there's a script
  440         // running in the background that uses WinKill, we don't want key and mouse events
  441         // to freeze for a long time).
  442         Util_WinKill(aWnd);
  443     else // Don't kill.
  444         // SC_CLOSE is the same as clicking a window's "X"(close) button or using Alt-F4.
  445         // Although it's a more friendly way to close windows than WM_CLOSE (and thus
  446         // avoids incompatibilities with apps such as MS Visual C++), apps that
  447         // have disabled Alt-F4 processing will not be successfully closed.  It seems
  448         // best not to send both SC_CLOSE and WM_CLOSE because some apps with an 
  449         // "Unsaved.  Are you sure?" type dialog might close down completely rather than
  450         // waiting for the user to confirm.  Anyway, it's extremely rare for a window
  451         // not to respond to Alt-F4 (though it is possible that it handles Alt-F4 in a
  452         // non-standard way, i.e. that sending SC_CLOSE equivalent to Alt-F4
  453         // for windows that handle Alt-F4 manually?)  But on the upside, this is nicer
  454         // for apps that upon receiving Alt-F4 do some behavior other than closing, such
  455         // as minimizing to the tray.  Such apps might shut down entirely if they received
  456         // a true WM_CLOSE, which is probably not what the user would want.
  457         // Update: Switched back to using WM_CLOSE so that instances of AutoHotkey
  458         // can be terminated via another instances use of the WinClose command:
  459         //PostMessage(aWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
  460         PostMessage(aWnd, WM_CLOSE, 0, 0);
  461 
  462     if (aTimeToWaitForClose < 0)
  463         aTimeToWaitForClose = 0;
  464     if (!aTimeToWaitForClose)
  465         return aWnd; // v1.0.30: EnumParentActUponAll() relies on the ability to do no delay at all.
  466 
  467     // Slight delay.  Might help avoid user having to modify script to use WinWaitClose()
  468     // in many cases.  UPDATE: But this does a Sleep(0), which won't yield the remainder
  469     // of our thread's timeslice unless there's another app trying to use 100% of the CPU?
  470     // So, in reality it really doesn't accomplish anything because the window we just
  471     // closed won't get any CPU time (unless, perhaps, it receives the close message in
  472     // time to ask the OS for us to yield the timeslice).  Perhaps some finer tuning
  473     // of this can be done in the future.  UPDATE: Testing of WinActivate, which also
  474     // uses this to do a Sleep(0), reveals that it may in fact help even when the CPU
  475     // isn't under load.  Perhaps this is because upon Sleep(0), the OS runs the
  476     // WindowProc's of windows that have messages waiting for them so that appropriate
  477     // action can be taken (which may often be nearly instantaneous, perhaps under
  478     // 1ms for a Window to be logically destroyed even if it hasn't physically been
  479     // removed from the screen?) prior to returning the CPU to our thread:
  480     DWORD start_time = GetTickCount(); // Before doing any MsgSleeps, set this.
  481     //MsgSleep(0); // Always do one small one, see above comments.
  482     // UPDATE: It seems better just to always do one unspecified-interval sleep
  483     // rather than MsgSleep(0), which often returns immediately, probably having
  484     // no effect.
  485 
  486     // Remember that once the first call to MsgSleep() is done, a new hotkey subroutine
  487     // may fire and suspend what we're doing here.  Such a subroutine might also overwrite
  488     // the values our params, some of which may be in the deref buffer.  So be sure not
  489     // to refer to those strings once MsgSleep() has been done, below:
  490 
  491     // This is the same basic code used for ACT_WINWAITCLOSE and such:
  492     for (;;)
  493     {
  494         // Seems best to always do the first one regardless of the value 
  495         // of aTimeToWaitForClose:
  496         MsgSleep(INTERVAL_UNSPECIFIED);
  497         if (!IsWindow(aWnd)) // It's gone, so we're done.
  498             return aWnd;
  499         // Must cast to int or any negative result will be lost due to DWORD type:
  500         if ((int)(aTimeToWaitForClose - (GetTickCount() - start_time)) <= SLEEP_INTERVAL_HALF)
  501             break;
  502             // Last param 0 because we don't want it to restore the
  503             // current active window after the time expires (in case
  504             // it's suspended).  INTERVAL_UNSPECIFIED performs better.
  505     }
  506     return aWnd;  // Done waiting.
  507 }
  508 
  509 
  510     
  511 HWND WinActive(global_struct &aSettings, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText
  512     , bool aUpdateLastUsed)
  513 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
  514 // In addition, it must not change the value of anything in aSettings except when aUpdateLastUsed==true.
  515 {
  516     HWND target_window;
  517     if (USE_FOREGROUND_WINDOW(aTitle, aText, aExcludeTitle, aExcludeText))
  518     {
  519         // User asked us if the "active" window is active, which is true if it's not a
  520         // hidden window, or if DetectHiddenWindows is ON:
  521         SET_TARGET_TO_ALLOWABLE_FOREGROUND(aSettings.DetectHiddenWindows)
  522         #define UPDATE_AND_RETURN_LAST_USED_WINDOW(hwnd) \
  523         {\
  524             if (aUpdateLastUsed && hwnd)\
  525                 aSettings.hWndLastUsed = hwnd;\
  526             return hwnd;\
  527         }
  528         UPDATE_AND_RETURN_LAST_USED_WINDOW(target_window)
  529     }
  530 
  531     HWND fore_win = GetForegroundWindow();
  532     if (!fore_win)
  533         return NULL;
  534 
  535     if (!(*aTitle || *aText || *aExcludeTitle || *aExcludeText)) // Use the "last found" window.
  536         return (fore_win == GetValidLastUsedWindow(aSettings)) ? fore_win : NULL;
  537 
  538     // Only after the above check should the below be done.  This is because "IfWinActive" (with no params)
  539     // should be "true" if one of the script's GUI windows is active:
  540     if (!aSettings.DetectWindow(fore_win)) // In this case, the caller's window can't be active.
  541         return NULL;
  542 
  543     WindowSearch ws;
  544     ws.SetCandidate(fore_win);
  545 
  546     if (ws.SetCriteria(aSettings, aTitle, aText, aExcludeTitle, aExcludeText) && ws.IsMatch()) // aSettings.DetectHiddenWindows was already checked above.
  547         UPDATE_AND_RETURN_LAST_USED_WINDOW(fore_win) // This also does a "return".
  548     else // If the line above didn't return, indicate that the specified window is not active.
  549         return NULL;
  550 }
  551 
  552 
  553 
  554 HWND WinExist(global_struct &aSettings, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText
  555     , bool aFindLastMatch, bool aUpdateLastUsed, HWND aAlreadyVisited[], int aAlreadyVisitedCount)
  556 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
  557 // In addition, it must not change the value of anything in aSettings except when aUpdateLastUsed==true.
  558 {
  559     HWND target_window;
  560     if (USE_FOREGROUND_WINDOW(aTitle, aText, aExcludeTitle, aExcludeText))
  561     {
  562         // User asked us if the "active" window exists, which is true if it's not a
  563         // hidden window or DetectHiddenWindows is ON:
  564         SET_TARGET_TO_ALLOWABLE_FOREGROUND(aSettings.DetectHiddenWindows)
  565         // Updating LastUsed to be hwnd even if it's NULL seems best for consistency?
  566         // UPDATE: No, it's more flexible not to never set it to NULL, because there
  567         // will be times when the old value is still useful:
  568         UPDATE_AND_RETURN_LAST_USED_WINDOW(target_window);
  569     }
  570 
  571     if (!(*aTitle || *aText || *aExcludeTitle || *aExcludeText))
  572         // User passed no params, so use the window most recently found by WinExist().
  573         // It's correct to do this even in this function because it's called by
  574         // WINWAITCLOSE and IFWINEXIST specifically to discover if the Last-Used
  575         // window still exists.
  576         return GetValidLastUsedWindow(aSettings);
  577 
  578     WindowSearch ws;
  579     if (!ws.SetCriteria(aSettings, aTitle, aText, aExcludeTitle, aExcludeText)) // No match is possible with these criteria.
  580         return NULL;
  581 
  582     ws.mFindLastMatch = aFindLastMatch;
  583     ws.mAlreadyVisited = aAlreadyVisited;
  584     ws.mAlreadyVisitedCount = aAlreadyVisitedCount;
  585 
  586     if (ws.mCriteria & CRITERION_ID) // "ahk_id" will be satisfied if that HWND still exists and is valid.
  587     {
  588         // Explicitly allow HWND_BROADCAST for all commands that use WinExist (which is just about all
  589         // window commands), even though it's only valid with ScriptPostSendMessage().
  590         // This is because HWND_BROADCAST is probably never used as the HWND for a real window, so there
  591         // should be no danger of any reasonable script ever passing that value in as a real target window,
  592         // which should thus minimize the chance of a crash due to calling various API functions
  593         // with invalid window handles.
  594         if (   ws.mCriterionHwnd != HWND_BROADCAST // It's not exempt from the other checks on the two lines below.
  595             && (!IsWindow(ws.mCriterionHwnd)    // And it's either not a valid window...
  596                 // ...or the window is not detectible (in v1.0.40.05, child windows are detectible even if hidden):
  597                 || !(aSettings.DetectWindow(ws.mCriterionHwnd)
  598                     || (GetWindowLong(ws.mCriterionHwnd, GWL_STYLE) & WS_CHILD)))   )
  599             return NULL;
  600 
  601         // Otherwise, the window is valid and detectible.
  602         ws.SetCandidate(ws.mCriterionHwnd);
  603         if (!ws.IsMatch()) // Checks if it matches any other criteria: WinTitle, WinText, ExcludeTitle, and anything in the aAlreadyVisited list.
  604             return NULL;
  605         //else fall through to the section below, since ws.mFoundCount and ws.mFoundParent were set by ws.IsMatch().
  606     }
  607     else // aWinTitle doesn't start with "ahk_id".  Try to find a matching window.
  608         EnumWindows(EnumParentFind, (LPARAM)&ws);
  609 
  610     UPDATE_AND_RETURN_LAST_USED_WINDOW(ws.mFoundParent) // This also does a "return".
  611 }
  612 
  613 
  614 
  615 HWND GetValidLastUsedWindow(global_struct &aSettings)
  616 // If the last found window is one of the script's own GUI windows, it is considered valid even if
  617 // DetectHiddenWindows is ON.  Note that this exemption does not apply to things like "IfWinExist,
  618 // My Gui Title", "WinActivate, ahk_id <gui id>", etc.
  619 // A GUI window can become the last found window while DetectHiddenWindows is ON in two ways:
  620 // Gui +LastFound
  621 // The launch of a GUI thread that explicitly set the last found window to be that GUI window.
  622 {
  623     if (!aSettings.hWndLastUsed || !IsWindow(aSettings.hWndLastUsed))
  624         return NULL;
  625     if (   aSettings.DetectWindow(aSettings.hWndLastUsed)
  626         || (GetWindowLong(aSettings.hWndLastUsed, GWL_STYLE) & WS_CHILD)   ) // v1.0.40.05: Child windows (via ahk_id) are always detectible.
  627         return aSettings.hWndLastUsed;
  628     // Otherwise, DetectHiddenWindows is OFF and the window is not visible.  Return NULL
  629     // unless this is a GUI window belonging to this particular script, in which case
  630     // the setting of DetectHiddenWindows is ignored (as of v1.0.25.13).
  631     return GuiType::FindGui(aSettings.hWndLastUsed) ? aSettings.hWndLastUsed : NULL;
  632 }
  633 
  634 
  635 
  636 BOOL CALLBACK EnumParentFind(HWND aWnd, LPARAM lParam)
  637 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
  638 // To continue enumeration, the function must return TRUE; to stop enumeration, it must return FALSE. 
  639 // It's a little strange, but I think EnumWindows() returns FALSE when the callback stopped
  640 // the enumeration prematurely by returning false to its caller.  Otherwise (the enumeration went
  641 // through every window), it returns TRUE:
  642 {
  643     WindowSearch &ws = *(WindowSearch *)lParam;  // For performance and convenience.
  644     if (!ws.mSettings->DetectWindow(aWnd)) // Skip windows the script isn't supposed to detect.
  645         return TRUE;
  646     ws.SetCandidate(aWnd);
  647     // If this window doesn't match, continue searching for more windows (via TRUE).  Likewise, if
  648     // mFindLastMatch is true, continue searching even if this window is a match.  Otherwise, this
  649     // first match is the one that's desired so stop here:
  650     return ws.IsMatch() ? ws.mFindLastMatch : TRUE;
  651 }
  652 
  653 
  654 
  655 BOOL CALLBACK EnumChildFind(HWND aWnd, LPARAM lParam)
  656 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
  657 // Although this function could be rolled into a generalized version of the EnumWindowsProc(),
  658 // it will perform better this way because there's less checking required and no mode/flag indicator
  659 // is needed inside lParam to indicate which struct element should be searched for.  In addition,
  660 // it's more comprehensible this way.  lParam is a pointer to the struct rather than just a
  661 // string because we want to give back the HWND of any matching window.
  662 {
  663     // Since WinText and ExcludeText are seldom used in typical scripts, the following buffer
  664     // is put on the stack here rather than on our callers (inside the WindowSearch object),
  665     // which should help conserve stack space on average.  Can't use the ws.mCandidateTitle
  666     // buffer because ws.mFindLastMatch might be true, in which case the original title must
  667     // be preserved.
  668     TCHAR win_text[WINDOW_TEXT_SIZE];
  669     WindowSearch &ws = *(WindowSearch *)lParam;  // For performance and convenience.
  670 
  671     if (!(ws.mSettings->DetectHiddenText || IsWindowVisible(aWnd))) // This text element should not be detectible by the script.
  672         return TRUE;  // Skip this child and keep enumerating to try to find a match among the other children.
  673 
  674     // The below was formerly outsourced to the following function, but since it is only called from here,
  675     // it has been moved inline:
  676     // int GetWindowTextByTitleMatchMode(HWND aWnd, LPTSTR aBuf = NULL, int aBufSize = 0)
  677     int text_length = ws.mSettings->TitleFindFast ? GetWindowText(aWnd, win_text, _countof(win_text))
  678         : GetWindowTextTimeout(aWnd, win_text, _countof(win_text));  // The slower method that is able to get text from more types of controls (e.g. large edit controls).
  679     // Older idea that for the above that was not adopted:
  680     // Only if GetWindowText() gets 0 length would we try the other method (and of course, don't bother
  681     // using GetWindowTextTimeout() at all if "fast" mode is in effect).  The problem with this is that
  682     // many controls always return 0 length regardless of which method is used, so this would slow things
  683     // down a little (but not too badly since GetWindowText() is so much faster than GetWindowTextTimeout()).
  684     // Another potential problem is that some controls may return less text, or different text, when used
  685     // with the fast mode vs. the slow mode (unverified).  So it seems best NOT to do this and stick with
  686     // the simple approach above.
  687     if (!text_length) // It has no text (or failure to fetch it).
  688         *win_text = '\0';
  689 
  690     // For compatibility with AutoIt v2, strstr() is always used for control/child text elements.
  691 
  692     // EXCLUDE-TEXT: The following check takes precedence over the next, so it's done first:
  693     if (*ws.mCriterionExcludeText) // For performance, avoid doing the checks below when blank.
  694     {
  695         if (ws.mSettings->TitleMatchMode == FIND_REGEX)
  696         {
  697             if (RegExMatch(win_text, ws.mCriterionExcludeText))
  698             {
  699                 ws.mFoundChild = NULL; // In case a previous child window matched.
  700                 return FALSE; // Parent can't be a match, so stop searching its children.
  701             }
  702         }
  703         else // For backward compatibility, all modes other than RegEx behave as follows.
  704             if (_tcsstr(win_text, ws.mCriterionExcludeText))
  705             {
  706                 // Since this child window contains the specified ExcludeText anywhere inside its text,
  707                 // the parent window is always a non-match.
  708                 ws.mFoundChild = NULL; // In case a previous child window matched.
  709                 return FALSE; // Parent can't be a match, so stop searching its children.
  710             }
  711     }
  712 
  713     if (!ws.mFoundChild) // No matching child window found yet.
  714     {
  715         // WIN-TEXT:
  716         if (!*ws.mCriterionText) // Match always found in this case. This check is for performance: it avoids doing the checks below when not needed, especially RegEx. Note: It's possible for mCriterionText to be blank, at least when mCriterionExcludeText isn't blank.
  717         {
  718             ws.mFoundChild = aWnd;
  719             // Don't do the following since it makes ExcludeText ineffective for any control after the
  720             // first matching control (which might simply be the first control if WinText is blank):
  721             //return FALSE; // Match found, so stop searching.
  722         }
  723         else if (ws.mSettings->TitleMatchMode == FIND_REGEX)
  724         {
  725             if (RegExMatch(win_text, ws.mCriterionText)) // Match found.
  726                 ws.mFoundChild = aWnd;
  727         }
  728         else // For backward compatibility, all modes other than RegEx behave as follows.
  729             if (_tcsstr(win_text, ws.mCriterionText)) // Match found.
  730                 ws.mFoundChild = aWnd;
  731     }
  732 
  733     // UPDATE to the below: The MSDN docs state that EnumChildWindows() already handles the
  734     // recursion for us: "If a child window has created child windows of its own,
  735     // EnumChildWindows() enumerates those windows as well."
  736     // Mostly obsolete comments: Since this child doesn't match, make sure none of its
  737     // children (recursive) match prior to continuing the original enumeration.  We don't
  738     // discard the return value from EnumChildWindows() because it's FALSE in two cases:
  739     // 1) The given HWND has no children.
  740     // 2) The given EnumChildProc() stopped prematurely rather than enumerating all the windows.
  741     // and there's no way to distinguish between the two cases without using the
  742     // struct's hwnd because GetLastError() seems to return ERROR_SUCCESS in both
  743     // cases.
  744     //EnumChildWindows(aWnd, EnumChildFind, lParam);
  745     // If matching HWND still hasn't been found, return TRUE to keep searching:
  746     //return ws.mFoundChild == NULL;
  747 
  748     return TRUE; // Keep searching.
  749 }
  750 
  751 
  752 
  753 ResultType StatusBarUtil(Var *aOutputVar, HWND aBarHwnd, int aPartNumber, LPTSTR aTextToWaitFor
  754     , int aWaitTime, int aCheckInterval)
  755 // aOutputVar is allowed to be NULL if aTextToWaitFor isn't NULL or blank. aBarHwnd is allowed
  756 // to be NULL because in that case, the caller wants us to set ErrorLevel appropriately and also
  757 // make aOutputVar empty.
  758 {
  759     if (aOutputVar)
  760         aOutputVar->Assign(); // Init to blank in case of early return.
  761 
  762     // Legacy: Waiting 500ms in place of a "0" seems more useful than a true zero, which doens't need
  763     // to be supported because it's the same thing as something like "IfWinExist":
  764     if (!aWaitTime)
  765         aWaitTime = 500;
  766     if (aCheckInterval < 1)
  767         aCheckInterval = SB_DEFAULT_CHECK_INTERVAL; // Caller relies on us doing this.
  768     if (aPartNumber < 1)
  769         aPartNumber = 1;  // Caller relies on us to set default in this case.
  770 
  771     // Must have at least one of these.  UPDATE: We want to allow this so that the command can be
  772     // used to wait for the status bar text to become blank:
  773     //if (!aOutputVar && !*aTextToWaitFor) return OK;
  774 
  775     // Whenever using SendMessageTimeout(), our app will be unresponsive until
  776     // the call returns, since our message loop isn't running.  In addition,
  777     // if the keyboard or mouse hook is installed, the input events will lag during
  778     // this call.  So keep the timeout value fairly short.  Update for v1.0.24:
  779     // There have been at least two reports of the StatusBarWait command ending
  780     // prematurely with an ErrorLevel of 2.  The most likely culprit is the below,
  781     // which has now been increased from 100 to 2000:
  782     #define SB_TIMEOUT 2000
  783 
  784     HANDLE handle;
  785     LPVOID remote_buf;
  786     LRESULT part_count; // The number of parts this status bar has.
  787     if (!aBarHwnd  // These conditions rely heavily on short-circuit boolean order.
  788         || !SendMessageTimeout(aBarHwnd, SB_GETPARTS, 0, 0, SMTO_ABORTIFHUNG, SB_TIMEOUT, (PDWORD_PTR)&part_count) // It failed or timed out.
  789         || aPartNumber > part_count
  790         || !(remote_buf = AllocInterProcMem(handle, _TSIZE(WINDOW_TEXT_SIZE + 1), aBarHwnd))) // Alloc mem last.
  791         goto error;
  792 
  793     TCHAR local_buf[WINDOW_TEXT_SIZE + 1]; // The local counterpart to the buf allocated remotely above.
  794 
  795     DWORD_PTR result, start_time;
  796     --aPartNumber; // Convert to zero-based for use below.
  797 
  798     // Always do the first iteration so that at least one check is done.  Also,  start_time is initialized
  799     // unconditionally in the name of code size reduction (it's a low overhead call):
  800     for (*local_buf = '\0', start_time = GetTickCount();;)
  801     {
  802         // MSDN recommends always checking the length of the bar text.  It implies that the length is
  803         // unrestricted, so a crash due to buffer overflow could otherwise occur:
  804         if (SendMessageTimeout(aBarHwnd, SB_GETTEXTLENGTH, aPartNumber, 0, SMTO_ABORTIFHUNG, SB_TIMEOUT, &result))
  805         {
  806             // Testing confirms that LOWORD(result) [the length] does not include the zero terminator.
  807             if (LOWORD(result) > WINDOW_TEXT_SIZE) // Text would be too large (very unlikely but good to check for security).
  808                 break; // Abort the operation and leave ErrorLevel set to its default to indicate the problem.
  809             // Retrieve the bar's text:
  810             if (SendMessageTimeout(aBarHwnd, SB_GETTEXT, aPartNumber, (LPARAM)remote_buf, SMTO_ABORTIFHUNG, SB_TIMEOUT, &result))
  811             {
  812                 if (!ReadProcessMemory(handle, remote_buf, local_buf, _TSIZE(LOWORD(result) + 1), NULL)) // +1 to include the terminator (verified: length doesn't include zero terminator).
  813                 {
  814                     // Fairly critical error (though rare) so seems best to abort.
  815                     *local_buf = '\0';  // In case it changed the buf before failing.
  816                     break;
  817                 }
  818 
  819                 // Check if the retrieved text matches the caller's criteria. In addition to
  820                 // normal/intuitive matching, a match is also achieved if both are empty strings.
  821                 // In fact, IsTextMatch() yields "true" whenever aTextToWaitFor is the empty string:
  822                 if (IsTextMatch(local_buf, aTextToWaitFor))
  823                 {
  824                     g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate "match found".
  825                     break;
  826                 }
  827             }
  828             //else SB_GETTEXT msg timed out or failed.  Leave local_buf unaltered.  See comment below.
  829         }
  830         //else SB_GETTEXTLENGTH msg timed out or failed.  For v1.0.37, continue to wait (if other checks
  831         // say its okay below) rather than aborting the operation.  This should help prevent an abort
  832         // when the target window (or the entire system) is unresponsive for a long time, perhaps due
  833         // to a drive spinning up, etc.
  834 
  835         // Only when above didn't break are the following secondary conditions checked.  When aOutputVar
  836         // is non-NULL, the caller wanted a single check only (no waiting) [however, in most such cases,
  837         // the checking above would already have done a "break" because of aTextToWaitFor being blank when
  838         // passed to IsTextMatch()].  Also, don't continue to wait if the status bar no longer exists
  839         // (which is usually caused by the parent window having been destroyed):
  840         if (aOutputVar || !IsWindow(aBarHwnd))
  841             break; // Leave ErrorLevel at its default to indicate bar text retrieval problem in both cases.
  842 
  843         // Since above didn't break, we're in "wait" mode (more than one iteration).
  844         // In the following, must cast to int or any negative result will be lost due to DWORD type.
  845         // Note: A negative aWaitTime means we're waiting indefinitely for a match to appear.
  846         if (aWaitTime < 0 || (int)(aWaitTime - (GetTickCount() - start_time)) > SLEEP_INTERVAL_HALF)
  847             MsgSleep(aCheckInterval);
  848         else // Timed out.
  849         {
  850             g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // Indicate "timeout".
  851             break;
  852         }
  853     } // for()
  854 
  855     // Consider this to be always successful, even if aBarHwnd == NULL
  856     // or the status bar didn't have the part number provided, unless the below fails.
  857     // Note we use a temp buf rather than writing directly to the var contents above, because
  858     // we don't know how long the text will be until after the above operation finishes.
  859     ResultType result_to_return = aOutputVar ? aOutputVar->Assign(local_buf) : OK;
  860     FreeInterProcMem(handle, remote_buf);
  861     return result_to_return;
  862 
  863 error:
  864     return g_script.SetErrorLevelOrThrowStr(aOutputVar ? ERRORLEVEL_ERROR : ERRORLEVEL_ERROR2);
  865 }
  866 
  867 
  868 
  869 HWND ControlExist(HWND aParentWindow, LPTSTR aClassNameAndNum)
  870 // This can return target_window itself for cases such as ahk_id %ControlHWND%.
  871 {
  872     if (!aParentWindow)
  873         return NULL;
  874     if (!aClassNameAndNum || !*aClassNameAndNum)
  875         return (GetWindowLong(aParentWindow, GWL_STYLE) & WS_CHILD) ? aParentWindow : GetTopChild(aParentWindow);
  876         // Above: In v1.0.43.06, the parent window itself is returned if it's a child rather than its top child
  877         // because it seems more useful and intuitive.  This change allows ahk_id %ControlHwnd% to always operate
  878         // directly on the specified control's HWND rather than some sub-child.
  879 
  880     WindowSearch ws;
  881     bool is_class_name = _istdigit(aClassNameAndNum[_tcslen(aClassNameAndNum) - 1]);
  882 
  883     if (is_class_name)
  884     {
  885         // Tell EnumControlFind() to search by Class+Num.  Don't call ws.SetCriteria() because
  886         // that has special handling for ahk_id, ahk_class, etc. in the first parameter.
  887         tcslcpy(ws.mCriterionClass, aClassNameAndNum, _countof(ws.mCriterionClass));
  888         ws.mCriterionText = _T("");
  889     }
  890     else // Tell EnumControlFind() to search the control's text.
  891     {
  892         *ws.mCriterionClass = '\0';
  893         ws.mCriterionText = aClassNameAndNum;
  894     }
  895 
  896     EnumChildWindows(aParentWindow, EnumControlFind, (LPARAM)&ws); // mFoundChild was initialized by the constructor.
  897 
  898     if (is_class_name && !ws.mFoundChild)
  899     {
  900         // To reduce problems with ambiguity (a class name and number of one control happens
  901         // to match the title/text of another control), search again only after the search
  902         // for the ClassNameAndNum didn't turn up anything.
  903         // Tell EnumControlFind() to search the control's text.
  904         *ws.mCriterionClass = '\0';
  905         ws.mCriterionText = aClassNameAndNum;
  906         EnumChildWindows(aParentWindow, EnumControlFind, (LPARAM)&ws); // ws.mFoundChild is already initialized to NULL due to the above check.
  907     }
  908 
  909     return ws.mFoundChild;
  910 }
  911 
  912 
  913 
  914 BOOL CALLBACK EnumControlFind(HWND aWnd, LPARAM lParam)
  915 {
  916     WindowSearch &ws = *(WindowSearch *)lParam;  // For performance and convenience.
  917     if (*ws.mCriterionClass) // Caller told us to search by class name and number.
  918     {
  919         int length = GetClassName(aWnd, ws.mCandidateTitle, WINDOW_CLASS_SIZE); // Restrict the length to a small fraction of the buffer's size (also serves to leave room to append the sequence number).
  920         // Below: i.e. this control's title (e.g. List) in contained entirely
  921         // within the leading part of the user specified title (e.g. ListBox).
  922         // Even though this is incorrect, the appending of the sequence number
  923         // in the second comparison will weed out any false matches.
  924         // Note: since some controls end in a number (e.g. SysListView32),
  925         // it would not be easy to parse out the user's sequence number to
  926         // simplify/accelerate the search here.  So instead, use a method
  927         // more certain to work even though it's a little ugly.  It's also
  928         // necessary to do this in a way functionally identical to the below
  929         // so that Window Spy's sequence numbers match the ones generated here:
  930         // Concerning strnicmp(), see lstrcmpi note below for why a locale-insensitive match isn't done instead.
  931         if (length && !_tcsnicmp(ws.mCriterionClass, ws.mCandidateTitle, length)) // Preliminary match of base class name.
  932         {
  933             // mAlreadyVisitedCount was initialized to zero by WindowSearch's constructor.  It is used
  934             // to accumulate how many quasi-matches on this class have been found so far.  Also,
  935             // comparing ws.mAlreadyVisitedCount to atoi(ws.mCriterionClass + length) would not be
  936             // the same as the below examples such as the following:
  937             // Say the ClassNN being searched for is List01 (where List0 is the class name and 1
  938             // is the sequence number). If a class called "List" exists in the parent window, it
  939             // would be found above as a preliminary match.  The below would copy "1" into the buffer,
  940             // which is correctly deemed not to match "01".  By contrast, the atoi() method would give
  941             // the wrong result because the two numbers are numerically equal.
  942             _itot(++ws.mAlreadyVisitedCount, ws.mCandidateTitle, 10);  // Overwrite the buffer to contain only the count.
  943             // lstrcmpi() is not used: 1) avoids breaking existing scripts; 2) provides consistent behavior
  944             // across multiple locales:
  945             if (!_tcsicmp(ws.mCandidateTitle, ws.mCriterionClass + length)) // The counts match too, so it's a full match.
  946             {
  947                 ws.mFoundChild = aWnd; // Save this in here for return to the caller.
  948                 return FALSE; // stop the enumeration.
  949             }
  950         }
  951     }
  952     else // Caller told us to search by the text of the control (e.g. the text printed on a button)
  953     {
  954         // Use GetWindowText() rather than GetWindowTextTimeout() because we don't want to find
  955         // the name accidentally in the vast amount of text present in some edit controls (e.g.
  956         // if the script's source code is open for editing in notepad, GetWindowText() would
  957         // likely find an unwanted match for just about anything).  In addition,
  958         // GetWindowText() is much faster.  Update: Yes, it seems better not to use
  959         // GetWindowTextByTitleMatchMode() in this case, since control names tend to be so
  960         // short (i.e. they would otherwise be very likely to be undesirably found in any large
  961         // edit controls the target window happens to own).  Update: Changed from strstr()
  962         // to strncmp() for greater selectivity.  Even with this degree of selectivity, it's
  963         // still possible to have ambiguous situations where a control can't be found due
  964         // to its title being entirely contained within that of another (e.g. a button
  965         // with title "Connect" would be found in the title of a button "Connect All").
  966         // The only way to address that would be to insist on an entire title match, but
  967         // that might be tedious if the title of the control is very long.  As alleviation,
  968         // the class name + seq. number method above can often be used instead in cases
  969         // of such ambiguity.  Update: Using IsTextMatch() now so that user-specified
  970         // TitleMatchMode will be in effect for this also.  Also, it's case sensitivity
  971         // helps increase selectivity, which is helpful due to how common short or ambiguous
  972         // control names tend to be:
  973         GetWindowText(aWnd, ws.mCandidateTitle, _countof(ws.mCandidateTitle));
  974         if (IsTextMatch(ws.mCandidateTitle, ws.mCriterionText))
  975         {
  976             ws.mFoundChild = aWnd; // save this in here for return to the caller.
  977             return FALSE;
  978         }
  979     }
  980     // Note: The MSDN docs state that EnumChildWindows already handles the
  981     // recursion for us: "If a child window has created child windows of its own,
  982     // EnumChildWindows() enumerates those windows as well."
  983     return TRUE; // Keep searching.
  984 }
  985 
  986 
  987 
  988 int MsgBox(int aValue)
  989 {
  990     TCHAR str[128];
  991     sntprintf(str, _countof(str), _T("Value = %d (0x%X)"), aValue, aValue);
  992     return MsgBox(str);
  993 }
  994 
  995 
  996 
  997 int MsgBox(LPCTSTR aText, UINT uType, LPTSTR aTitle, double aTimeout, HWND aOwner)
  998 // Returns 0 if the attempt failed because of too many existing MessageBox windows,
  999 // or if MessageBox() itself failed.
 1000 // MB_SETFOREGROUND or some similar setting appears to dismiss some types of screen savers (if active).
 1001 // However, it doesn't undo monitor low-power mode.
 1002 {
 1003     // Set the below globals so that any WM_TIMER messages dispatched by this call to
 1004     // MsgBox() (which may result in a recursive call back to us) know not to display
 1005     // any more MsgBoxes:
 1006     if (g_nMessageBoxes > MAX_MSGBOXES + 1)  // +1 for the final warning dialog.  Verified correct.
 1007         return 0;
 1008 
 1009     if (g_nMessageBoxes == MAX_MSGBOXES)
 1010     {
 1011         // Do a recursive call to self so that it will be forced to the foreground.
 1012         // But must increment this so that the recursive call allows the last MsgBox
 1013         // to be displayed:
 1014         ++g_nMessageBoxes;
 1015         MsgBox(_T("The maximum number of MsgBoxes has been reached."));
 1016         --g_nMessageBoxes;
 1017         return 0;
 1018     }
 1019 
 1020     // Set these in case the caller explicitly called it with a NULL, overriding the default:
 1021     if (!aText)
 1022         aText = _T("");
 1023     if (!aTitle || !*aTitle)
 1024         // If available, the script's filename seems a much better title in case the user has
 1025         // more than one script running:
 1026         aTitle = (g_script.mFileName && *g_script.mFileName) ? g_script.mFileName : T_AHK_NAME_VERSION;
 1027 
 1028     // It doesn't feel safe to modify the contents of the caller's aText and aTitle,
 1029     // even if the caller were to tell us it is modifiable.  This is because the text
 1030     // might be the actual contents of a variable, which we wouldn't want to truncate,
 1031     // even temporarily, since other hotkeys can fire while this hotkey subroutine is
 1032     // suspended, and those subroutines may refer to the contents of this (now-altered)
 1033     // variable.  In addition, the text may reside in the clipboard's locked memory
 1034     // area, and altering that might result in the clipboard's contents changing
 1035     // when MsgSleep() closes the clipboard for us (after we display our dialog here).
 1036     // Even though testing reveals that the contents aren't altered (somehow), it
 1037     // seems best to have our own local, limited length versions here:
 1038     // Note: 8000 chars is about the max you could ever fit on-screen at 1024x768 on some
 1039     // XP systems, but it will hold much more before refusing to display at all (i.e.
 1040     // MessageBox() returning failure), perhaps about 150K:
 1041     TCHAR text[MSGBOX_TEXT_SIZE];
 1042     TCHAR title[DIALOG_TITLE_SIZE];
 1043     tcslcpy(text, aText, _countof(text));
 1044     tcslcpy(title, aTitle, _countof(title));
 1045 
 1046     uType |= MB_SETFOREGROUND;  // Always do these so that caller doesn't have to specify.
 1047 
 1048     // In the below, make the MsgBox owned by the topmost window rather than our main
 1049     // window, in case there's another modal dialog already displayed.  The forces the
 1050     // user to deal with the modal dialogs starting with the most recent one, which
 1051     // is what we want.  Otherwise, if a middle dialog was dismissed, it probably
 1052     // won't be able to return which button was pressed to its original caller.
 1053     // UPDATE: It looks like these modal dialogs can't own other modal dialogs,
 1054     // so disabling this:
 1055     /*
 1056     HWND topmost = GetTopWindow(g_hWnd);
 1057     if (!topmost) // It has no child windows.
 1058         topmost = g_hWnd;
 1059     */
 1060 
 1061     // Unhide the main window, but have it minimized.  This creates a task
 1062     // bar button so that it's easier the user to remember that a dialog
 1063     // is open and waiting (there are probably better ways to handle
 1064     // this whole thing).  UPDATE: This isn't done because it seems
 1065     // best not to have the main window be inaccessible until the
 1066     // dialogs are dismissed (in case ever want to use it to display
 1067     // status info, etc).  It seems that MessageBoxes get their own
 1068     // task bar button when they're not AppModal, which is one of the
 1069     // main things I wanted, so that's good too):
 1070 //  if (!IsWindowVisible(g_hWnd) || !IsIconic(g_hWnd))
 1071 //      ShowWindowAsync(g_hWnd, SW_SHOWMINIMIZED);
 1072 
 1073     /*
 1074     If the script contains a line such as "#y::MsgBox, test", and a hotkey is used
 1075     to activate Windows Explorer and another hotkey is then used to invoke a MsgBox,
 1076     that MsgBox will be psuedo-minimized or invisible, even though it does have the
 1077     input focus.  This attempt to fix it didn't work, so something is probably checking
 1078     the physical key state of LWIN/RWIN and seeing that they're down:
 1079     modLR_type modLR_now = GetModifierLRState();
 1080     modLR_type win_keys_down = modLR_now & (MOD_LWIN | MOD_RWIN);
 1081     if (win_keys_down)
 1082         SetModifierLRStateSpecific(win_keys_down, modLR_now, KEYUP);
 1083     */
 1084 
 1085     // Note: Even though when multiple messageboxes exist, they might be
 1086     // destroyed via a direct call to their WindowProc from our message pump's
 1087     // DispatchMessage, or that of another MessageBox's message pump, it
 1088     // appears that MessageBox() is designed to be called recursively like
 1089     // this, since it always returns the proper result for the button on the
 1090     // actual MessageBox it originally invoked.  In other words, if a bunch
 1091     // of Messageboxes are displayed, and this user dismisses an older
 1092     // one prior to dealing with a newer one, all the MessageBox()
 1093     // return values will still wind up being correct anyway, at least
 1094     // on XP.  The only downside to the way this is designed is that
 1095     // the keyboard can't be used to navigate the buttons on older
 1096     // messageboxes (only the most recent one).  This is probably because
 1097     // the message pump of MessageBox() isn't designed to properly dispatch
 1098     // keyboard messages to other MessageBox window instances.  I tried
 1099     // to fix that by making our main message pump handle all messages
 1100     // for all dialogs, but that turns out to be pretty complicated, so
 1101     // I abandoned it for now.
 1102 
 1103     // Note: It appears that MessageBox windows, and perhaps all modal dialogs in general,
 1104     // cannot own other windows.  That's too bad because it would have allowed each new
 1105     // MsgBox window to be owned by any previously existing one, so that the user would
 1106     // be forced to close them in order if they were APPL_MODAL.  But it's not too big
 1107     // an issue since the only disadvantage is that the keyboard can't be use to
 1108     // to navigate in MessageBoxes other than the most recent.  And it's actually better
 1109     // the way it is now in the sense that the user can dismiss the messageboxes out of
 1110     // order, which might (in rare cases) be desirable.
 1111 
 1112     if (aTimeout > 2147483) // This is approximately the max number of seconds that SetTimer can handle.
 1113         aTimeout = 2147483;
 1114     if (aTimeout < 0) // But it can be equal to zero to indicate no timeout at all.
 1115         aTimeout = 0.1;  // A value that might cue the user that something is wrong.
 1116     // For the above:
 1117     // MsgBox's smart comma handling will usually prevent negatives due to the fact that it considers
 1118     // a negative to be part of the text param.  But if it does happen, timeout after a short time,
 1119     // which may signal the user that the script passed a bad parameter.
 1120 
 1121     // v1.0.33: The following is a workaround for the fact that an MsgBox with only an OK button
 1122     // doesn't obey EndDialog()'s parameter:
 1123     g->DialogHWND = NULL;
 1124     g->MsgBoxTimedOut = false;
 1125 
 1126     // At this point, we know a dialog will be displayed.  See macro's comments for details:
 1127     DIALOG_PREP // Must be done prior to POST_AHK_DIALOG() below.
 1128     POST_AHK_DIALOG((DWORD)(aTimeout * 1000))
 1129 
 1130     ++g_nMessageBoxes;  // This value will also be used as the Timer ID if there's a timeout.
 1131     g->MsgBoxResult = MessageBox(aOwner, text, title, uType);
 1132     --g_nMessageBoxes;
 1133     // Above's use of aOwner: MsgBox, FileSelectFile, and other dialogs seem to consider aOwner to be NULL
 1134     // when aOwner is minimized or hidden.
 1135 
 1136     DIALOG_END
 1137 
 1138     // If there's a timer, kill it for performance reasons since it's no longer needed.
 1139     // Actually, this isn't easy to do because we don't know what the HWND of the MsgBox
 1140     // window was, so don't bother:
 1141     //if (aTimeout != 0.0)
 1142     //  KillTimer(...);
 1143 
 1144 //  if (!g_nMessageBoxes)
 1145 //      ShowWindowAsync(g_hWnd, SW_HIDE);  // Hide the main window if it no longer has any child windows.
 1146 //  else
 1147 
 1148     // This is done so that the next message box of ours will be brought to the foreground,
 1149     // to remind the user that they're still out there waiting, and for convenience.
 1150     // Update: It seems bad to do this in cases where the user intentionally wants the older
 1151     // messageboxes left in the background, to deal with them later.  So, in those cases,
 1152     // we might be doing more harm than good because the user's foreground window would
 1153     // be intrusively changed by this:
 1154     //WinActivateOurTopDialog();
 1155 
 1156     // The following comment is apparently not always true -- sometimes the AHK_TIMEOUT from
 1157     // EndDialog() is received correctly.  But I haven't discovered the circumstances of how
 1158     // and why the behavior varies:
 1159     // Unfortunately, it appears that MessageBox() will return zero rather
 1160     // than AHK_TIMEOUT that was specified in EndDialog() at least under WinXP.
 1161     if (g->MsgBoxTimedOut || (!g->MsgBoxResult && aTimeout > 0)) // v1.0.33: Added g->MsgBoxTimedOut, see comment higher above.
 1162         // Assume it timed out rather than failed, since failure should be VERY rare.
 1163         g->MsgBoxResult = AHK_TIMEOUT;
 1164     // else let the caller handle the display of the error message because only it knows
 1165     // whether to also tell the user something like "the script will not continue".
 1166     return g->MsgBoxResult;
 1167 }
 1168 
 1169 
 1170 
 1171 HWND FindOurTopDialog()
 1172 // Returns the HWND of our topmost MsgBox or FileOpen dialog (and perhaps other types of modal
 1173 // dialogs if they are of class #32770) even if it wasn't successfully brought to
 1174 // the foreground here.
 1175 // Using Enum() seems to be the only easy way to do this, since these modal MessageBoxes are
 1176 // *owned*, not children of the main window.  There doesn't appear to be any easier way to
 1177 // find out which windows another window owns.  GetTopWindow(), GetActiveWindow(), and GetWindow()
 1178 // do not work for this purpose.  And using FindWindow() discouraged because it can hang
 1179 // in certain circumstances (Enum is probably just as fast anyway).
 1180 {
 1181     // The return value of EnumWindows() is probably a raw indicator of success or failure,
 1182     // not whether the Enum found something or continued all the way through all windows.
 1183     // So don't bother using it.
 1184     pid_and_hwnd_type pid_and_hwnd;
 1185     pid_and_hwnd.pid = GetCurrentProcessId();
 1186     pid_and_hwnd.hwnd = NULL;  // Init.  Called function will set this for us if it finds a match.
 1187     EnumWindows(EnumDialog, (LPARAM)&pid_and_hwnd);
 1188     return pid_and_hwnd.hwnd;
 1189 }
 1190 
 1191 
 1192 
 1193 BOOL CALLBACK EnumDialog(HWND aWnd, LPARAM lParam)
 1194 // lParam should be a pointer to a ProcessId (ProcessIds are always non-zero?)
 1195 // To continue enumeration, the function must return TRUE; to stop enumeration, it must return FALSE. 
 1196 {
 1197     pid_and_hwnd_type &pah = *(pid_and_hwnd_type *)lParam;  // For performance and convenience.
 1198     if (!lParam || !pah.pid) return FALSE;
 1199     DWORD pid;
 1200     GetWindowThreadProcessId(aWnd, &pid);
 1201     if (pid == pah.pid)
 1202     {
 1203         TCHAR buf[32];
 1204         GetClassName(aWnd, buf, _countof(buf));
 1205         // This is the class name for windows created via MessageBox(), GetOpenFileName(), and probably
 1206         // other things that use modal dialogs:
 1207         if (!_tcscmp(buf, _T("#32770")))
 1208         {
 1209             pah.hwnd = aWnd;  // An output value for the caller.
 1210             return FALSE;  // We're done.
 1211         }
 1212     }
 1213     return TRUE;  // Keep searching.
 1214 }
 1215 
 1216 
 1217 
 1218 struct owning_struct {HWND owner_hwnd; HWND first_child;};
 1219 HWND WindowOwnsOthers(HWND aWnd)
 1220 // Only finds owned windows if they are visible, by design.
 1221 {
 1222     owning_struct own = {aWnd, NULL};
 1223     EnumWindows(EnumParentFindOwned, (LPARAM)&own);
 1224     return own.first_child;
 1225 }
 1226 
 1227 
 1228 
 1229 BOOL CALLBACK EnumParentFindOwned(HWND aWnd, LPARAM lParam)
 1230 {
 1231     HWND owner_hwnd = GetWindow(aWnd, GW_OWNER);
 1232     // Note: Many windows seem to own other invisible windows that have blank titles.
 1233     // In our case, require that it be visible because we don't want to return an invisible
 1234     // window to the caller because such windows aren't designed to be activated:
 1235     if (owner_hwnd && owner_hwnd == ((owning_struct *)lParam)->owner_hwnd && IsWindowVisible(aWnd))
 1236     {
 1237         ((owning_struct *)lParam)->first_child = aWnd;
 1238         return FALSE; // Match found, we're done.
 1239     }
 1240     return TRUE;  // Continue enumerating.
 1241 }
 1242 
 1243 
 1244 
 1245 HWND GetNonChildParent(HWND aWnd)
 1246 // Returns the first ancestor of aWnd that isn't itself a child.  aWnd itself is returned if
 1247 // it is not a child.  Returns NULL only if aWnd is NULL.  Also, it should always succeed
 1248 // based on the axiom that any window with the WS_CHILD style (aka WS_CHILDWINDOW) must have
 1249 // a non-child ancestor somewhere up the line.
 1250 // This function doesn't do anything special with owned vs. unowned windows.  Despite what MSDN
 1251 // says, GetParent() does not return the owner window, at least in some cases on Windows XP
 1252 // (e.g. BulletProof FTP Server). It returns NULL instead. In any case, it seems best not to
 1253 // worry about owner windows for this function's caller (MouseGetPos()), since it might be
 1254 // desirable for that command to return the owner window even though it can't actually be
 1255 // activated.  This is because attempts to activate an owner window should automatically cause
 1256 // the OS to activate the topmost owned window instead.  In addition, the owner window may
 1257 // contain the actual title or text that the user is interested in.  UPDATE: Due to the fact
 1258 // that this function retrieves the first parent that's not a child window, it's likely that
 1259 // that window isn't its owner anyway (since the owner problem usually applies to a parent
 1260 // window being owned by some controlling window behind it).
 1261 {
 1262     if (!aWnd) return aWnd;
 1263     HWND parent, parent_prev;
 1264     for (parent_prev = aWnd; ; parent_prev = parent)
 1265     {
 1266         if (!(GetWindowLong(parent_prev, GWL_STYLE) & WS_CHILD))  // Found the first non-child parent, so return it.
 1267             return parent_prev;
 1268         // Because Windows 95 doesn't support GetAncestor(), we'll use GetParent() instead:
 1269         if (   !(parent = GetParent(parent_prev))   )
 1270             return parent_prev;  // This will return aWnd if aWnd has no parents.
 1271     }
 1272 }
 1273 
 1274 
 1275 
 1276 HWND GetTopChild(HWND aParent)
 1277 {
 1278     if (!aParent) return aParent;
 1279     HWND hwnd_top, next_top;
 1280     // Get the topmost window of the topmost window of...
 1281     // i.e. Since child windows can also have children, we keep going until
 1282     // we reach the "last topmost" window:
 1283     for (hwnd_top = GetTopWindow(aParent)
 1284         ; hwnd_top && (next_top = GetTopWindow(hwnd_top))
 1285         ; hwnd_top = next_top);
 1286 
 1287     //if (!hwnd_top)
 1288     //{
 1289     //  MsgBox("no top");
 1290     //  return FAIL;
 1291     //}
 1292     //else
 1293     //{
 1294     //  //if (GetTopWindow(hwnd_top))
 1295     //  //  hwnd_top = GetTopWindow(hwnd_top);
 1296     //  char class_name[64];
 1297     //  GetClassName(next_top, class_name, sizeof(class_name));
 1298     //  MsgBox(class_name);
 1299     //}
 1300 
 1301     return hwnd_top ? hwnd_top : aParent;  // Caller relies on us never returning NULL if aParent is non-NULL.
 1302 }
 1303 
 1304 
 1305 
 1306 bool IsWindowHung(HWND aWnd)
 1307 {
 1308     if (!aWnd) return false;
 1309 
 1310     // OLD, SLOWER METHOD:
 1311     // Don't want to use a long delay because then our messages wouldn't get processed
 1312     // in a timely fashion.  But I'm not entirely sure if the 10ms delay used below
 1313     // is even used by the function in this case?  Also, the docs aren't clear on whether
 1314     // the function returns success or failure if the window is hung (probably failure).
 1315     // If failure, perhaps you have to call GetLastError() to determine whether it failed
 1316     // due to being hung or some other reason?  Does the output param dwResult have any
 1317     // useful info in this case?  I expect what will happen is that in most cases, the OS
 1318     // will already know that the window is hung.  However, if the window just became hung
 1319     // in the last 5 seconds, I think it may take the remainder of the 5 seconds for the OS
 1320     // to notice it.  However, allowing it the option of sleeping up to 5 seconds seems
 1321     // really bad, since keyboard and mouse input would probably be frozen (actually it
 1322     // would just be really laggy because the OS would bypass the hook during that time).
 1323     // So some compromise value seems in order.  500ms seems about right.  UPDATE: Some
 1324     // windows might need longer than 500ms because their threads are engaged in
 1325     // heavy operations.  Since this method is only used as a fallback method now,
 1326     // it seems best to give them the full 5000ms default, which is what (all?) Windows
 1327     // OSes use as a cutoff to determine whether a window is "not responding":
 1328     DWORD_PTR dwResult;
 1329     #define Slow_IsWindowHung !SendMessageTimeout(aWnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 5000, &dwResult)
 1330 
 1331     // NEW, FASTER METHOD:
 1332     // This newer method's worst-case performance is at least 30x faster than the worst-case
 1333     // performance of the old method that  uses SendMessageTimeout().
 1334     // And an even worse case can be envisioned which makes the use of this method
 1335     // even more compelling: If the OS considers a window NOT to be hung, but the
 1336     // window's message pump is sluggish about responding to the SendMessageTimeout() (perhaps
 1337     // taking 2000ms or more to respond due to heavy disk I/O or other activity), the old method
 1338     // will take several seconds to return, causing mouse and keyboard lag if our hook(s)
 1339     // are installed; not to mention making our app's windows, tray menu, and other GUI controls
 1340     // unresponsive during that time).  But I believe in this case the new method will return
 1341     // instantly, since the OS has been keeping track in the background, and can tell us
 1342     // immediately that the window isn't hung.
 1343     // Here are some seemingly contradictory statements uttered by MSDN.  Perhaps they're
 1344     // not contradictory if the first sentence really means "by a different thread of the same
 1345     // process":
 1346     // "If the specified window was created by a different thread, the system switches to that
 1347     // thread and calls the appropriate window procedure.  Messages sent between threads are
 1348     // processed only when the receiving thread executes message retrieval code. The sending
 1349     // thread is blocked until the receiving thread processes the message."
 1350 
 1351     // The use of IsHungAppWindow() (supported under Win2k+) is discouraged by MS,
 1352     // but it's useful to prevent the script from getting hung when it tries to do something
 1353     // to a hung window.
 1354     typedef BOOL (WINAPI *MyIsHungAppWindow)(HWND);
 1355     static MyIsHungAppWindow IsHungAppWindow = (MyIsHungAppWindow)GetProcAddress(GetModuleHandle(_T("user32"))
 1356         , "IsHungAppWindow");
 1357     return IsHungAppWindow ? IsHungAppWindow(aWnd) : Slow_IsWindowHung;
 1358 }
 1359 
 1360 
 1361 
 1362 bool IsWindowCloaked(HWND aWnd)
 1363 {
 1364     static auto *pfn = (decltype(&DwmGetWindowAttribute))
 1365         GetProcAddress(LoadLibrary(_T("dwmapi.dll")), "DwmGetWindowAttribute");
 1366     if (!pfn) // Windows XP or earlier.
 1367         return false;
 1368     int cloaked = 0;
 1369     const int DWMWA_CLOAKED = 14; // Work around SDK troubles.
 1370     auto result = pfn(aWnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
 1371     return SUCCEEDED(result) && cloaked; // Result is "invalid parameter" on Windows 7.
 1372 }
 1373 
 1374 
 1375 
 1376 bool global_struct::DetectWindow(HWND aWnd)
 1377 {
 1378     return DetectHiddenWindows || (IsWindowVisible(aWnd) && !IsWindowCloaked(aWnd));
 1379 }
 1380 
 1381 
 1382 
 1383 int GetWindowTextTimeout(HWND aWnd, LPTSTR aBuf, INT_PTR aBufSize, UINT aTimeout)
 1384 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
 1385 // aBufSize is an int so that any negative values passed in from caller are not lost.
 1386 // Returns the length of what would be copied (not including the zero terminator).
 1387 // In addition, if aBuf is not NULL, the window text is copied into aBuf (not to exceed aBufSize).
 1388 // AutoIt3 author indicates that using WM_GETTEXT vs. GetWindowText() sometimes yields more text.
 1389 // Perhaps this is because GetWindowText() has built-in protection against hung windows and
 1390 // thus isn't actually sending WM_GETTEXT.  The method here is hopefully the best of both worlds
 1391 // (protection against hung windows causing our thread to hang, and getting more text).
 1392 // Another tidbit from MSDN about SendMessage() that might be of use here sometime:
 1393 // "However, the sending thread will process incoming nonqueued (those sent directly to a window
 1394 // procedure) messages while waiting for its message to be processed. To prevent this, use
 1395 // SendMessageTimeout with SMTO_BLOCK set."  Currently not using SMTO_BLOCK because it
 1396 // doesn't seem necessary.
 1397 // Update: GetWindowText() is so much faster than SendMessage() and SendMessageTimeout(), at
 1398 // least on XP, so GetWindowTextTimeout() should probably only be used when getting the max amount
 1399 // of text is important (e.g. this function can fetch the text in a RichEdit20A control and
 1400 // other edit controls, whereas GetWindowText() doesn't).  This function is used to implement
 1401 // things like WinGetText and ControlGetText, in which getting the maximum amount and types
 1402 // of text is more important than performance.
 1403 {
 1404     if (!aWnd || (aBuf && aBufSize < 1)) // No HWND or no room left in buffer (some callers rely on this check).
 1405         return 0; // v1.0.40.04: Fixed to return 0 rather than setting aBuf to NULL and continuing (callers don't want that).
 1406 
 1407     LRESULT result, length;
 1408     if (aBuf)
 1409     {
 1410         *aBuf = '\0';  // Init just to get it out of the way in case of early return/error.
 1411         if (aBufSize == 1) // Room only for the terminator, so go no further (some callers rely on this check).
 1412             return 0;
 1413 
 1414         // Below demonstrated that GetWindowText() is dramatically faster than either SendMessage()
 1415         // or SendMessageTimeout() (noticeably faster when you have hotkeys that activate
 1416         // windows, or toggle between two windows):
 1417         //return GetWindowText(aWnd, aBuf, aBufSize);
 1418         //return (int)SendMessage(aWnd, WM_GETTEXT, (WPARAM)aBufSize, (LPARAM)aBuf);
 1419 
 1420         // Don't bother calling IsWindowHung() because the below call will return
 1421         // nearly instantly if the OS already "knows" that the target window has
 1422         // be unresponsive for 5 seconds or so (i.e. it keeps track of such things
 1423         // on an ongoing basis, at least XP seems to).
 1424         result = SendMessageTimeout(aWnd, WM_GETTEXT, (WPARAM)aBufSize, (LPARAM)aBuf
 1425             , SMTO_ABORTIFHUNG, aTimeout, (PDWORD_PTR) &length);
 1426         if (length >= aBufSize) // Happens sometimes (at least ==aBufSize) for apps that wrongly include the terminator in the reported length.
 1427             length = aBufSize - 1; // Override.
 1428 
 1429         // v1.0.40.04: The following check was added because when the text is too large to to fit in the
 1430         // buffer, the OS (or at least certain applications such as AIM) return a length that *includes*
 1431         // the zero terminator, violating the documented behavior of WM_GETTEXT.  In case the returned
 1432         // length is too long by 1 (or even more than 1), calculate the length explicitly by checking if
 1433         // there's another terminator to the left of the indicated length.  The following loop
 1434         // is used in lieu of strlen() for performance reasons (because sometimes the text is huge).
 1435         // It assumes that there will be no more than one additional terminator to the left of the
 1436         // indicated length, which so far seems to be true:
 1437         for (LPTSTR cp = aBuf + length; cp >= aBuf; --cp)
 1438         {
 1439             if (!*cp)
 1440             {
 1441                 // Keep going to the left until the last consecutive terminator is found.
 1442                 // Necessary for AIM when compiled in release mode (but not in debug mode
 1443                 // for some reason!):
 1444                 for (; cp > aBuf && !cp[-1]; --cp); // Self-contained loop.  Verified correct.
 1445                 length = cp - aBuf;
 1446                 break;
 1447             }
 1448         }
 1449         // If the above loop didn't "break", a terminator wasn't found.
 1450         // Terminate explicitly because MSDN docs aren't clear that it will always be terminated automatically.
 1451         // Update: This also protects against misbehaving apps that might handle the WM_GETTEXT message
 1452         // rather than passing it to DefWindowProc() but that don't terminate the buffer.  This has been
 1453         // confirmed to be necessary at least for AIM when aBufSize==1 (although 1 is no longer possible due
 1454         // to a check that has been added further above):
 1455         aBuf[length] = '\0';
 1456     }
 1457     else
 1458     {
 1459         result = SendMessageTimeout(aWnd, WM_GETTEXTLENGTH, 0, 0, SMTO_ABORTIFHUNG, aTimeout, (PDWORD_PTR)&length);
 1460         // The following can be temporarily uncommented out to demonstrate how some apps such as AIM's
 1461         // write-an-instant-message window have some controls that respond to WM_GETTEXTLENGTH with a
 1462         // length that's completely different than the length with which they respond to WM_GETTEXT.
 1463         // Here are some of the discrepancies:
 1464         // WM_GETTEXTLENGTH vs. WM_GETTEXT:
 1465         // 92 vs. 318 (bigger)
 1466         // 50 vs. 159 (bigger)
 1467         // 3 vs. 0 (smaller)
 1468         // 24 vs. 88 (etc.)
 1469         // 80 vs. 188
 1470         // 24 vs. 88
 1471         // 80 vs. 188
 1472         //char buf[32000];
 1473         //LRESULT length2;
 1474         //result = SendMessageTimeout(aWnd, WM_GETTEXT, (WPARAM)sizeof(buf), (LPARAM)buf
 1475         //  , SMTO_ABORTIFHUNG, aTimeout, (LPDWORD)&length2);
 1476         //if (length2 != length)
 1477         //{
 1478         //  int x = 0;  // Put breakpoint here.
 1479         //}
 1480         // An attempt to fix the size estimate to be larger for misbehaving apps like AIM, but it's ineffective
 1481         // so commented out:
 1482         //if (!length)
 1483         //  length = GetWindowTextLength(aWnd);
 1484     }
 1485 
 1486     // "length" contains the length of what was (or would have been) copied, not including the terminator:
 1487     return result ? (int)length : 0;  // "result" is zero upon failure or timeout.
 1488 }
 1489 
 1490 
 1491 
 1492 ///////////////////////////////////////////////////////////////////////////
 1493 
 1494 
 1495 
 1496 ResultType WindowSearch::SetCriteria(global_struct &aSettings, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 1497 // Returns FAIL if the new criteria can't possibly match a window (due to ahk_id being in invalid
 1498 // window or the specified ahk_group not existing).  Otherwise, it returns OK.
 1499 // Callers must ensure that aText, aExcludeTitle, and aExcludeText point to buffers whose contents
 1500 // will be available for the entire duration of the search.  In other words, the caller should not
 1501 // call MsgSleep() in a way that would allow another thread to launch and overwrite the contents
 1502 // of the sDeref buffer (which might contain the contents).  Things like mFoundHWND and mFoundCount
 1503 // are not initialized here because sometimes the caller changes the criteria without wanting to
 1504 // reset the search.
 1505 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
 1506 {
 1507     // Set any criteria which are not context sensitive.  It doesn't seem necessary to make copies of
 1508     // mCriterionText, mCriterionExcludeTitle, and mCriterionExcludeText because they're never altered
 1509     // here, nor does there seem to be a risk that deref buffer's contents will get overwritten
 1510     // while this set of criteria is in effect because our callers never allow interrupting script-threads
 1511     // *during* the duration of any one set of criteria.
 1512     bool exclude_title_became_non_blank = *aExcludeTitle && !*mCriterionExcludeTitle;
 1513     mCriterionExcludeTitle = aExcludeTitle;
 1514     mCriterionExcludeTitleLength = _tcslen(mCriterionExcludeTitle); // Pre-calculated for performance.
 1515     mCriterionText = aText;
 1516     mCriterionExcludeText = aExcludeText;
 1517     mSettings = &aSettings;
 1518 
 1519     DWORD orig_criteria = mCriteria;
 1520     TCHAR *ahk_flag, *cp, buf[MAX_VAR_NAME_LENGTH + 1];
 1521     int criteria_count;
 1522     size_t size;
 1523 
 1524     for (mCriteria = 0, ahk_flag = aTitle, criteria_count = 0;; ++criteria_count, ahk_flag += 4) // +4 only since an "ahk_" string that isn't qualified may have been found.
 1525     {
 1526         if (   !(ahk_flag = tcscasestr(ahk_flag, _T("ahk_")))   ) // No other special strings are present.
 1527         {
 1528             if (!criteria_count) // Since no special "ahk_" criteria were present, it is CRITERION_TITLE by default.
 1529             {
 1530                 mCriteria = CRITERION_TITLE; // In this case, there is only one criterion.
 1531                 tcslcpy(mCriterionTitle, aTitle, _countof(mCriterionTitle));
 1532                 mCriterionTitleLength = _tcslen(mCriterionTitle); // Pre-calculated for performance.
 1533             }
 1534             break;
 1535         }
 1536         // Since above didn't break, another instance of "ahk_" has been found. To reduce ambiguity,
 1537         // the following requires that any "ahk_" criteria beyond the first be preceded by at least
 1538         // one space or tab:
 1539         if (criteria_count && !IS_SPACE_OR_TAB(ahk_flag[-1])) // Relies on short-circuit boolean order.
 1540         {
 1541             --criteria_count; // Decrement criteria_count to compensate for the loop's increment.
 1542             continue;
 1543         }
 1544         // Since above didn't "continue", it meets the basic test.  But is it an exact match for one of the
 1545         // special criteria strings?  If not, it's really part of the title criterion instead.
 1546         cp = ahk_flag + 4;
 1547         if (!_tcsnicmp(cp, _T("id"), 2))
 1548         {
 1549             cp += 2;
 1550             mCriteria |= CRITERION_ID;
 1551             mCriterionHwnd = (HWND)ATOU64(cp);
 1552             // Note that this can validly be the HWND of a child window; i.e. ahk_id %ChildWindowHwnd% is supported.
 1553             if (mCriterionHwnd != HWND_BROADCAST && !IsWindow(mCriterionHwnd)) // Checked here once rather than each call to IsMatch().
 1554             {
 1555                 mCriterionHwnd = NULL;
 1556                 return FAIL; // Inform caller of invalid criteria.  No need to do anything else further below.
 1557             }
 1558         }
 1559         else if (!_tcsnicmp(cp, _T("pid"), 3))
 1560         {
 1561             cp += 3;
 1562             mCriteria |= CRITERION_PID;
 1563             mCriterionPID = ATOU(cp);
 1564         }
 1565         else if (!_tcsnicmp(cp, _T("group"), 5))
 1566         {
 1567             cp += 5;
 1568             mCriteria |= CRITERION_GROUP;
 1569             tcslcpy(buf, omit_leading_whitespace(cp), _countof(buf));
 1570             if (cp = StrChrAny(buf, _T(" \t"))) // Group names can't contain spaces, so terminate at the first one to exclude any "ahk_" criteria that come afterward.
 1571                 *cp = '\0';
 1572             if (   !(mCriterionGroup = g_script.FindGroup(buf))   )
 1573                 return FAIL; // No such group: Inform caller of invalid criteria.  No need to do anything else further below.
 1574         }
 1575         else
 1576         {
 1577             // Fix for v1.1.09: ahk_exe is handled with ahk_class so that it can be followed by
 1578             // another criterion, such as in "ahk_exe explorer.exe ahk_class CabinetWClass".
 1579             TCHAR *criterion = NULL;
 1580             if (!_tcsnicmp(cp, _T("exe"), 3))
 1581             {
 1582                 cp += 3;
 1583                 mCriteria |= CRITERION_PATH;
 1584                 criterion = mCriterionPath;
 1585                 
 1586             }
 1587             else if (!_tcsnicmp(cp, _T("class"), 5))
 1588             {
 1589                 cp += 5;
 1590                 mCriteria |= CRITERION_CLASS;
 1591                 criterion = mCriterionClass;
 1592             }
 1593             else // It doesn't qualify as a special criteria name even though it starts with "ahk_".
 1594             {
 1595                 --criteria_count; // Decrement criteria_count to compensate for the loop's increment.
 1596                 continue;
 1597             }
 1598 
 1599             // In the following line, it may have been preferable to skip only zero or one spaces rather than
 1600             // calling omit_leading_whitespace().  But now this should probably be kept for backward compatibility.
 1601             // Besides, even if it's possible for a class name to start with a space, a RegEx dot or other symbol
 1602             // can be used to match it via SetTitleMatchMode RegEx.
 1603             tcslcpy(criterion, omit_leading_whitespace(cp), SEARCH_PHRASE_SIZE); // Copy all of the remaining string to simplify the below.
 1604             for (cp = criterion; cp = tcscasestr(cp, _T("ahk_")); cp += 4)  // Fix for v1.0.47.06: strstr() changed to strcasestr() for consistency with the other sections.
 1605             {
 1606                 // This loop truncates any other criteria from the class criteria.  It's not a complete
 1607                 // solution because it doesn't validate that what comes after the "ahk_" string is a
 1608                 // valid criterion name. But for it not to be and yet also be part of some valid class
 1609                 // name seems far too unlikely to worry about.  It would have to be a legitimate class name
 1610                 // such as "ahk_class SomeClassName ahk_wrong".
 1611                 if (cp == criterion) // This check prevents underflow in the next check.
 1612                 {
 1613                     *cp = '\0';
 1614                     break;
 1615                 }
 1616                 else
 1617                     if (IS_SPACE_OR_TAB(cp[-1]))
 1618                     {
 1619                         cp[-1] = '\0';
 1620                         break;
 1621                     }
 1622                     //else assume this "ahk_" string is part of the literal text, continue looping in case
 1623                     // there is a legitimate "ahk_" string after this one.
 1624             } // for()
 1625 
 1626             if (criterion == mCriterionPath)
 1627             {
 1628                 // Allow something like "ahk_exe firefox.exe" to be an exact match for the process name
 1629                 // instead of full path, but for flexibility, always use full path when in regex mode.
 1630                 mCriterionPathIsNameOnly = mSettings->TitleMatchMode != FIND_REGEX && !_tcschr(mCriterionPath, '\\');
 1631             }
 1632         }
 1633         // Since above didn't return or continue, a valid "ahk_" criterion has been discovered.
 1634         // If this is the first such criterion, any text that lies to its left should be interpreted
 1635         // as CRITERION_TITLE.  However, for backward compatibility it seems best to disqualify any title
 1636         // consisting entirely of whitespace.  This is because some scripts might have a variable containing
 1637         // whitespace followed by the string ahk_class, etc. (however, any such whitespace is included as a
 1638         // literal part of the title criterion for flexibility and backward compatibility).
 1639         if (!criteria_count && ahk_flag > omit_leading_whitespace(aTitle))
 1640         {
 1641             mCriteria |= CRITERION_TITLE;
 1642             // Omit exactly one space or tab from the title criterion. That space or tab is the one
 1643             // required to delimit the special "ahk_" string.  Any other spaces or tabs to the left of
 1644             // that one are considered literal (for flexibility):
 1645             size = ahk_flag - aTitle; // This will always be greater than one due to other checks above, which will result in at least one non-whitespace character in the title criterion.
 1646             if (size > _countof(mCriterionTitle)) // Prevent overflow.
 1647                 size = _countof(mCriterionTitle);
 1648             tcslcpy(mCriterionTitle, aTitle, size); // Copy only the eligible substring as the criteria.
 1649             mCriterionTitleLength = _tcslen(mCriterionTitle); // Pre-calculated for performance.
 1650         }
 1651     }
 1652 
 1653     // Since this function doesn't change mCandidateParent, there is no need to update the candidate's
 1654     // attributes unless the type of criterion has changed or if mExcludeTitle became non-blank as
 1655     // a result of our action above:
 1656     if (mCriteria != orig_criteria || exclude_title_became_non_blank)
 1657         UpdateCandidateAttributes(); // In case mCandidateParent isn't NULL, fetch different attributes based on what was set above.
 1658     //else for performance reasons, avoid unnecessary updates.
 1659     return OK;
 1660 }
 1661 
 1662 
 1663 
 1664 void WindowSearch::UpdateCandidateAttributes()
 1665 // This function must be kept thread-safe because it may be called (indirectly) by hook thread too.
 1666 {
 1667     // Nothing to do until SetCandidate() is called with a non-NULL candidate and SetCriteria()
 1668     // has been called for the first time (otherwise, mCriterionExcludeTitle and other things
 1669     // are not yet initialized:
 1670     if (!mCandidateParent || !mCriteria)
 1671         return;
 1672     if ((mCriteria & CRITERION_TITLE) || *mCriterionExcludeTitle) // Need the window's title in both these cases.
 1673         if (!GetWindowText(mCandidateParent, mCandidateTitle, _countof(mCandidateTitle)))
 1674             *mCandidateTitle = '\0'; // Failure or blank title is okay.
 1675     if (mCriteria & CRITERION_PID) // In which case mCriterionPID should already be filled in, though it might be an explicitly specified zero.
 1676         GetWindowThreadProcessId(mCandidateParent, &mCandidatePID);
 1677     if (mCriteria & CRITERION_PATH)
 1678     {
 1679         DWORD dwPid;
 1680         if (GetWindowThreadProcessId(mCandidateParent, &dwPid))
 1681             if (!GetProcessName(dwPid, mCandidatePath, _countof(mCandidatePath), mCriterionPathIsNameOnly))
 1682                 *mCandidatePath = '\0';
 1683     }
 1684     if (mCriteria & CRITERION_CLASS)
 1685         GetClassName(mCandidateParent, mCandidateClass, _countof(mCandidateClass)); // Limit to WINDOW_CLASS_SIZE in this case since that's the maximum that can be searched.
 1686     // Nothing to do for these:
 1687     //CRITERION_GROUP:    Can't be pre-processed at this stage.
 1688     //CRITERION_ID:       It is mCandidateParent, which has already been set by SetCandidate().
 1689 }
 1690 
 1691 
 1692 
 1693 HWND WindowSearch::IsMatch(bool aInvert)
 1694 // Caller must have called SetCriteria prior to calling this method, at least for the purpose of setting
 1695 // mSettings to a valid address (and possibly other reasons).
 1696 // This method returns the HWND of mCandidateParent if it matches the previously specified criteria
 1697 // (title/pid/id/class/group) or NULL otherwise.  Upon NULL, it doesn't reset mFoundParent or mFoundCount
 1698 // in case previous match(es) were found when mFindLastMatch is in effect.
 1699 // Thread-safety: With the following exception, this function must be kept thread-safe because it may be
 1700 // called (indirectly) by hook thread too: The hook thread must never call here directly or indirectly with
 1701 // mArrayStart!=NULL because the corresponding section below is probably not thread-safe.
 1702 {
 1703     if (!mCandidateParent || !mCriteria) // Nothing to check, so no match.
 1704         return NULL;
 1705 
 1706     if ((mCriteria & CRITERION_TITLE) && *mCriterionTitle) // For performance, avoid the calls below (especially RegEx) when mCriterionTitle is blank (assuming it's even possible for it to be blank under these conditions).
 1707     {
 1708         switch(mSettings->TitleMatchMode)
 1709         {
 1710         case FIND_ANYWHERE:
 1711             if (!_tcsstr(mCandidateTitle, mCriterionTitle)) // Suitable even if mCriterionTitle is blank, though that's already ruled out above.
 1712                 return NULL;
 1713             break;
 1714         case FIND_IN_LEADING_PART:
 1715             if (_tcsncmp(mCandidateTitle, mCriterionTitle, mCriterionTitleLength)) // Suitable even if mCriterionTitle is blank, though that's already ruled out above. If it were possible, mCriterionTitleLength would be 0 and thus strncmp would yield 0 to indicate "strings are equal".
 1716                 return NULL;
 1717             break;
 1718         case FIND_REGEX:
 1719             if (!RegExMatch(mCandidateTitle, mCriterionTitle))
 1720                 return NULL;
 1721             break;
 1722         default: // Exact match.
 1723             if (_tcscmp(mCandidateTitle, mCriterionTitle))
 1724                 return NULL;
 1725         }
 1726         // If above didn't return, it's a match so far so continue onward to the other checks.
 1727     }
 1728 
 1729     if (mCriteria & CRITERION_CLASS) // mCriterionClass is probably always non-blank when CRITERION_CLASS is present (harmless even if it isn't), so *mCriterionClass isn't checked.
 1730     {
 1731         if (mSettings->TitleMatchMode == FIND_REGEX)
 1732         {
 1733             if (!RegExMatch(mCandidateClass, mCriterionClass))
 1734                 return NULL;
 1735         }
 1736         else // For backward compatibility, all other modes use exact-match for Class.
 1737             if (_tcscmp(mCandidateClass, mCriterionClass)) // Doesn't match the required class name.
 1738                 return NULL;
 1739         // If nothing above returned, it's a match so far so continue onward to the other checks.
 1740     }
 1741 
 1742     // For the following, mCriterionPID would already be filled in, though it might be an explicitly specified zero.
 1743     if ((mCriteria & CRITERION_PID) && mCandidatePID != mCriterionPID) // Doesn't match required PID.
 1744         return NULL;
 1745     //else it's a match so far, but continue onward in case there are other criteria.
 1746 
 1747     if (mCriteria & CRITERION_PATH)
 1748     {
 1749         if (mSettings->TitleMatchMode == FIND_REGEX)
 1750         {
 1751             if (!RegExMatch(mCandidatePath, mCriterionPath))
 1752                 return NULL;
 1753         }
 1754         else
 1755             if (_tcsicmp(mCandidatePath, mCriterionPath)) // Doesn't match the required path.
 1756                 return NULL;
 1757         // If nothing above returned, it's a match so far so continue onward to the other checks.
 1758     }
 1759 
 1760     // The following also handles the fact that mCriterionGroup might be NULL if the specified group
 1761     // does not exist or was never successfully created:
 1762     if ((mCriteria & CRITERION_GROUP) && (!mCriterionGroup || !mCriterionGroup->IsMember(mCandidateParent, *mSettings)))
 1763         return NULL; // Isn't a member of specified group.
 1764     //else it's a match so far, but continue onward in case there are other criteria (a little strange in this case, but might be useful).
 1765 
 1766     // CRITERION_ID is listed last since in terms of actual calling frequency, this part is hardly ever
 1767     // executed: It's only ever called this way from WinActive(), and possibly indirectly by an ahk_group
 1768     // that contains an ahk_id specification.  It's also called by WinGetList()'s EnumWindows(), though
 1769     // extremely rarely. It's also called this way from other places to determine whether an ahk_id window
 1770     // matches the other criteria such as WinText, ExcludeTitle, and mAlreadyVisited.
 1771     // mCriterionHwnd should already be filled in, though it might be an explicitly specified zero.
 1772     // Note: IsWindow(mCriterionHwnd) was already called by SetCriteria().
 1773     if ((mCriteria & CRITERION_ID) && mCandidateParent != mCriterionHwnd) // Doesn't match the required HWND.
 1774         return NULL;
 1775     //else it's a match so far, but continue onward in case there are other criteria.
 1776 
 1777     // The above would have returned if the candidate window isn't a match for what was specified by
 1778     // the script's WinTitle parameter.  So now check that the ExcludeTitle criterion is satisfied.
 1779     // This is done prior to checking WinText/ExcludeText for performance reasons:
 1780 
 1781     if (*mCriterionExcludeTitle)
 1782     {
 1783         switch(mSettings->TitleMatchMode)
 1784         {
 1785         case FIND_ANYWHERE:
 1786             if (_tcsstr(mCandidateTitle, mCriterionExcludeTitle))
 1787                 return NULL;
 1788             break;
 1789         case FIND_IN_LEADING_PART:
 1790             if (!_tcsncmp(mCandidateTitle, mCriterionExcludeTitle, mCriterionExcludeTitleLength))
 1791                 return NULL;
 1792             break;
 1793         case FIND_REGEX:
 1794             if (RegExMatch(mCandidateTitle, mCriterionExcludeTitle))
 1795                 return NULL;
 1796             break;
 1797         default: // Exact match.
 1798             if (!_tcscmp(mCandidateTitle, mCriterionExcludeTitle))
 1799                 return NULL;
 1800         }
 1801         // If above didn't return, WinTitle and ExcludeTitle are both satisfied.  So continue
 1802         // on below in case there is some WinText or ExcludeText to search.
 1803     }
 1804 
 1805     if (!aInvert) // If caller specified aInvert==true, it will do the below instead of us.
 1806         for (int i = 0; i < mAlreadyVisitedCount; ++i)
 1807             if (mCandidateParent == mAlreadyVisited[i])
 1808                 return NULL;
 1809 
 1810     if (*mCriterionText || *mCriterionExcludeText) // It's not quite a match yet since there are more criteria.
 1811     {
 1812         // Check the child windows for the specified criteria.
 1813         // EnumChildWindows() will return FALSE (failure) in at least two common conditions:
 1814         // 1) It's EnumChildProc callback returned false (i.e. it ended the enumeration prematurely).
 1815         // 2) The specified parent has no children.
 1816         // Since in both these cases GetLastError() returns ERROR_SUCCESS, we discard the return
 1817         // value and just check mFoundChild to determine whether a match has been found:
 1818         mFoundChild = NULL;  // Init prior to each call, in case mFindLastMatch is true.
 1819         EnumChildWindows(mCandidateParent, EnumChildFind, (LPARAM)this);
 1820         if (!mFoundChild) // This parent has no matching child, or no children at all.
 1821             return NULL;
 1822     }
 1823 
 1824     // Since the above didn't return or none of the checks above were needed, it's a complete match.
 1825     // If mFindLastMatch is true, this new value for mFoundParent will stay in effect until
 1826     // overridden by another matching window later:
 1827     if (!aInvert)
 1828     {
 1829         mFoundParent = mCandidateParent;
 1830         ++mFoundCount; // This must be done prior to the mArrayStart section below.
 1831     }
 1832     //else aInvert==true, which means caller doesn't want the above set.
 1833 
 1834     if (mArrayStart) // Probably not thread-safe due to FindOrAddVar(), so hook thread must call only with NULL mArrayStart.
 1835     {
 1836         // Make it longer than Max var name so that FindOrAddVar() will be able to spot and report
 1837         // var names that are too long:
 1838         TCHAR var_name[MAX_VAR_NAME_LENGTH + 20];
 1839         Var *array_item = g_script.FindOrAddVar(var_name
 1840             , sntprintf(var_name, _countof(var_name), _T("%s%u"), mArrayStart->mName, mFoundCount)
 1841             , FINDVAR_FOR_PSEUDO_ARRAY(*mArrayStart));
 1842         if (array_item)
 1843             array_item->AssignHWND(mFoundParent);
 1844         //else no error reporting currently, since should be very rare.
 1845     }
 1846 
 1847     // Fix for v1.0.30.01: Don't return mFoundParent because its NULL when aInvert is true.
 1848     // At this stage, the candidate is a known match, so return it:
 1849     return mCandidateParent;
 1850 }
 1851 
 1852 
 1853 
 1854 ///////////////////////////////////////////////////////////////////
 1855 
 1856 
 1857 
 1858 void SetForegroundLockTimeout()
 1859 {
 1860     // Don't check for failure since this operation isn't critical, and don't want
 1861     // users continually haunted by startup error if for some reason this doesn't
 1862     // work on their system:
 1863     if (SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &g_OriginalTimeout, 0))
 1864         if (g_OriginalTimeout) // Anti-focus stealing measure is in effect.
 1865         {
 1866             // Set it to zero instead, disabling the measure:
 1867             SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (PVOID)0, SPIF_SENDCHANGE);
 1868         }
 1869 }
 1870 
 1871 
 1872 
 1873 bool DialogPrep()
 1874 // Having it as a function vs. macro should reduce code size due to expansion of macros inside.
 1875 {
 1876     bool thread_was_critical = g->ThreadIsCritical;
 1877     g->ThreadIsCritical = false;
 1878     g->AllowThreadToBeInterrupted = true;
 1879     if (g_KeybdHook) // Workaround added in v1.1.22.01.
 1880     {
 1881         // If a Control or Shift key is physically down, the dialog somehow recognizes this
 1882         // and acts as though the key is logically down even if it really isn't.  Logically
 1883         // releasing the key before or after the dialog is shown appears to work around the
 1884         // issue.  If one L/R modifier is logically down and the opposite side is physically
 1885         // down, the workaround isn't wanted/needed, because the dialog *should* act like
 1886         // the key is down, and it does so until the key is released.
 1887         // UPDATE: I'm not able to reproduce this problem on Windows 10, but it's done there
 1888         // too just in case.
 1889         mod_type mods_logical = ConvertModifiersLR(g_modifiersLR_logical);
 1890         modLR_type mods_not_to_release = ConvertModifiers(mods_logical & (MOD_CONTROL | MOD_SHIFT))
 1891             | MOD_LALT | MOD_RALT | MOD_LWIN | MOD_RWIN; // v1.1.23.05: No need to release these keys.
 1892         modLR_type mods_to_release = g_modifiersLR_physical & ~mods_not_to_release;
 1893         if (mods_to_release)
 1894         {
 1895             if (mods_logical == MOD_WIN) // Fixed in v1.1.23.05 to exclude combinations like MOD_WIN|MOD_CONTROL, which don't need this.
 1896             {
 1897                 // Even though the Win key isn't being released, sending a Shift or Control key-up
 1898                 // triggers the Start menu.  To prevent that, we send the mask key.  This has been
 1899                 // confirmed to apply to #Control, #Shift and the left/right variants.
 1900                 KeyEventMenuMask(KEYDOWNANDUP, KEY_IGNORE_ALL_EXCEPT_MODIFIER);
 1901                 // Releasing the Win key now does not prevent it from being "masked" again later:
 1902                 //mods_to_release |= (g_modifiersLR_logical & (MOD_LWIN | MOD_RWIN));
 1903             }
 1904             SetModifierLRState(0, mods_to_release, NULL, false, false);
 1905         }
 1906     }
 1907     if (HIWORD(GetQueueStatus(QS_ALLEVENTS))) // See DIALOG_PREP for explanation.
 1908         MsgSleep(-1);
 1909     return thread_was_critical; // Caller is responsible for using this to later restore g->ThreadIsCritical.
 1910 }