"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/script2.cpp" (8 May 2021, 886000 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 "script2.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 <olectl.h> // for OleLoadPicture()
   19 #include <winioctl.h> // For PREVENT_MEDIA_REMOVAL and CD lock/unlock.
   20 #include "qmath.h" // Used by Transform() [math.h incurs 2k larger code size just for ceil() & floor()]
   21 #include "mt19937ar-cok.h" // for sorting in random order
   22 #include "script.h"
   23 #include "window.h" // for IF_USE_FOREGROUND_WINDOW
   24 #include "application.h" // for MsgSleep()
   25 #include "resources/resource.h"  // For InputBox.
   26 #include "TextIO.h"
   27 #include <Psapi.h> // for GetModuleBaseName.
   28 
   29 #undef _WIN32_WINNT // v1.1.10.01: Redefine this just for these APIs, to avoid breaking some other commands on Win XP (such as Process Close).
   30 #define _WIN32_WINNT 0x0600 // Windows Vista
   31 #include <mmdeviceapi.h> // for SoundSet/SoundGet.
   32 #pragma warning(push)
   33 #pragma warning(disable:4091) // Work around a bug in the SDK used by the v140_xp toolset.
   34 #include <endpointvolume.h> // for SoundSet/SoundGet.
   35 #pragma warning(pop)
   36 
   37 #define PCRE_STATIC             // For RegEx. PCRE_STATIC tells PCRE to declare its functions for normal, static
   38 #include "lib_pcre/pcre/pcre.h" // linkage rather than as functions inside an external DLL.
   39 
   40 #include "script_func_impl.h"
   41 
   42 
   43 
   44 ////////////////////
   45 // Window related //
   46 ////////////////////
   47 
   48 ResultType Line::Splash(LPTSTR aOptions, LPTSTR aSubText, LPTSTR aMainText, LPTSTR aTitle, LPTSTR aFontName
   49     , LPTSTR aImageFile, bool aSplashImage)
   50 {
   51     int window_index = 0;  // Set the default window to operate upon (the first).
   52     LPTSTR options, image_filename = aImageFile;  // Set default.
   53     bool turn_off = false;
   54     bool show_it_only = false;
   55     int bar_pos;
   56     bool bar_pos_has_been_set = false;
   57     bool options_consist_of_bar_pos_only = false;
   58 
   59     if (aSplashImage)
   60     {
   61         options = aOptions;
   62         if (*aImageFile)
   63         {
   64             LPTSTR colon_pos = _tcschr(aImageFile, ':');
   65             LPTSTR image_filename_omit_leading_whitespace = omit_leading_whitespace(image_filename); // Added in v1.0.38.04 per someone's suggestion.
   66             if (colon_pos)
   67             {
   68                 TCHAR window_number_str[32];  // Allow extra room in case leading spaces or in hex format, e.g. 0x02
   69                 size_t length_to_copy = colon_pos - aImageFile;
   70                 if (length_to_copy < _countof(window_number_str))
   71                 {
   72                     tcslcpy(window_number_str, aImageFile, length_to_copy + 1);
   73                     if (IsPureNumeric(window_number_str, false, false, true)) // Seems best to allow float at runtime.
   74                     {
   75                         // Note that filenames can start with spaces, so omit_leading_whitespace() is only
   76                         // used if the string is entirely blank:
   77                         image_filename = colon_pos + 1;
   78                         image_filename_omit_leading_whitespace = omit_leading_whitespace(image_filename); // Update to reflect the change above.
   79                         if (!*image_filename_omit_leading_whitespace)
   80                             image_filename = image_filename_omit_leading_whitespace;
   81                         window_index = ATOI(window_number_str) - 1;
   82                         if (window_index < 0 || window_index >= MAX_SPLASHIMAGE_WINDOWS)
   83                             return LineError(_T("Max window number is ") MAX_SPLASHIMAGE_WINDOWS_STR _T(".")
   84                                 , FAIL, aOptions);
   85                     }
   86                 }
   87             }
   88             if (!_tcsicmp(image_filename_omit_leading_whitespace, _T("Off"))) // v1.0.38.04: Ignores leading whitespace per someone's suggestion.
   89                 turn_off = true;
   90             else if (!_tcsicmp(image_filename_omit_leading_whitespace, _T("Show"))) // v1.0.38.04: Ignores leading whitespace per someone's suggestion.
   91                 show_it_only = true;
   92         }
   93     }
   94     else // Progress Window.
   95     {
   96         if (   !(options = _tcschr(aOptions, ':'))   )
   97             options = aOptions;
   98         else
   99         {
  100             window_index = ATOI(aOptions) - 1;
  101             if (window_index < 0 || window_index >= MAX_PROGRESS_WINDOWS)
  102                 return LineError(_T("Max window number is ") MAX_PROGRESS_WINDOWS_STR _T("."), FAIL, aOptions);
  103             ++options;
  104         }
  105         options = omit_leading_whitespace(options); // Added in v1.0.38.04 per someone's suggestion.
  106         if (!_tcsicmp(options, _T("Off")))
  107             turn_off = true;
  108         else if (!_tcsicmp(options, _T("Show")))
  109             show_it_only = true;
  110         else
  111         {
  112             // Allow floats at runtime for flexibility (i.e. in case aOptions was in a variable reference).
  113             // But still use ATOI for the conversion:
  114             if (IsPureNumeric(options, true, false, true)) // Negatives are allowed as of v1.0.25.
  115             {
  116                 bar_pos = ATOI(options);
  117                 bar_pos_has_been_set = true;
  118                 options_consist_of_bar_pos_only = true;
  119             }
  120             //else leave it set to the default.
  121         }
  122     }
  123 
  124     SplashType &splash = aSplashImage ? g_SplashImage[window_index] : g_Progress[window_index];
  125 
  126     // In case it's possible for the window to get destroyed by other means (WinClose?).
  127     // Do this only after the above options were set so that the each window's settings
  128     // will be remembered until such time as "Command, Off" is used:
  129     if (splash.hwnd && !IsWindow(splash.hwnd))
  130         splash.hwnd = NULL;
  131 
  132     if (show_it_only)
  133     {
  134         if (splash.hwnd && !IsWindowVisible(splash.hwnd))
  135             ShowWindow(splash.hwnd,  SW_SHOWNOACTIVATE); // See bottom of this function for comments on SW_SHOWNOACTIVATE.
  136         //else for simplicity, do nothing.
  137         return OK;
  138     }
  139 
  140     if (!turn_off && splash.hwnd && !*image_filename && (options_consist_of_bar_pos_only || !*options)) // The "modify existing window" mode is in effect.
  141     {
  142         // If there is an existing window, just update its bar position and text.
  143         // If not, do nothing since we don't have the original text of the window to recreate it.
  144         // Since this is our thread's window, it shouldn't be necessary to use SendMessageTimeout()
  145         // since the window cannot be hung since by definition our thread isn't hung.  Also, setting
  146         // a text item from non-blank to blank is not supported so that elements can be omitted from an
  147         // update command without changing the text that's in the window.  The script can specify %a_space%
  148         // to explicitly make an element blank.
  149         if (!aSplashImage && bar_pos_has_been_set && splash.bar_pos != bar_pos) // Avoid unnecessary redrawing.
  150         {
  151             splash.bar_pos = bar_pos;
  152             if (splash.hwnd_bar)
  153                 SendMessage(splash.hwnd_bar, PBM_SETPOS, (WPARAM)bar_pos, 0);
  154         }
  155         // SendMessage() vs. SetWindowText() is used for controls so that tabs are expanded.
  156         // For simplicity, the hwnd_text1 control is not expanded dynamically if it is currently of
  157         // height zero.  The user can recreate the window if a different height is needed.
  158         if (*aMainText && splash.hwnd_text1)
  159             SendMessage(splash.hwnd_text1, WM_SETTEXT, 0, (LPARAM)(aMainText));
  160         if (*aSubText)
  161             SendMessage(splash.hwnd_text2, WM_SETTEXT, 0, (LPARAM)(aSubText));
  162         if (*aTitle)
  163             SetWindowText(splash.hwnd, aTitle); // Use the simple method for parent window titles.
  164         return OK;
  165     }
  166 
  167     // Otherwise, destroy any existing window first:
  168     if (splash.hwnd)
  169         DestroyWindow(splash.hwnd);
  170     if (splash.hfont1) // Destroy font only after destroying the window that uses it.
  171         DeleteObject(splash.hfont1);
  172     if (splash.hfont2)
  173         DeleteObject(splash.hfont2);
  174     if (splash.hbrush)
  175         DeleteObject(splash.hbrush);
  176     if (splash.pic_bmp)
  177     {
  178         if (splash.pic_type == IMAGE_BITMAP)
  179             DeleteObject(splash.pic_bmp);
  180         else
  181             DestroyIcon(splash.pic_icon);
  182     }
  183     ZeroMemory(&splash, sizeof(splash)); // Set the above and all other fields to zero.
  184 
  185     if (turn_off)
  186         return OK;
  187     // Otherwise, the window needs to be created or recreated.
  188 
  189     if (!*aTitle) // Provide default title.
  190         aTitle = (g_script.mFileName && *g_script.mFileName) ? g_script.mFileName : _T("");
  191 
  192     // Since there is often just one progress/splash window, and it defaults to always-on-top,
  193     // it seems best to default owned to be true so that it doesn't get its own task bar button:
  194     bool owned = true;          // Whether this window is owned by the main window.
  195     bool centered_main = true;  // Whether the main text is centered.
  196     bool centered_sub = true;   // Whether the sub text is centered.
  197     bool initially_hidden = false;  // Whether the window should kept hidden (for later showing by the script).
  198     int style = WS_DISABLED|WS_POPUP|WS_CAPTION;  // WS_CAPTION implies WS_BORDER
  199     int exstyle = WS_EX_TOPMOST;
  200     int xpos = COORD_UNSPECIFIED;
  201     int ypos = COORD_UNSPECIFIED;
  202     int range_min = 0, range_max = 0;  // For progress bars.
  203     int font_size1 = 0; // 0 is the flag to "use default size".
  204     int font_size2 = 0;
  205     int font_weight1 = FW_DONTCARE;  // Flag later logic to use default.
  206     int font_weight2 = FW_DONTCARE;  // Flag later logic to use default.
  207     COLORREF bar_color = CLR_DEFAULT;
  208     splash.color_bk = CLR_DEFAULT;
  209     splash.color_text = CLR_DEFAULT;
  210     splash.height = COORD_UNSPECIFIED;
  211     if (aSplashImage)
  212     {
  213         #define SPLASH_DEFAULT_WIDTH DPIScale(300)
  214         splash.width = COORD_UNSPECIFIED;
  215         splash.object_height = COORD_UNSPECIFIED;
  216     }
  217     else // Progress window.
  218     {
  219         splash.width = SPLASH_DEFAULT_WIDTH;
  220         splash.object_height = 20;
  221     }
  222     splash.object_width = COORD_UNSPECIFIED;  // Currently only used for SplashImage, not Progress.
  223     if (*aMainText || *aSubText || !aSplashImage)
  224     {
  225         splash.margin_x = 10;
  226         splash.margin_y = 5;
  227     }
  228     else // Displaying only a naked image, so don't use borders.
  229         splash.margin_x = splash.margin_y = 0;
  230 
  231     for (LPTSTR cp2, cp = options; *cp; ++cp)
  232     {
  233         switch(ctoupper(*cp))
  234         {
  235         case 'A':  // Non-Always-on-top.  Synonymous with A0 in early versions.
  236             // Decided against this enforcement.  In the enhancement mentioned below is ever done (unlikely),
  237             // it seems that A1 can turn always-on-top on and A0 or A by itself can turn it off:
  238             //if (cp[1] == '0') // The zero is required to allow for future enhancement: modify attrib. of existing window.
  239             exstyle &= ~WS_EX_TOPMOST;
  240             break;
  241         case 'B': // Borderless and/or Titleless
  242             style &= ~WS_CAPTION;
  243             if (cp[1] == '1')
  244                 style |= WS_BORDER;
  245             else if (cp[1] == '2')
  246                 style |= WS_DLGFRAME;
  247             break;
  248         case 'C': // Colors
  249             if (!cp[1]) // Avoids out-of-bounds when the loop's own ++cp is done.
  250                 break;
  251             ++cp; // Always increment to omit the next char from consideration by the next loop iteration.
  252             switch(ctoupper(*cp))
  253             {
  254             case 'B': // Bar color.
  255             case 'T': // Text color.
  256             case 'W': // Window/Background color.
  257             {
  258                 TCHAR color_str[32];
  259                 tcslcpy(color_str, cp + 1, _countof(color_str));
  260                 LPTSTR space_pos = StrChrAny(color_str, _T(" \t"));  // space or tab
  261                 if (space_pos)
  262                     *space_pos = '\0';
  263                 //else a color name can still be present if it's at the end of the string.
  264                 COLORREF color = ColorNameToBGR(color_str);
  265                 if (color == CLR_NONE) // A matching color name was not found, so assume it's in hex format.
  266                 {
  267                     if (_tcslen(color_str) > 6)
  268                         color_str[6] = '\0';  // Shorten to exactly 6 chars, which happens if no space/tab delimiter is present.
  269                     color = rgb_to_bgr(_tcstol(color_str, NULL, 16));
  270                     // if color_str does not contain something hex-numeric, black (0x00) will be assumed,
  271                     // which seems okay given how rare such a problem would be.
  272                 }
  273                 switch (ctoupper(*cp))
  274                 {
  275                 case 'B':
  276                     bar_color = color;
  277                     break;
  278                 case 'T':
  279                     splash.color_text = color;
  280                     break;
  281                 case 'W':
  282                     splash.color_bk = color;
  283                     splash.hbrush = CreateSolidBrush(color); // Used for window & control backgrounds.
  284                     break;
  285                 }
  286                 // Skip over the color string to avoid interpreting hex digits or color names as option letters:
  287                 cp += _tcslen(color_str);
  288                 break;
  289             }
  290             default:
  291                 centered_sub = (*cp != '0');
  292                 centered_main = (cp[1] != '0');
  293             }
  294         case 'F':
  295             if (!cp[1]) // Avoids out-of-bounds when the loop's own ++cp is done.
  296                 break;
  297             ++cp; // Always increment to omit the next char from consideration by the next loop iteration.
  298             switch(ctoupper(*cp))
  299             {
  300             case 'M':
  301                 if ((font_size1 = _ttoi(cp + 1)) < 0)
  302                     font_size1 = 0;
  303                 break;
  304             case 'S':
  305                 if ((font_size2 = _ttoi(cp + 1)) < 0)
  306                     font_size2 = 0;
  307                 break;
  308             }
  309             break;
  310         case 'M': // Movable and (optionally) resizable.
  311             style &= ~WS_DISABLED;
  312             if (cp[1] == '1')
  313                 style |= WS_SIZEBOX;
  314             if (cp[1] == '2')
  315                 style |= WS_SIZEBOX|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU;
  316             break;
  317         case 'P': // Starting position of progress bar [v1.0.25]
  318             bar_pos = _ttoi(cp + 1);
  319             bar_pos_has_been_set = true;
  320             break;
  321         case 'R': // Range of progress bar [v1.0.25]
  322             if (!cp[1]) // Ignore it because we don't want cp to ever point to the NULL terminator due to the loop's increment.
  323                 break;
  324             range_min = ATOI(++cp); // Increment cp to point it to range_min.
  325             if (cp2 = _tcschr(cp + 1, '-'))  // +1 to omit the min's minus sign, if it has one.
  326             {
  327                 cp = cp2;
  328                 if (!cp[1]) // Ignore it because we don't want cp to ever point to the NULL terminator due to the loop's increment.
  329                     break;
  330                 range_max = ATOI(++cp); // Increment cp to point it to range_max, which can be negative as in this example: R-100--50
  331             }
  332             break;
  333         case 'T': // Give it a task bar button by making it a non-owned window.
  334             owned = false;
  335             break;
  336         // For options such as W, X and Y:
  337         // Use atoi() vs. ATOI() to avoid interpreting something like 0x01B as hex when in fact
  338         // the B was meant to be an option letter:
  339         case 'W':
  340             if (!cp[1]) // Avoids out-of-bounds when the loop's own ++cp is done.
  341                 break;
  342             ++cp; // Always increment to omit the next char from consideration by the next loop iteration.
  343             switch(ctoupper(*cp))
  344             {
  345             case 'M':
  346                 if ((font_weight1 = _ttoi(cp + 1)) < 0)
  347                     font_weight1 = 0;
  348                 break;
  349             case 'S':
  350                 if ((font_weight2 = _ttoi(cp + 1)) < 0)
  351                     font_weight2 = 0;
  352                 break;
  353             default:
  354                 splash.width = _ttoi(cp);
  355                 if (!aSplashImage)
  356                     splash.width = DPIScale(splash.width);
  357             }
  358             break;
  359         case 'H':
  360             if (!_tcsnicmp(cp, _T("Hide"), 4)) // Hide vs. Hidden is debatable.
  361             {
  362                 initially_hidden = true;
  363                 cp += 3; // +3 vs. +4 due to the loop's own ++cp.
  364             }
  365             else // Allow any width/height to be specified so that the window can be "rolled up" to its title bar:
  366             {
  367                 splash.height = _ttoi(cp + 1);
  368                 if (!aSplashImage)
  369                     splash.height = DPIScale(splash.height);
  370             }
  371             break;
  372         case 'X':
  373             xpos = _ttoi(cp + 1);
  374             break;
  375         case 'Y':
  376             ypos = _ttoi(cp + 1);
  377             break;
  378         case 'Z':
  379             if (!cp[1]) // Avoids out-of-bounds when the loop's own ++cp is done.
  380                 break;
  381             ++cp; // Always increment to omit the next char from consideration by the next loop iteration.
  382             switch(ctoupper(*cp))
  383             {
  384             case 'B':  // for backward compatibility with interim releases of v1.0.14
  385             case 'H':
  386                 splash.object_height = _ttoi(cp + 1); // Allow it to be zero or negative to omit the object.
  387                 break;
  388             case 'W':
  389                 if (aSplashImage)
  390                     splash.object_width = _ttoi(cp + 1); // Allow it to be zero or negative to omit the object.
  391                 //else for Progress, don't allow width to be changed since a zero would omit the bar.
  392                 break;
  393             case 'X':
  394                 splash.margin_x = _ttoi(cp + 1);
  395                 break;
  396             case 'Y':
  397                 splash.margin_y = _ttoi(cp + 1);
  398                 break;
  399             }
  400             break;
  401         // Otherwise: Ignore other characters, such as the digits that occur after the P/X/Y option letters.
  402         } // switch()
  403     } // for()
  404 
  405     HDC hdc = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
  406     int pixels_per_point_y = GetDeviceCaps(hdc, LOGPIXELSY);
  407 
  408     // Get name and size of default font.
  409     HFONT hfont_default = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
  410     HFONT hfont_old = (HFONT)SelectObject(hdc, hfont_default);
  411     TCHAR default_font_name[65];
  412     GetTextFace(hdc, _countof(default_font_name) - 1, default_font_name);
  413     TEXTMETRIC tm;
  414     GetTextMetrics(hdc, &tm);
  415     int default_gui_font_height = tm.tmHeight;
  416 
  417     // If both are zero or less, reset object height/width for maintainability and sizing later.
  418     // However, if one of them is -1, the caller is asking for that dimension to be auto-calc'd
  419     // to "keep aspect ratio" with the the other specified dimension:
  420     if (   splash.object_height < 1 && splash.object_height != COORD_UNSPECIFIED
  421         && splash.object_width < 1 && splash.object_width != COORD_UNSPECIFIED
  422         || !splash.object_height || !splash.object_width   )
  423         splash.object_height = splash.object_width = 0;
  424 
  425     // If there's an image, handle it first so that automatic-width can be applied (if no width was specified)
  426     // for later font calculations:
  427     if (aSplashImage && *image_filename && splash.object_height)
  428     {
  429         for (bool use_gdi_plus = false; ; use_gdi_plus = true)
  430         {
  431             splash.pic_bmp = LoadPicture(image_filename,
  432                 splash.object_width == COORD_UNSPECIFIED ? 0 : splash.object_width,
  433                 splash.object_height == COORD_UNSPECIFIED ? 0 : splash.object_height,
  434                 splash.pic_type, 0, use_gdi_plus);
  435             if (splash.pic_bmp || use_gdi_plus)
  436                 break;
  437             // Re-attempt with GDI+. The first attempt is made without it for backward compatibility.
  438             // In particular, GDI+ causes some issues with WinSet TransColor on Windows XP.
  439         }
  440         if (splash.pic_bmp && (splash.object_height < 0 || splash.object_width < 0))
  441         {
  442             HBITMAP hbmp_to_measure = NULL;
  443             ICONINFO iconinfo;
  444             if (splash.pic_type == IMAGE_BITMAP)
  445                 hbmp_to_measure = splash.pic_bmp;
  446             else // IMAGE_ICON
  447                 if (GetIconInfo(splash.pic_icon, &iconinfo))
  448                     hbmp_to_measure = iconinfo.hbmColor;
  449             if (hbmp_to_measure)
  450             {
  451                 BITMAP bmp;
  452                 if (GetObject(hbmp_to_measure, sizeof(BITMAP), &bmp))
  453                 {
  454                     if (splash.object_height == -1 && splash.object_width > 0)
  455                     {
  456                         // Caller wants height calculated based on the specified width (keep aspect ratio).
  457                         if (bmp.bmWidth) // Avoid any chance of divide-by-zero.
  458                             splash.object_height = (int)(((double)bmp.bmHeight / bmp.bmWidth) * splash.object_width + .5); // Round.
  459                     }
  460                     else if (splash.object_width == -1 && splash.object_height > 0)
  461                     {
  462                         // Caller wants width calculated based on the specified height (keep aspect ratio).
  463                         if (bmp.bmHeight) // Avoid any chance of divide-by-zero.
  464                             splash.object_width = (int)(((double)bmp.bmWidth / bmp.bmHeight) * splash.object_height + .5); // Round.
  465                     }
  466                     else
  467                     {
  468                         // Use actual width/height where unspecified:
  469                         if (splash.object_height == COORD_UNSPECIFIED)
  470                             splash.object_height = bmp.bmHeight;
  471                         if (splash.object_width == COORD_UNSPECIFIED)
  472                             splash.object_width = bmp.bmWidth;
  473                     }
  474                     if (splash.width == COORD_UNSPECIFIED)
  475                         splash.width = splash.object_width + (2 * splash.margin_x);
  476                 }
  477                 if (splash.pic_type == IMAGE_ICON)
  478                 {
  479                     // Delete the bitmaps created by GetIconInfo above:
  480                     DeleteObject(iconinfo.hbmColor);
  481                     DeleteObject(iconinfo.hbmMask);
  482                 }
  483             }
  484         }
  485     }
  486 
  487     // If width is still unspecified -- which should only happen if it's a SplashImage window with
  488     // no image, or there was a problem getting the image above -- set it to be the default.
  489     if (splash.width == COORD_UNSPECIFIED)
  490         splash.width = SPLASH_DEFAULT_WIDTH;
  491     // Similarly, object_height is set to zero if the object is not present:
  492     if (splash.object_height == COORD_UNSPECIFIED)
  493         splash.object_height = 0;
  494 
  495     // Lay out client area.  If height is COORD_UNSPECIFIED, use a temp value for now until
  496     // it can be later determined.
  497     RECT client_rect, draw_rect;
  498     SetRect(&client_rect, 0, 0, splash.width, splash.height == COORD_UNSPECIFIED ? 500 : splash.height);
  499 
  500     // Create fonts based on specified point sizes.  A zero value for font_size1 & 2 are correctly handled
  501     // by CreateFont():
  502     if (*aMainText)
  503     {
  504         // If a zero size is specified, it should use the default size.  But the default brought about
  505         // by passing a zero here is not what the system uses as a default, so instead use a font size
  506         // that is 25% larger than the default size (since the default size itself is used for aSubtext).
  507         // On a typical system, the default GUI font's point size is 8, so this will make it 10 by default.
  508         // Also, it appears that changing the system's font size in Control Panel -> Display -> Appearance
  509         // does not affect the reported default font size.  Thus, the default is probably 8/9 for most/all
  510         // XP systems and probably other OSes as well.
  511         // By specifying PROOF_QUALITY the nearest matching font size should be chosen, which should avoid
  512         // any scaling artifacts that might be caused if default_gui_font_height is not 8.
  513         if (   !(splash.hfont1 = CreateFont(font_size1 ? -MulDiv(font_size1, pixels_per_point_y, 72) : (int)(1.25 * default_gui_font_height)
  514             , 0, 0, 0, font_weight1 ? font_weight1 : FW_SEMIBOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_TT_PRECIS
  515             , CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FF_DONTCARE, *aFontName ? aFontName : default_font_name))   )
  516             // Call it again with default font in case above failed due to non-existent aFontName.
  517             // Update: I don't think this actually does any good, at least on XP, because it appears
  518             // that CreateFont() does not fail merely due to a non-existent typeface.  But it is kept
  519             // in case it ever fails for other reasons:
  520             splash.hfont1 = CreateFont(font_size1 ? -MulDiv(font_size1, pixels_per_point_y, 72) : (int)(1.25 * default_gui_font_height)
  521                 , 0, 0, 0, font_weight1 ? font_weight1 : FW_SEMIBOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_TT_PRECIS
  522                 , CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FF_DONTCARE, default_font_name);
  523         // To avoid showing a runtime error, fall back to the default font if other font wasn't created:
  524         SelectObject(hdc, splash.hfont1 ? splash.hfont1 : hfont_default);
  525         // Calc height of text by taking into account font size, number of lines, and space between lines:
  526         draw_rect = client_rect;
  527         draw_rect.left += splash.margin_x;
  528         draw_rect.right -= splash.margin_x;
  529         splash.text1_height = DrawText(hdc, aMainText, -1, &draw_rect, DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS);
  530     }
  531     // else leave the above fields set to the zero defaults.
  532 
  533     if (font_size2 || font_weight2 || aFontName)
  534         if (   !(splash.hfont2 = CreateFont(-MulDiv(font_size2, pixels_per_point_y, 72), 0, 0, 0
  535             , font_weight2, 0, 0, 0, DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS
  536             , PROOF_QUALITY, FF_DONTCARE, *aFontName ? aFontName : default_font_name))   )
  537             // Call it again with default font in case above failed due to non-existent aFontName.
  538             // Update: I don't think this actually does any good, at least on XP, because it appears
  539             // that CreateFont() does not fail merely due to a non-existent typeface.  But it is kept
  540             // in case it ever fails for other reasons:
  541             if (font_size2 || font_weight2)
  542                 splash.hfont2 = CreateFont(-MulDiv(font_size2, pixels_per_point_y, 72), 0, 0, 0
  543                     , font_weight2, 0, 0, 0, DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS
  544                     , PROOF_QUALITY, FF_DONTCARE, default_font_name);
  545     //else leave it NULL so that hfont_default will be used.
  546 
  547     // The font(s) will be deleted the next time this window is destroyed or recreated,
  548     // or by the g_script destructor.
  549 
  550     SPLASH_CALC_YPOS  // Calculate the Y position of each control in the window.
  551 
  552     if (splash.height == COORD_UNSPECIFIED)
  553     {
  554         // Since the window height was not specified, determine what it should be based on the height
  555         // of all the controls in the window:
  556         int subtext_height;
  557         if (*aSubText)
  558         {
  559             SelectObject(hdc, splash.hfont2 ? splash.hfont2 : hfont_default);
  560             // Calc height of text by taking into account font size, number of lines, and space between lines:
  561             // Reset unconditionally because the previous DrawText() sometimes alters the rect:
  562             draw_rect = client_rect;
  563             draw_rect.left += splash.margin_x;
  564             draw_rect.right -= splash.margin_x;
  565             subtext_height = DrawText(hdc, aSubText, -1, &draw_rect, DT_CALCRECT | DT_WORDBREAK);
  566         }
  567         else
  568             subtext_height = 0;
  569         // For the below: sub_y was previously calc'd to be the top of the subtext control.
  570         // Also, splash.margin_y is added because the text looks a little better if the window
  571         // doesn't end immediately beneath it:
  572         splash.height = subtext_height + sub_y + splash.margin_y;
  573         client_rect.bottom = splash.height;
  574     }
  575 
  576     SelectObject(hdc, hfont_old); // Necessary to avoid memory leak.
  577     if (!DeleteDC(hdc))
  578         return FAIL;  // Force a failure to detect bugs such as hdc still having a created handle inside.
  579 
  580     // Based on the client area determined above, expand the main_rect to include title bar, borders, etc.
  581     // If the window has a border or caption this also changes top & left *slightly* from zero.
  582     RECT main_rect = client_rect;
  583     AdjustWindowRectEx(&main_rect, style, FALSE, exstyle);
  584     int main_width = main_rect.right - main_rect.left;  // main.left might be slightly less than zero.
  585     int main_height = main_rect.bottom - main_rect.top; // main.top might be slightly less than zero.
  586 
  587     RECT work_rect;
  588     SystemParametersInfo(SPI_GETWORKAREA, 0, &work_rect, 0);  // Get desktop rect excluding task bar.
  589     int work_width = work_rect.right - work_rect.left;  // Note that "left" won't be zero if task bar is on left!
  590     int work_height = work_rect.bottom - work_rect.top;  // Note that "top" won't be zero if task bar is on top!
  591 
  592     // Seems best (and easier) to unconditionally restrict window size to the size of the desktop,
  593     // since most users would probably want that.  This can be overridden by using WinMove afterward.
  594     if (main_width > work_width)
  595         main_width = work_width;
  596     if (main_height > work_height)
  597         main_height = work_height;
  598 
  599     // Centering doesn't currently handle multi-monitor systems explicitly, since those calculations
  600     // require API functions that don't exist in Win95/NT (and thus would have to be loaded
  601     // dynamically to allow the program to launch).  Therefore, windows will likely wind up
  602     // being centered across the total dimensions of all monitors, which usually results in
  603     // half being on one monitor and half in the other.  This doesn't seem too terrible and
  604     // might even be what the user wants in some cases (i.e. for really big windows).
  605     // See comments above for why work_rect.left and top are added in (they aren't always zero).
  606     if (xpos == COORD_UNSPECIFIED)
  607         xpos = work_rect.left + ((work_width - main_width) / 2);  // Don't use splash.width.
  608     if (ypos == COORD_UNSPECIFIED)
  609         ypos = work_rect.top + ((work_height - main_height) / 2);  // Don't use splash.width.
  610 
  611     // CREATE Main Splash Window
  612     // It seems best to make this an unowned window for two reasons:
  613     // 1) It will get its own task bar icon then, which is usually desirable for cases where
  614     //    there are several progress/splash windows or the window is monitoring something.
  615     // 2) The progress/splash window won't prevent the main window from being used (owned windows
  616     //    prevent their owners from ever becoming active).
  617     // However, it seems likely that some users would want the above to be configurable,
  618     // so now there is an option to change this behavior.
  619     HWND dialog_owner = THREAD_DIALOG_OWNER;  // Resolve macro only once to reduce code size.
  620     if (!(splash.hwnd = CreateWindowEx(exstyle, WINDOW_CLASS_SPLASH, aTitle, style, xpos, ypos
  621         , main_width, main_height, owned ? (dialog_owner ? dialog_owner : g_hWnd) : NULL // v1.0.35.01: For flexibility, allow these windows to be owned by GUIs via +OwnDialogs.
  622         , NULL, g_hInstance, NULL)))
  623         return FAIL;  // No error msg since so rare.
  624 
  625     if ((style & WS_SYSMENU) || !owned)
  626     {
  627         // Setting the small icon puts it in the upper left corner of the dialog window.
  628         // Setting the big icon makes the dialog show up correctly in the Alt-Tab menu (but big seems to
  629         // have no effect unless the window is unowned, i.e. it has a button on the task bar).
  630         
  631         // L17: Use separate big/small icons for best results.
  632         LPARAM big_icon, small_icon;
  633         if (g_script.mCustomIcon)
  634         {
  635             big_icon = (LPARAM)g_script.mCustomIcon;
  636             small_icon = (LPARAM)g_script.mCustomIconSmall; // Should always be non-NULL when mCustomIcon is non-NULL.
  637         }
  638         else
  639         {
  640             big_icon = (LPARAM)g_IconLarge;
  641             small_icon = (LPARAM)g_IconSmall;
  642         }
  643 
  644         if (style & WS_SYSMENU)
  645             SendMessage(splash.hwnd, WM_SETICON, ICON_SMALL, small_icon);
  646         if (!owned)
  647             SendMessage(splash.hwnd, WM_SETICON, ICON_BIG, big_icon);
  648     }
  649 
  650     // Update client rect in case it was resized due to being too large (above) or in case the OS
  651     // auto-resized it for some reason.  These updated values are also used by SPLASH_CALC_CTRL_WIDTH
  652     // to position the static text controls so that text will be centered properly:
  653     GetClientRect(splash.hwnd, &client_rect);
  654     splash.height = client_rect.bottom;
  655     splash.width = client_rect.right;
  656     int control_width = client_rect.right - (splash.margin_x * 2);
  657 
  658     // CREATE Main label
  659     if (*aMainText)
  660     {
  661         splash.hwnd_text1 = CreateWindowEx(0, _T("static"), aMainText
  662             , WS_CHILD|WS_VISIBLE|SS_NOPREFIX|(centered_main ? SS_CENTER : SS_LEFT)
  663             , PROGRESS_MAIN_POS, splash.hwnd, NULL, g_hInstance, NULL);
  664         SendMessage(splash.hwnd_text1, WM_SETFONT, (WPARAM)(splash.hfont1 ? splash.hfont1 : hfont_default), MAKELPARAM(TRUE, 0));
  665     }
  666 
  667     if (!aSplashImage && splash.object_height > 0) // Progress window
  668     {
  669         // CREATE Progress control (always starts off at its default position as determined by OS/common controls):
  670         if (splash.hwnd_bar = CreateWindowEx(WS_EX_CLIENTEDGE, PROGRESS_CLASS, NULL, WS_CHILD|WS_VISIBLE|PBS_SMOOTH
  671             , PROGRESS_BAR_POS, splash.hwnd, NULL, NULL, NULL))
  672         {
  673             if (range_min || range_max) // i.e. if both are zero, leave it at the default range, which is 0-100.
  674             {
  675                 if (range_min > -1 && range_min < 0x10000 && range_max > -1 && range_max < 0x10000)
  676                     // Since the values fall within the bounds for Win95/NT to support, use the old method
  677                     // in case Win95/NT lacks MSIE 3.0:
  678                     SendMessage(splash.hwnd_bar, PBM_SETRANGE, 0, MAKELPARAM(range_min, range_max));
  679                 else
  680                     SendMessage(splash.hwnd_bar, PBM_SETRANGE32, range_min, range_max);
  681             }
  682 
  683 
  684             if (bar_color != CLR_DEFAULT)
  685             {
  686                 // Remove visual styles so that specified color will be obeyed:
  687                 MySetWindowTheme(splash.hwnd_bar, L"", L"");
  688                 SendMessage(splash.hwnd_bar, PBM_SETBARCOLOR, 0, bar_color); // Set color.
  689             }
  690             if (splash.color_bk != CLR_DEFAULT)
  691                 SendMessage(splash.hwnd_bar, PBM_SETBKCOLOR, 0, splash.color_bk); // Set color.
  692             if (bar_pos_has_been_set) // Note that the window is not yet visible at this stage.
  693                 // This happens when the window doesn't exist and a command such as the following is given:
  694                 // Progress, 50 [, ...].  As of v1.0.25, it also happens via the new 'P' option letter:
  695                 SendMessage(splash.hwnd_bar, PBM_SETPOS, (WPARAM)bar_pos, 0);
  696             else // Ask the control its starting/default position in case a custom range is in effect.
  697                 bar_pos = (int)SendMessage(splash.hwnd_bar, PBM_GETPOS, 0, 0);
  698             splash.bar_pos = bar_pos; // Save the current position to avoid redraws when future positions are identical to current.
  699         }
  700     }
  701 
  702     // CREATE Sub label
  703     if (splash.hwnd_text2 = CreateWindowEx(0, _T("static"), aSubText
  704         , WS_CHILD|WS_VISIBLE|SS_NOPREFIX|(centered_sub ? SS_CENTER : SS_LEFT)
  705         , PROGRESS_SUB_POS, splash.hwnd, NULL, g_hInstance, NULL))
  706         SendMessage(splash.hwnd_text2, WM_SETFONT, (WPARAM)(splash.hfont2 ? splash.hfont2 : hfont_default), MAKELPARAM(TRUE, 0));
  707 
  708     // Show it without activating it.  Even with options that allow the window to be activated (such
  709     // as movable), it seems best to do this to prevent changing the current foreground window, which
  710     // is usually desirable for progress/splash windows since they should be seen but not be disruptive:
  711     if (!initially_hidden)
  712         ShowWindow(splash.hwnd,  SW_SHOWNOACTIVATE);
  713     return OK;
  714 }
  715 
  716 
  717 
  718 ResultType Line::ToolTip(LPTSTR aText, LPTSTR aX, LPTSTR aY, LPTSTR aID)
  719 {
  720     int window_index = *aID ? ATOI(aID) - 1 : 0;
  721     if (window_index < 0 || window_index >= MAX_TOOLTIPS)
  722         return LineError(_T("Max window number is ") MAX_TOOLTIPS_STR _T("."), FAIL, aID);
  723     HWND tip_hwnd = g_hWndToolTip[window_index];
  724 
  725     // Destroy windows except the first (for performance) so that resources/mem are conserved.
  726     // The first window will be hidden by the TTM_UPDATETIPTEXT message if aText is blank.
  727     // UPDATE: For simplicity, destroy even the first in this way, because otherwise a script
  728     // that turns off a non-existent first tooltip window then later turns it on will cause
  729     // the window to appear in an incorrect position.  Example:
  730     // ToolTip
  731     // ToolTip, text, 388, 24
  732     // Sleep, 1000
  733     // ToolTip, text, 388, 24
  734     if (!*aText)
  735     {
  736         if (tip_hwnd && IsWindow(tip_hwnd))
  737             DestroyWindow(tip_hwnd);
  738         g_hWndToolTip[window_index] = NULL;
  739         return OK;
  740     }
  741 
  742     // Use virtual desktop so that tooltip can move onto non-primary monitor in a multi-monitor system:
  743     RECT dtw;
  744     GetVirtualDesktopRect(dtw);
  745 
  746     bool one_or_both_coords_unspecified = !*aX || !*aY;
  747     POINT pt, pt_cursor;
  748     if (one_or_both_coords_unspecified)
  749     {
  750         // Don't call GetCursorPos() unless absolutely needed because it seems to mess
  751         // up double-click timing, at least on XP.  UPDATE: Is isn't GetCursorPos() that's
  752         // interfering with double clicks, so it seems it must be the displaying of the ToolTip
  753         // window itself.
  754         GetCursorPos(&pt_cursor);
  755         pt.x = pt_cursor.x + 16;  // Set default spot to be near the mouse cursor.
  756         pt.y = pt_cursor.y + 16;  // Use 16 to prevent the tooltip from overlapping large cursors.
  757         // Update: Below is no longer needed due to a better fix further down that handles multi-line tooltips.
  758         // 20 seems to be about the right amount to prevent it from "warping" to the top of the screen,
  759         // at least on XP:
  760         //if (pt.y > dtw.bottom - 20)
  761         //  pt.y = dtw.bottom - 20;
  762     }
  763 
  764     POINT origin = {0};
  765     if (*aX || *aY) // Need the offsets.
  766         CoordToScreen(origin, COORD_MODE_TOOLTIP);
  767 
  768     // This will also convert from relative to screen coordinates if appropriate:
  769     if (*aX)
  770         pt.x = ATOI(aX) + origin.x;
  771     if (*aY)
  772         pt.y = ATOI(aY) + origin.y;
  773 
  774     TOOLINFO ti = {0};
  775     ti.cbSize = sizeof(ti) - sizeof(void *); // Fixed for v1.0.36.05: Tooltips fail to work on Windows 2000 unless the size for the *lpReserved member in _WIN32_WINNT 0x0501 is omitted.
  776     ti.uFlags = TTF_TRACK;
  777     ti.lpszText = aText;
  778     // Note that the ToolTip won't work if ti.hwnd is assigned the HWND from GetDesktopWindow().
  779     // All of ti's other members are left at NULL/0, including the following:
  780     //ti.hinst = NULL;
  781     //ti.uId = 0;
  782     //ti.rect.left = ti.rect.top = ti.rect.right = ti.rect.bottom = 0;
  783 
  784     // My: This does more harm that good (it causes the cursor to warp from the right side to the left
  785     // if it gets to close to the right side), so for now, I did a different fix (above) instead:
  786     //ti.rect.bottom = dtw.bottom;
  787     //ti.rect.right = dtw.right;
  788     //ti.rect.top = dtw.top;
  789     //ti.rect.left = dtw.left;
  790 
  791     // No need to use SendMessageTimeout() since the ToolTip() is owned by our own thread, which
  792     // (since we're here) we know is not hung or heavily occupied.
  793 
  794     // v1.0.40.12: Added the IsWindow() check below to recreate the tooltip in cases where it was destroyed
  795     // by external means such as Alt-F4 or WinClose.
  796     if (!tip_hwnd || !IsWindow(tip_hwnd))
  797     {
  798         // This this window has no owner, it won't be automatically destroyed when its owner is.
  799         // Thus, it will be explicitly by the program's exit function.
  800         tip_hwnd = g_hWndToolTip[window_index] = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, TTS_NOPREFIX | TTS_ALWAYSTIP
  801             , CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
  802         SendMessage(tip_hwnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  803         // v1.0.21: GetSystemMetrics(SM_CXSCREEN) is used for the maximum width because even on a
  804         // multi-monitor system, most users would not want a tip window to stretch across multiple monitors:
  805         SendMessage(tip_hwnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)GetSystemMetrics(SM_CXSCREEN));
  806         // Must do these next two when the window is first created, otherwise GetWindowRect() below will retrieve
  807         // a tooltip window size that is quite a bit taller than it winds up being:
  808         SendMessage(tip_hwnd, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x, pt.y));
  809         SendMessage(tip_hwnd, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
  810     }
  811     // Bugfix for v1.0.21: The below is now called unconditionally, even if the above newly created the window.
  812     // If this is not done, the tip window will fail to appear the first time it is invoked, at least when
  813     // all of the following are true:
  814     // 1) Windows XP;
  815     // 2) Common controls v6 (via manifest);
  816     // 3) "Control Panel >> Display >> Effects >> Use transition >> Fade effect" setting is in effect.
  817     SendMessage(tip_hwnd, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  818 
  819     RECT ttw = {0};
  820     GetWindowRect(tip_hwnd, &ttw); // Must be called this late to ensure the tooltip has been created by above.
  821     int tt_width = ttw.right - ttw.left;
  822     int tt_height = ttw.bottom - ttw.top;
  823 
  824     // v1.0.21: Revised for multi-monitor support.  I read somewhere that dtw.left can be negative (perhaps
  825     // if the secondary monitor is to the left of the primary).  So it seems best to assume it is possible:
  826     if (pt.x + tt_width >= dtw.right)
  827         pt.x = dtw.right - tt_width - 1;
  828     if (pt.y + tt_height >= dtw.bottom)
  829         pt.y = dtw.bottom - tt_height - 1;
  830     // It seems best not to have each of the below paired with the above.  This is because it allows
  831     // the flexibility to explicitly move the tooltip above or to the left of the screen.  Such a feat
  832     // should only be possible if done via explicitly passed-in negative coordinates for aX and/or aY.
  833     // In other words, it should be impossible for a tooltip window to follow the mouse cursor somewhere
  834     // off the virtual screen because:
  835     // 1) The mouse cursor is normally trapped within the bounds of the virtual screen.
  836     // 2) The tooltip window defaults to appearing South-East of the cursor.  It can only appear
  837     //    in some other quadrant if jammed against the right or bottom edges of the screen, in which
  838     //    case it can't be partially above or to the left of the virtual screen unless it's really
  839     //    huge, which seems very unlikely given that it's limited to the maximum width of the
  840     //    primary display as set by TTM_SETMAXTIPWIDTH above.
  841     //else if (pt.x < dtw.left) // Should be impossible for this to happen due to mouse being off the screen.
  842     //  pt.x = dtw.left;      // But could happen if user explicitly passed in a coord that was too negative.
  843     //...
  844     //else if (pt.y < dtw.top)
  845     //  pt.y = dtw.top;
  846 
  847     if (one_or_both_coords_unspecified)
  848     {
  849         // Since Tooltip is being shown at the cursor's coordinates, try to ensure that the above
  850         // adjustment doesn't result in the cursor being inside the tooltip's window boundaries,
  851         // since that tends to cause problems such as blocking the tray area (which can make a
  852         // tooltip script impossible to terminate).  Normally, that can only happen in this case
  853         // (one_or_both_coords_unspecified == true) when the cursor is near the bottom-right
  854         // corner of the screen (unless the mouse is moving more quickly than the script's
  855         // ToolTip update-frequency can cope with, but that seems inconsequential since it
  856         // will adjust when the cursor slows down):
  857         ttw.left = pt.x;
  858         ttw.top = pt.y;
  859         ttw.right = ttw.left + tt_width;
  860         ttw.bottom = ttw.top + tt_height;
  861         if (pt_cursor.x >= ttw.left && pt_cursor.x <= ttw.right && pt_cursor.y >= ttw.top && pt_cursor.y <= ttw.bottom)
  862         {
  863             // Push the tool tip to the upper-left side, since normally the only way the cursor can
  864             // be inside its boundaries (when one_or_both_coords_unspecified == true) is when the
  865             // cursor is near the bottom right corner of the screen.
  866             pt.x = pt_cursor.x - tt_width - 3;    // Use a small offset since it can't overlap the cursor
  867             pt.y = pt_cursor.y - tt_height - 3;   // when pushed to the the upper-left side of it.
  868         }
  869     }
  870 
  871     SendMessage(tip_hwnd, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x, pt.y));
  872     // And do a TTM_TRACKACTIVATE even if the tooltip window already existed upon entry to this function,
  873     // so that in case it was hidden or dismissed while its HWND still exists, it will be shown again:
  874     SendMessage(tip_hwnd, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
  875     return OK;
  876 }
  877 
  878 
  879 
  880 ResultType Line::TrayTip(LPTSTR aTitle, LPTSTR aText, LPTSTR aTimeout, LPTSTR aOptions)
  881 {
  882     NOTIFYICONDATA nic = {0};
  883     nic.cbSize = sizeof(nic);
  884     nic.uID = AHK_NOTIFYICON;  // This must match our tray icon's uID or Shell_NotifyIcon() will return failure.
  885     nic.hWnd = g_hWnd;
  886     nic.uFlags = NIF_INFO;
  887     nic.uTimeout = ATOI(aTimeout) * 1000;
  888     nic.dwInfoFlags = ATOI(aOptions);
  889     tcslcpy(nic.szInfoTitle, aTitle, _countof(nic.szInfoTitle)); // Empty title omits the title line entirely.
  890     tcslcpy(nic.szInfo, aText, _countof(nic.szInfo));   // Empty text removes the balloon.
  891     Shell_NotifyIcon(NIM_MODIFY, &nic);
  892     return OK; // i.e. never a critical error if it fails.
  893 }
  894 
  895 
  896 
  897 ResultType Line::Transform(LPTSTR aCmd, LPTSTR aValue1, LPTSTR aValue2)
  898 {
  899     Var &output_var = *OUTPUT_VAR;
  900     TransformCmds trans_cmd = ConvertTransformCmd(aCmd);
  901     // Since command names are validated at load-time, this only happens if the command name
  902     // was contained in a variable reference.  Since that is very rare, output_var is simply
  903     // made blank to indicate the problem:
  904     if (trans_cmd == TRANS_CMD_INVALID)
  905         return output_var.Assign();
  906 
  907     TCHAR buf[32];
  908     int value32;
  909     INT64 value64;
  910     double value_double1, value_double2, multiplier;
  911     double result_double;
  912     SymbolType value1_is_pure_numeric, value2_is_pure_numeric;
  913 
  914     #undef DETERMINE_NUMERIC_TYPES
  915     #define DETERMINE_NUMERIC_TYPES \
  916         value1_is_pure_numeric = IsPureNumeric(aValue1, true, false, true);\
  917         value2_is_pure_numeric = IsPureNumeric(aValue2, true, false, true);
  918 
  919     #define EITHER_IS_FLOAT (value1_is_pure_numeric == PURE_FLOAT || value2_is_pure_numeric == PURE_FLOAT)
  920 
  921     // If neither input is float, the result is assigned as an integer (i.e. no decimal places):
  922     #define ASSIGN_BASED_ON_TYPE \
  923         DETERMINE_NUMERIC_TYPES \
  924         if (EITHER_IS_FLOAT) \
  925             return output_var.Assign(result_double);\
  926         else\
  927             return output_var.Assign((INT64)result_double);
  928 
  929     // Have a negative exponent always cause a floating point result:
  930     #define ASSIGN_BASED_ON_TYPE_POW \
  931         DETERMINE_NUMERIC_TYPES \
  932         if (EITHER_IS_FLOAT || value_double2 < 0) \
  933             return output_var.Assign(result_double);\
  934         else\
  935             return output_var.Assign((INT64)result_double);
  936 
  937     #define ASSIGN_BASED_ON_TYPE_SINGLE \
  938         if (IsPureNumeric(aValue1, true, false, true) == PURE_FLOAT)\
  939             return output_var.Assign(result_double);\
  940         else\
  941             return output_var.Assign((INT64)result_double);
  942 
  943     // If rounding to an integer, ensure the result is stored as an integer:
  944     #define ASSIGN_BASED_ON_TYPE_SINGLE_ROUND \
  945         if (IsPureNumeric(aValue1, true, false, true) == PURE_FLOAT && value32 > 0)\
  946             return output_var.Assign(result_double);\
  947         else\
  948             return output_var.Assign((INT64)result_double);
  949 
  950     switch(trans_cmd)
  951     {
  952     case TRANS_CMD_ASC:
  953         if (*aValue1)
  954             return output_var.Assign(TRANS_CHAR_TO_INT(*aValue1));
  955         else
  956             return output_var.Assign();
  957 
  958     case TRANS_CMD_CHR:
  959         value32 = ATOI(aValue1);
  960         if (value32 < 0 || value32 > TRANS_CHAR_MAX)
  961             return output_var.Assign();
  962         else
  963         {
  964             *buf = value32;  // Store value as a single-character string.
  965             *(buf + 1) = '\0';
  966             return output_var.Assign(buf);
  967         }
  968 
  969     case TRANS_CMD_DEREF:
  970         return Deref(&output_var, aValue1);
  971 
  972 #ifndef UNICODE
  973     case TRANS_CMD_UNICODE:
  974         int char_count;
  975         if (output_var.Type() == VAR_CLIPBOARD)
  976         {
  977             // Since the output var is the clipboard, the mode is autodetected as the following:
  978             // Convert aValue1 from UTF-8 to Unicode and put the result onto the clipboard.
  979             // MSDN: "Windows 95: Under the Microsoft Layer for Unicode, MultiByteToWideChar also
  980             // supports CP_UTF7 and CP_UTF8."
  981             if (   !(char_count = UTF8ToWideChar(aValue1, NULL, 0))   ) // Get required buffer size in WCHARs (includes terminator).
  982                 return output_var.Assign(); // Make output_var (the clipboard in this case) blank to indicate failure.
  983             LPVOID clip_buf;
  984             if (   !(clip_buf = g_clip.PrepareForWrite(char_count * sizeof(WCHAR)))   )
  985                 return output_var.Assign(); // Make output_var (the clipboard in this case) blank to indicate failure.
  986             // Perform the conversion:
  987             if (!UTF8ToWideChar(aValue1, (LPWSTR)clip_buf, char_count))
  988             {
  989                 g_clip.AbortWrite();
  990                 return output_var.Assign(); // Make clipboard blank to indicate failure.
  991             }
  992             return g_clip.Commit(CF_UNICODETEXT); // Save as type Unicode. It will display any error that occurs.
  993         }
  994         // Otherwise, going in the reverse direction: convert the clipboard contents to UTF-8 and put
  995         // the result into a normal variable.
  996         if (!IsClipboardFormatAvailable(CF_UNICODETEXT) || !g_clip.Open()) // Relies on short-circuit boolean order.
  997             return output_var.Assign(); // Make the (non-clipboard) output_var blank to indicate failure.
  998         if (   !(g_clip.mClipMemNow = g_clip.GetClipboardDataTimeout(CF_UNICODETEXT)) // Relies on short-circuit boolean order.
  999             || !(g_clip.mClipMemNowLocked = (LPTSTR)GlobalLock(g_clip.mClipMemNow))
 1000             || !(char_count = WideCharToUTF8((LPCWSTR)g_clip.mClipMemNowLocked, NULL, 0))   ) // char_count includes terminator.
 1001         {
 1002             // Above finds out how large the contents will be when converted to UTF-8.
 1003             // In this case, it failed to determine the count, perhaps due to Win95 lacking Unicode layer, etc.
 1004             g_clip.Close();
 1005             return output_var.Assign(); // Make the (non-clipboard) output_var blank to indicate failure.
 1006         }
 1007         // Otherwise, it found the count.  Set up the output variable, enlarging it if needed:
 1008         if (output_var.AssignString(NULL, char_count - 1) != OK) // Don't combine this with the above or below it can return FAIL.
 1009         {
 1010             g_clip.Close();
 1011             return FAIL;  // It already displayed the error.
 1012         }
 1013         // Perform the conversion:
 1014         char_count = WideCharToUTF8((LPCWSTR)g_clip.mClipMemNowLocked, output_var.Contents(), char_count);
 1015         g_clip.Close(); // Close the clipboard and free the memory.
 1016         output_var.Close(); // Length() was already set properly by Assign() above. Currently it can't be VAR_CLIPBOARD since that would auto-detect as the reverse direction.
 1017         if (!char_count)
 1018             return output_var.Assign(); // Make non-clipboard output_var blank to indicate failure.
 1019         return OK;
 1020 #endif
 1021 
 1022     case TRANS_CMD_HTML:
 1023     {
 1024         // These are the encoding-neutral translations for ASC 128 through 255 as shown by Dreamweaver.
 1025         // It's possible that using just the &#number convention (e.g. &#128 through &#255;) would be
 1026         // more appropriate for some users, but that mode can be added in the future if it is ever
 1027         // needed (by passing a mode setting for aValue2):
 1028         // €‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿
 1029         // ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
 1030         static const LPTSTR sHtml[] = { // v1.0.40.02: Removed leading '&' and trailing ';' to reduce code size.
 1031 #ifndef UNICODE
 1032               _T("euro"), _T("#129"), _T("sbquo"), _T("fnof"), _T("bdquo"), _T("hellip"), _T("dagger"), _T("Dagger")
 1033             , _T("circ"), _T("permil"), _T("Scaron"), _T("lsaquo"), _T("OElig"), _T("#141"), _T("#381"), _T("#143")
 1034             , _T("#144"), _T("lsquo"), _T("rsquo"), _T("ldquo"), _T("rdquo"), _T("bull"), _T("ndash"), _T("mdash")
 1035             , _T("tilde"), _T("trade"), _T("scaron"), _T("rsaquo"), _T("oelig"), _T("#157"), _T("#382"), _T("Yuml")
 1036             ,
 1037 #endif
 1038               _T("nbsp"), _T("iexcl"), _T("cent"), _T("pound"), _T("curren"), _T("yen"), _T("brvbar"), _T("sect")
 1039             , _T("uml"), _T("copy"), _T("ordf"), _T("laquo"), _T("not"), _T("shy"), _T("reg"), _T("macr")
 1040             , _T("deg"), _T("plusmn"), _T("sup2"), _T("sup3"), _T("acute"), _T("micro"), _T("para"), _T("middot")
 1041             , _T("cedil"), _T("sup1"), _T("ordm"), _T("raquo"), _T("frac14"), _T("frac12"), _T("frac34"), _T("iquest")
 1042             , _T("Agrave"), _T("Aacute"), _T("Acirc"), _T("Atilde"), _T("Auml"), _T("Aring"), _T("AElig"), _T("Ccedil")
 1043             , _T("Egrave"), _T("Eacute"), _T("Ecirc"), _T("Euml"), _T("Igrave"), _T("Iacute"), _T("Icirc"), _T("Iuml")
 1044             , _T("ETH"), _T("Ntilde"), _T("Ograve"), _T("Oacute"), _T("Ocirc"), _T("Otilde"), _T("Ouml"), _T("times")
 1045             , _T("Oslash"), _T("Ugrave"), _T("Uacute"), _T("Ucirc"), _T("Uuml"), _T("Yacute"), _T("THORN"), _T("szlig")
 1046             , _T("agrave"), _T("aacute"), _T("acirc"), _T("atilde"), _T("auml"), _T("aring"), _T("aelig"), _T("ccedil")
 1047             , _T("egrave"), _T("eacute"), _T("ecirc"), _T("euml"), _T("igrave"), _T("iacute"), _T("icirc"), _T("iuml")
 1048             , _T("eth"), _T("ntilde"), _T("ograve"), _T("oacute"), _T("ocirc"), _T("otilde"), _T("ouml"), _T("divide")
 1049             , _T("oslash"), _T("ugrave"), _T("uacute"), _T("ucirc"), _T("uuml"), _T("yacute"), _T("thorn"), _T("yuml")
 1050         };
 1051 #ifdef UNICODE
 1052         #define TRANS_HTML_NAMED        0x00000001
 1053         #define TRANS_HTML_NUMBERED     0x00000002
 1054         DWORD aFlags = *aValue2 ? ATOI(aValue2) : TRANS_HTML_NAMED;
 1055 #endif
 1056 
 1057         // Determine how long the result string will be so that the output variable can be expanded
 1058         // to handle it:
 1059         VarSizeType length;
 1060         TBYTE *ucp;
 1061         for (length = 0, ucp = (TBYTE *)aValue1; *ucp; ++ucp)
 1062         {
 1063             switch(*ucp)
 1064             {
 1065             case '"':  // &quot;
 1066                 length += 6;
 1067                 break;
 1068             case '&': // &amp;
 1069             case '\n': // <br>\n
 1070                 length += 5;
 1071                 break; // v1.0.45: Added missing break.  This had caused incorrect lengths inside some variables, which led to problems in places that relied on the accuracy of the internal lengths.
 1072             case '<': // &lt;
 1073             case '>': // &gt;
 1074                 length += 4;
 1075                 break; // v1.0.45: Added missing break.
 1076             default:
 1077 #ifdef UNICODE
 1078                 if (*ucp >= 0x80) {
 1079                     if (aFlags & TRANS_HTML_NAMED) {
 1080                         switch (*ucp) {
 1081                             case 0x0178: // &Yuml;
 1082                             case 0x0192: // &fnof;
 1083                             case 0x02C6: // &circ;
 1084                             case 0x2022: // &bull;
 1085                             case 0x20AC: // &euro;
 1086                                 length += 6;
 1087                                 goto end_get_length;
 1088                             case 0x0152: // &OElig;
 1089                             case 0x0153: // &oelig;
 1090                             case 0x02DC: // &tilde;
 1091                             case 0x2013: // &ndash;
 1092                             case 0x2014: // &mdash;
 1093                             case 0x2018: // &lsquo;
 1094                             case 0x2019: // &rsquo;
 1095                             case 0x201C: // &ldquo;
 1096                             case 0x201D: // &rdquo;
 1097                             case 0x201E: // &bdquo;
 1098                             case 0x2122: // &trade;
 1099                                 length += 7;
 1100                                 goto end_get_length;
 1101                             case 0x0160: // &Scaron;
 1102                             case 0x0161: // &scaron;
 1103                             case 0x2020: // &dagger;
 1104                             case 0x2021: // &Dagger;
 1105                             case 0x2026: // &hellip;
 1106                             case 0x2030: // &permil;
 1107                             case 0x2039: // &lsaquo;
 1108                             case 0x203A: // &rsaquo;
 1109                                 length += 8;
 1110                                 goto end_get_length;
 1111                             default:
 1112                                 if (*ucp >= 0xA0 && *ucp <= 0xFF) {
 1113                                     length += (VarSizeType)_tcslen(sHtml[*ucp - 0xA0]) + 2; // +2 for the leading '&' and the trailing ';'.
 1114                                     goto end_get_length;
 1115                                 }
 1116                                 // else handled by the following
 1117                                 break;
 1118                         }
 1119                     }
 1120                     if (aFlags & TRANS_HTML_NUMBERED)
 1121                         length += ((int) qmathLog10(*ucp)) + 4; // &#NNN;
 1122                     else
 1123                         ++length;
 1124 end_get_length:
 1125                     ; // prevents compilation error
 1126                 }
 1127 #else
 1128                 if (*ucp >= 0x80)
 1129                     length += (VarSizeType)_tcslen(sHtml[*ucp - 0x80]) + 2; // +2 for the leading '&' and the trailing ';'.
 1130 #endif
 1131                 else
 1132                     ++length;
 1133             }
 1134         }
 1135 
 1136         // Set up the var, enlarging it if necessary.  If the output_var is of type VAR_CLIPBOARD,
 1137         // this call will set up the clipboard for writing:
 1138         if (output_var.AssignString(NULL, length) != OK)
 1139             return FAIL;  // It already displayed the error.
 1140         LPTSTR contents = output_var.Contents();  // For performance and tracking.
 1141 
 1142         // Translate the text to HTML:
 1143         for (ucp = (TBYTE *)aValue1; *ucp; ++ucp)
 1144         {
 1145             #define SET_HTML_ENTITY(s) { _tcscpy(contents, _T(s)); contents += (_countof(s) - 1); }
 1146             switch(*ucp)
 1147             {
 1148             case '"':  // &quot;
 1149                 SET_HTML_ENTITY("&quot;");
 1150                 break;
 1151             case '&': // &amp;
 1152                 SET_HTML_ENTITY("&amp;");
 1153                 break;
 1154             case '\n': // <br>\n
 1155                 SET_HTML_ENTITY("<br>\n");
 1156                 break;
 1157             case '<': // &lt;
 1158                 SET_HTML_ENTITY("&lt;");
 1159                 break;
 1160             case '>': // &gt;
 1161                 SET_HTML_ENTITY("&gt;");
 1162                 break;
 1163             default:
 1164                 if (*ucp >= 0x80)
 1165                 {
 1166 #ifdef UNICODE
 1167                     if (aFlags & TRANS_HTML_NAMED) {
 1168                         switch (*ucp) {
 1169                             case 0x0152:
 1170                                 SET_HTML_ENTITY("&OElig;");
 1171                                 goto end_set_entity;
 1172                             case 0x0153:
 1173                                 SET_HTML_ENTITY("&oelig;");
 1174                                 goto end_set_entity;
 1175                             case 0x0160:
 1176                                 SET_HTML_ENTITY("&Scaron;");
 1177                                 goto end_set_entity;
 1178                             case 0x0161:
 1179                                 SET_HTML_ENTITY("&scaron;");
 1180                                 goto end_set_entity;
 1181                             case 0x0178:
 1182                                 SET_HTML_ENTITY("&Yuml;");
 1183                                 goto end_set_entity;
 1184                             case 0x0192:
 1185                                 SET_HTML_ENTITY("&fnof;");
 1186                                 goto end_set_entity;
 1187                             case 0x02C6:
 1188                                 SET_HTML_ENTITY("&circ;");
 1189                                 goto end_set_entity;
 1190                             case 0x02DC:
 1191                                 SET_HTML_ENTITY("&tilde;");
 1192                                 goto end_set_entity;
 1193                             case 0x2013:
 1194                                 SET_HTML_ENTITY("&ndash;");
 1195                                 goto end_set_entity;
 1196                             case 0x2014:
 1197                                 SET_HTML_ENTITY("&mdash;");
 1198                                 goto end_set_entity;
 1199                             case 0x2018:
 1200                                 SET_HTML_ENTITY("&lsquo;");
 1201                                 goto end_set_entity;
 1202                             case 0x2019:
 1203                                 SET_HTML_ENTITY("&rsquo;");
 1204                                 goto end_set_entity;
 1205                             case 0x201A:
 1206                                 SET_HTML_ENTITY("&sbquo;");
 1207                                 goto end_set_entity;
 1208                             case 0x201C:
 1209                                 SET_HTML_ENTITY("&ldquo;");
 1210                                 goto end_set_entity;
 1211                             case 0x201D:
 1212                                 SET_HTML_ENTITY("&rdquo;");
 1213                                 goto end_set_entity;
 1214                             case 0x201E:
 1215                                 SET_HTML_ENTITY("&bdquo;");
 1216                                 goto end_set_entity;
 1217                             case 0x2020:
 1218                                 SET_HTML_ENTITY("&dagger;");
 1219                                 goto end_set_entity;
 1220                             case 0x2021:
 1221                                 SET_HTML_ENTITY("&Dagger;");
 1222                                 goto end_set_entity;
 1223                             case 0x2022:
 1224                                 SET_HTML_ENTITY("&bull;");
 1225                                 goto end_set_entity;
 1226                             case 0x2026:
 1227                                 SET_HTML_ENTITY("&hellip;");
 1228                                 goto end_set_entity;
 1229                             case 0x2030:
 1230                                 SET_HTML_ENTITY("&permil;");
 1231                                 goto end_set_entity;
 1232                             case 0x2039:
 1233                                 SET_HTML_ENTITY("&lsaquo;");
 1234                                 goto end_set_entity;
 1235                             case 0x203A:
 1236                                 SET_HTML_ENTITY("&rsaquo;");
 1237                                 goto end_set_entity;
 1238                             case 0x20AC:
 1239                                 SET_HTML_ENTITY("&euro;");
 1240                                 goto end_set_entity;
 1241                             case 0x2122:
 1242                                 SET_HTML_ENTITY("&trade;");
 1243                                 goto end_set_entity;
 1244                             default:
 1245                                 if (*ucp >= 0xA0 && *ucp <= 0xFF)
 1246                                 {
 1247                                     *contents++ = '&';
 1248                                     _tcscpy(contents, sHtml[*ucp - 0xA0]);
 1249                                     contents += _tcslen(contents);
 1250                                     *contents++ = ';';
 1251                                     goto end_set_entity;
 1252                                 }
 1253                                 // else handled by the following
 1254                                 break;
 1255                         } // switch (*ucp)
 1256                     } // if (aFlags & TRANS_HTML_NAMED)
 1257                     if (aFlags & TRANS_HTML_NUMBERED)
 1258                         contents += _stprintf(contents, _T("&#%d;"), (int) *ucp);
 1259                     else
 1260                         *contents++ = *ucp;
 1261 end_set_entity:
 1262                     ; // prevents compilation error
 1263 #else
 1264                     *contents++ = '&'; // v1.0.40.02
 1265                     _tcscpy(contents, sHtml[*ucp - 0x80]);
 1266                     contents += _tcslen(contents); // Added as a fix in v1.0.41 (broken in v1.0.40.02).
 1267                     *contents++ = ';'; // v1.0.40.02
 1268 #endif
 1269                 }
 1270                 else
 1271                     *contents++ = *ucp;
 1272             }
 1273             #undef SET_HTML_ENTITY
 1274         }
 1275         *contents = '\0';  // Terminate the string.
 1276         return output_var.Close(); // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
 1277     }
 1278 
 1279     case TRANS_CMD_MOD:
 1280         if (   !(value_double2 = ATOF(aValue2))   ) // Divide by zero, set it to be blank to indicate the problem.
 1281             return output_var.Assign();
 1282         // Otherwise:
 1283         result_double = qmathFmod(ATOF(aValue1), value_double2);
 1284         ASSIGN_BASED_ON_TYPE
 1285 
 1286     case TRANS_CMD_POW:
 1287     {
 1288         // v1.0.44.11: With Laszlo's help, negative bases are now supported as long as the exponent is not fractional.
 1289         // See SYM_POWER in script_expression.cpp for similar code and more comments.
 1290         value_double1 = ATOF(aValue1);
 1291         value_double2 = ATOF(aValue2);
 1292         bool value1_was_negative = (value_double1 < 0);
 1293         if (value_double1 == 0.0 && value_double2 < 0  // In essence, this is divide-by-zero.
 1294             || value1_was_negative && qmathFmod(value_double2, 1.0) != 0.0) // Negative base but exponent isn't close enough to being an integer: unsupported (to simplify code).
 1295             return output_var.Assign();  // Return a consistent result (blank) rather than something that varies.
 1296         // Otherwise:
 1297         if (value1_was_negative)
 1298             value_double1 = -value_double1; // Force a positive due to the limitations of qmathPow().
 1299         result_double = qmathPow(value_double1, value_double2);
 1300         if (value1_was_negative && qmathFabs(qmathFmod(value_double2, 2.0)) == 1.0) // Negative base and exactly-odd exponent (otherwise, it can only be zero or even because if not it would have returned higher above).
 1301             result_double = -result_double;
 1302         ASSIGN_BASED_ON_TYPE_POW
 1303     }
 1304 
 1305     case TRANS_CMD_EXP:
 1306         return output_var.Assign(qmathExp(ATOF(aValue1)));
 1307 
 1308     case TRANS_CMD_SQRT:
 1309         value_double1 = ATOF(aValue1);
 1310         if (value_double1 < 0)
 1311             return output_var.Assign();
 1312         return output_var.Assign(qmathSqrt(value_double1));
 1313 
 1314     case TRANS_CMD_LOG:
 1315         value_double1 = ATOF(aValue1);
 1316         if (value_double1 < 0)
 1317             return output_var.Assign();
 1318         return output_var.Assign(qmathLog10(ATOF(aValue1)));
 1319 
 1320     case TRANS_CMD_LN:
 1321         value_double1 = ATOF(aValue1);
 1322         if (value_double1 < 0)
 1323             return output_var.Assign();
 1324         return output_var.Assign(qmathLog(ATOF(aValue1)));
 1325 
 1326     case TRANS_CMD_ROUND:
 1327         // In the future, a string conversion algorithm might be better to avoid the loss
 1328         // of 64-bit integer precision that it currently caused by the use of doubles in
 1329         // the calculation:
 1330         value32 = ATOI(aValue2);
 1331         multiplier = *aValue2 ? qmathPow(10, value32) : 1;
 1332         value_double1 = ATOF(aValue1);
 1333         result_double = (value_double1 >= 0.0 ? qmathFloor(value_double1 * multiplier + 0.5)
 1334             : qmathCeil(value_double1 * multiplier - 0.5)) / multiplier;
 1335         ASSIGN_BASED_ON_TYPE_SINGLE_ROUND
 1336 
 1337     case TRANS_CMD_CEIL:
 1338     case TRANS_CMD_FLOOR:
 1339         // The code here is similar to that in BIF_FloorCeil(), so maintain them together.
 1340         result_double = ATOF(aValue1);
 1341         result_double = (trans_cmd == TRANS_CMD_FLOOR) ? qmathFloor(result_double) : qmathCeil(result_double);
 1342         return output_var.Assign((__int64)(result_double + (result_double > 0 ? 0.2 : -0.2))); // Fixed for v1.0.40.05: See comments in BIF_FloorCeil() for details.
 1343 
 1344     case TRANS_CMD_ABS:
 1345     {
 1346         // Seems better to convert as string to avoid loss of 64-bit integer precision
 1347         // that would be caused by conversion to double.  I think this will work even
 1348         // for negative hex numbers that are close to the 64-bit limit since they too have
 1349         // a minus sign when generated by the script (e.g. -0x1).
 1350         //result_double = qmathFabs(ATOF(aValue1));
 1351         //ASSIGN_BASED_ON_TYPE_SINGLE
 1352         LPTSTR cp = omit_leading_whitespace(aValue1); // i.e. caller doesn't have to have ltrimmed it.
 1353         if (*cp == '-')
 1354             return output_var.Assign(cp + 1);  // Omit the first minus sign (simple conversion only).
 1355         // Otherwise, no minus sign, so just omit the leading whitespace for consistency:
 1356         return output_var.Assign(cp);
 1357     }
 1358 
 1359     case TRANS_CMD_SIN:
 1360         return output_var.Assign(qmathSin(ATOF(aValue1)));
 1361 
 1362     case TRANS_CMD_COS:
 1363         return output_var.Assign(qmathCos(ATOF(aValue1)));
 1364 
 1365     case TRANS_CMD_TAN:
 1366         return output_var.Assign(qmathTan(ATOF(aValue1)));
 1367 
 1368     case TRANS_CMD_ASIN:
 1369         value_double1 = ATOF(aValue1);
 1370         if (value_double1 > 1 || value_double1 < -1)
 1371             return output_var.Assign(); // ASin and ACos aren't defined for other values.
 1372         return output_var.Assign(qmathAsin(ATOF(aValue1)));
 1373 
 1374     case TRANS_CMD_ACOS:
 1375         value_double1 = ATOF(aValue1);
 1376         if (value_double1 > 1 || value_double1 < -1)
 1377             return output_var.Assign(); // ASin and ACos aren't defined for other values.
 1378         return output_var.Assign(qmathAcos(ATOF(aValue1)));
 1379 
 1380     case TRANS_CMD_ATAN:
 1381         return output_var.Assign(qmathAtan(ATOF(aValue1)));
 1382 
 1383     // For all of the below bitwise operations:
 1384     // Seems better to convert to signed rather than unsigned so that signed values can
 1385     // be supported.  i.e. it seems better to trade one bit in capacity in order to support
 1386     // negative numbers.  Another reason is that commands such as IfEquals use ATOI64 (signed),
 1387     // so if we were to produce unsigned 64 bit values here, they would be somewhat incompatible
 1388     // with other script operations.
 1389     case TRANS_CMD_BITAND:
 1390         return output_var.Assign(ATOI64(aValue1) & ATOI64(aValue2));
 1391 
 1392     case TRANS_CMD_BITOR:
 1393         return output_var.Assign(ATOI64(aValue1) | ATOI64(aValue2));
 1394 
 1395     case TRANS_CMD_BITXOR:
 1396         return output_var.Assign(ATOI64(aValue1) ^ ATOI64(aValue2));
 1397 
 1398     case TRANS_CMD_BITNOT:
 1399         value64 = ATOI64(aValue1);
 1400         if (value64 < 0 || value64 > UINT_MAX)
 1401             // Treat it as a 64-bit signed value, since no other aspects of the program
 1402             // (e.g. IfEqual) will recognize an unsigned 64 bit number.
 1403             return output_var.Assign(~value64);
 1404         else
 1405             // Treat it as a 32-bit unsigned value when inverting and assigning.  This is
 1406             // because assigning it as a signed value would "convert" it into a 64-bit
 1407             // value, which in turn is caused by the fact that the script sees all negative
 1408             // numbers as 64-bit values (e.g. -1 is 0xFFFFFFFFFFFFFFFF).
 1409             return output_var.Assign(~(DWORD)value64);
 1410 
 1411     case TRANS_CMD_BITSHIFTLEFT:  // Equivalent to multiplying by 2^value2
 1412         return output_var.Assign(ATOI64(aValue1) << ATOI(aValue2));
 1413 
 1414     case TRANS_CMD_BITSHIFTRIGHT:  // Equivalent to dividing (integer) by 2^value2
 1415         return output_var.Assign(ATOI64(aValue1) >> ATOI(aValue2));
 1416     }
 1417 
 1418     return FAIL;  // Never executed (increases maintainability and avoids compiler warning).
 1419 }
 1420 
 1421 
 1422 
 1423 ResultType Line::Input()
 1424 // OVERVIEW:
 1425 // Although a script can have many concurrent quasi-threads, there can only be one input
 1426 // at a time.  Thus, if an input is ongoing and a new thread starts, and it begins its
 1427 // own input, that input should terminate the prior input prior to beginning the new one.
 1428 // In a "worst case" scenario, each interrupted quasi-thread could have its own
 1429 // input, which is in turn terminated by the thread that interrupts it.
 1430 {
 1431     auto *prior_input = InputFind(NULL); // Not g_input, which could belong to an object and should not be ended.
 1432 
 1433     // Since other script threads can interrupt this command while it's running, it's important that
 1434     // this command not refer to sArgDeref[] and sArgVar[] anytime after an interruption becomes possible.
 1435     // This is because an interrupting thread usually changes the values to something inappropriate for this thread.
 1436     Var *output_var = OUTPUT_VAR; // See comment above.
 1437     if (!output_var)
 1438     {
 1439         // No output variable, which due to load-time validation means there are no other args either.
 1440         // This means that the user is specifically canceling the prior input (if any).  Thus, our
 1441         // ErrorLevel here is set to 1 or 0, but the prior input's ErrorLevel will be set to "NewInput"
 1442         // when its quasi-thread is resumed:
 1443         if (prior_input)
 1444             prior_input->EndByNewInput();
 1445         return SetErrorLevelOrThrowBool(prior_input == NULL);
 1446         // Above: It's considered an "error" of sorts when there is no prior input to terminate.
 1447     }
 1448 
 1449     // Below are done directly this way rather than passed in as args mainly to emphasize that
 1450     // ArgLength() can safely be called in Line methods like this one (which is done further below).
 1451     // It also may also slightly improve performance and reduce code size.
 1452     LPTSTR aOptions = ARG2, aEndKeys = ARG3, aMatchList = ARG4;
 1453     // The aEndKeys string must be modifiable (not constant), since for performance reasons,
 1454     // it's allowed to be temporarily altered by this function.
 1455     size_t aMatchList_length = ArgLength(4); // Performs better than _tcslen(aMatchList);
 1456     
 1457     input_type input;
 1458     input.VisibleNonText = false; // Override InputHook default.
 1459     input.BufferLengthMax = INPUT_BUFFER_SIZE - 1;
 1460     if (!input.Setup(aOptions, aEndKeys, aMatchList, aMatchList_length))
 1461         return FAIL;
 1462     // Only now is it safe to do things which might cause interruption (see comments above).
 1463 
 1464     if (prior_input)
 1465         prior_input->EndByNewInput();
 1466 
 1467     ResultType input_result = InputStart(input, output_var);
 1468     // Ensure input is not present in the input chain, since its life time is about to end.
 1469     input_type *result = InputRelease(&input);
 1470     ASSERT(result == NULL);
 1471     
 1472     return input_result;
 1473 }
 1474 
 1475 
 1476 ResultType input_type::Setup(LPTSTR aOptions, LPTSTR aEndKeys, LPTSTR aMatchList, size_t aMatchList_length)
 1477 {
 1478     ParseOptions(aOptions);
 1479     if (!SetKeyFlags(aEndKeys))
 1480         return FAIL;
 1481     if (!SetMatchList(aMatchList, aMatchList_length))
 1482         return FAIL;
 1483     
 1484     // For maintainability/simplicity/code size, it's allocated even if BufferLengthMax == 0.
 1485     if (  !(Buffer = tmalloc(BufferLengthMax + 1))  )
 1486         return g_script.ScriptError(ERR_OUTOFMEM);
 1487     *Buffer = '\0';
 1488 
 1489     return OK;
 1490 }
 1491 
 1492 
 1493 ResultType InputStart(input_type &input, Var *output_var)
 1494 {
 1495     ASSERT(!input.InProgress());
 1496 
 1497     // Keep the object alive while it is active, even if the script discards it.
 1498     // The corresponding Release() is done when g_input is reset by InputRelease().
 1499     if (input.ScriptObject)
 1500         input.ScriptObject->AddRef();
 1501     
 1502     // Set or update the timeout timer if needed.  The timer proc takes care to end
 1503     // only those inputs which are due, and will reset or kill the timer as needed.
 1504     if (input.Timeout > 0)
 1505         input.SetTimeoutTimer();
 1506 
 1507     input.Prev = g_input;
 1508     input.Start();
 1509     g_input = &input; // Signal the hook to start the input.
 1510 
 1511     // Make script persistent.  This is mostly for backward compatibility because it is documented behavior.
 1512     // even though as of v1.0.42.03, the keyboard hook does not become permanent (which allows a subsequent
 1513     // use of the commands Suspend/Hotkey to deinstall it, which seems to add flexibility/benefit).
 1514     if (output_var)
 1515         g_persistent = true;
 1516     Hotkey::InstallKeybdHook(); // Install the hook (if needed).
 1517 
 1518     if (!output_var)
 1519         return OK;
 1520     return InputWait(output_var, input);
 1521 }
 1522 
 1523 
 1524 void input_type::ParseOptions(LPTSTR aOptions)
 1525 {
 1526     for (LPTSTR cp = aOptions; *cp; ++cp)
 1527     {
 1528         switch(ctoupper(*cp))
 1529         {
 1530         case 'B':
 1531             BackspaceIsUndo = false;
 1532             break;
 1533         case 'C':
 1534             CaseSensitive = true;
 1535             break;
 1536         case 'I':
 1537             MinSendLevel = (cp[1] <= '9' && cp[1] >= '0') ? (SendLevelType)_ttoi(cp + 1) : 1;
 1538             break;
 1539         case 'M':
 1540             TranscribeModifiedKeys = true;
 1541             break;
 1542         case 'L':
 1543             // Use atoi() vs. ATOI() to avoid interpreting something like 0x01C as hex
 1544             // when in fact the C was meant to be an option letter:
 1545             BufferLengthMax = _ttoi(cp + 1);
 1546             if (BufferLengthMax < 0)
 1547                 BufferLengthMax = 0;
 1548             break;
 1549         case 'T':
 1550             // Although ATOF() supports hex, it's been documented in the help file that hex should
 1551             // not be used (see comment above) so if someone does it anyway, some option letters
 1552             // might be misinterpreted:
 1553             Timeout = (int)(ATOF(cp + 1) * 1000);
 1554             break;
 1555         case 'V':
 1556             VisibleText = true;
 1557             VisibleNonText = true;
 1558             break;
 1559         case '*':
 1560             FindAnywhere = true;
 1561             break;
 1562         case 'E':
 1563             // Interpret single-character keys as characters rather than converting them to VK codes.
 1564             // This tends to work better when using multiple keyboard layouts, but changes behaviour:
 1565             // for instance, an end char of "." cannot be triggered while holding Alt.
 1566             EndCharMode = true;
 1567             break;
 1568         }
 1569     }
 1570 }
 1571 
 1572 
 1573 void input_type::SetTimeoutTimer()
 1574 {
 1575     DWORD now = GetTickCount();
 1576     TimeoutAt = now + Timeout;
 1577     if (!g_InputTimerExists || Timeout < int(g_InputTimeoutAt - now))
 1578         SET_INPUT_TIMER(Timeout, TimeoutAt)
 1579 }
 1580 
 1581 
 1582 ResultType input_type::SetKeyFlags(LPTSTR aKeys, bool aEndKeyMode, UCHAR aFlagsRemove, UCHAR aFlagsAdd)
 1583 {
 1584     bool vk_by_number, sc_by_number;
 1585     vk_type vk;
 1586     sc_type sc = 0;
 1587     modLR_type modifiersLR;
 1588     size_t key_text_length;
 1589     UINT single_char_count = 0;
 1590     TCHAR *end_pos, single_char_string[2];
 1591     single_char_string[1] = '\0'; // Init its second character once, since the loop only changes the first char.
 1592     
 1593     const bool endchar_mode = aEndKeyMode && EndCharMode;
 1594     UCHAR * const end_vk = KeyVK;
 1595     UCHAR * const end_sc = KeySC;
 1596 
 1597     for (TCHAR *end_key = aKeys; *end_key; ++end_key) // This a modified version of the processing loop used in SendKeys().
 1598     {
 1599         vk = 0; // Set default.  Not strictly necessary but more maintainable.
 1600         *single_char_string = '\0';  // Set default as "this key name is not a single-char string".
 1601 
 1602         switch (*end_key)
 1603         {
 1604         case '}': continue;  // Important that these be ignored.
 1605         case '{':
 1606         {
 1607             if (   !(end_pos = _tcschr(end_key + 1, '}'))   )
 1608                 continue;  // Do nothing, just ignore the unclosed '{' and continue.
 1609             if (   !(key_text_length = end_pos - end_key - 1)   )
 1610             {
 1611                 if (end_pos[1] == '}') // The string "{}}" has been encountered, which is interpreted as a single "}".
 1612                 {
 1613                     ++end_pos;
 1614                     key_text_length = 1;
 1615                 }
 1616                 else // Empty braces {} were encountered.
 1617                     continue;  // do nothing: let it proceed to the }, which will then be ignored.
 1618             }
 1619             if (key_text_length == 1) // A single-char key name, such as {.} or {{}.
 1620             {
 1621                 if (endchar_mode) // Handle this single-char key name by char code, not by VK.
 1622                 {
 1623                     // Although it might be sometimes useful to treat "x" as a character and "{x}" as a key,
 1624                     // "{{}" and "{}}" can't be included without the extra braces.  {vkNN} can still be used
 1625                     // to handle the key by VK instead of by character.
 1626                     single_char_count++;
 1627                     continue; // It will be processed by another section.
 1628                 }
 1629                 *single_char_string = end_key[1]; // Only used when vk != 0.
 1630             }
 1631 
 1632             *end_pos = '\0';  // temporarily terminate the string here.
 1633 
 1634             sc_by_number = false; // Set default.
 1635             modifiersLR = 0;  // Init prior to below.
 1636             // For backward compatibility, the Input command handles all keys by VK if
 1637             // one is returned by TextToVK().  Although this behaviour seems like a bug,
 1638             // changing it would require changing the way ErrorLevel is determined (so
 1639             // that the correct name is returned for the primary SC of any key), which
 1640             // carries a larger risk of breaking scripts.
 1641             // Also handle the key by VK if it was given by number, such as {vk26}.
 1642             // Otherwise, for any key name which has a VK shared by two possible SCs
 1643             // (such as Up and NumpadUp), handle it by SC so it's identified correctly.
 1644             if (vk = TextToVK(end_key + 1, &modifiersLR, true))
 1645             {
 1646                 vk_by_number = ctoupper(end_key[1]) == 'V' && ctoupper(end_key[2]) == 'K';
 1647                 if (ScriptObject && !vk_by_number && (sc = vk_to_sc(vk, true)))
 1648                 {
 1649                     sc ^= 0x100; // Convert sc to the primary scan code, which is the one named by end_key.
 1650                     vk = 0; // Handle it only by SC.
 1651                 }
 1652             }
 1653             else
 1654                 // No virtual key, so try to find a scan code.
 1655                 sc = TextToSC(end_key + 1, &sc_by_number);
 1656 
 1657             *end_pos = '}';  // undo the temporary termination
 1658 
 1659             end_key = end_pos;  // In prep for ++end_key at the top of the loop.
 1660             break; // Break out of the switch() and do the vk handling beneath it (if there is a vk).
 1661         }
 1662 
 1663         default:
 1664             if (endchar_mode)
 1665             {
 1666                 single_char_count++;
 1667                 continue; // It will be processed by another section.
 1668             }
 1669             *single_char_string = *end_key;
 1670             modifiersLR = 0;  // Init prior to below.
 1671             vk = TextToVK(single_char_string, &modifiersLR, true);
 1672         } // switch()
 1673 
 1674         if (vk) // A valid virtual key code was discovered above.
 1675         {
 1676             // Insist the shift key be down to form genuinely different symbols --
 1677             // namely punctuation marks -- but not for alphabetic chars.
 1678             if (*single_char_string && aEndKeyMode && !IsCharAlpha(*single_char_string)) // v1.0.46.05: Added check for "*single_char_string" so that non-single-char strings like {F9} work as end keys even when the Shift key is being held down (this fixes the behavior to be like it was in pre-v1.0.45).
 1679             {
 1680                 // Now we know it's not alphabetic, and it's not a key whose name
 1681                 // is longer than one char such as a function key or numpad number.
 1682                 // That leaves mostly just the number keys (top row) and all
 1683                 // punctuation chars, which are the ones that we want to be
 1684                 // distinguished between shifted and unshifted:
 1685                 if (modifiersLR & (MOD_LSHIFT | MOD_RSHIFT))
 1686                     end_vk[vk] |= END_KEY_WITH_SHIFT;
 1687                 else
 1688                     end_vk[vk] |= END_KEY_WITHOUT_SHIFT;
 1689             }
 1690             else
 1691             {
 1692                 end_vk[vk] = (end_vk[vk] & ~aFlagsRemove) | aFlagsAdd;
 1693                 // Apply flag removal to this key's SC as well.  This is primarily
 1694                 // to support combinations like {All} +E, {LCtrl}{RCtrl} -E.
 1695                 sc_type temp_sc;
 1696                 if (aFlagsRemove && !vk_by_number && (temp_sc = vk_to_sc(vk)))
 1697                 {
 1698                     end_sc[temp_sc] &= ~aFlagsRemove; // But apply aFlagsAdd only by VK.
 1699                     // Since aFlagsRemove implies ScriptObject != NULL and !vk_by_number
 1700                     // was also checked, that implies vk_to_sc(vk, true) was already called
 1701                     // and did not find a secondary SC.
 1702                 }
 1703             }
 1704         }
 1705         if (sc || sc_by_number) // Fixed for v1.1.33.02: Allow sc000 for setting/unsetting flags for any events that lack a scan code.
 1706         {
 1707             end_sc[sc] = (end_sc[sc] & ~aFlagsRemove) | aFlagsAdd;
 1708         }
 1709     } // for()
 1710 
 1711     if (single_char_count)  // See single_char_count++ above for comments.
 1712     {
 1713         if (single_char_count > EndCharsMax)
 1714         {
 1715             // Allocate a bigger buffer.
 1716             if (EndCharsMax) // If zero, EndChars may point to static memory.
 1717                 free(EndChars);
 1718             if (  !(EndChars = tmalloc(single_char_count + 1))  )
 1719                 return g_script.ScriptError(ERR_OUTOFMEM);
 1720             EndCharsMax = single_char_count;
 1721         }
 1722         TCHAR *dst, *src;
 1723         for (dst = EndChars, src = aKeys; *src; ++src)
 1724         {
 1725             switch (*src)
 1726             {
 1727             case '{':
 1728                 if (end_pos = _tcschr(src + 1, '}'))
 1729                 {
 1730                     if (end_pos == src + 1 && end_pos[1] == '}') // {}}
 1731                         end_pos++;
 1732                     if (end_pos == src + 2)
 1733                         *dst++ = src[1]; // Copy the single character from between the braces.
 1734                     src = end_pos; // Skip '{key'.  Loop does ++src to skip the '}'.
 1735                 }
 1736                 // Otherwise, just ignore the '{'.
 1737             case '}':
 1738                 continue;
 1739             }
 1740             *dst++ = *src;
 1741         }
 1742         ASSERT(dst > EndChars);
 1743         *dst = '\0';
 1744     }
 1745     else if (aEndKeyMode) // single_char_count is false
 1746     {
 1747         if (EndCharsMax)
 1748             *EndChars = '\0';
 1749         else
 1750             EndChars = _T("");
 1751     }
 1752     return OK;
 1753 }
 1754 
 1755 
 1756 ResultType input_type::SetMatchList(LPTSTR aMatchList, size_t aMatchList_length)
 1757 {
 1758     LPTSTR *realloc_temp;  // Needed since realloc returns NULL on failure but leaves original block allocated.
 1759     MatchCount = 0;  // Set default.
 1760     if (*aMatchList)
 1761     {
 1762         // If needed, create the array of pointers that points into MatchBuf to each match phrase:
 1763         if (!match)
 1764         {
 1765             if (   !(match = (LPTSTR *)malloc(INPUT_ARRAY_BLOCK_SIZE * sizeof(LPTSTR)))   )
 1766                 return g_script.ScriptError(ERR_OUTOFMEM);  // Short msg. since so rare.
 1767             MatchCountMax = INPUT_ARRAY_BLOCK_SIZE;
 1768         }
 1769         // If needed, create or enlarge the buffer that contains all the match phrases:
 1770         size_t space_needed = aMatchList_length + 1;  // +1 for the final zero terminator.
 1771         if (space_needed > MatchBufSize)
 1772         {
 1773             MatchBufSize = (UINT)(space_needed > 4096 ? space_needed : 4096);
 1774             if (MatchBuf) // free the old one since it's too small.
 1775                 free(MatchBuf);
 1776             if (   !(MatchBuf = tmalloc(MatchBufSize))   )
 1777             {
 1778                 MatchBufSize = 0;
 1779                 return g_script.ScriptError(ERR_OUTOFMEM);  // Short msg. since so rare.
 1780             }
 1781         }
 1782         // Copy aMatchList into the match buffer:
 1783         LPTSTR source, dest;
 1784         for (source = aMatchList, dest = match[MatchCount] = MatchBuf
 1785             ; *source; ++source)
 1786         {
 1787             if (*source != ',') // Not a comma, so just copy it over.
 1788             {
 1789                 *dest++ = *source;
 1790                 continue;
 1791             }
 1792             // Otherwise: it's a comma, which becomes the terminator of the previous key phrase unless
 1793             // it's a double comma, in which case it's considered to be part of the previous phrase
 1794             // rather than the next.
 1795             if (*(source + 1) == ',') // double comma
 1796             {
 1797                 *dest++ = *source;
 1798                 ++source;  // Omit the second comma of the pair, i.e. each pair becomes a single literal comma.
 1799                 continue;
 1800             }
 1801             // Otherwise, this is a delimiting comma.
 1802             *dest = '\0';
 1803             // If the previous item is blank -- which I think can only happen now if the MatchList
 1804             // begins with an orphaned comma (since two adjacent commas resolve to one literal comma)
 1805             // -- don't add it to the match list:
 1806             if (*match[MatchCount])
 1807             {
 1808                 ++MatchCount;
 1809                 match[MatchCount] = ++dest;
 1810                 *dest = '\0';  // Init to prevent crash on orphaned comma such as "btw,otoh,"
 1811             }
 1812             if (*(source + 1)) // There is a next element.
 1813             {
 1814                 if (MatchCount >= MatchCountMax) // Rarely needed, so just realloc() to expand.
 1815                 {
 1816                     // Expand the array by one block:
 1817                     if (   !(realloc_temp = (LPTSTR *)realloc(match  // Must use a temp variable.
 1818                         , (MatchCountMax + INPUT_ARRAY_BLOCK_SIZE) * sizeof(LPTSTR)))   )
 1819                         return g_script.ScriptError(ERR_OUTOFMEM);  // Short msg. since so rare.
 1820                     match = realloc_temp;
 1821                     MatchCountMax += INPUT_ARRAY_BLOCK_SIZE;
 1822                 }
 1823             }
 1824         } // for()
 1825         *dest = '\0';  // Terminate the last item.
 1826         // This check is necessary for only a single isolated case: When the match list
 1827         // consists of nothing except a single comma.  See above comment for details:
 1828         if (*match[MatchCount]) // i.e. omit empty strings from the match list.
 1829             ++MatchCount;
 1830     }
 1831     return OK;
 1832 }
 1833 
 1834 
 1835 ResultType InputWait(Var *output_var, input_type &input)
 1836 {
 1837     //////////////////////////////////////////////////////////////////
 1838     // Wait for one of the following to terminate our input:
 1839     // 1) The hook (due a match in aEndKeys or aMatchList);
 1840     // 2) A thread that interrupts us with a new Input of its own;
 1841     // 3) The timer we put in effect for our timeout (if we have one).
 1842     //////////////////////////////////////////////////////////////////
 1843     for (;;)
 1844     {
 1845         // Rather than monitoring the timeout here, just wait for the incoming WM_TIMER message
 1846         // to take effect as a TimerProc() call during the MsgSleep():
 1847         MsgSleep();
 1848         if (!input.InProgress())
 1849             break;
 1850     }
 1851     
 1852     // Translate the "ending" to an ErrorLevel string.  Even if we were interrupted by another
 1853     // Input which terminated for a different reason, that instance had its own struct, so ours
 1854     // hasn't been overwritten.
 1855     TCHAR key_name[128];
 1856     g_ErrorLevel->Assign(input.GetEndReason(key_name, _countof(key_name)));
 1857 
 1858     return output_var->Assign(input.Buffer, input.BufferLength);
 1859 }
 1860 
 1861 
 1862 LPTSTR input_type::GetEndReason(LPTSTR aKeyBuf, int aKeyBufSize, bool aCombined)
 1863 {
 1864     switch (Status)
 1865     {
 1866     case INPUT_TIMED_OUT:
 1867         return _T("Timeout");
 1868     case INPUT_TERMINATED_BY_MATCH:
 1869         return _T("Match");
 1870     case INPUT_TERMINATED_BY_ENDKEY:
 1871     {
 1872         LPTSTR key_name = aKeyBuf;
 1873         if (!key_name)
 1874             return _T("EndKey");
 1875         if (aCombined) // Traditional "EndKey:xxx" mode.
 1876         {
 1877             ASSERT(aKeyBufSize > 7);
 1878             _tcscpy(key_name, _T("EndKey:"));
 1879             key_name += 7;
 1880             aKeyBufSize -= 7;
 1881         }
 1882         if (EndingChar)
 1883         {
 1884             key_name[0] = EndingChar;
 1885             key_name[1] = '\0';
 1886         }
 1887         else if (EndingRequiredShift)
 1888         {
 1889             // Since the only way a shift key can be required in our case is if it's a key whose name
 1890             // is a single char (such as a shifted punctuation mark), use a diff. method to look up the
 1891             // key name based on fact that the shift key was down to terminate the input.  We also know
 1892             // that the key is an EndingVK because there's no way for the shift key to have been
 1893             // required by a scan code based on the logic (above) that builds the end_key arrays.
 1894             // MSDN: "Typically, ToAscii performs the translation based on the virtual-key code.
 1895             // In some cases, however, bit 15 of the uScanCode parameter may be used to distinguish
 1896             // between a key press and a key release. The scan code is used for translating ALT+
 1897             // number key combinations.
 1898             BYTE state[256] = { 0 };
 1899             state[VK_SHIFT] |= 0x80; // Indicate that the neutral shift key is down for conversion purposes.
 1900             Get_active_window_keybd_layout // Defines the variable active_window_keybd_layout for use below.
 1901             int count = ToUnicodeOrAsciiEx(EndingVK, vk_to_sc(EndingVK), (PBYTE)&state // Nothing is done about ToAsciiEx's dead key side-effects here because it seems to rare to be worth it (assuming its even a problem).
 1902                 , key_name, g_MenuIsVisible ? 1 : 0, active_window_keybd_layout); // v1.0.44.03: Changed to call ToAsciiEx() so that active window's layout can be specified (see hook.cpp for details).
 1903             key_name[count] = '\0';  // Terminate the string.
 1904         }
 1905         else
 1906         {
 1907             *key_name = '\0';
 1908             if (EndingBySC)
 1909                 SCtoKeyName(EndingSC, key_name, aKeyBufSize, false);
 1910             if (!*key_name && !(aCombined && EndingBySC))
 1911                 VKtoKeyName(EndingVK, key_name, aKeyBufSize, !EndingBySC);
 1912             if (!*key_name)
 1913                 sntprintf(key_name, aKeyBufSize, _T("sc%03X"), EndingSC);
 1914             // For partial backward-compatibility, keys A-Z are upper-cased when handled by VK,
 1915             // but only if they actually correspond to those characters.  If this wasn't done,
 1916             // the character would always be lowercase since the shift state is not considered.
 1917             if (*key_name >= 'a' && *key_name <= 'z' && !key_name[1] && aCombined)
 1918                 *key_name -= 32;
 1919         }
 1920         return aCombined ? aKeyBuf : _T("EndKey");
 1921     }
 1922     case INPUT_LIMIT_REACHED:
 1923         return _T("Max");
 1924     case INPUT_INTERRUPTED:
 1925         // Our input was terminated due to a new input in a quasi-thread that interrupted ours.
 1926         return _T("NewInput");
 1927     case INPUT_OFF:
 1928         return _T("Stopped");
 1929     default: // In progress.
 1930         return _T("");
 1931     }
 1932 }
 1933 
 1934 
 1935 void input_type::Start()
 1936 {
 1937     ASSERT(!InProgress());
 1938     Status = INPUT_IN_PROGRESS;
 1939 }
 1940 
 1941 void input_type::EndByMatch(UINT aMatchIndex)
 1942 {
 1943     ASSERT(InProgress());
 1944     EndingMatchIndex = aMatchIndex;
 1945     EndByReason(INPUT_TERMINATED_BY_MATCH);
 1946 }
 1947 
 1948 void input_type::EndByKey(vk_type aVK, sc_type aSC, bool aBySC, bool aRequiredShift)
 1949 {
 1950     ASSERT(InProgress());
 1951     EndingVK = aVK;
 1952     EndingSC = aSC;
 1953     EndingBySC = aBySC;
 1954     EndingRequiredShift = aRequiredShift;
 1955     EndingChar = 0; // Must be zero if the above are to be used.
 1956     EndByReason(INPUT_TERMINATED_BY_ENDKEY);
 1957 }
 1958 
 1959 void input_type::EndByChar(TCHAR aChar)
 1960 {
 1961     ASSERT(aChar && InProgress());
 1962     EndingChar = aChar;
 1963     // The other EndKey related fields are ignored when Char is non-zero.
 1964     EndByReason(INPUT_TERMINATED_BY_ENDKEY);
 1965 }
 1966 
 1967 void input_type::EndByReason(InputStatusType aReason)
 1968 {
 1969     ASSERT(InProgress());
 1970     EndingMods = g_modifiersLR_logical; // Not relevant to all end reasons, but might be useful anyway.
 1971     Status = aReason;
 1972 
 1973     // It's done this way rather than calling InputRelease() directly...
 1974     // ...so that we can rely on MsgSleep() to create a new thread for the OnEnd event.
 1975     // ...because InputRelease() can't be called by the hook thread.
 1976     // ...because some callers rely on the list not being broken by this call.
 1977     PostMessage(g_hWnd, AHK_INPUT_END, (WPARAM)this, 0);
 1978 }
 1979 
 1980 
 1981 input_type *InputRelease(input_type *aInput)
 1982 {
 1983     if (!aInput)
 1984         return NULL;
 1985     // Input should already have ended prior to this function being called.
 1986     // Otherwise, removal of aInput from the chain will end input collection.
 1987     if (g_input == aInput)
 1988         g_input = aInput->Prev;
 1989     else
 1990         for (auto *input = g_input; ; input = input->Prev)
 1991         {
 1992             if (!input)
 1993                 return NULL; // aInput is not valid (faked AHK_INPUT_END message?) or not active.
 1994             if (input->Prev == aInput)
 1995             {
 1996                 input->Prev = aInput->Prev;
 1997                 break;
 1998             }
 1999         }
 2000 
 2001     // Ensure any pending use of aInput by the hook is finished.
 2002     WaitHookIdle();
 2003     
 2004     aInput->Prev = NULL;
 2005     if (aInput->ScriptObject)
 2006     {
 2007         Hotkey::MaybeUninstallHook();
 2008         if (aInput->ScriptObject->onEnd)
 2009             return aInput; // Return for caller to call OnEnd and Release.
 2010         aInput->ScriptObject->Release();
 2011         aInput->ScriptObject = NULL;
 2012     }
 2013     return NULL;
 2014 }
 2015 
 2016 
 2017 input_type *InputFind(InputObject *object)
 2018 {
 2019     for (auto *input = g_input; input; input = input->Prev)
 2020         if (input->ScriptObject == object)
 2021             return input;
 2022     return NULL;
 2023 }
 2024 
 2025 
 2026 
 2027 ResultType Line::PerformShowWindow(ActionTypeType aActionType, LPTSTR aTitle, LPTSTR aText
 2028     , LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2029 {
 2030     // By design, the WinShow command must always unhide a hidden window, even if the user has
 2031     // specified that hidden windows should not be detected.  So set this now so that
 2032     // DetermineTargetWindow() will make its calls in the right mode:
 2033     bool need_restore = (aActionType == ACT_WINSHOW && !g->DetectHiddenWindows);
 2034     if (need_restore)
 2035         g->DetectHiddenWindows = true;
 2036     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2037     if (need_restore)
 2038         g->DetectHiddenWindows = false;
 2039     if (!target_window)
 2040         return OK;
 2041 
 2042     // WinGroup's EnumParentActUponAll() is quite similar to the following, so the two should be
 2043     // maintained together.
 2044 
 2045     int nCmdShow = SW_NONE; // Set default.
 2046 
 2047     switch (aActionType)
 2048     {
 2049     // SW_FORCEMINIMIZE: supported only in Windows 2000/XP and beyond: "Minimizes a window,
 2050     // even if the thread that owns the window is hung. This flag should only be used when
 2051     // minimizing windows from a different thread."
 2052     // My: It seems best to use SW_FORCEMINIMIZE on OS's that support it because I have
 2053     // observed ShowWindow() to hang (thus locking up our app's main thread) if the target
 2054     // window is hung.
 2055     // UPDATE: For now, not using "force" every time because it has undesirable side-effects such
 2056     // as the window not being restored to its maximized state after it was minimized
 2057     // this way.
 2058     case ACT_WINMINIMIZE:
 2059         if (IsWindowHung(target_window))
 2060         {
 2061             nCmdShow = SW_FORCEMINIMIZE;
 2062             // SW_MINIMIZE can lock up our thread on WinXP, which is why we revert to SW_FORCEMINIMIZE above.
 2063             // Older/obsolete comment for background: don't attempt to minimize hung windows because that
 2064             // might hang our thread because the call to ShowWindow() would never return.
 2065         }
 2066         else
 2067             nCmdShow = SW_MINIMIZE;
 2068         break;
 2069     case ACT_WINMAXIMIZE: if (!IsWindowHung(target_window)) nCmdShow = SW_MAXIMIZE; break;
 2070     case ACT_WINRESTORE:  if (!IsWindowHung(target_window)) nCmdShow = SW_RESTORE;  break;
 2071     // Seems safe to assume it's not hung in these cases, since I'm inclined to believe
 2072     // (untested) that hiding and showing a hung window won't lock up our thread, and
 2073     // there's a chance they may be effective even against hung windows, unlike the
 2074     // others above (except ACT_WINMINIMIZE, which has a special FORCE method):
 2075     case ACT_WINHIDE: nCmdShow = SW_HIDE; break;
 2076     case ACT_WINSHOW: nCmdShow = SW_SHOW; break;
 2077     }
 2078 
 2079     // UPDATE:  Trying ShowWindowAsync()
 2080     // now, which should avoid the problems with hanging.  UPDATE #2: Went back to
 2081     // not using Async() because sometimes the script lines that come after the one
 2082     // that is doing this action here rely on this action having been completed
 2083     // (e.g. a window being maximized prior to clicking somewhere inside it).
 2084     if (nCmdShow != SW_NONE)
 2085     {
 2086         // I'm not certain that SW_FORCEMINIMIZE works with ShowWindowAsync(), but
 2087         // it probably does since there's absolutely no mention to the contrary
 2088         // anywhere on MS's site or on the web.  But clearly, if it does work, it
 2089         // does so only because Async() doesn't really post the message to the thread's
 2090         // queue, instead opting for more aggressive measures.  Thus, it seems best
 2091         // to do it this way to have maximum confidence in it:
 2092         //if (nCmdShow == SW_FORCEMINIMIZE) // Safer not to use ShowWindowAsync() in this case.
 2093             ShowWindow(target_window, nCmdShow);
 2094         //else
 2095         //  ShowWindowAsync(target_window, nCmdShow);
 2096 //PostMessage(target_window, WM_SYSCOMMAND, SC_MINIMIZE, 0);
 2097         DoWinDelay;
 2098     }
 2099     return OK;  // Return success for all the above cases.
 2100 }
 2101 
 2102 
 2103 
 2104 ResultType Line::PerformWait()
 2105 // Since other script threads can interrupt these commands while they're running, it's important that
 2106 // these commands not refer to sArgDeref[] and sArgVar[] anytime after an interruption becomes possible.
 2107 // This is because an interrupting thread usually changes the values to something inappropriate for this thread.
 2108 // fincs: it seems best that this function not throw an exception if the wait timeouts.
 2109 {
 2110     bool wait_indefinitely;
 2111     int sleep_duration;
 2112     DWORD start_time;
 2113 
 2114     vk_type vk; // For GetKeyState.
 2115     HANDLE running_process; // For RUNWAIT
 2116     DWORD exit_code; // For RUNWAIT
 2117 
 2118     // For ACT_KEYWAIT:
 2119     bool wait_for_keydown;
 2120     KeyStateTypes key_state_type;
 2121     JoyControls joy;
 2122     int joystick_id;
 2123     ExprTokenType token;
 2124     TCHAR buf[LINE_SIZE];
 2125 
 2126     if (mActionType == ACT_RUNWAIT)
 2127     {
 2128         bool use_el = tcscasestr(ARG3, _T("UseErrorLevel"));
 2129         if (!g_script.ActionExec(ARG1, NULL, ARG2, !use_el, ARG3, &running_process, use_el, true, ARGVAR4)) // Load-time validation has ensured that the arg is a valid output variable (e.g. not a built-in var).
 2130             return use_el ? g_ErrorLevel->Assign(_T("ERROR")) : FAIL;
 2131         //else fall through to the waiting-phase of the operation.
 2132         // Above: The special string ERROR is used, rather than a number like 1, because currently
 2133         // RunWait might in the future be able to return any value, including 259 (STATUS_PENDING).
 2134     }
 2135     
 2136     // Must NOT use ELSE-IF in line below due to ELSE further down needing to execute for RunWait.
 2137     if (mActionType == ACT_KEYWAIT)
 2138     {
 2139         if (   !(vk = TextToVK(ARG1))   )
 2140         {
 2141             if (   !(joy = (JoyControls)ConvertJoy(ARG1, &joystick_id))   ) // Not a valid key name.
 2142                 // Indicate immediate timeout (if timeout was specified) or error.
 2143                 return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
 2144             if (!IS_JOYSTICK_BUTTON(joy)) // Currently, only buttons are supported.
 2145                 return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
 2146         }
 2147         // Set defaults:
 2148         wait_for_keydown = false;  // The default is to wait for the key to be released.
 2149         key_state_type = KEYSTATE_PHYSICAL;  // Since physical is more often used.
 2150         wait_indefinitely = true;
 2151         sleep_duration = 0;
 2152         for (LPTSTR cp = ARG2; *cp; ++cp)
 2153         {
 2154             switch(ctoupper(*cp))
 2155             {
 2156             case 'D':
 2157                 wait_for_keydown = true;
 2158                 break;
 2159             case 'L':
 2160                 key_state_type = KEYSTATE_LOGICAL;
 2161                 break;
 2162             case 'T':
 2163                 // Although ATOF() supports hex, it's been documented in the help file that hex should
 2164                 // not be used (see comment above) so if someone does it anyway, some option letters
 2165                 // might be misinterpreted:
 2166                 wait_indefinitely = false;
 2167                 sleep_duration = (int)(ATOF(cp + 1) * 1000);
 2168                 break;
 2169             }
 2170         }
 2171         // The following must be set for ScriptGetJoyState():
 2172         token.symbol = SYM_STRING;
 2173         token.marker = buf;
 2174     }
 2175     else if (   (mActionType != ACT_RUNWAIT && mActionType != ACT_CLIPWAIT && *ARG3)
 2176         || (mActionType == ACT_CLIPWAIT && *ARG1)   )
 2177     {
 2178         // Since the param containing the timeout value isn't blank, it must be numeric,
 2179         // otherwise, the loading validation would have prevented the script from loading.
 2180         wait_indefinitely = false;
 2181         sleep_duration = (int)(ATOF(mActionType == ACT_CLIPWAIT ? ARG1 : ARG3) * 1000); // Can be zero.
 2182         if (sleep_duration < 1)
 2183             // Waiting 500ms in place of a "0" seems more useful than a true zero, which
 2184             // doens't need to be supported because it's the same thing as something like
 2185             // "IfWinExist".  A true zero for clipboard would be the same as
 2186             // "IfEqual, clipboard, , xxx" (though admittedly it's higher overhead to
 2187             // actually fetch the contents of the clipboard).
 2188             sleep_duration = 500;
 2189     }
 2190     else
 2191     {
 2192         wait_indefinitely = true;
 2193         sleep_duration = 0; // Just to catch any bugs.
 2194     }
 2195 
 2196     if (mActionType != ACT_RUNWAIT)
 2197         g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Set default ErrorLevel to be possibly overridden later on.
 2198 
 2199     bool any_clipboard_format = (mActionType == ACT_CLIPWAIT && ArgToInt(2) == 1);
 2200 
 2201     // Right before starting the wait-loop, make a copy of our args using the stack
 2202     // space in our recursion layer.  This is done in case other hotkey subroutine(s)
 2203     // are launched while we're waiting here, which might cause our args to be overwritten
 2204     // if any of them happen to be in the Deref buffer:
 2205     LPTSTR arg[MAX_ARGS], marker;
 2206     int i, space_remaining;
 2207     for (i = 0, space_remaining = LINE_SIZE, marker = buf; i < mArgc; ++i)
 2208     {
 2209         if (!space_remaining) // Realistically, should never happen.
 2210             arg[i] = _T("");
 2211         else
 2212         {
 2213             arg[i] = marker;  // Point it to its place in the buffer.
 2214             tcslcpy(marker, sArgDeref[i], space_remaining); // Make the copy.
 2215             marker += _tcslen(marker) + 1;  // +1 for the zero terminator of each arg.
 2216             space_remaining = (int)(LINE_SIZE - (marker - buf));
 2217         }
 2218     }
 2219 
 2220     for (start_time = GetTickCount();;) // start_time is initialized unconditionally for use with v1.0.30.02's new logging feature further below.
 2221     { // Always do the first iteration so that at least one check is done.
 2222         switch(mActionType)
 2223         {
 2224         case ACT_WINWAIT:
 2225             #define SAVED_WIN_ARGS SAVED_ARG1, SAVED_ARG2, SAVED_ARG4, SAVED_ARG5
 2226             if (WinExist(*g, SAVED_WIN_ARGS, false, true))
 2227             {
 2228                 DoWinDelay;
 2229                 return OK;
 2230             }
 2231             break;
 2232         case ACT_WINWAITCLOSE:
 2233             if (!WinExist(*g, SAVED_WIN_ARGS))
 2234             {
 2235                 DoWinDelay;
 2236                 return OK;
 2237             }
 2238             break;
 2239         case ACT_WINWAITACTIVE:
 2240             if (WinActive(*g, SAVED_WIN_ARGS, true))
 2241             {
 2242                 DoWinDelay;
 2243                 return OK;
 2244             }
 2245             break;
 2246         case ACT_WINWAITNOTACTIVE:
 2247             if (!WinActive(*g, SAVED_WIN_ARGS, true))
 2248             {
 2249                 DoWinDelay;
 2250                 return OK;
 2251             }
 2252             break;
 2253         case ACT_CLIPWAIT:
 2254             // Seems best to consider CF_HDROP to be a non-empty clipboard, since we
 2255             // support the implicit conversion of that format to text:
 2256             if (any_clipboard_format)
 2257             {
 2258                 if (CountClipboardFormats())
 2259                     return OK;
 2260             }
 2261             else
 2262                 if (IsClipboardFormatAvailable(CF_NATIVETEXT) || IsClipboardFormatAvailable(CF_HDROP))
 2263                     return OK;
 2264             break;
 2265         case ACT_KEYWAIT:
 2266             if (vk) // Waiting for key or mouse button, not joystick.
 2267             {
 2268                 if (ScriptGetKeyState(vk, key_state_type) == wait_for_keydown)
 2269                     return OK;
 2270             }
 2271             else // Waiting for joystick button
 2272             {
 2273                 if ((bool)ScriptGetJoyState(joy, joystick_id, token, false) == wait_for_keydown)
 2274                     return OK;
 2275             }
 2276             break;
 2277         case ACT_RUNWAIT:
 2278             // Pretty nasty, but for now, nothing is done to prevent an infinite loop.
 2279             // In the future, maybe OpenProcess() can be used to detect if a process still
 2280             // exists (is there any other way?):
 2281             // MSDN: "Warning: If a process happens to return STILL_ACTIVE (259) as an error code,
 2282             // applications that test for this value could end up in an infinite loop."
 2283             if (running_process)
 2284                 GetExitCodeProcess(running_process, &exit_code);
 2285             else // it can be NULL in the case of launching things like "find D:\" or "www.yahoo.com"
 2286                 exit_code = 0;
 2287             if (exit_code != STATUS_PENDING) // STATUS_PENDING == STILL_ACTIVE
 2288             {
 2289                 if (running_process)
 2290                     CloseHandle(running_process);
 2291                 // Use signed vs. unsigned, since that is more typical?  No, it seems better
 2292                 // to use unsigned now that script variables store 64-bit ints.  This is because
 2293                 // GetExitCodeProcess() yields a DWORD, implying that the value should be unsigned.
 2294                 // Unsigned also is more useful in cases where an app returns a (potentially large)
 2295                 // count of something as its result.  However, if this is done, it won't be easy
 2296                 // to check against a return value of -1, for example, which I suspect many apps
 2297                 // return.  AutoIt3 (and probably 2) use a signed int as well, so that is another
 2298                 // reason to keep it this way:
 2299                 return g_ErrorLevel->Assign((int)exit_code);
 2300             }
 2301             break;
 2302         }
 2303 
 2304         // Must cast to int or any negative result will be lost due to DWORD type:
 2305         if (wait_indefinitely || (int)(sleep_duration - (GetTickCount() - start_time)) > SLEEP_INTERVAL_HALF)
 2306         {
 2307             if (MsgSleep(INTERVAL_UNSPECIFIED)) // INTERVAL_UNSPECIFIED performs better.
 2308             {
 2309                 // v1.0.30.02: Since MsgSleep() launched and returned from at least one new thread, put the
 2310                 // current waiting line into the line-log again to make it easy to see what the current
 2311                 // thread is doing.  This is especially useful for figuring out which subroutine is holding
 2312                 // another thread interrupted beneath it.  For example, if a timer gets interrupted by
 2313                 // a hotkey that has an indefinite WinWait, and that window never appears, this will allow
 2314                 // the user to find out the culprit thread by showing its line in the log (and usually
 2315                 // it will appear as the very last line, since usually the script is idle and thus the
 2316                 // currently active thread is the one that's still waiting for the window).
 2317                 if (g->ListLinesIsEnabled)
 2318                 {
 2319                     // ListLines is enabled in this thread, but if it was disabled in the interrupting thread,
 2320                     // the very last log entry will be ours.  In that case, we don't want to duplicate it.
 2321                     int previous_log_index = (sLogNext ? sLogNext : LINE_LOG_SIZE) - 1; // Wrap around if needed (the entry can be NULL in that case).
 2322                     if (sLog[previous_log_index] != this || sLogTick[previous_log_index] != start_time) // The previously logged line was not this one, or it was added by the interrupting thread (different start_time).
 2323                     {
 2324                         sLog[sLogNext] = this;
 2325                         sLogTick[sLogNext++] = start_time; // Store a special value so that Line::LogToText() can report that its "still waiting" from earlier.
 2326                         if (sLogNext >= LINE_LOG_SIZE)
 2327                             sLogNext = 0;
 2328                         // The lines above are the similar to those used in ExecUntil(), so the two should be
 2329                         // maintained together.
 2330                     }
 2331                 }
 2332             }
 2333         }
 2334         else // Done waiting.
 2335             return g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // Since it timed out, we override the default with this.
 2336     } // for()
 2337 }
 2338 
 2339 
 2340 
 2341 ResultType Line::WinMove(LPTSTR aTitle, LPTSTR aText, LPTSTR aX, LPTSTR aY
 2342     , LPTSTR aWidth, LPTSTR aHeight, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2343 {
 2344     // So that compatibility is retained, don't set ErrorLevel for commands that are native to AutoIt2
 2345     // but that AutoIt2 doesn't use ErrorLevel with (such as this one).
 2346     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2347     if (!target_window)
 2348         return OK;
 2349     RECT rect;
 2350     if (!GetWindowRect(target_window, &rect))
 2351         return OK;  // Can't set errorlevel, see above.
 2352     MoveWindow(target_window
 2353         , *aX && _tcsicmp(aX, _T("default")) ? ATOI(aX) : rect.left  // X-position
 2354         , *aY && _tcsicmp(aY, _T("default")) ? ATOI(aY) : rect.top   // Y-position
 2355         , *aWidth && _tcsicmp(aWidth, _T("default")) ? ATOI(aWidth) : rect.right - rect.left
 2356         , *aHeight && _tcsicmp(aHeight, _T("default")) ? ATOI(aHeight) : rect.bottom - rect.top
 2357         , TRUE);  // Do repaint.
 2358     DoWinDelay;
 2359     return OK;
 2360 }
 2361 
 2362 
 2363 
 2364 ResultType Line::ControlSend(LPTSTR aControl, LPTSTR aKeysToSend, LPTSTR aTitle, LPTSTR aText
 2365     , LPTSTR aExcludeTitle, LPTSTR aExcludeText, SendRawModes aSendRaw)
 2366 {
 2367     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2368     if (!target_window)
 2369         goto error;
 2370     HWND control_window = _tcsicmp(aControl, _T("ahk_parent"))
 2371         ? ControlExist(target_window, aControl) // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2372         : target_window;
 2373     if (!control_window)
 2374         goto error;
 2375     SendKeys(aKeysToSend, aSendRaw, SM_EVENT, control_window);
 2376     // But don't do WinDelay because KeyDelay should have been in effect for the above.
 2377     return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 2378 
 2379 error:
 2380     return SetErrorLevelOrThrow();
 2381 }
 2382 
 2383 
 2384 
 2385 ResultType Line::ControlClick(vk_type aVK, int aClickCount, LPTSTR aOptions, LPTSTR aControl
 2386     , LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2387 {
 2388     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2389     if (!target_window)
 2390         goto error;
 2391 
 2392     // Set the defaults that will be in effect unless overridden by options:
 2393     KeyEventTypes event_type = KEYDOWNANDUP;
 2394     bool position_mode = false;
 2395     bool do_activate = true;
 2396     // These default coords can be overridden either by aOptions or aControl's X/Y mode:
 2397     {POINT click = {COORD_UNSPECIFIED, COORD_UNSPECIFIED};
 2398 
 2399     for (LPTSTR cp = aOptions; *cp; ++cp)
 2400     {
 2401         switch(ctoupper(*cp))
 2402         {
 2403         case 'D':
 2404             event_type = KEYDOWN;
 2405             break;
 2406         case 'U':
 2407             event_type = KEYUP;
 2408             break;
 2409         case 'N':
 2410             // v1.0.45:
 2411             // It was reported (and confirmed through testing) that this new NA mode (which avoids
 2412             // AttachThreadInput() and SetActiveWindow()) improves the reliability of ControlClick when
 2413             // the user is moving the mouse fairly quickly at the time the command tries to click a button.
 2414             // In addition, the new mode avoids activating the window, which tends to happen otherwise.
 2415             // HOWEVER, the new mode seems no more reliable than the old mode when the target window is
 2416             // the active window.  In addition, there may be side-effects of the new mode (I caught it
 2417             // causing Notepad's Save-As dialog to hang once, during the display of its "Overwrite?" dialog).
 2418             // ALSO, SetControlDelay -1 seems to fix the unreliability issue as well (independently of NA),
 2419             // though it might not work with some types of windows/controls (thus, for backward
 2420             // compatibility, ControlClick still obeys SetControlDelay).
 2421             if (ctoupper(cp[1]) == 'A')
 2422             {
 2423                 cp += 1;  // Add 1 vs. 2 to skip over the rest of the letters in this option word.
 2424                 do_activate = false;
 2425             }
 2426             break;
 2427         case 'P':
 2428             if (!_tcsnicmp(cp, _T("Pos"), 3))
 2429             {
 2430                 cp += 2;  // Add 2 vs. 3 to skip over the rest of the letters in this option word.
 2431                 position_mode = true;
 2432             }
 2433             break;
 2434         // For the below:
 2435         // Use atoi() vs. ATOI() to avoid interpreting something like 0x01D as hex
 2436         // when in fact the D was meant to be an option letter:
 2437         case 'X':
 2438             click.x = _ttoi(cp + 1); // Will be overridden later below if it turns out that position_mode is in effect.
 2439             break;
 2440         case 'Y':
 2441             click.y = _ttoi(cp + 1); // Will be overridden later below if it turns out that position_mode is in effect.
 2442             break;
 2443         }
 2444     }
 2445 
 2446     // It's debatable, but might be best for flexibility (and backward compatibility) to allow target_window to itself
 2447     // be a control (at least for the position_mode handler below).  For example, the script may have called SetParent
 2448     // to make a top-level window the child of some other window, in which case this policy allows it to be seen like
 2449     // a non-child.
 2450     HWND control_window = position_mode ? NULL : ControlExist(target_window, aControl); // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2451     if (!control_window) // Even if position_mode is false, the below is still attempted, as documented.
 2452     {
 2453         // New section for v1.0.24.  But only after the above fails to find a control do we consider
 2454         // whether aControl contains X and Y coordinates.  That way, if a control class happens to be
 2455         // named something like "X1 Y1", it will still be found by giving precedence to class names.
 2456         point_and_hwnd_type pah = {0};
 2457         pah.ignore_disabled_controls = true; // v1.1.20: Ignore disabled controls.
 2458         // Parse the X an Y coordinates in a strict way to reduce ambiguity with control names and also
 2459         // to keep the code simple.
 2460         LPTSTR cp = omit_leading_whitespace(aControl);
 2461         if (ctoupper(*cp) != 'X')
 2462             goto error;
 2463         ++cp;
 2464         if (!*cp)
 2465             goto error;
 2466         pah.pt.x = ATOI(cp);
 2467         if (   !(cp = StrChrAny(cp, _T(" \t")))   ) // Find next space or tab (there must be one for it to be considered valid).
 2468             goto error;
 2469         cp = omit_leading_whitespace(cp + 1);
 2470         if (!*cp || _totupper(*cp) != 'Y')
 2471             goto error;
 2472         ++cp;
 2473         if (!*cp)
 2474             goto error;
 2475         pah.pt.y = ATOI(cp);
 2476         // The passed-in coordinates are always relative to target_window's upper left corner because offering
 2477         // an option for absolute/screen coordinates doesn't seem useful.
 2478         RECT rect;
 2479         GetWindowRect(target_window, &rect);
 2480         pah.pt.x += rect.left; // Convert to screen coordinates.
 2481         pah.pt.y += rect.top;
 2482         EnumChildWindows(target_window, EnumChildFindPoint, (LPARAM)&pah); // Find topmost control containing point.
 2483         // If no control is at this point, try posting the mouse event message(s) directly to the
 2484         // parent window to increase the flexibility of this feature:
 2485         control_window = pah.hwnd_found ? pah.hwnd_found : target_window;
 2486         // Convert click's target coordinates to be relative to the client area of the control or
 2487         // parent window because that is the format required by messages such as WM_LBUTTONDOWN
 2488         // used later below:
 2489         click = pah.pt;
 2490         ScreenToClient(control_window, &click);
 2491     }
 2492 
 2493     // This is done this late because it seems better to set an ErrorLevel of 1 (above) whenever the
 2494     // target window or control isn't found, or any other error condition occurs above:
 2495     if (aClickCount < 1)
 2496         // Allow this to simply "do nothing", because it increases flexibility
 2497         // in the case where the number of clicks is a dereferenced script variable
 2498         // that may sometimes (by intent) resolve to zero or negative:
 2499         return g_ErrorLevel->Assign(ERRORLEVEL_NONE);
 2500 
 2501     RECT rect;
 2502     if (click.x == COORD_UNSPECIFIED || click.y == COORD_UNSPECIFIED)
 2503     {
 2504         // The following idea is from AutoIt3. It states: "Get the dimensions of the control so we can click
 2505         // the centre of it" (maybe safer and more natural than 0,0).
 2506         // My: In addition, this is probably better for some large controls (e.g. SysListView32) because
 2507         // clicking at 0,0 might activate a part of the control that is not even visible:
 2508         if (!GetWindowRect(control_window, &rect))
 2509             goto error;
 2510         if (click.x == COORD_UNSPECIFIED)
 2511             click.x = (rect.right - rect.left) / 2;
 2512         if (click.y == COORD_UNSPECIFIED)
 2513             click.y = (rect.bottom - rect.top) / 2;
 2514     }
 2515     LPARAM lparam = MAKELPARAM(click.x, click.y);
 2516 
 2517     UINT msg_down, msg_up;
 2518     WPARAM wparam, wparam_up = 0;
 2519     bool vk_is_wheel = aVK == VK_WHEEL_UP || aVK == VK_WHEEL_DOWN;
 2520     bool vk_is_hwheel = aVK == VK_WHEEL_LEFT || aVK == VK_WHEEL_RIGHT; // v1.0.48: Lexikos: Support horizontal scrolling in Windows Vista and later.
 2521 
 2522     if (vk_is_wheel)
 2523     {
 2524         wparam = (aClickCount * ((aVK == VK_WHEEL_UP) ? WHEEL_DELTA : -WHEEL_DELTA)) << 16;  // High order word contains the delta.
 2525         msg_down = WM_MOUSEWHEEL;
 2526         // Make the event more accurate by having the state of the keys reflected in the event.
 2527         // The logical state (not physical state) of the modifier keys is used so that something
 2528         // like this is supported:
 2529         // Send, {ShiftDown}
 2530         // MouseClick, WheelUp
 2531         // Send, {ShiftUp}
 2532         // In addition, if the mouse hook is installed, use its logical mouse button state so that
 2533         // something like this is supported:
 2534         // MouseClick, left, , , , , D  ; Hold down the left mouse button
 2535         // MouseClick, WheelUp
 2536         // MouseClick, left, , , , , U  ; Release the left mouse button.
 2537         // UPDATE: Since the other ControlClick types (such as leftclick) do not reflect these
 2538         // modifiers -- and we want to keep it that way, at least by default, for compatibility
 2539         // reasons -- it seems best for consistency not to do them for WheelUp/Down either.
 2540         // A script option can be added in the future to obey the state of the modifiers:
 2541         //mod_type mod = GetModifierState();
 2542         //if (mod & MOD_SHIFT)
 2543         //  wparam |= MK_SHIFT;
 2544         //if (mod & MOD_CONTROL)
 2545         //  wparam |= MK_CONTROL;
 2546         //if (g_MouseHook)
 2547         //  wparam |= g_mouse_buttons_logical;
 2548     }
 2549     else if (vk_is_hwheel)  // Lexikos: Support horizontal scrolling in Windows Vista and later.
 2550     {
 2551         wparam = (aClickCount * ((aVK == VK_WHEEL_LEFT) ? -WHEEL_DELTA : WHEEL_DELTA)) << 16;
 2552         msg_down = WM_MOUSEHWHEEL;
 2553     }
 2554     else
 2555     {
 2556         switch (aVK)
 2557         {
 2558             case VK_LBUTTON:  msg_down = WM_LBUTTONDOWN; msg_up = WM_LBUTTONUP; wparam = MK_LBUTTON; break;
 2559             case VK_RBUTTON:  msg_down = WM_RBUTTONDOWN; msg_up = WM_RBUTTONUP; wparam = MK_RBUTTON; break;
 2560             case VK_MBUTTON:  msg_down = WM_MBUTTONDOWN; msg_up = WM_MBUTTONUP; wparam = MK_MBUTTON; break;
 2561             case VK_XBUTTON1: msg_down = WM_XBUTTONDOWN; msg_up = WM_XBUTTONUP; wparam_up = XBUTTON1<<16; wparam = MK_XBUTTON1|wparam_up; break;
 2562             case VK_XBUTTON2: msg_down = WM_XBUTTONDOWN; msg_up = WM_XBUTTONUP; wparam_up = XBUTTON2<<16; wparam = MK_XBUTTON2|wparam_up; break;
 2563             default: goto error; // Just do nothing since this should realistically never happen.
 2564         }
 2565     }
 2566 
 2567     // SetActiveWindow() requires ATTACH_THREAD_INPUT to succeed.  Even though the MSDN docs state
 2568     // that SetActiveWindow() has no effect unless the parent window is foreground, Jon insists
 2569     // that SetActiveWindow() resolved some problems for some users.  In any case, it seems best
 2570     // to do this in case the window really is foreground, in which case MSDN indicates that
 2571     // it will help for certain types of dialogs.
 2572     ATTACH_THREAD_INPUT_AND_SETACTIVEWINDOW_IF_DO_ACTIVATE  // It's kept with a similar macro for maintainability.
 2573     // v1.0.44.13: Notes for the above: Unlike some other Control commands, GetNonChildParent() is not
 2574     // called here when target_window==control_window.  This is because the script may have called
 2575     // SetParent to make target_window the child of some other window, in which case target_window
 2576     // should still be used above (unclear).  Perhaps more importantly, it's allowed for control_window
 2577     // to be the same as target_window, at least in position_mode, whose docs state, "If there is no
 2578     // control, the target window itself will be sent the event (which might have no effect depending
 2579     // on the nature of the window)."  In other words, it seems too complicated and rare to add explicit
 2580     // handling for "ahk_id %ControlHWND%" (though the below rules should work).
 2581     // The line "ControlClick,, ahk_id %HWND%" can have multiple meanings depending on the nature of HWND:
 2582     // 1) If HWND is a top-level window, its topmost child will be clicked.
 2583     // 2) If HWND is a top-level window that has become a child of another window via SetParent: same.
 2584     // 3) If HWND is a control, its topmost child will be clicked (or itself if it has no children).
 2585     //    For example, the following works (as documented in the first parameter):
 2586     //    ControlGet, HWND, HWND,, OK, A  ; Get the HWND of the OK button.
 2587     //    ControlClick,, ahk_id %HWND%
 2588 
 2589     if (vk_is_wheel || vk_is_hwheel) // v1.0.48: Lexikos: Support horizontal scrolling in Windows Vista and later.
 2590     {
 2591         PostMessage(control_window, msg_down, wparam, lparam);
 2592         DoControlDelay;
 2593     }
 2594     else
 2595     {
 2596         for (int i = 0; i < aClickCount; ++i)
 2597         {
 2598             if (event_type != KEYUP) // It's either down-only or up-and-down so always to the down-event.
 2599             {
 2600                 PostMessage(control_window, msg_down, wparam, lparam);
 2601                 // Seems best to do this one too, which is what AutoIt3 does also.  User can always reduce
 2602                 // ControlDelay to 0 or -1.  Update: Jon says this delay might be causing it to fail in
 2603                 // some cases.  Upon reflection, it seems best not to do this anyway because PostMessage()
 2604                 // should queue up the message for the app correctly even if it's busy.  Update: But I
 2605                 // think the timestamp is available on every posted message, so if some apps check for
 2606                 // inhumanly fast clicks (to weed out transients with partial clicks of the mouse, or
 2607                 // to detect artificial input), the click might not work.  So it might be better after
 2608                 // all to do the delay until it's proven to be problematic (Jon implies that he has
 2609                 // no proof yet).  IF THIS IS EVER DISABLED, be sure to do the ControlDelay anyway
 2610                 // if event_type == KEYDOWN:
 2611                 DoControlDelay;
 2612             }
 2613             if (event_type != KEYDOWN) // It's either up-only or up-and-down so always to the up-event.
 2614             {
 2615                 PostMessage(control_window, msg_up, wparam_up, lparam);
 2616                 DoControlDelay;
 2617             }
 2618         }
 2619     }
 2620 
 2621     DETACH_THREAD_INPUT  // Also takes into account do_activate, indirectly.
 2622 
 2623     return g_ErrorLevel->Assign(ERRORLEVEL_NONE);} // Indicate success.
 2624 
 2625 error:
 2626     return SetErrorLevelOrThrow();
 2627 }
 2628 
 2629 
 2630 
 2631 ResultType Line::ControlMove(LPTSTR aControl, LPTSTR aX, LPTSTR aY, LPTSTR aWidth, LPTSTR aHeight
 2632     , LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2633 {
 2634     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2635     if (!target_window)
 2636         goto error;
 2637     HWND control_window = ControlExist(target_window, aControl); // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2638     if (!control_window)
 2639         goto error;
 2640 
 2641     POINT point;
 2642     point.x = *aX ? ATOI(aX) : COORD_UNSPECIFIED;
 2643     point.y = *aY ? ATOI(aY) : COORD_UNSPECIFIED;
 2644 
 2645     // First convert the user's given coordinates -- which by default are relative to the window's
 2646     // upper left corner -- to screen coordinates:
 2647     if (point.x != COORD_UNSPECIFIED || point.y != COORD_UNSPECIFIED)
 2648     {
 2649         RECT rect;
 2650         // v1.0.44.13: Below was fixed to allow for the fact that target_window might be the control
 2651         // itself (e.g. via ahk_id %ControlHWND%).  For consistency with ControlGetPos and other things,
 2652         // it seems best to call GetNonChildParent rather than GetParent(); for example, a Tab control
 2653         // that contains a child window that in turn contains the actual controls should probably report
 2654         // the position of each control relative to the dialog itself rather than the tab control or its
 2655         // master window.  The lost argument in favor of GetParent is that it seems more flexible, such
 2656         // as cases where the script has called SetParent() to make a top-level window the child of some
 2657         // other window, in which case the target control's immediate parent should be used, not its most
 2658         // distant ancestor. This might also be desirable for controls that are children of other controls,
 2659         // such as Combobox's Edit.
 2660         if (!GetWindowRect(target_window == control_window ? GetNonChildParent(target_window) : target_window
 2661             , &rect))
 2662             goto error;
 2663         if (point.x != COORD_UNSPECIFIED)
 2664             point.x += rect.left;
 2665         if (point.y != COORD_UNSPECIFIED)
 2666             point.y += rect.top;
 2667     }
 2668 
 2669     // If either coordinate is unspecified, put the control's current screen coordinate(s)
 2670     // into point:
 2671     RECT control_rect;
 2672     if (!GetWindowRect(control_window, &control_rect))
 2673         goto error;
 2674     if (point.x == COORD_UNSPECIFIED)
 2675         point.x = control_rect.left;
 2676     if (point.y == COORD_UNSPECIFIED)
 2677         point.y = control_rect.top;
 2678 
 2679     // Use the immediate parent since controls can themselves have child controls:
 2680     HWND immediate_parent = GetParent(control_window);
 2681     if (!immediate_parent)
 2682         goto error;
 2683 
 2684     // Convert from absolute screen coordinates to coordinates used with MoveWindow(),
 2685     // which are relative to control_window's parent's client area:
 2686     if (!ScreenToClient(immediate_parent, &point))
 2687         goto error;
 2688 
 2689     MoveWindow(control_window
 2690         , point.x
 2691         , point.y
 2692         , *aWidth ? ATOI(aWidth) : control_rect.right - control_rect.left
 2693         , *aHeight ? ATOI(aHeight) : control_rect.bottom - control_rect.top
 2694         , TRUE);  // Do repaint.
 2695 
 2696     DoControlDelay
 2697     return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 2698 
 2699 error:
 2700     return SetErrorLevelOrThrow();
 2701 }
 2702 
 2703 
 2704 
 2705 ResultType Line::ControlGetPos(LPTSTR aControl, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2706 {
 2707     Var *output_var_x = ARGVAR1;  // Ok if NULL. Load-time validation has ensured that these are valid output variables (e.g. not built-in vars).
 2708     Var *output_var_y = ARGVAR2;  // Ok if NULL.
 2709     Var *output_var_width = ARGVAR3;  // Ok if NULL.
 2710     Var *output_var_height = ARGVAR4;  // Ok if NULL.
 2711 
 2712     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2713     HWND control_window = target_window ? ControlExist(target_window, aControl) : NULL; // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2714     if (!control_window)
 2715     {
 2716         if (output_var_x)
 2717             output_var_x->Assign();
 2718         if (output_var_y)
 2719             output_var_y->Assign();
 2720         if (output_var_width)
 2721             output_var_width->Assign();
 2722         if (output_var_height)
 2723             output_var_height->Assign();
 2724         return OK;
 2725     }
 2726 
 2727     RECT parent_rect, child_rect;
 2728     // Realistically never fails since DetermineTargetWindow() and ControlExist() should always yield
 2729     // valid window handles:
 2730     GetWindowRect(target_window == control_window ? GetNonChildParent(target_window) : target_window
 2731         , &parent_rect); // v1.0.44.13: Above was fixed to allow for the fact that target_window might be the control itself (e.g. via ahk_id %ControlHWND%).  See ControlMove for details.
 2732     GetWindowRect(control_window, &child_rect);
 2733 
 2734     if (output_var_x && !output_var_x->Assign(child_rect.left - parent_rect.left))
 2735         return FAIL;
 2736     if (output_var_y && !output_var_y->Assign(child_rect.top - parent_rect.top))
 2737         return FAIL;
 2738     if (output_var_width && !output_var_width->Assign(child_rect.right - child_rect.left))
 2739         return FAIL;
 2740     if (output_var_height && !output_var_height->Assign(child_rect.bottom - child_rect.top))
 2741         return FAIL;
 2742 
 2743     return OK;
 2744 }
 2745 
 2746 
 2747 
 2748 ResultType Line::ControlGetFocus(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2749 {
 2750     Var &output_var = *OUTPUT_VAR; // Must be resolved only once and prior to DetermineTargetWindow().  See Line::WinGetClass() for explanation.
 2751     output_var.Assign();  // Set default: blank for the output variable.
 2752     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2753     if (!target_window)
 2754         goto error;
 2755 
 2756     GUITHREADINFO guithreadInfo;
 2757     guithreadInfo.cbSize = sizeof(GUITHREADINFO);
 2758     if (!GetGUIThreadInfo(GetWindowThreadProcessId(target_window, NULL), &guithreadInfo))
 2759         goto error;
 2760 
 2761     class_and_hwnd_type cah;
 2762     TCHAR class_name[WINDOW_CLASS_SIZE];
 2763     cah.hwnd = guithreadInfo.hwndFocus;
 2764     cah.class_name = class_name;
 2765     if (!GetClassName(cah.hwnd, class_name, _countof(class_name) - 5)) // -5 to allow room for sequence number.
 2766         goto error;
 2767     
 2768     cah.class_count = 0;  // Init for the below.
 2769     cah.is_found = false; // Same.
 2770     EnumChildWindows(target_window, EnumChildFindSeqNum, (LPARAM)&cah);
 2771     if (!cah.is_found)
 2772         goto error;
 2773     // Append the class sequence number onto the class name set the output param to be that value:
 2774     sntprintfcat(class_name, _countof(class_name), _T("%d"), cah.class_count);
 2775     g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 2776     return output_var.Assign(class_name);
 2777 
 2778 error:
 2779     return SetErrorLevelOrThrow();
 2780 }
 2781 
 2782 
 2783 
 2784 BOOL CALLBACK EnumChildFindSeqNum(HWND aWnd, LPARAM lParam)
 2785 {
 2786     class_and_hwnd_type &cah = *(class_and_hwnd_type *)lParam;  // For performance and convenience.
 2787     TCHAR class_name[WINDOW_CLASS_SIZE];
 2788     if (!GetClassName(aWnd, class_name, _countof(class_name)))
 2789         return TRUE;  // Continue the enumeration.
 2790     if (!_tcscmp(class_name, cah.class_name)) // Class names match.
 2791     {
 2792         ++cah.class_count;
 2793         if (aWnd == cah.hwnd)  // The caller-specified window has been found.
 2794         {
 2795             cah.is_found = true;
 2796             return FALSE;
 2797         }
 2798     }
 2799     return TRUE; // Continue enumeration until a match is found or there aren't any windows remaining.
 2800 }
 2801 
 2802 
 2803 
 2804 ResultType Line::ControlFocus(LPTSTR aControl, LPTSTR aTitle, LPTSTR aText
 2805     , LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2806 {
 2807     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2808     if (!target_window)
 2809         goto error;
 2810     HWND control_window = ControlExist(target_window, aControl); // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2811     if (!control_window)
 2812         goto error;
 2813 
 2814     // Unlike many of the other Control commands, this one requires AttachThreadInput()
 2815     // to have any realistic chance of success (though sometimes it may work by pure
 2816     // chance even without it):
 2817     ATTACH_THREAD_INPUT
 2818 
 2819     if (SetFocus(control_window))
 2820     {
 2821         g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 2822         DoControlDelay;
 2823     }
 2824 
 2825     // Very important to detach any threads whose inputs were attached above,
 2826     // prior to returning, otherwise the next attempt to attach thread inputs
 2827     // for these particular windows may result in a hung thread or other
 2828     // undesirable effect:
 2829     DETACH_THREAD_INPUT
 2830 
 2831     return OK;
 2832 
 2833 error:
 2834     return SetErrorLevelOrThrow();
 2835 }
 2836 
 2837 
 2838 
 2839 ResultType Line::ControlSetText(LPTSTR aControl, LPTSTR aNewText, LPTSTR aTitle, LPTSTR aText
 2840     , LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2841 {
 2842     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2843     if (!target_window)
 2844         goto error;
 2845     HWND control_window = ControlExist(target_window, aControl); // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2846     if (!control_window)
 2847         goto error;
 2848     // SendMessage must be used, not PostMessage(), at least for some (probably most) apps.
 2849     // Also: No need to call IsWindowHung() because SendMessageTimeout() should return
 2850     // immediately if the OS already "knows" the window is hung:
 2851     DWORD_PTR result;
 2852     SendMessageTimeout(control_window, WM_SETTEXT, (WPARAM)0, (LPARAM)aNewText
 2853         , SMTO_ABORTIFHUNG, 5000, &result);
 2854     DoControlDelay;
 2855     return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 2856 
 2857 error:
 2858     return SetErrorLevelOrThrow();
 2859 }
 2860 
 2861 
 2862 
 2863 ResultType Line::ControlGetText(LPTSTR aControl, LPTSTR aTitle, LPTSTR aText
 2864     , LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 2865 {
 2866     Var &output_var = *OUTPUT_VAR;
 2867     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 2868     HWND control_window = target_window ? ControlExist(target_window, aControl) : NULL; // This can return target_window itself for cases such as ahk_id %ControlHWND%.
 2869     // Even if control_window is NULL, we want to continue on so that the output
 2870     // param is set to be the empty string, which is the proper thing to do
 2871     // rather than leaving whatever was in there before.
 2872 
 2873     // Handle the output parameter.  This section is similar to that in
 2874     // PerformAssign().  Note: Using GetWindowTextTimeout() vs. GetWindowText()
 2875     // because it is able to get text from more types of controls (e.g. large edit controls):
 2876     VarSizeType space_needed = control_window ? GetWindowTextTimeout(control_window) + 1 : 1; // 1 for terminator.
 2877     if (space_needed > g_MaxVarCapacity) // Allow the command to succeed by truncating the text.
 2878         space_needed = g_MaxVarCapacity;
 2879 
 2880     // Set up the var, enlarging it if necessary.  If the output_var is of type VAR_CLIPBOARD,
 2881     // this call will set up the clipboard for writing:
 2882     if (output_var.AssignString(NULL, space_needed - 1) != OK)
 2883         return FAIL;  // It already displayed the error.
 2884     // Fetch the text directly into the var.  Also set the length explicitly
 2885     // in case actual size written was off from the estimated size (since
 2886     // GetWindowTextLength() can return more space that will actually be required
 2887     // in certain circumstances, see MS docs):
 2888     if (control_window)
 2889     {
 2890         if (   !(output_var.SetCharLength((VarSizeType)GetWindowTextTimeout(control_window
 2891             , output_var.Contents(), space_needed)))   ) // There was no text to get or GetWindowTextTimeout() failed.
 2892             *output_var.Contents() = '\0';  // Safe because Assign() gave us a non-constant memory area.
 2893     }
 2894     else
 2895     {
 2896         *output_var.Contents() = '\0';
 2897         output_var.SetCharLength(0);
 2898         // And leave bSucceeded set to false to distinguish a non-existent control
 2899         // from a one that does exist but returns no text.
 2900     }
 2901     // Consider the above to be always successful, even if the window wasn't found, except
 2902     // when below returns an error:
 2903     ResultType result = output_var.Close(); // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
 2904     if (result != OK)
 2905         return result;
 2906     return SetErrorLevelOrThrowBool(!control_window);
 2907 }
 2908 
 2909 
 2910 
 2911 ResultType Line::ControlGetListView(Var &aOutputVar, HWND aHwnd, LPTSTR aOptions)
 2912 // Called by ControlGet() below.  It has ensured that aHwnd is a valid handle to a ListView.
 2913 {
 2914     aOutputVar.Assign(); // Init to blank in case of early return.
 2915 
 2916     // GET ROW COUNT
 2917     LRESULT row_count;
 2918     if (!SendMessageTimeout(aHwnd, LVM_GETITEMCOUNT, 0, 0, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&row_count)) // Timed out or failed.
 2919         return SetErrorLevelOrThrow();
 2920 
 2921     // GET COLUMN COUNT
 2922     // Through testing, could probably get to a level of 90% certainty that a ListView for which
 2923     // InsertColumn() was never called (or was called only once) might lack a header control if the LV is
 2924     // created in List/Icon view-mode and/or with LVS_NOCOLUMNHEADER. The problem is that 90% doesn't
 2925     // seem to be enough to justify elimination of the code for "undetermined column count" mode.  If it
 2926     // ever does become a certainty, the following could be changed:
 2927     // 1) The extra code for "undetermined" mode rather than simply forcing col_count to be 1.
 2928     // 2) Probably should be kept for compatibility: -1 being returned when undetermined "col count".
 2929     //
 2930     // The following approach might be the only simple yet reliable way to get the column count (sending
 2931     // LVM_GETITEM until it returns false doesn't work because it apparently returns true even for
 2932     // nonexistent subitems -- the same is reported to happen with LVM_GETCOLUMN and such, though I seem
 2933     // to remember that LVM_SETCOLUMN fails on non-existent columns -- but calling that on a ListView
 2934     // that isn't in Report view has been known to traumatize the control).
 2935     // Fix for v1.0.37.01: It appears that the header doesn't always exist.  For example, when an
 2936     // Explorer window opens and is *initially* in icon or list mode vs. details/tiles mode, testing
 2937     // shows that there is no header control.  Testing also shows that there is exactly one column
 2938     // in such cases but only for Explorer and other things that avoid creating the invisible columns.
 2939     // For example, a script can create a ListView in Icon-mode and give it retrievable column data for
 2940     // columns beyond the first.  Thus, having the undetermined-col-count mode preserves flexibility
 2941     // by allowing individual columns beyond the first to be retrieved.  On a related note, testing shows
 2942     // that attempts to explicitly retrieve columns (i.e. fields/subitems) other than the first in the
 2943     // case of Explorer's Icon/List view modes behave the same as fetching the first column (i.e. Col3
 2944     // would retrieve the same text as specifying Col1 or not having the Col option at all).
 2945     // Obsolete because not always true: Testing shows that a ListView always has a header control
 2946     // (at least on XP), even if you can't see it (such as when the view is Icon/Tile or when -Hdr has
 2947     // been specified in the options).
 2948     HWND header_control;
 2949     LRESULT col_count = -1;  // Fix for v1.0.37.01: Use -1 to indicate "undetermined col count".
 2950     if (SendMessageTimeout(aHwnd, LVM_GETHEADER, 0, 0, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&header_control)
 2951         && header_control) // Relies on short-circuit boolean order.
 2952         SendMessageTimeout(header_control, HDM_GETITEMCOUNT, 0, 0, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&col_count);
 2953         // Return value is not checked because if it fails, col_count is left at its default of -1 set above.
 2954         // In fact, if any of the above conditions made it impossible to determine col_count, col_count stays
 2955         // at -1 to indicate "undetermined".
 2956 
 2957     // PARSE OPTIONS (a simple vs. strict method is used to reduce code size)
 2958     bool get_count = tcscasestr(aOptions, _T("Count"));
 2959     bool include_selected_only = tcscasestr(aOptions, _T("Selected")); // Explicit "ed" to reserve "Select" for possible future use.
 2960     bool include_focused_only = tcscasestr(aOptions, _T("Focused"));  // Same.
 2961     LPTSTR col_option = tcscasestr(aOptions, _T("Col")); // Also used for mode "Count Col"
 2962     int requested_col = col_option ? ATOI(col_option + 3) - 1 : -1;
 2963     // If the above yields a negative col number for any reason, it's ok because below will just ignore it.
 2964     if (col_count > -1 && requested_col > -1 && requested_col >= col_count) // Specified column does not exist.
 2965         return SetErrorLevelOrThrow();
 2966 
 2967     // IF THE "COUNT" OPTION IS PRESENT, FULLY HANDLE THAT AND RETURN
 2968     if (get_count)
 2969     {
 2970         int result; // Must be signed to support writing a col count of -1 to aOutputVar.
 2971         if (include_focused_only) // Listed first so that it takes precedence over include_selected_only.
 2972         {
 2973             if (!SendMessageTimeout(aHwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&result)) // Timed out or failed.
 2974                 return SetErrorLevelOrThrow();
 2975             ++result; // i.e. Set it to 0 if not found, or the 1-based row-number otherwise.
 2976         }
 2977         else if (include_selected_only)
 2978         {
 2979             if (!SendMessageTimeout(aHwnd, LVM_GETSELECTEDCOUNT, 0, 0, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&result)) // Timed out or failed.
 2980                 return SetErrorLevelOrThrow();
 2981         }
 2982         else if (col_option) // "Count Col" returns the number of columns.
 2983             result = (int)col_count;
 2984         else // Total row count.
 2985             result = (int)row_count;
 2986         g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 2987         return aOutputVar.Assign(result);
 2988     }
 2989 
 2990     // FINAL CHECKS
 2991     if (row_count < 1 || !col_count) // But don't return when col_count == -1 (i.e. always make the attempt when col count is undetermined).
 2992         return g_ErrorLevel->Assign(ERRORLEVEL_NONE);  // No text in the control, so indicate success.
 2993     
 2994     // Notes about the following struct definitions:  The layout of LVITEM depends on
 2995     // which platform THIS executable was compiled for, but we need it to match what
 2996     // the TARGET process expects.  If the target process is 32-bit and we are 64-bit
 2997     // or vice versa, LVITEM can't be used.  The following structs are copies of
 2998     // LVITEM with UINT (32-bit) or UINT64 (64-bit) in place of the pointer fields.
 2999     struct LVITEM32
 3000     {
 3001         UINT mask;
 3002         int iItem;
 3003         int iSubItem;
 3004         UINT state;
 3005         UINT stateMask;
 3006         UINT pszText;
 3007         int cchTextMax;
 3008         int iImage;
 3009         UINT lParam;
 3010         int iIndent;
 3011         int iGroupId;
 3012         UINT cColumns;
 3013         UINT puColumns;
 3014         UINT piColFmt;
 3015         int iGroup;
 3016     };
 3017     struct LVITEM64
 3018     {
 3019         UINT mask;
 3020         int iItem;
 3021         int iSubItem;
 3022         UINT state;
 3023         UINT stateMask;
 3024         UINT64 pszText;
 3025         int cchTextMax;
 3026         int iImage;
 3027         UINT64 lParam;
 3028         int iIndent;
 3029         int iGroupId;
 3030         UINT cColumns;
 3031         UINT64 puColumns;
 3032         UINT64 piColFmt;
 3033         int iGroup;
 3034     };
 3035     union
 3036     {
 3037         LVITEM32 i32;
 3038         LVITEM64 i64;
 3039     } local_lvi;
 3040 
 3041     // ALLOCATE INTERPROCESS MEMORY FOR TEXT RETRIEVAL
 3042     HANDLE handle;
 3043     LPVOID p_remote_lvi; // Not of type LPLVITEM to help catch bugs where p_remote_lvi->member is wrongly accessed here in our process.
 3044     if (   !(p_remote_lvi = AllocInterProcMem(handle, sizeof(local_lvi) + _TSIZE(LV_REMOTE_BUF_SIZE), aHwnd, PROCESS_QUERY_INFORMATION))   ) // Allocate both the LVITEM struct and its internal string buffer in one go because VirtualAllocEx() is probably a high overhead call.
 3045         return SetErrorLevelOrThrow();
 3046     LPVOID p_remote_text = (LPVOID)((UINT_PTR)p_remote_lvi + sizeof(local_lvi)); // The next buffer is the memory area adjacent to, but after the struct.
 3047     
 3048     // PREPARE LVI STRUCT MEMBERS FOR TEXT RETRIEVAL
 3049     if (IsProcess64Bit(handle))
 3050     {
 3051         // See the section below for comments.
 3052         local_lvi.i64.cchTextMax = LV_REMOTE_BUF_SIZE - 1;
 3053         local_lvi.i64.pszText = (UINT64)p_remote_text;
 3054     }
 3055     else
 3056     {
 3057         // Subtract 1 because of that nagging doubt about size vs. length. Some MSDN examples subtract one,
 3058         // such as TabCtrl_GetItem()'s cchTextMax:
 3059         local_lvi.i32.cchTextMax = LV_REMOTE_BUF_SIZE - 1; // Note that LVM_GETITEM doesn't update this member to reflect the new length.
 3060         local_lvi.i32.pszText = (UINT)(UINT_PTR)p_remote_text; // Extra cast avoids a truncation warning (C4311).
 3061     }
 3062 
 3063     LRESULT i, next, length, total_length;
 3064     bool is_selective = include_focused_only || include_selected_only;
 3065     bool single_col_mode = (requested_col > -1 || col_count == -1); // Get only one column in these cases.
 3066 
 3067     // ESTIMATE THE AMOUNT OF MEMORY NEEDED TO STORE ALL THE TEXT
 3068     // It's important to note that a ListView might legitimately have a collection of rows whose
 3069     // fields are all empty.  Since it is difficult to know whether the control is truly owner-drawn
 3070     // (checking its style might not be enough?), there is no way to distinguish this condition
 3071     // from one where the control's text can't be retrieved due to being owner-drawn.  In any case,
 3072     // this all-empty-field behavior simplifies the code and will be documented in the help file.
 3073     for (i = 0, next = -1, total_length = 0; i < row_count; ++i) // For each row:
 3074     {
 3075         if (is_selective)
 3076         {
 3077             // Fix for v1.0.37.01: Prevent an infinite loop that might occur if the target control no longer
 3078             // exists (perhaps having been closed in the middle of the operation) or is permanently hung.
 3079             // If GetLastError() were to return zero after the below, it would mean the function timed out.
 3080             // However, rather than checking and retrying, it seems better to abort the operation because:
 3081             // 1) Timeout should be quite rare.
 3082             // 2) Reduces code size.
 3083             // 3) Having a retry really should be accompanied by SLEEP_WITHOUT_INTERRUPTION because all this
 3084             //    time our thread would not pumping messages (and worse, if the keyboard/mouse hooks are installed,
 3085             //    mouse/key lag would occur).
 3086             if (!SendMessageTimeout(aHwnd, LVM_GETNEXTITEM, next, include_focused_only ? LVNI_FOCUSED : LVNI_SELECTED
 3087                 , SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&next) // Timed out or failed.
 3088                 || next == -1) // No next item.  Relies on short-circuit boolean order.
 3089                 break; // End of estimation phase (if estimate is too small, the text retrieval below will truncate it).
 3090         }
 3091         else
 3092             next = i;
 3093         for (local_lvi.i32.iSubItem = (requested_col > -1) ? requested_col : 0 // iSubItem is which field to fetch. If it's zero, the item vs. subitem will be fetched.
 3094             ; col_count == -1 || local_lvi.i32.iSubItem < col_count // If column count is undetermined (-1), always make the attempt.
 3095             ; ++local_lvi.i32.iSubItem) // For each column:
 3096         {
 3097             if (WriteProcessMemory(handle, p_remote_lvi, &local_lvi, sizeof(local_lvi), NULL)
 3098                 && SendMessageTimeout(aHwnd, LVM_GETITEMTEXT, next, (LPARAM)p_remote_lvi, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&length))
 3099                 total_length += length;
 3100             //else timed out or failed, don't include the length in the estimate.  Instead, the
 3101             // text-fetching routine below will ensure the text doesn't overflow the var capacity.
 3102             if (single_col_mode)
 3103                 break;
 3104         }
 3105     }
 3106     // Add to total_length enough room for one linefeed per row, and one tab after each column
 3107     // except the last (formula verified correct, though it's inflated by 1 for safety). "i" contains the
 3108     // actual number of rows that will be transcribed, which might be less than row_count if is_selective==true.
 3109     total_length += i * (single_col_mode ? 1 : col_count);
 3110 
 3111     // SET UP THE OUTPUT VARIABLE, ENLARGING IT IF NECESSARY
 3112     // If the aOutputVar is of type VAR_CLIPBOARD, this call will set up the clipboard for writing:
 3113     aOutputVar.AssignString(NULL, (VarSizeType)total_length, true, false); // Since failure is extremely rare, continue onward using the available capacity.
 3114     LPTSTR contents = aOutputVar.Contents();
 3115     LRESULT capacity = (int)aOutputVar.CharCapacity(); // LRESULT avoids signed vs. unsigned compiler warnings.
 3116     if (capacity > 0) // For maintainability, avoid going negative.
 3117         --capacity; // Adjust to exclude the zero terminator, which simplifies things below.
 3118 
 3119     // RETRIEVE THE TEXT FROM THE REMOTE LISTVIEW
 3120     // Start total_length at zero in case actual size is greater than estimate, in which case only a partial set of text along with its '\t' and '\n' chars will be written.
 3121     for (i = 0, next = -1, total_length = 0; i < row_count; ++i) // For each row:
 3122     {
 3123         if (is_selective)
 3124         {
 3125             // Fix for v1.0.37.01: Prevent an infinite loop (for details, see comments in the estimation phase above).
 3126             if (!SendMessageTimeout(aHwnd, LVM_GETNEXTITEM, next, include_focused_only ? LVNI_FOCUSED : LVNI_SELECTED
 3127                 , SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&next) // Timed out or failed.
 3128                 || next == -1) // No next item.
 3129                 break; // See comment above for why unconditional break vs. continue.
 3130         }
 3131         else // Retrieve every row, so the "next" row becomes the "i" index.
 3132             next = i;
 3133         // Insert a linefeed before each row except the first:
 3134         if (i && total_length < capacity) // If we're at capacity, it will exit the loops when the next field is read.
 3135         {
 3136             *contents++ = '\n';
 3137             ++total_length;
 3138         }
 3139 
 3140         // iSubItem is which field to fetch. If it's zero, the item vs. subitem will be fetched:
 3141         for (local_lvi.i32.iSubItem = (requested_col > -1) ? requested_col : 0
 3142             ; col_count == -1 || local_lvi.i32.iSubItem < col_count // If column count is undetermined (-1), always make the attempt.
 3143             ; ++local_lvi.i32.iSubItem) // For each column:
 3144         {
 3145             // Insert a tab before each column except the first and except when in single-column mode:
 3146             if (!single_col_mode && local_lvi.i32.iSubItem && total_length < capacity)  // If we're at capacity, it will exit the loops when the next field is read.
 3147             {
 3148                 *contents++ = '\t';
 3149                 ++total_length;
 3150             }
 3151 
 3152             if (!WriteProcessMemory(handle, p_remote_lvi, &local_lvi, sizeof(local_lvi), NULL)
 3153                 || !SendMessageTimeout(aHwnd, LVM_GETITEMTEXT, next, (LPARAM)p_remote_lvi, SMTO_ABORTIFHUNG, 2000, (PDWORD_PTR)&length))
 3154                 continue; // Timed out or failed. It seems more useful to continue getting text rather than aborting the operation.
 3155 
 3156             // Otherwise, the message was successfully sent.
 3157             if (length > 0)
 3158             {
 3159                 if (total_length + length > capacity)
 3160                     goto break_both; // "goto" for simplicity and code size reduction.
 3161                 // Otherwise:
 3162                 // READ THE TEXT FROM THE REMOTE PROCESS
 3163                 // Although MSDN has the following comment about LVM_GETITEM, it is not present for
 3164                 // LVM_GETITEMTEXT. Therefore, to improve performance (by avoiding a second call to
 3165                 // ReadProcessMemory) and to reduce code size, we'll take them at their word until
 3166                 // proven otherwise.  Here is the MSDN comment about LVM_GETITEM: "Applications
 3167                 // should not assume that the text will necessarily be placed in the specified
 3168                 // buffer. The control may instead change the pszText member of the structure
 3169                 // to point to the new text, rather than place it in the buffer."
 3170                 if (ReadProcessMemory(handle, p_remote_text, contents, length * sizeof(TCHAR), NULL))
 3171                 {
 3172                     contents += length; // Point it to the position where the next char will be written.
 3173                     total_length += length; // Recalculate length in case its different than the estimate (for any reason).
 3174                 }
 3175                 //else it failed; but even so, continue on to put in a tab (if called for).
 3176             }
 3177             //else length is zero; but even so, continue on to put in a tab (if called for).
 3178             if (single_col_mode)
 3179                 break;
 3180         } // for() each column
 3181     } // for() each row
 3182 
 3183 break_both:
 3184     if (contents) // Might be NULL if Assign() failed and thus var has zero capacity.
 3185         *contents = '\0'; // Final termination.  Above has reserved room for this one byte.
 3186 
 3187     // CLEAN UP
 3188     FreeInterProcMem(handle, p_remote_lvi);
 3189     aOutputVar.Close(); // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
 3190     aOutputVar.SetCharLength((VarSizeType)total_length); // Update to actual vs. estimated length.
 3191     return g_ErrorLevel->Assign(ERRORLEVEL_NONE);  // Indicate success.
 3192 }
 3193 
 3194 
 3195 
 3196 ResultType Line::StatusBarGetText(LPTSTR aPart, LPTSTR aTitle, LPTSTR aText
 3197     , LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3198 {
 3199     // Note: ErrorLevel is handled by StatusBarUtil(), below.
 3200     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 3201     HWND control_window = target_window ? ControlExist(target_window, _T("msctls_statusbar321")) : NULL;
 3202     // Call this even if control_window is NULL because in that case, it will set the output var to
 3203     // be blank for us:
 3204     return StatusBarUtil(OUTPUT_VAR, control_window, ATOI(aPart)); // It will handle any zero part# for us.
 3205 }
 3206 
 3207 
 3208 
 3209 ResultType Line::StatusBarWait(LPTSTR aTextToWaitFor, LPTSTR aSeconds, LPTSTR aPart, LPTSTR aTitle, LPTSTR aText
 3210     , LPTSTR aInterval, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3211 // Since other script threads can interrupt this command while it's running, it's important that
 3212 // this command not refer to sArgDeref[] and sArgVar[] anytime after an interruption becomes possible.
 3213 // This is because an interrupting thread usually changes the values to something inappropriate for this thread.
 3214 {
 3215     // Note: ErrorLevel is handled by StatusBarUtil(), below.
 3216     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 3217     // Make a copy of any memory areas that are volatile (due to Deref buf being overwritten
 3218     // if a new hotkey subroutine is launched while we are waiting) but whose contents we
 3219     // need to refer to while we are waiting:
 3220     TCHAR text_to_wait_for[4096];
 3221     tcslcpy(text_to_wait_for, aTextToWaitFor, _countof(text_to_wait_for));
 3222     HWND control_window = target_window ? ControlExist(target_window, _T("msctls_statusbar321")) : NULL;
 3223     return StatusBarUtil(NULL, control_window, ATOI(aPart) // It will handle a NULL control_window or zero part# for us.
 3224         , text_to_wait_for, *aSeconds ? (int)(ATOF(aSeconds)*1000) : -1 // Blank->indefinite.  0 means 500ms.
 3225         , ATOI(aInterval));
 3226 }
 3227 
 3228 
 3229 
 3230 ResultType Line::ScriptPostSendMessage(bool aUseSend)
 3231 // Arg list:
 3232 // sArgDeref[0]: Msg number
 3233 // sArgDeref[1]: wParam
 3234 // sArgDeref[2]: lParam
 3235 // sArgDeref[3]: Control
 3236 // sArgDeref[4]: WinTitle
 3237 // sArgDeref[5]: WinText
 3238 // sArgDeref[6]: ExcludeTitle
 3239 // sArgDeref[7]: ExcludeText
 3240 // sArgDeref[8]: Timeout
 3241 {
 3242     HWND target_window, control_window;
 3243     if (   !(target_window = DetermineTargetWindow(sArgDeref[4], sArgDeref[5], sArgDeref[6], sArgDeref[7]))
 3244         || !(control_window = *sArgDeref[3] ? ControlExist(target_window, sArgDeref[3]) : target_window)   ) // Relies on short-circuit boolean order.
 3245         return SetErrorLevelOrThrowStr(aUseSend ? _T("FAIL") : ERRORLEVEL_ERROR); // Need a special value to distinguish this from numeric reply-values.
 3246 
 3247     // v1.0.40.05: Support the passing of a literal (quoted) string by checking whether the
 3248     // original/raw arg's first character is '"'.  The avoids the need to put the string into a
 3249     // variable and then pass something like &MyVar.
 3250     UINT msg = ArgToUInt(1);
 3251     WPARAM wparam = (mArgc > 1 && mArg[1].text[0] == '"') ? (WPARAM)sArgDeref[1] : (WPARAM)ArgToInt64(2);
 3252     LPARAM lparam = (mArgc > 2 && mArg[2].text[0] == '"') ? (LPARAM)sArgDeref[2] : (LPARAM)ArgToInt64(3);
 3253     // Timeout increased from 2000 to 5000 in v1.0.27:
 3254     // jackieku: specify timeout by the parameter.
 3255     UINT timeout = mArgc > 8 ? ArgToUInt(9) : 5000;
 3256 
 3257     // Fixed for v1.0.48.04: Make copies of the wParam and lParam variables (if eligible for updating) prior
 3258     // to sending the message in case the message triggers a callback or OnMessage function, which would be
 3259     // likely to change the contents of the mArg array before we're doing using them after the Post/SendMsg.
 3260     // Seems best to do the above EVEN for PostMessage in case it can ever trigger a SendMessage internally
 3261     // (I seem to remember that the OS sometimes converts a PostMessage call into a SendMessage if the
 3262     // origin and destination are the same thread.)
 3263     // v1.0.43.06: If either wParam or lParam contained the address of a variable, update the mLength
 3264     // member after sending the message in case the receiver of the message wrote something to the buffer.
 3265     // This is similar to the way "Str" parameters work in DllCall.
 3266     Var *var_to_update[2];
 3267     int i;
 3268     for (i = 1; i < 3; ++i) // Two iterations: wParam and lParam.
 3269     {
 3270         if (mArgc > i) // The arg exists.
 3271         {
 3272             ArgStruct &this_arg = mArg[i];
 3273             var_to_update[i-1] = this_arg.text[0] == '&'  // Must start with '&', so things like 5+&MyVar aren't supported.
 3274                 && this_arg.deref && !this_arg.deref->is_function
 3275                 && this_arg.deref->var->Type() == VAR_NORMAL // Check VAR_NORMAL to be extra-certain it can't be the clipboard or a built-in variable (ExpandExpression() probably prevents taking the address of such a variable, but might not stop it from being in the deref array that way).
 3276                 ? this_arg.deref->var
 3277                 : NULL;
 3278         }
 3279         else // L32: Bugfix - var_to_update must be initialised.
 3280             var_to_update[i-1] = NULL;
 3281     }
 3282 
 3283     if (aUseSend)
 3284     {
 3285         DWORD_PTR dwResult;
 3286         if (!SendMessageTimeout(control_window, msg, wparam, lparam, SMTO_ABORTIFHUNG, timeout, &dwResult))
 3287             return SetErrorLevelOrThrowStr(_T("FAIL")); // Need a special value to distinguish this from numeric reply-values.
 3288         g_ErrorLevel->Assign(dwResult); // UINT seems best most of the time?
 3289     }
 3290     else // Post vs. Send
 3291     {
 3292         if (!PostMessage(control_window, msg, wparam, lparam))
 3293             return SetErrorLevelOrThrow();
 3294         g_ErrorLevel->Assign(ERRORLEVEL_NONE);
 3295     }
 3296 
 3297     for (i = 0; i < 2; ++i) // Two iterations: wParam and lParam.
 3298         if (var_to_update[i])
 3299             var_to_update[i]->SetLengthFromContents();
 3300 
 3301     return OK;
 3302 }
 3303 
 3304 
 3305 
 3306 ResultType Line::ScriptProcess(LPTSTR aCmd, LPTSTR aProcess, LPTSTR aParam3)
 3307 {
 3308     ProcessCmds process_cmd = ConvertProcessCmd(aCmd);
 3309     // Runtime error is rare since it is caught at load-time unless it's in a var. ref.
 3310     if (process_cmd == PROCESS_CMD_INVALID)
 3311         return LineError(ERR_PARAM1_INVALID, FAIL, aCmd);
 3312 
 3313     HANDLE hProcess;
 3314     DWORD pid, priority;
 3315     BOOL result;
 3316 
 3317     switch (process_cmd)
 3318     {
 3319     case PROCESS_CMD_EXIST:
 3320         return g_ErrorLevel->Assign(*aProcess ? ProcessExist(aProcess) : GetCurrentProcessId()); // The discovered PID or zero if none.
 3321 
 3322     case PROCESS_CMD_CLOSE:
 3323         if (pid = ProcessExist(aProcess))  // Assign
 3324         {
 3325             if (hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
 3326             {
 3327                 result = TerminateProcess(hProcess, 0);
 3328                 CloseHandle(hProcess);
 3329                 return g_ErrorLevel->Assign(result ? pid : 0); // Indicate success or failure.
 3330             }
 3331         }
 3332         // Since above didn't return, yield a PID of 0 to indicate failure.
 3333         return g_ErrorLevel->Assign(_T("0"));
 3334 
 3335     case PROCESS_CMD_PRIORITY:
 3336         switch (_totupper(*aParam3))
 3337         {
 3338         case 'L': priority = IDLE_PRIORITY_CLASS; break;
 3339         case 'B': priority = BELOW_NORMAL_PRIORITY_CLASS; break;
 3340         case 'N': priority = NORMAL_PRIORITY_CLASS; break;
 3341         case 'A': priority = ABOVE_NORMAL_PRIORITY_CLASS; break;
 3342         case 'H': priority = HIGH_PRIORITY_CLASS; break;
 3343         case 'R': priority = REALTIME_PRIORITY_CLASS; break;
 3344         default:
 3345             return g_ErrorLevel->Assign(_T("0"));  // 0 indicates failure in this case (i.e. a PID of zero).
 3346         }
 3347         if (pid = *aProcess ? ProcessExist(aProcess) : GetCurrentProcessId())  // Assign
 3348         {
 3349             if (hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, pid)) // Assign
 3350             {
 3351                 result = SetPriorityClass(hProcess, priority);
 3352                 CloseHandle(hProcess);
 3353                 return g_ErrorLevel->Assign(result ? pid : 0); // Indicate success or failure.
 3354             }
 3355         }
 3356         // Otherwise, return a PID of 0 to indicate failure.
 3357         return g_ErrorLevel->Assign(_T("0"));
 3358 
 3359     case PROCESS_CMD_WAIT:
 3360     case PROCESS_CMD_WAITCLOSE:
 3361     {
 3362         // This section is similar to that used for WINWAIT and RUNWAIT:
 3363         bool wait_indefinitely;
 3364         int sleep_duration;
 3365         DWORD start_time;
 3366         if (*aParam3) // the param containing the timeout value isn't blank.
 3367         {
 3368             wait_indefinitely = false;
 3369             sleep_duration = (int)(ATOF(aParam3) * 1000); // Can be zero.
 3370             start_time = GetTickCount();
 3371         }
 3372         else
 3373         {
 3374             wait_indefinitely = true;
 3375             sleep_duration = 0; // Just to catch any bugs.
 3376         }
 3377         for (;;)
 3378         { // Always do the first iteration so that at least one check is done.
 3379             pid = ProcessExist(aProcess);
 3380             if (process_cmd == PROCESS_CMD_WAIT)
 3381             {
 3382                 if (pid)
 3383                     return g_ErrorLevel->Assign(pid);
 3384             }
 3385             else // PROCESS_CMD_WAITCLOSE
 3386             {
 3387                 // Since PID cannot always be determined (i.e. if process never existed, there was
 3388                 // no need to wait for it to close), for consistency, return 0 on success.
 3389                 if (!pid)
 3390                     return g_ErrorLevel->Assign(_T("0"));
 3391             }
 3392             // Must cast to int or any negative result will be lost due to DWORD type:
 3393             if (wait_indefinitely || (int)(sleep_duration - (GetTickCount() - start_time)) > SLEEP_INTERVAL_HALF)
 3394                 MsgSleep(100);  // For performance reasons, don't check as often as the WinWait family does.
 3395             else // Done waiting.
 3396                 return g_ErrorLevel->Assign(pid);
 3397                 // Above assigns 0 if "Process Wait" times out; or the PID of the process that still exists
 3398                 // if "Process WaitClose" times out.
 3399         } // for()
 3400     } // case
 3401     } // switch()
 3402 
 3403     return FAIL;  // Should never be executed; just here to catch bugs.
 3404 }
 3405 
 3406 
 3407 
 3408 ResultType Line::WinSetRegion(HWND aWnd, LPTSTR aPoints)
 3409 {
 3410     if (!*aPoints) // Attempt to restore the window's normal/correct region.
 3411     {
 3412         // Fix for v1.0.31.07: The old method used the following, but apparently it's not the correct
 3413         // way to restore a window's proper/normal region because when such a window is later maximized,
 3414         // it retains its incorrect/smaller region:
 3415         //if (GetWindowRect(aWnd, &rect))
 3416         //{
 3417         //  // Adjust the rect to keep the same size but have its upper-left corner at 0,0:
 3418         //  rect.right -= rect.left;
 3419         //  rect.bottom -= rect.top;
 3420         //  rect.left = 0;
 3421         //  rect.top = 0;
 3422         //  if (hrgn = CreateRectRgnIndirect(&rect)) // Assign
 3423         //  {
 3424         //      // Presumably, the system deletes the former region when upon a successful call to SetWindowRgn().
 3425         //      if (SetWindowRgn(aWnd, hrgn, TRUE))
 3426         //          return g_ErrorLevel->Assign(ERRORLEVEL_NONE);
 3427         //      // Otherwise, get rid of it since it didn't take effect:
 3428         //      DeleteObject(hrgn);
 3429         //  }
 3430         //}
 3431         //// Since above didn't return:
 3432         //return OK; // Let ErrorLevel tell the story.
 3433 
 3434         // It's undocumented by MSDN, but apparently setting the Window's region to NULL restores it
 3435         // to proper working order:
 3436         return SetErrorLevelOrThrowBool(!SetWindowRgn(aWnd, NULL, TRUE)); // Let ErrorLevel tell the story.
 3437     }
 3438 
 3439     #define MAX_REGION_POINTS 2000  // 2000 requires 16 KB of stack space.
 3440     POINT pt[MAX_REGION_POINTS];
 3441     int pt_count;
 3442     LPTSTR cp;
 3443 
 3444     // Set defaults prior to parsing options in case any options are absent:
 3445     int width = COORD_UNSPECIFIED;
 3446     int height = COORD_UNSPECIFIED;
 3447     int rr_width = COORD_UNSPECIFIED; // These two are for the rounded-rectangle method.
 3448     int rr_height = COORD_UNSPECIFIED;
 3449     bool use_ellipse = false;
 3450 
 3451     int fill_mode = ALTERNATE;
 3452     // Concerning polygon regions: ALTERNATE is used by default (somewhat arbitrarily, but it seems to be the
 3453     // more typical default).
 3454     // MSDN: "In general, the modes [ALTERNATE vs. WINDING] differ only in cases where a complex,
 3455     // overlapping polygon must be filled (for example, a five-sided polygon that forms a five-pointed
 3456     // star with a pentagon in the center). In such cases, ALTERNATE mode fills every other enclosed
 3457     // region within the polygon (that is, the points of the star), but WINDING mode fills all regions
 3458     // (that is, the points and the pentagon)."
 3459 
 3460     for (pt_count = 0, cp = aPoints; *(cp = omit_leading_whitespace(cp));)
 3461     {
 3462         // To allow the MAX to be increased in the future with less chance of breaking existing scripts, consider this an error.
 3463         if (pt_count >= MAX_REGION_POINTS)
 3464             goto error;
 3465 
 3466         if (isdigit(*cp) || *cp == '-' || *cp == '+') // v1.0.38.02: Recognize leading minus/plus sign so that the X-coord is just as tolerant as the Y.
 3467         {
 3468             // Assume it's a pair of X/Y coordinates.  It's done this way rather than using X and Y
 3469             // as option letters because:
 3470             // 1) The script is more readable when there are multiple coordinates (for polygon).
 3471             // 2) It enforces the fact that each X must have a Y and that X must always come before Y
 3472             //    (which simplifies and reduces the size of the code).
 3473             pt[pt_count].x = ATOI(cp);
 3474             // For the delimiter, dash is more readable than pipe, even though it overlaps with "minus sign".
 3475             // "x" is not used to avoid detecting "x" inside hex numbers.
 3476             #define REGION_DELIMITER '-'
 3477             if (   !(cp = _tcschr(cp + 1, REGION_DELIMITER))   ) // v1.0.38.02: cp + 1 to omit any leading minus sign.
 3478                 goto error;
 3479             pt[pt_count].y = ATOI(++cp);  // Increment cp by only 1 to support negative Y-coord.
 3480             ++pt_count; // Move on to the next element of the pt array.
 3481         }
 3482         else
 3483         {
 3484             ++cp;
 3485             switch(_totupper(cp[-1]))
 3486             {
 3487             case 'E':
 3488                 use_ellipse = true;
 3489                 break;
 3490             case 'R':
 3491                 if (!*cp || *cp == ' ') // Use 30x30 default.
 3492                 {
 3493                     rr_width = 30;
 3494                     rr_height = 30;
 3495                 }
 3496                 else
 3497                 {
 3498                     rr_width = ATOI(cp);
 3499                     if (cp = _tcschr(cp, REGION_DELIMITER)) // Assign
 3500                         rr_height = ATOI(++cp);
 3501                     else // Avoid problems with going beyond the end of the string.
 3502                         goto error;
 3503                 }
 3504                 break;
 3505             case 'W':
 3506                 if (!_tcsnicmp(cp, _T("ind"), 3)) // [W]ind.
 3507                     fill_mode = WINDING;
 3508                 else
 3509                     width = ATOI(cp);
 3510                 break;
 3511             case 'H':
 3512                 height = ATOI(cp);
 3513                 break;
 3514             default: // For simplicity and to reserve other letters for future use, unknown options result in failure.
 3515                 goto error;
 3516             } // switch()
 3517         } // else
 3518 
 3519         if (   !(cp = _tcschr(cp, ' '))   ) // No more items.
 3520             break;
 3521     }
 3522 
 3523     if (!pt_count)
 3524         goto error;
 3525 
 3526     bool width_and_height_were_both_specified = !(width == COORD_UNSPECIFIED || height == COORD_UNSPECIFIED);
 3527     if (width_and_height_were_both_specified)
 3528     {
 3529         width += pt[0].x;   // Make width become the right side of the rect.
 3530         height += pt[0].y;  // Make height become the bottom.
 3531     }
 3532 
 3533     HRGN hrgn;
 3534     if (use_ellipse) // Ellipse.
 3535         hrgn = width_and_height_were_both_specified ? CreateEllipticRgn(pt[0].x, pt[0].y, width, height) : NULL;
 3536     else if (rr_width != COORD_UNSPECIFIED) // Rounded rectangle.
 3537         hrgn = width_and_height_were_both_specified ? CreateRoundRectRgn(pt[0].x, pt[0].y, width, height, rr_width, rr_height) : NULL;
 3538     else if (width_and_height_were_both_specified) // Rectangle.
 3539         hrgn = CreateRectRgn(pt[0].x, pt[0].y, width, height);
 3540     else // Polygon
 3541         hrgn = CreatePolygonRgn(pt, pt_count, fill_mode);
 3542     if (!hrgn)
 3543         goto error;
 3544     // Since above didn't return, hrgn is now a non-NULL region ready to be assigned to the window.
 3545 
 3546     // Presumably, the system deletes the window's former region upon a successful call to SetWindowRgn():
 3547     if (!SetWindowRgn(aWnd, hrgn, TRUE))
 3548     {
 3549         DeleteObject(hrgn);
 3550         goto error;
 3551     }
 3552     //else don't delete hrgn since the system has taken ownership of it.
 3553 
 3554     // Since above didn't return, indicate success.
 3555     return SetErrorLevelOrThrowBool(false);
 3556 
 3557 error:
 3558     return SetErrorLevelOrThrow();
 3559 }
 3560 
 3561 
 3562                         
 3563 ResultType Line::WinSet(LPTSTR aAttrib, LPTSTR aValue, LPTSTR aTitle, LPTSTR aText
 3564     , LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3565 {
 3566     WinSetAttributes attrib = ConvertWinSetAttribute(aAttrib);
 3567     if (attrib == WINSET_INVALID)
 3568         return LineError(ERR_PARAM1_INVALID, FAIL, aAttrib);
 3569 
 3570     // Only the following sub-commands affect ErrorLevel:
 3571     bool use_errorlevel = (attrib == WINSET_STYLE || attrib == WINSET_EXSTYLE || attrib == WINSET_REGION);
 3572 
 3573     // Since this is a macro, avoid repeating it for every case of the switch():
 3574     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 3575     if (!target_window)
 3576         goto error;
 3577 
 3578     int value;
 3579     DWORD exstyle;
 3580 
 3581     switch (attrib)
 3582     {
 3583     case WINSET_ALWAYSONTOP:
 3584     {
 3585         HWND topmost_or_not;
 3586         switch(ConvertOnOffToggle(aValue))
 3587         {
 3588         case TOGGLED_ON: topmost_or_not = HWND_TOPMOST; break;
 3589         case TOGGLED_OFF: topmost_or_not = HWND_NOTOPMOST; break;
 3590         case NEUTRAL: // parameter was blank, so it defaults to TOGGLE.
 3591         case TOGGLE:
 3592             exstyle = GetWindowLong(target_window, GWL_EXSTYLE);
 3593             topmost_or_not = (exstyle & WS_EX_TOPMOST) ? HWND_NOTOPMOST : HWND_TOPMOST;
 3594             break;
 3595         default: return OK;
 3596         }
 3597         // SetWindowLong() didn't seem to work, at least not on some windows.  But this does.
 3598         // As of v1.0.25.14, SWP_NOACTIVATE is also specified, though its absence does not actually
 3599         // seem to activate the window, at least on XP (perhaps due to anti-focus-stealing measure
 3600         // in Win98/2000 and beyond).  Or perhaps its something to do with the presence of
 3601         // topmost_or_not (HWND_TOPMOST/HWND_NOTOPMOST), which might always avoid activating the
 3602         // window.
 3603         SetWindowPos(target_window, topmost_or_not, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
 3604         break;
 3605     }
 3606 
 3607     // Note that WINSET_TOP is not offered as an option since testing reveals it has no effect on
 3608     // top level (parent) windows, perhaps due to the anti focus-stealing measures in the OS.
 3609     case WINSET_BOTTOM:
 3610         // Note: SWP_NOACTIVATE must be specified otherwise the target window often/always fails to go
 3611         // to the bottom:
 3612         SetWindowPos(target_window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
 3613         break;
 3614     case WINSET_TOP:
 3615         // Note: SWP_NOACTIVATE must be specified otherwise the target window often/always fails to go
 3616         // to the bottom:
 3617         SetWindowPos(target_window, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
 3618         break;
 3619 
 3620     case WINSET_TRANSPARENT:
 3621     case WINSET_TRANSCOLOR:
 3622     {
 3623         // IMPORTANT (when considering future enhancements to these commands): Unlike
 3624         // SetLayeredWindowAttributes(), which works on Windows 2000, GetLayeredWindowAttributes()
 3625         // is supported only on XP or later.
 3626 
 3627         // It appears that turning on WS_EX_LAYERED in an attempt to retrieve the window's
 3628         // former transparency setting does not work.  The OS probably does not store the
 3629         // former transparency level (i.e. it forgets it the moment the WS_EX_LAYERED exstyle
 3630         // is turned off).  This is true even if the following are done after the SetWindowLong():
 3631         //MySetLayeredWindowAttributes(target_window, 0, 0, 0)
 3632         // or:
 3633         //if (MyGetLayeredWindowAttributes(target_window, &color, &alpha, &flags))
 3634         //  MySetLayeredWindowAttributes(target_window, color, alpha, flags);
 3635         // The above is why there is currently no "on" or "toggle" sub-command, just "Off".
 3636 
 3637         // Since the color of an HBRUSH can't be easily determined (since it can be a pattern and
 3638         // since there seem to be no easy API calls to discover the colors of pixels in an HBRUSH),
 3639         // the following is not yet implemented: Use window's own class background color (via
 3640         // GetClassLong) if aValue is entirely blank.
 3641 
 3642         exstyle = GetWindowLong(target_window, GWL_EXSTYLE);
 3643         if (!_tcsicmp(aValue, _T("Off")))
 3644             // One user reported that turning off the attribute helps window's scrolling performance.
 3645             SetWindowLong(target_window, GWL_EXSTYLE, exstyle & ~WS_EX_LAYERED);
 3646         else
 3647         {
 3648             if (attrib == WINSET_TRANSPARENT)
 3649             {
 3650                 // Update to the below for v1.0.23: WS_EX_LAYERED can now be removed via the above:
 3651                 // NOTE: It seems best never to remove the WS_EX_LAYERED attribute, even if the value is 255
 3652                 // (non-transparent), since the window might have had that attribute previously and may need
 3653                 // it to function properly.  For example, an app may support making its own windows transparent
 3654                 // but might not expect to have to turn WS_EX_LAYERED back on if we turned it off.  One drawback
 3655                 // of this is a quote from somewhere that might or might not be accurate: "To make this window
 3656                 // completely opaque again, remove the WS_EX_LAYERED bit by calling SetWindowLong and then ask
 3657                 // the window to repaint. Removing the bit is desired to let the system know that it can free up
 3658                 // some memory associated with layering and redirection."
 3659                 value = ATOI(aValue);
 3660                 // A little debatable, but this behavior seems best, at least in some cases:
 3661                 if (value < 0)
 3662                     value = 0;
 3663                 else if (value > 255)
 3664                     value = 255;
 3665                 SetWindowLong(target_window, GWL_EXSTYLE, exstyle | WS_EX_LAYERED);
 3666                 SetLayeredWindowAttributes(target_window, 0, value, LWA_ALPHA);
 3667             }
 3668             else // attrib == WINSET_TRANSCOLOR
 3669             {
 3670                 // The reason WINSET_TRANSCOLOR accepts both the color and an optional transparency settings
 3671                 // is that calling SetLayeredWindowAttributes() with only the LWA_COLORKEY flag causes the
 3672                 // window to lose its current transparency setting in favor of the transparent color.  This
 3673                 // is true even though the LWA_ALPHA flag was not specified, which seems odd and is a little
 3674                 // disappointing, but that's the way it is on XP at least.
 3675                 TCHAR aValue_copy[256];
 3676                 tcslcpy(aValue_copy, aValue, _countof(aValue_copy)); // Make a modifiable copy.
 3677                 LPTSTR space_pos = StrChrAny(aValue_copy, _T(" \t")); // Space or tab.
 3678                 if (space_pos)
 3679                 {
 3680                     *space_pos = '\0';
 3681                     ++space_pos;  // Point it to the second substring.
 3682                 }
 3683                 COLORREF color = ColorNameToBGR(aValue_copy);
 3684                 if (color == CLR_NONE) // A matching color name was not found, so assume it's in hex format.
 3685                     // It seems _tcstol() automatically handles the optional leading "0x" if present:
 3686                     color = rgb_to_bgr(_tcstol(aValue_copy, NULL, 16));
 3687                 DWORD flags;
 3688                 if (   space_pos && *(space_pos = omit_leading_whitespace(space_pos))   ) // Relies on short-circuit boolean.
 3689                 {
 3690                     value = ATOI(space_pos);  // To keep it simple, don't bother with 0 to 255 range validation in this case.
 3691                     flags = LWA_COLORKEY|LWA_ALPHA;  // i.e. set both the trans-color and the transparency level.
 3692                 }
 3693                 else // No translucency value is present, only a trans-color.
 3694                 {
 3695                     value = 0;  // Init to avoid possible compiler warning.
 3696                     flags = LWA_COLORKEY;
 3697                 }
 3698                 SetWindowLong(target_window, GWL_EXSTYLE, exstyle | WS_EX_LAYERED);
 3699                 SetLayeredWindowAttributes(target_window, color, value, flags);
 3700             }
 3701         }
 3702         break;
 3703     }
 3704 
 3705     case WINSET_STYLE:
 3706     case WINSET_EXSTYLE:
 3707     {
 3708         if (!*aValue)
 3709             goto error; // Seems best not to treat an explicit blank as zero.
 3710         int style_index = (attrib == WINSET_STYLE) ? GWL_STYLE : GWL_EXSTYLE;
 3711         DWORD new_style, orig_style = GetWindowLong(target_window, style_index);
 3712         if (!_tcschr(_T("+-^"), *aValue))
 3713             new_style = ATOU(aValue); // No prefix, so this new style will entirely replace the current style.
 3714         else
 3715         {
 3716             ++aValue; // Won't work combined with next line, due to next line being a macro that uses the arg twice.
 3717             DWORD style_change = ATOU(aValue);
 3718             // +/-/^ are used instead of |&^ because the latter is confusing, namely that
 3719             // "&" really means &=~style, etc.
 3720             switch(aValue[-1])
 3721             {
 3722             case '+': new_style = orig_style | style_change; break;
 3723             case '-': new_style = orig_style & ~style_change; break;
 3724             case '^': new_style = orig_style ^ style_change; break;
 3725             }
 3726         }
 3727         SetLastError(0); // Prior to SetWindowLong(), as recommended by MSDN.
 3728         if (SetWindowLong(target_window, style_index, new_style) || !GetLastError()) // This is the precise way to detect success according to MSDN.
 3729         {
 3730             // Even if it indicated success, sometimes it failed anyway.  Find out for sure:
 3731             if (GetWindowLong(target_window, style_index) != orig_style) // Even a partial change counts as a success.
 3732             {
 3733                 // SetWindowPos is also necessary, otherwise the frame thickness entirely around the window
 3734                 // does not get updated (just parts of it):
 3735                 SetWindowPos(target_window, NULL, 0, 0, 0, 0, SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
 3736                 // Since SetWindowPos() probably doesn't know that the style changed, below is probably necessary
 3737                 // too, at least in some cases:
 3738                 InvalidateRect(target_window, NULL, TRUE); // Quite a few styles require this to become visibly manifest.
 3739                 break; // Success. See below.
 3740             }
 3741         }
 3742         goto error; // Since above didn't break, it's a failure.
 3743     }
 3744 
 3745     case WINSET_ENABLE:
 3746     case WINSET_DISABLE: // These are separate sub-commands from WINSET_STYLE because merely changing the WS_DISABLED style is usually not as effective as calling EnableWindow().
 3747         EnableWindow(target_window, attrib == WINSET_ENABLE);
 3748         return OK;
 3749 
 3750     case WINSET_REGION:
 3751         return WinSetRegion(target_window, aValue);
 3752 
 3753     case WINSET_REDRAW:
 3754         // Seems best to always have the last param be TRUE, for now, so that aValue can be
 3755         // reserved for future use such as invalidating only part of a window, etc. Also, it
 3756         // seems best not to call UpdateWindow(), which forces the window to immediately
 3757         // process a WM_PAINT message, since that might not be desirable as a default (maybe
 3758         // an option someday).  Other future options might include alternate methods of
 3759         // getting a window to redraw, such as:
 3760         // SendMessage(mHwnd, WM_NCPAINT, 1, 0);
 3761         // RedrawWindow(mHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
 3762         // SetWindowPos(mHwnd, NULL, 0, 0, 0, 0, SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
 3763         // GetClientRect(mControl[mDefaultButtonIndex].hwnd, &client_rect);
 3764         // InvalidateRect(mControl[mDefaultButtonIndex].hwnd, &client_rect, TRUE);
 3765         InvalidateRect(target_window, NULL, TRUE);
 3766         break;
 3767 
 3768     } // switch()
 3769     return use_errorlevel ? SetErrorLevelOrThrowBool(false) : OK;
 3770 
 3771 error:
 3772     // Only STYLE, EXSTYLE and REDRAW affect ErrorLevel for compatibility reasons,
 3773     // but seems best to allow the other sub-commands to throw exceptions:
 3774     return (use_errorlevel || g->InTryBlock()) ? SetErrorLevelOrThrow() : OK;
 3775 }
 3776 
 3777 
 3778 
 3779 ResultType Line::WinSetTitle(LPTSTR aTitle, LPTSTR aText, LPTSTR aNewTitle, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3780 // Like AutoIt2, this function and others like it always return OK, even if the target window doesn't
 3781 // exist or there action doesn't actually succeed.
 3782 {
 3783     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 3784     if (!target_window)
 3785         return OK;
 3786     SetWindowText(target_window, aNewTitle);
 3787     return OK;
 3788 }
 3789 
 3790 
 3791 
 3792 ResultType Line::WinGetTitle(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3793 {
 3794     Var &output_var = *OUTPUT_VAR; // Must be resolved only once and prior to DetermineTargetWindow().  See Line::WinGetClass() for explanation.
 3795     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 3796     // Even if target_window is NULL, we want to continue on so that the output
 3797     // param is set to be the empty string, which is the proper thing to do
 3798     // rather than leaving whatever was in there before.
 3799 
 3800     // Handle the output parameter.  See the comments in ACT_CONTROLGETTEXT for details.
 3801     int space_needed = target_window ? GetWindowTextLength(target_window) + 1 : 1; // 1 for terminator.
 3802     if (output_var.AssignString(NULL, space_needed - 1) != OK)
 3803         return FAIL;  // It already displayed the error.
 3804     if (target_window)
 3805     {
 3806         // Update length using the actual length, rather than the estimate provided by GetWindowTextLength():
 3807         output_var.SetCharLength((VarSizeType)GetWindowText(target_window, output_var.Contents(), space_needed));
 3808         if (!output_var.Length())
 3809             // There was no text to get or GetWindowTextTimeout() failed.
 3810             *output_var.Contents() = '\0';  // Safe because Assign() gave us a non-constant memory area.
 3811     }
 3812     else
 3813     {
 3814         *output_var.Contents() = '\0';
 3815         output_var.SetCharLength(0);
 3816     }
 3817     return output_var.Close(); // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
 3818 }
 3819 
 3820 
 3821 
 3822 ResultType Line::WinGetClass(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3823 {
 3824     Var &output_var = *OUTPUT_VAR; // Fix for v1.0.48: Must be resolved only once and prior to DetermineTargetWindow() due to the following from Lexikos:
 3825     // WinGetClass causes an access violation if one of the script's windows is sub-classed by the script [unless the above is done].
 3826     // This occurs because WM_GETTEXT is sent to the GUI, triggering the window procedure. The script callback
 3827     // then executes and invalidates sArgVar[0], which WinGetClass attempts to dereference. 
 3828     // (Thanks to TodWulff for bringing this issue to my attention.) 
 3829     // Solution: WinGetTitle resolves the OUTPUT_VAR (*sArgVar) macro once, before searching for the window.
 3830     // I suggest the same be done for WinGetClass.
 3831     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 3832     if (!target_window)
 3833         return output_var.Assign();
 3834     TCHAR class_name[WINDOW_CLASS_SIZE];
 3835     if (!GetClassName(target_window, class_name, _countof(class_name)))
 3836         return output_var.Assign();
 3837     return output_var.Assign(class_name);
 3838 }
 3839 
 3840 
 3841 
 3842 ResultType WinGetList(Var &aOutputVar, WinGetCmds aCmd, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3843 // Helper function for WinGet() to avoid having a WindowSearch object on its stack (since that object
 3844 // normally isn't needed).
 3845 {
 3846     WindowSearch ws;
 3847     ws.mFindLastMatch = true; // Must set mFindLastMatch to get all matches rather than just the first.
 3848     ws.mArrayStart = (aCmd == WINGET_CMD_LIST) ? &aOutputVar : NULL; // Provide the position in the var list of where the array-element vars will be.
 3849     // If aTitle is ahk_id nnnn, the Enum() below will be inefficient.  However, ahk_id is almost unheard of
 3850     // in this context because it makes little sense, so no extra code is added to make that case efficient.
 3851     if (ws.SetCriteria(*g, aTitle, aText, aExcludeTitle, aExcludeText)) // These criteria allow the possibility of a match.
 3852         EnumWindows(EnumParentFind, (LPARAM)&ws);
 3853     //else leave ws.mFoundCount set to zero (by the constructor).
 3854     return aOutputVar.Assign(ws.mFoundCount);
 3855 }
 3856 
 3857 
 3858 
 3859 ResultType Line::WinGet(LPTSTR aCmd, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 3860 {
 3861     Var &output_var = *OUTPUT_VAR;  // This is done even for WINGET_CMD_LIST.
 3862     WinGetCmds cmd = ConvertWinGetCmd(aCmd);
 3863     // Since command names are validated at load-time, this only happens if the command name
 3864     // was contained in a variable reference.  But for simplicity of design here, return
 3865     // failure in this case (unlike other functions similar to this one):
 3866     if (cmd == WINGET_CMD_INVALID)
 3867         return LineError(ERR_PARAM2_INVALID, FAIL, aCmd);
 3868 
 3869     bool target_window_determined = true;  // Set default.
 3870     HWND target_window;
 3871     IF_USE_FOREGROUND_WINDOW(g->DetectHiddenWindows, aTitle, aText, aExcludeTitle, aExcludeText)
 3872     else if (!(*aTitle || *aText || *aExcludeTitle || *aExcludeText)
 3873         && !(cmd == WINGET_CMD_LIST || cmd == WINGET_CMD_COUNT)) // v1.0.30.02/v1.0.30.03: Have "list"/"count" get all windows on the system when there are no parameters.
 3874         target_window = GetValidLastUsedWindow(*g);
 3875     else
 3876         target_window_determined = false;  // A different method is required.
 3877 
 3878     // Used with WINGET_CMD_LIST to create an array (if needed).  Make it longer than Max var name
 3879     // so that FindOrAddVar() will be able to spot and report var names that are too long:
 3880     TCHAR var_name[MAX_VAR_NAME_LENGTH + 20], buf[32];
 3881     Var *array_item;
 3882 
 3883     switch(cmd)
 3884     {
 3885     case WINGET_CMD_ID:
 3886     case WINGET_CMD_IDLAST:
 3887         if (!target_window_determined)
 3888             target_window = WinExist(*g, aTitle, aText, aExcludeTitle, aExcludeText, cmd == WINGET_CMD_IDLAST);
 3889         if (target_window)
 3890             return output_var.AssignHWND(target_window);
 3891         else
 3892             return output_var.Assign();
 3893 
 3894     case WINGET_CMD_PID:
 3895     case WINGET_CMD_PROCESSNAME:
 3896     case WINGET_CMD_PROCESSPATH:
 3897         if (!target_window_determined)
 3898             target_window = WinExist(*g, aTitle, aText, aExcludeTitle, aExcludeText);
 3899         if (target_window)
 3900         {
 3901             DWORD pid;
 3902             GetWindowThreadProcessId(target_window, &pid);
 3903             if (cmd == WINGET_CMD_PID)
 3904                 return output_var.Assign(pid);
 3905             // Otherwise, get the full path and name of the executable that owns this window.
 3906             TCHAR process_name[MAX_PATH];
 3907             GetProcessName(pid, process_name, _countof(process_name), cmd == WINGET_CMD_PROCESSNAME);
 3908             return output_var.Assign(process_name);
 3909         }
 3910         // If above didn't return:
 3911         return output_var.Assign();
 3912 
 3913     case WINGET_CMD_COUNT:
 3914     case WINGET_CMD_LIST:
 3915         // LIST retrieves a list of HWNDs for the windows that match the given criteria and stores them in
 3916         // an array.  The number of items in the array is stored in the base array name (unlike
 3917         // StringSplit, which stores them in array element #0).  This is done for performance reasons
 3918         // (so that element #0 doesn't have to be looked up at runtime), but mostly because of the
 3919         // complexity of resolving a parameter than can be either an output-var or an array name at
 3920         // load-time -- namely that if param #1 were allowed to be an array name, there is ambiguity
 3921         // about where the name of the array is actually stored depending on whether param#1 was literal
 3922         // text or a deref.  So it's easier and performs better just to do it this way, even though it
 3923         // breaks from the StringSplit tradition:
 3924         if (target_window_determined)
 3925         {
 3926             if (!target_window)
 3927                 return output_var.Assign(_T("0")); // 0 windows found
 3928             if (cmd == WINGET_CMD_LIST)
 3929             {
 3930                 // Otherwise, since the target window has been determined, we know that it is
 3931                 // the only window to be put into the array:
 3932                 if (   !(array_item = g_script.FindOrAddVar(var_name
 3933                     , sntprintf(var_name, _countof(var_name), _T("%s1"), output_var.mName)
 3934                     , FINDVAR_FOR_PSEUDO_ARRAY(output_var)))   )  // Find or create element #1.
 3935 
 3936                     return FAIL;  // It will have already displayed the error.
 3937                 if (!array_item->AssignHWND(target_window))
 3938                     return FAIL;
 3939             }
 3940             return output_var.Assign(_T("1"));  // 1 window found
 3941         }
 3942         // Otherwise, the target window(s) have not yet been determined and a special method
 3943         // is required to gather them.
 3944         return WinGetList(output_var, cmd, aTitle, aText, aExcludeTitle, aExcludeText); // Outsourced to avoid having a WindowSearch object on this function's stack.
 3945 
 3946     case WINGET_CMD_MINMAX:
 3947         if (!target_window_determined)
 3948             target_window = WinExist(*g, aTitle, aText, aExcludeTitle, aExcludeText);
 3949         // Testing shows that it's not possible for a minimized window to also be maximized (under
 3950         // the theory that upon restoration, it *would* be maximized.  This is unfortunate if there
 3951         // is no other way to determine what the restoration size and maximized state will be for a
 3952         // minimized window.
 3953         if (target_window)
 3954             return output_var.Assign(IsZoomed(target_window) ? 1 : (IsIconic(target_window) ? -1 : 0));
 3955         else
 3956             return output_var.Assign();
 3957 
 3958     case WINGET_CMD_CONTROLLIST:
 3959     case WINGET_CMD_CONTROLLISTHWND:
 3960         if (!target_window_determined)
 3961             target_window = WinExist(*g, aTitle, aText, aExcludeTitle, aExcludeText);
 3962         return target_window ? WinGetControlList(output_var, target_window, cmd == WINGET_CMD_CONTROLLISTHWND)
 3963             : output_var.Assign();
 3964 
 3965     case WINGET_CMD_STYLE:
 3966     case WINGET_CMD_EXSTYLE:
 3967         if (!target_window_determined)
 3968             target_window = WinExist(*g, aTitle, aText, aExcludeTitle, aExcludeText);
 3969         if (!target_window)
 3970             return output_var.Assign();
 3971         _stprintf(buf, _T("0x%08X"), GetWindowLong(target_window, cmd == WINGET_CMD_STYLE ? GWL_STYLE : GWL_EXSTYLE));
 3972         return output_var.Assign(buf);
 3973 
 3974     case WINGET_CMD_TRANSPARENT:
 3975     case WINGET_CMD_TRANSCOLOR:
 3976         if (!target_window_determined)
 3977             target_window = WinExist(*g, aTitle, aText, aExcludeTitle, aExcludeText);
 3978         if (!target_window)
 3979             return output_var.Assign();
 3980         typedef BOOL (WINAPI *MyGetLayeredWindowAttributesType)(HWND, COLORREF*, BYTE*, DWORD*);
 3981         static MyGetLayeredWindowAttributesType MyGetLayeredWindowAttributes = (MyGetLayeredWindowAttributesType)
 3982             GetProcAddress(GetModuleHandle(_T("user32")), "GetLayeredWindowAttributes");
 3983         COLORREF color;
 3984         BYTE alpha;
 3985         DWORD flags;
 3986         // IMPORTANT (when considering future enhancements to these commands): Unlike
 3987         // SetLayeredWindowAttributes(), which works on Windows 2000, GetLayeredWindowAttributes()
 3988         // is supported only on XP or later.
 3989         if (!MyGetLayeredWindowAttributes || !(MyGetLayeredWindowAttributes(target_window, &color, &alpha, &flags)))
 3990             return output_var.Assign();
 3991         if (cmd == WINGET_CMD_TRANSPARENT)
 3992             return (flags & LWA_ALPHA) ? output_var.Assign((DWORD)alpha) : output_var.Assign();
 3993         else // WINGET_CMD_TRANSCOLOR
 3994         {
 3995             if (flags & LWA_COLORKEY)
 3996             {
 3997                 // Store in hex format to aid in debugging scripts.  Also, the color is always
 3998                 // stored in RGB format, since that's what WinSet uses:
 3999                 _stprintf(buf, _T("0x%06X"), bgr_to_rgb(color));
 4000                 return output_var.Assign(buf);
 4001             }
 4002             else // This window does not have a transparent color (or it's not accessible to us, perhaps for reasons described at MSDN GetLayeredWindowAttributes()).
 4003                 return output_var.Assign();
 4004         }
 4005     }
 4006 
 4007     return FAIL;  // Never executed (increases maintainability and avoids compiler warning).
 4008 }
 4009 
 4010 
 4011 
 4012 ResultType Line::WinGetControlList(Var &aOutputVar, HWND aTargetWindow, bool aFetchHWNDs)
 4013 // Caller must ensure that aTargetWindow is non-NULL and valid.
 4014 // Every control is fetched rather than just a list of distinct class names (possibly with a
 4015 // second script array containing the quantity of each class) because it's conceivable that the
 4016 // z-order of the controls will be useful information to some script authors.
 4017 // A delimited list is used rather than the array technique used by "WinGet, OutputVar, List" because:
 4018 // 1) It allows the flexibility of searching the list more easily with something like IfInString.
 4019 // 2) It seems rather rare that the count of items in the list would be useful info to a script author
 4020 //    (the count can be derived with a parsing loop if it's ever needed).
 4021 // 3) It saves memory since script arrays are permanent and since each array element would incur
 4022 //    the overhead of being a script variable, not to mention that each variable has a minimum
 4023 //    capacity (additional overhead) of 64 bytes.
 4024 {
 4025     control_list_type cl; // A big struct containing room to store class names and counts for each.
 4026     CL_INIT_CONTROL_LIST(cl)
 4027     cl.fetch_hwnds = aFetchHWNDs;
 4028     cl.target_buf = NULL;  // First pass: Signal it not not to write to the buf, but instead only calculate the length.
 4029     EnumChildWindows(aTargetWindow, EnumChildGetControlList, (LPARAM)&cl);
 4030     if (!cl.total_length) // No controls in the window.
 4031         return aOutputVar.Assign();
 4032     // This adjustment was added because someone reported that max variable capacity was being
 4033     // exceeded in some cases (perhaps custom controls that retrieve large amounts of text
 4034     // from the disk in response to the "get text" message):
 4035     if (cl.total_length >= g_MaxVarCapacity) // Allow the command to succeed by truncating the text.
 4036         cl.total_length = g_MaxVarCapacity - 1;
 4037     // Set up the var, enlarging it if necessary.  If the aOutputVar is of type VAR_CLIPBOARD,
 4038     // this call will set up the clipboard for writing:
 4039     if (aOutputVar.AssignString(NULL, (VarSizeType)cl.total_length) != OK)
 4040         return FAIL;  // It already displayed the error.
 4041     // Fetch the text directly into the var.  Also set the length explicitly
 4042     // in case actual size written was off from the estimated size (in case the list of
 4043     // controls changed in the split second between pass #1 and pass #2):
 4044     CL_INIT_CONTROL_LIST(cl)
 4045     cl.target_buf = aOutputVar.Contents();  // Second pass: Write to the buffer.
 4046     cl.capacity = aOutputVar.Capacity(); // Because granted capacity might be a little larger than we asked for.
 4047     EnumChildWindows(aTargetWindow, EnumChildGetControlList, (LPARAM)&cl);
 4048     aOutputVar.SetCharLength((VarSizeType)cl.total_length);  // In case it wound up being smaller than expected.
 4049     if (!cl.total_length) // Something went wrong, so make sure its terminated just in case.
 4050         *aOutputVar.Contents() = '\0';  // Safe because Assign() gave us a non-constant memory area.
 4051     return aOutputVar.Close(); // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
 4052 }
 4053 
 4054 
 4055 
 4056 BOOL CALLBACK EnumChildGetControlList(HWND aWnd, LPARAM lParam)
 4057 {
 4058     control_list_type &cl = *(control_list_type *)lParam;  // For performance and convenience.
 4059     TCHAR line[WINDOW_CLASS_SIZE + 5];  // +5 to allow room for the sequence number to be appended later below.
 4060     int line_length;
 4061 
 4062     // cl.fetch_hwnds==true is a new mode in v1.0.43.06+ to help performance of AHK Window Info and other
 4063     // scripts that want to operate directly on the HWNDs.
 4064     if (cl.fetch_hwnds)
 4065     {
 4066         line_length = (int)_tcslen(HwndToString(aWnd, line));
 4067     }
 4068     else // The mode that fetches ClassNN vs. HWND.
 4069     {
 4070         // Note: IsWindowVisible(aWnd) is not checked because although Window Spy does not reveal
 4071         // hidden controls if the mouse happens to be hovering over one, it does include them in its
 4072         // sequence numbering (which is a relieve, since results are probably much more consistent
 4073         // then, esp. for apps that hide and unhide controls in response to actions on other controls).
 4074         if (  !(line_length = GetClassName(aWnd, line, WINDOW_CLASS_SIZE))   ) // Don't include the +5 extra size since that is reserved for seq. number.
 4075             return TRUE; // Probably very rare. Continue enumeration since Window Spy doesn't even check for failure.
 4076         // It has been verified that GetClassName()'s returned length does not count the terminator.
 4077 
 4078         // Check if this class already exists in the class array:
 4079         int class_index;
 4080         for (class_index = 0; class_index < cl.total_classes; ++class_index)
 4081             if (!_tcsicmp(cl.class_name[class_index], line)) // lstrcmpi() is not used: 1) avoids breaking existing scripts; 2) provides consistent behavior across multiple locales.
 4082                 break;
 4083         if (class_index < cl.total_classes) // Match found.
 4084         {
 4085             ++cl.class_count[class_index]; // Increment the number of controls of this class that have been found so far.
 4086             if (cl.class_count[class_index] > 99999) // Sanity check; prevents buffer overflow or number truncation in "line".
 4087                 return TRUE;  // Continue the enumeration.
 4088         }
 4089         else // No match found, so create new entry if there's room.
 4090         {
 4091             if (cl.total_classes == CL_MAX_CLASSES // No pointers left.
 4092                 || CL_CLASS_BUF_SIZE - (cl.buf_free_spot - cl.class_buf) - 1 < line_length) // Insuff. room in buf.
 4093                 return TRUE; // Very rare. Continue the enumeration so that class names already found can be collected.
 4094             // Otherwise:
 4095             cl.class_name[class_index] = cl.buf_free_spot;  // Set this pointer to its place in the buffer.
 4096             _tcscpy(cl.class_name[class_index], line); // Copy the string into this place.
 4097             cl.buf_free_spot += line_length + 1;  // +1 because every string in the buf needs its own terminator.
 4098             cl.class_count[class_index] = 1;  // Indicate that the quantity of this class so far is 1.
 4099             ++cl.total_classes;
 4100         }
 4101 
 4102         _itot(cl.class_count[class_index], line + line_length, 10); // Append the seq. number to line.
 4103         line_length = (int)_tcslen(line);  // Update the length.
 4104     }
 4105 
 4106     int extra_length;
 4107     if (cl.is_first_iteration)
 4108     {
 4109         extra_length = 0; // All items except the first are preceded by a delimiting LF.
 4110         cl.is_first_iteration = false;
 4111     }
 4112     else
 4113         extra_length = 1;
 4114 
 4115     if (cl.target_buf)
 4116     {
 4117         if ((int)(cl.capacity - cl.total_length - extra_length - 1) < line_length)
 4118             // No room in target_buf (i.e. don't write a partial item to the buffer).
 4119             return TRUE;  // Rare: it should only happen if size in pass #2 differed from that calc'd in pass #1.
 4120         if (extra_length)
 4121         {
 4122             cl.target_buf[cl.total_length] = '\n'; // Replace previous item's terminator with newline.
 4123             cl.total_length += extra_length;
 4124         }
 4125         _tcscpy(cl.target_buf + cl.total_length, line); // Write hwnd or class name+seq. number.
 4126         cl.total_length += line_length;
 4127     }
 4128     else // Caller only wanted the total length calculated.
 4129         cl.total_length += line_length + extra_length;
 4130 
 4131     return TRUE; // Continue enumeration through all the windows.
 4132 }
 4133 
 4134 
 4135 
 4136 ResultType Line::WinGetText(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 4137 {
 4138     Var &output_var = *OUTPUT_VAR;
 4139     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 4140     // Even if target_window is NULL, we want to continue on so that the output
 4141     // variables are set to be the empty string, which is the proper thing to do
 4142     // rather than leaving whatever was in there before:
 4143     if (!target_window)
 4144     {
 4145         if (output_var.Assign() != OK) // Tell it not to free the memory by omitting all params.
 4146             return FAIL;
 4147         return SetErrorLevelOrThrow();
 4148     }
 4149 
 4150     length_and_buf_type sab;
 4151     sab.buf = NULL; // Tell it just to calculate the length this time around.
 4152     sab.total_length = 0; // Init
 4153     sab.capacity = 0;     //
 4154     EnumChildWindows(target_window, EnumChildGetText, (LPARAM)&sab);
 4155 
 4156     if (!sab.total_length) // No text in window.
 4157     {
 4158         g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 4159         return output_var.Assign(); // Tell it not to free the memory by omitting all params.
 4160     }
 4161     // This adjustment was added because someone reported that max variable capacity was being
 4162     // exceeded in some cases (perhaps custom controls that retrieve large amounts of text
 4163     // from the disk in response to the "get text" message):
 4164     if (sab.total_length >= g_MaxVarCapacity)    // Allow the command to succeed by truncating the text.
 4165         sab.total_length = g_MaxVarCapacity - 1; // And this length will be used to limit the retrieval capacity below.
 4166 
 4167     // Set up the var, enlarging it if necessary.  If the output_var is of type VAR_CLIPBOARD,
 4168     // this call will set up the clipboard for writing:
 4169     if (output_var.AssignString(NULL, (VarSizeType)sab.total_length) != OK)
 4170         return FAIL;  // It already displayed the error.
 4171 
 4172     // Fetch the text directly into the var.  Also set the length explicitly
 4173     // in case actual size written was different than the estimated size (since
 4174     // GetWindowTextLength() can return more space that will actually be required
 4175     // in certain circumstances, see MSDN):
 4176     sab.buf = output_var.Contents();
 4177     sab.total_length = 0; // Init
 4178     // Note: The capacity member below exists because granted capacity might be a little larger than we asked for,
 4179     // which allows the actual text fetched to be larger than the length estimate retrieved by the first pass
 4180     // (which generally shouldn't happen since MSDN docs say that the actual length can be less, but never greater,
 4181     // than the estimate length):
 4182     sab.capacity = output_var.Capacity(); // Capacity includes the zero terminator, i.e. it's the size of the memory area.
 4183     EnumChildWindows(target_window, EnumChildGetText, (LPARAM)&sab); // Get the text.
 4184 
 4185     // Length is set explicitly below in case it wound up being smaller than expected/estimated.
 4186     // MSDN says that can happen generally, and also specifically because: "ANSI applications may have
 4187     // the string in the buffer reduced in size (to a minimum of half that of the wParam value) due to
 4188     // conversion from ANSI to Unicode."
 4189     output_var.SetCharLength((VarSizeType)sab.total_length);
 4190     if (!sab.total_length)
 4191         // Something went wrong, so make sure we set to empty string.
 4192         *sab.buf = '\0';  // Safe because Assign() gave us a non-constant memory area.
 4193     if (output_var.Close() != OK) // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
 4194         return FAIL;
 4195     return SetErrorLevelOrThrowBool(!sab.total_length);
 4196 }
 4197 
 4198 
 4199 
 4200 BOOL CALLBACK EnumChildGetText(HWND aWnd, LPARAM lParam)
 4201 {
 4202     if (!g->DetectHiddenText && !IsWindowVisible(aWnd))
 4203         return TRUE;  // This child/control is hidden and user doesn't want it considered, so skip it.
 4204     length_and_buf_type &lab = *(length_and_buf_type *)lParam;  // For performance and convenience.
 4205     int length;
 4206     if (lab.buf)
 4207         length = GetWindowTextTimeout(aWnd, lab.buf + lab.total_length
 4208             , (int)(lab.capacity - lab.total_length)); // Not +1.  Verified correct because WM_GETTEXT accepts size of buffer, not its length.
 4209     else
 4210         length = GetWindowTextTimeout(aWnd);
 4211     lab.total_length += length;
 4212     if (length)
 4213     {
 4214         if (lab.buf)
 4215         {
 4216             if (lab.capacity - lab.total_length > 2) // Must be >2 due to zero terminator.
 4217             {
 4218                 _tcscpy(lab.buf + lab.total_length, _T("\r\n")); // Something to delimit each control's text.
 4219                 lab.total_length += 2;
 4220             }
 4221             // else don't increment total_length
 4222         }
 4223         else
 4224             lab.total_length += 2; // Since buf is NULL, accumulate the size that *would* be needed.
 4225     }
 4226     return TRUE; // Continue enumeration through all the child windows of this parent.
 4227 }
 4228 
 4229 
 4230 
 4231 ResultType Line::WinGetPos(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText)
 4232 {
 4233     Var *output_var_x = ARGVAR1;  // Ok if NULL. Load-time validation has ensured that these are valid output variables (e.g. not built-in vars).
 4234     Var *output_var_y = ARGVAR2;  // Ok if NULL.
 4235     Var *output_var_width = ARGVAR3;  // Ok if NULL.
 4236     Var *output_var_height = ARGVAR4;  // Ok if NULL.
 4237 
 4238     HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
 4239     // Even if target_window is NULL, we want to continue on so that the output
 4240     // variables are set to be the empty string, which is the proper thing to do
 4241     // rather than leaving whatever was in there before.
 4242     RECT rect;
 4243     if (target_window)
 4244         GetWindowRect(target_window, &rect);
 4245     else // ensure it's initialized for possible later use:
 4246         rect.bottom = rect.left = rect.right = rect.top = 0;
 4247 
 4248     ResultType result = OK; // Set default;
 4249 
 4250     if (output_var_x)
 4251         if (target_window)
 4252         {
 4253             if (!output_var_x->Assign(rect.left))  // X position
 4254                 result = FAIL;
 4255         }
 4256         else
 4257             if (!output_var_x->Assign(_T("")))
 4258                 result = FAIL;
 4259     if (output_var_y)
 4260         if (target_window)
 4261         {
 4262             if (!output_var_y->Assign(rect.top))  // Y position
 4263                 result = FAIL;
 4264         }
 4265         else
 4266             if (!output_var_y->Assign(_T("")))
 4267                 result = FAIL;
 4268     if (output_var_width) // else user didn't want this value saved to an output param
 4269         if (target_window)
 4270         {
 4271             if (!output_var_width->Assign(rect.right - rect.left))  // Width
 4272                 result = FAIL;
 4273         }
 4274         else
 4275             if (!output_var_width->Assign(_T(""))) // Set it to be empty to signal the user that the window wasn't found.
 4276                 result = FAIL;
 4277     if (output_var_height)
 4278         if (target_window)
 4279         {
 4280             if (!output_var_height->Assign(rect.bottom - rect.top))  // Height
 4281                 result = FAIL;
 4282         }
 4283         else
 4284             if (!output_var_height->Assign(_T("")))
 4285                 result = FAIL;
 4286 
 4287     return result;
 4288 }
 4289 
 4290 
 4291 
 4292 ResultType Line::EnvGet(LPTSTR aEnvVarName)
 4293 {
 4294     Var *output_var = OUTPUT_VAR;
 4295     // Don't use a size greater than 32767 because that will cause it to fail on Win95 (tested by Robert Yalkin).
 4296     // According to MSDN, 32767 is exactly large enough to handle the largest variable plus its zero terminator.
 4297     // Update: In practice, at least on Windows 7, the limit only applies to the ANSI functions.
 4298     TCHAR buf[32767];
 4299     // GetEnvironmentVariable() could be called twice, the first time to get the actual size.  But that would
 4300     // probably perform worse since GetEnvironmentVariable() is a very slow function, so it seems best to fetch
 4301     // it into a large buffer then just copy it to dest-var.
 4302     DWORD length = GetEnvironmentVariable(aEnvVarName, buf, _countof(buf));
 4303     if (length >= _countof(buf))
 4304     {
 4305         // In this case, length indicates the required buffer size, and the contents of the buffer are undefined.
 4306         // Since our buffer is 32767 characters, the var apparently exceeds the documented limit, as can happen
 4307         // if the var was set with the Unicode API.
 4308         if (!output_var->AssignString(NULL, length - 1, true))
 4309             return FAIL;
 4310         length = GetEnvironmentVariable(aEnvVarName, output_var->Contents(), length);
 4311         if (!length)
 4312             *output_var->Contents() = '\0'; // Ensure var is null-terminated.
 4313         return output_var->Close();
 4314     }
 4315     return output_var->Assign(length ? buf : _T(""), length);
 4316 }
 4317 
 4318 
 4319 
 4320 ResultType Line::SysGet(LPTSTR aCmd, LPTSTR aValue)
 4321 // Thanks to Gregory F. Hogg of Hogg's Software for providing sample code on which this function
 4322 // is based.
 4323 {
 4324     // For simplicity and array look-up performance, this is done even for sub-commands that output to an array:
 4325     Var &output_var = *OUTPUT_VAR;
 4326     SysGetCmds cmd = ConvertSysGetCmd(aCmd);
 4327     // Since command names are validated at load-time, this only happens if the command name
 4328     // was contained in a variable reference.  But for simplicity of design here, return
 4329     // failure in this case (unlike other functions similar to this one):
 4330     if (cmd == SYSGET_CMD_INVALID)
 4331         return LineError(ERR_PARAM2_INVALID, FAIL, aCmd);
 4332 
 4333     MonitorInfoPackage mip = {0};  // Improves maintainability to initialize unconditionally, here.
 4334     mip.monitor_info_ex.cbSize = sizeof(MONITORINFOEX); // Also improves maintainability.
 4335 
 4336     switch(cmd)
 4337     {
 4338     case SYSGET_CMD_METRICS: // In this case, aCmd is the value itself.
 4339         return output_var.Assign(GetSystemMetrics(ATOI(aCmd)));  // Input and output are both signed integers.
 4340 
 4341     // For the next few cases, I'm not sure if it is possible to have zero monitors.  Obviously it's possible
 4342     // to not have a monitor turned on or not connected at all.  But it seems likely that these various API
 4343     // functions will provide a "default monitor" in the absence of a physical monitor connected to the
 4344     // system.  To be safe, all of the below will assume that zero is possible, at least on some OSes or
 4345     // under some conditions.  However, on Win95/NT, "1" is assumed since there is probably no way to tell
 4346     // for sure if there are zero monitors except via GetSystemMetrics(SM_CMONITORS), which is a different
 4347     // animal as described below.
 4348     case SYSGET_CMD_MONITORCOUNT:
 4349         // Don't use GetSystemMetrics(SM_CMONITORS) because of this:
 4350         // MSDN: "GetSystemMetrics(SM_CMONITORS) counts only display monitors. This is different from
 4351         // EnumDisplayMonitors, which enumerates display monitors and also non-display pseudo-monitors."
 4352         mip.monitor_number_to_find = COUNT_ALL_MONITORS;
 4353         EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&mip);
 4354         return output_var.Assign(mip.count); // Will assign zero if the API ever returns a legitimate zero.
 4355 
 4356     // Even if the first monitor to be retrieved by the EnumProc is always the primary (which is doubtful
 4357     // since there's no mention of this in the MSDN docs) it seems best to have this sub-cmd in case that
 4358     // policy ever changes:
 4359     case SYSGET_CMD_MONITORPRIMARY:
 4360         // The mip struct's values have already initialized correctly for the below:
 4361         EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&mip);
 4362         return output_var.Assign(mip.count); // Will assign zero if the API ever returns a legitimate zero.
 4363 
 4364     case SYSGET_CMD_MONITORAREA:
 4365     case SYSGET_CMD_MONITORWORKAREA:
 4366         Var *output_var_left, *output_var_top, *output_var_right, *output_var_bottom;
 4367         // Make it longer than max var name so that FindOrAddVar() will be able to spot and report
 4368         // var names that are too long:
 4369         TCHAR var_name[MAX_VAR_NAME_LENGTH + 20];
 4370         // To help performance (in case the linked list of variables is huge), tell FindOrAddVar where
 4371         // to start the search.  Use the base array name rather than the preceding element because,
 4372         // for example, Array19 is alphabetically less than Array2, so we can't rely on the
 4373         // numerical ordering:
 4374         int always_use;
 4375         always_use = FINDVAR_FOR_PSEUDO_ARRAY(output_var);
 4376         if (   !(output_var_left = g_script.FindOrAddVar(var_name
 4377             , sntprintf(var_name, _countof(var_name), _T("%sLeft"), output_var.mName)
 4378             , always_use))   )
 4379             return FAIL;  // It already reported the error.
 4380         if (   !(output_var_top = g_script.FindOrAddVar(var_name
 4381             , sntprintf(var_name, _countof(var_name), _T("%sTop"), output_var.mName)
 4382             , always_use))   )
 4383             return FAIL;
 4384         if (   !(output_var_right = g_script.FindOrAddVar(var_name
 4385             , sntprintf(var_name, _countof(var_name), _T("%sRight"), output_var.mName)
 4386             , always_use))   )
 4387             return FAIL;
 4388         if (   !(output_var_bottom = g_script.FindOrAddVar(var_name
 4389             , sntprintf(var_name, _countof(var_name), _T("%sBottom"), output_var.mName)
 4390             , always_use))   )
 4391             return FAIL;
 4392 
 4393         RECT monitor_rect;
 4394         mip.monitor_number_to_find = ATOI(aValue);  // If this returns 0, it will default to the primary monitor.
 4395         EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&mip);
 4396         if (!mip.count || (mip.monitor_number_to_find && mip.monitor_number_to_find != mip.count))
 4397         {
 4398             // With the exception of the caller having specified a non-existent monitor number, all of
 4399             // the ways the above can happen are probably impossible in practice.  Make all the variables
 4400             // blank vs. zero to indicate the problem.
 4401             output_var_left->Assign();
 4402             output_var_top->Assign();
 4403             output_var_right->Assign();
 4404             output_var_bottom->Assign();
 4405             return OK;
 4406         }
 4407         // Otherwise:
 4408         monitor_rect = (cmd == SYSGET_CMD_MONITORAREA) ? mip.monitor_info_ex.rcMonitor : mip.monitor_info_ex.rcWork;
 4409         output_var_left->Assign(monitor_rect.left);
 4410         output_var_top->Assign(monitor_rect.top);
 4411         output_var_right->Assign(monitor_rect.right);
 4412         output_var_bottom->Assign(monitor_rect.bottom);
 4413         return OK;
 4414 
 4415     case SYSGET_CMD_MONITORNAME:
 4416         mip.monitor_number_to_find = ATOI(aValue);  // If this returns 0, it will default to the primary monitor.
 4417         EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&mip);
 4418         if (!mip.count || (mip.monitor_number_to_find && mip.monitor_number_to_find != mip.count))
 4419             // With the exception of the caller having specified a non-existent monitor number, all of
 4420             // the ways the above can happen are probably impossible in practice.  Make the variable
 4421             // blank to indicate the problem:
 4422             return output_var.Assign();
 4423         else
 4424             return output_var.Assign(mip.monitor_info_ex.szDevice);
 4425     } // switch()
 4426 
 4427     return FAIL;  // Never executed (increases maintainability and avoids compiler warning).
 4428 }
 4429 
 4430 
 4431 
 4432 BOOL CALLBACK EnumMonitorProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM lParam)
 4433 {
 4434     MonitorInfoPackage &mip = *(MonitorInfoPackage *)lParam;  // For performance and convenience.
 4435     if (mip.monitor_number_to_find == COUNT_ALL_MONITORS)
 4436     {
 4437         ++mip.count;
 4438         return TRUE;  // Enumerate all monitors so that they can be counted.
 4439     }
 4440     if (!GetMonitorInfo(hMonitor, &mip.monitor_info_ex)) // Failed.  Probably very rare.
 4441         return FALSE; // Due to the complexity of needing to stop at the correct monitor number, do not continue.
 4442         // In the unlikely event that the above fails when the caller wanted us to find the primary
 4443         // monitor, the caller will think the primary is the previously found monitor (if any).
 4444         // This is just documented here as a known limitation since this combination of circumstances
 4445         // is probably impossible.
 4446     ++mip.count; // So that caller can detect failure, increment only now that failure conditions have been checked.
 4447     if (mip.monitor_number_to_find) // Caller gave a specific monitor number, so don't search for the primary monitor.
 4448     {
 4449         if (mip.count == mip.monitor_number_to_find) // Since the desired monitor has been found, must not continue.
 4450             return FALSE;
 4451     }
 4452     else // Caller wants the primary monitor found.
 4453         // MSDN docs are unclear that MONITORINFOF_PRIMARY is a bitwise value, but the name "dwFlags" implies so:
 4454         if (mip.monitor_info_ex.dwFlags & MONITORINFOF_PRIMARY)
 4455             return FALSE;  // Primary has been found and "count" contains its number. Must not continue the enumeration.
 4456             // Above assumes that it is impossible to not have a primary monitor in a system that has at least
 4457             // one monitor.  MSDN certainly implies this through multiple references to the primary monitor.
 4458     // Otherwise, continue the enumeration:
 4459     return TRUE;
 4460 }
 4461 
 4462 
 4463 
 4464 LPCOLORREF getbits(HBITMAP ahImage, HDC hdc, LONG &aWidth, LONG &aHeight, bool &aIs16Bit, int aMinColorDepth = 8)
 4465 // Helper function used by PixelSearch below.
 4466 // Returns an array of pixels to the caller, which it must free when done.  Returns NULL on failure,
 4467 // in which case the contents of the output parameters is indeterminate.
 4468 {
 4469     HDC tdc = CreateCompatibleDC(hdc);
 4470     if (!tdc)
 4471         return NULL;
 4472 
 4473     // From this point on, "goto end" will assume tdc is non-NULL, but that the below
 4474     // might still be NULL.  Therefore, all of the following must be initialized so that the "end"
 4475     // label can detect them:
 4476     HGDIOBJ tdc_orig_select = NULL;
 4477     LPCOLORREF image_pixel = NULL;
 4478     bool success = false;
 4479 
 4480     // Confirmed:
 4481     // Needs extra memory to prevent buffer overflow due to: "A bottom-up DIB is specified by setting
 4482     // the height to a positive number, while a top-down DIB is specified by setting the height to a
 4483     // negative number. THE BITMAP COLOR TABLE WILL BE APPENDED to the BITMAPINFO structure."
 4484     // Maybe this applies only to negative height, in which case the second call to GetDIBits()
 4485     // below uses one.
 4486     struct BITMAPINFO3
 4487     {
 4488         BITMAPINFOHEADER    bmiHeader;
 4489         RGBQUAD             bmiColors[260];  // v1.0.40.10: 260 vs. 3 to allow room for color table when color depth is 8-bit or less.
 4490     } bmi;
 4491 
 4492     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 4493     bmi.bmiHeader.biBitCount = 0; // i.e. "query bitmap attributes" only.
 4494     if (!GetDIBits(tdc, ahImage, 0, 0, NULL, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS)
 4495         || bmi.bmiHeader.biBitCount < aMinColorDepth) // Relies on short-circuit boolean order.
 4496         goto end;
 4497 
 4498     // Set output parameters for caller:
 4499     aIs16Bit = (bmi.bmiHeader.biBitCount == 16);
 4500     aWidth = bmi.bmiHeader.biWidth;
 4501     aHeight = bmi.bmiHeader.biHeight;
 4502 
 4503     int image_pixel_count = aWidth * aHeight;
 4504     if (   !(image_pixel = (LPCOLORREF)malloc(image_pixel_count * sizeof(COLORREF)))   )
 4505         goto end;
 4506 
 4507     // v1.0.40.10: To preserve compatibility with callers who check for transparency in icons, don't do any
 4508     // of the extra color table handling for 1-bpp images.  Update: For code simplification, support only
 4509     // 8-bpp images.  If ever support lower color depths, use something like "bmi.bmiHeader.biBitCount > 1
 4510     // && bmi.bmiHeader.biBitCount < 9";
 4511     bool is_8bit = (bmi.bmiHeader.biBitCount == 8);
 4512     if (!is_8bit)
 4513         bmi.bmiHeader.biBitCount = 32;
 4514     bmi.bmiHeader.biHeight = -bmi.bmiHeader.biHeight; // Storing a negative inside the bmiHeader struct is a signal for GetDIBits().
 4515 
 4516     // Must be done only after GetDIBits() because: "The bitmap identified by the hbmp parameter
 4517     // must not be selected into a device context when the application calls GetDIBits()."
 4518     // (Although testing shows it works anyway, perhaps because GetDIBits() above is being
 4519     // called in its informational mode only).
 4520     // Note that this seems to return NULL sometimes even though everything still works.
 4521     // Perhaps that is normal.
 4522     tdc_orig_select = SelectObject(tdc, ahImage); // Returns NULL when we're called the second time?
 4523 
 4524     // Apparently there is no need to specify DIB_PAL_COLORS below when color depth is 8-bit because
 4525     // DIB_RGB_COLORS also retrieves the color indices.
 4526     if (   !(GetDIBits(tdc, ahImage, 0, aHeight, image_pixel, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS))   )
 4527         goto end;
 4528 
 4529     if (is_8bit) // This section added in v1.0.40.10.
 4530     {
 4531         // Convert the color indices to RGB colors by going through the array in reverse order.
 4532         // Reverse order allows an in-place conversion of each 8-bit color index to its corresponding
 4533         // 32-bit RGB color.
 4534         LPDWORD palette = (LPDWORD)_alloca(256 * sizeof(PALETTEENTRY));
 4535         GetSystemPaletteEntries(tdc, 0, 256, (LPPALETTEENTRY)palette); // Even if failure can realistically happen, consequences of using uninitialized palette seem acceptable.
 4536         // Above: GetSystemPaletteEntries() is the only approach that provided the correct palette.
 4537         // The following other approaches didn't give the right one:
 4538         // GetDIBits(): The palette it stores in bmi.bmiColors seems completely wrong.
 4539         // GetPaletteEntries()+GetCurrentObject(hdc, OBJ_PAL): Returned only 20 entries rather than the expected 256.
 4540         // GetDIBColorTable(): I think same as above or maybe it returns 0.
 4541 
 4542         // The following section is necessary because apparently each new row in the region starts on
 4543         // a DWORD boundary.  So if the number of pixels in each row isn't an exact multiple of 4, there
 4544         // are between 1 and 3 zero-bytes at the end of each row.
 4545         int remainder = aWidth % 4;
 4546         int empty_bytes_at_end_of_each_row = remainder ? (4 - remainder) : 0;
 4547 
 4548         // Start at the last RGB slot and the last color index slot:
 4549         BYTE *byte = (BYTE *)image_pixel + image_pixel_count - 1 + (aHeight * empty_bytes_at_end_of_each_row); // Pointer to 8-bit color indices.
 4550         DWORD *pixel = image_pixel + image_pixel_count - 1; // Pointer to 32-bit RGB entries.
 4551 
 4552         int row, col;
 4553         for (row = 0; row < aHeight; ++row) // For each row.
 4554         {
 4555             byte -= empty_bytes_at_end_of_each_row;
 4556             for (col = 0; col < aWidth; ++col) // For each column.
 4557                 *pixel-- = rgb_to_bgr(palette[*byte--]); // Caller always wants RGB vs. BGR format.
 4558         }
 4559     }
 4560     
 4561     // Since above didn't "goto end", indicate success:
 4562     success = true;
 4563 
 4564 end:
 4565     if (tdc_orig_select) // i.e. the original call to SelectObject() didn't fail.
 4566         SelectObject(tdc, tdc_orig_select); // Probably necessary to prevent memory leak.
 4567     DeleteDC(tdc);
 4568     if (!success && image_pixel)
 4569     {
 4570         free(image_pixel);
 4571         image_pixel = NULL;
 4572     }
 4573     return image_pixel;
 4574 }
 4575 
 4576 
 4577 
 4578 ResultType Line::PixelSearch(int aLeft, int aTop, int aRight, int aBottom, COLORREF aColorBGR
 4579     , int aVariation, LPTSTR aOptions, bool aIsPixelGetColor)
 4580 // Caller has ensured that aColor is in BGR format unless caller passed true for aUseRGB, in which case
 4581 // it's in RGB format.
 4582 // Author: The fast-mode PixelSearch was created by Aurelian Maga.
 4583 {
 4584     // For maintainability, get options and RGB/BGR conversion out of the way early.
 4585     bool fast_mode = aIsPixelGetColor || tcscasestr(aOptions, _T("Fast"));
 4586     bool use_rgb = tcscasestr(aOptions, _T("RGB")) != NULL;
 4587     COLORREF aColorRGB;
 4588     if (use_rgb) // aColorBGR currently contains an RGB value.
 4589     {
 4590         aColorRGB = aColorBGR;
 4591         aColorBGR = rgb_to_bgr(aColorBGR);
 4592     }
 4593     else
 4594         aColorRGB = rgb_to_bgr(aColorBGR); // rgb_to_bgr() also converts in the reverse direction, i.e. bgr_to_rgb().
 4595 
 4596     // Many of the following sections are similar to those in ImageSearch(), so they should be
 4597     // maintained together.
 4598 
 4599     Var *output_var_x = ARGVAR1;  // Ok if NULL. Load-time validation has ensured that these are valid output variables (e.g. not built-in vars).
 4600     Var *output_var_y = aIsPixelGetColor ? NULL : ARGVAR2;  // Ok if NULL. ARGVARRAW2 wouldn't be safe because load-time validation requires a min of only zero parameters to allow the output variables to be left blank.
 4601 
 4602     if (output_var_x)
 4603         output_var_x->Assign();  // Init to empty string regardless of whether we succeed here.
 4604     if (output_var_y)
 4605         output_var_y->Assign();  // Same.
 4606 
 4607     POINT origin = {0};
 4608     CoordToScreen(origin, COORD_MODE_PIXEL);
 4609     aLeft   += origin.x;
 4610     aTop    += origin.y;
 4611     aRight  += origin.x;
 4612     aBottom += origin.y;
 4613 
 4614     bool right_to_left = aLeft > aRight;
 4615     bool bottom_to_top = aTop > aBottom;
 4616 
 4617     int left = aLeft;
 4618     int top = aTop;
 4619     int right = aRight;
 4620     int bottom = aBottom;
 4621     if (right_to_left)
 4622     {
 4623         left = aRight;
 4624         right = aLeft;
 4625     }
 4626     if (bottom_to_top)
 4627     {
 4628         top = aBottom;
 4629         bottom = aTop;
 4630     }
 4631 
 4632     if (aVariation < 0)
 4633         aVariation = 0;
 4634     if (aVariation > 255)
 4635         aVariation = 255;
 4636 
 4637     // Allow colors to vary within the spectrum of intensity, rather than having them
 4638     // wrap around (which doesn't seem to make much sense).  For example, if the user specified
 4639     // a variation of 5, but the red component of aColorBGR is only 0x01, we don't want red_low to go
 4640     // below zero, which would cause it to wrap around to a very intense red color:
 4641     COLORREF pixel; // Used much further down.
 4642     BYTE red, green, blue; // Used much further down.
 4643     BYTE search_red, search_green, search_blue;
 4644     BYTE red_low, green_low, blue_low, red_high, green_high, blue_high;
 4645     if (aVariation > 0)
 4646     {
 4647         search_red = GetRValue(aColorBGR);
 4648         search_green = GetGValue(aColorBGR);
 4649         search_blue = GetBValue(aColorBGR);
 4650     }
 4651     //else leave uninitialized since they won't be used.
 4652 
 4653     HDC hdc = GetDC(NULL);
 4654     if (!hdc)
 4655         goto error;
 4656 
 4657     bool found = false; // Must init here for use by "goto fast_end" and for use by both fast and slow modes.
 4658 
 4659     if (fast_mode)
 4660     {
 4661         // From this point on, "goto fast_end" will assume hdc is non-NULL but that the below might still be NULL.
 4662         // Therefore, all of the following must be initialized so that the "fast_end" label can detect them:
 4663         HDC sdc = NULL;
 4664         HBITMAP hbitmap_screen = NULL;
 4665         LPCOLORREF screen_pixel = NULL;
 4666         HGDIOBJ sdc_orig_select = NULL;
 4667 
 4668         // Some explanation for the method below is contained in this quote from the newsgroups:
 4669         // "you shouldn't really be getting the current bitmap from the GetDC DC. This might
 4670         // have weird effects like returning the entire screen or not working. Create yourself
 4671         // a memory DC first of the correct size. Then BitBlt into it and then GetDIBits on
 4672         // that instead. This way, the provider of the DC (the video driver) can make sure that
 4673         // the correct pixels are copied across."
 4674 
 4675         // Create an empty bitmap to hold all the pixels currently visible on the screen (within the search area):
 4676         int search_width = right - left + 1;
 4677         int search_height = bottom - top + 1;
 4678         if (   !(sdc = CreateCompatibleDC(hdc)) || !(hbitmap_screen = CreateCompatibleBitmap(hdc, search_width, search_height))   )
 4679             goto fast_end;
 4680 
 4681         if (   !(sdc_orig_select = SelectObject(sdc, hbitmap_screen))   )
 4682             goto fast_end;
 4683 
 4684         // Copy the pixels in the search-area of the screen into the DC to be searched:
 4685         if (   !(BitBlt(sdc, 0, 0, search_width, search_height, hdc, left, top, SRCCOPY))   )
 4686             goto fast_end;
 4687 
 4688         LONG screen_width, screen_height;
 4689         bool screen_is_16bit;
 4690         if (   !(screen_pixel = getbits(hbitmap_screen, sdc, screen_width, screen_height, screen_is_16bit))   )
 4691             goto fast_end;
 4692 
 4693         // Concerning 0xF8F8F8F8: "On 16bit and 15 bit color the first 5 bits in each byte are valid
 4694         // (in 16bit there is an extra bit but i forgot for which color). And this will explain the
 4695         // second problem [in the test script], since GetPixel even in 16bit will return some "valid"
 4696         // data in the last 3bits of each byte."
 4697         register int i;
 4698         LONG screen_pixel_count = screen_width * screen_height;
 4699         if (screen_is_16bit)
 4700             for (i = 0; i < screen_pixel_count; ++i)
 4701                 screen_pixel[i] &= 0xF8F8F8F8;
 4702 
 4703         if (aIsPixelGetColor)
 4704         {
 4705             COLORREF color = screen_pixel[0] & 0x00FFFFFF; // See other 0x00FFFFFF below for explanation.
 4706             TCHAR buf[32];
 4707             _stprintf(buf, _T("0x%06X"), use_rgb ? color : rgb_to_bgr(color));
 4708             output_var_x->Assign(buf); // Caller has ensured that first output_var (x) won't be NULL in this mode.
 4709             found = true; // ErrorLevel will be set to 0 further below.
 4710         }
 4711         else if (aVariation < 1) // Caller wants an exact match on one particular color.
 4712         {
 4713             if (screen_is_16bit)
 4714                 aColorRGB &= 0xF8F8F8F8;
 4715 
 4716             for (int j = 0; j < screen_pixel_count; ++j)
 4717             {
 4718                 if (right_to_left && bottom_to_top)
 4719                     i = screen_pixel_count - j - 1;
 4720                 else if (right_to_left)
 4721                     i = j / screen_width * screen_width + screen_width - j % screen_width - 1;
 4722                 else if (bottom_to_top)
 4723                     i = (screen_pixel_count - j - 1) / screen_width * screen_width + j % screen_width;
 4724                 else
 4725                     i = j;
 4726 
 4727                 // Note that screen pixels sometimes have a non-zero high-order byte.  That's why
 4728                 // bit-and with 0x00FFFFFF is done.  Otherwise, reddish/orangish colors are not properly
 4729                 // found:
 4730                 if ((screen_pixel[i] & 0x00FFFFFF) == aColorRGB)
 4731                 {
 4732                     found = true;
 4733                     break;
 4734                 }
 4735             }
 4736         }
 4737         else
 4738         {
 4739             // It seems more appropriate to do the 16-bit conversion prior to SET_COLOR_RANGE,
 4740             // rather than applying 0xF8 to each of the high/low values individually.
 4741             if (screen_is_16bit)
 4742             {
 4743                 search_red &= 0xF8;
 4744                 search_green &= 0xF8;
 4745                 search_blue &= 0xF8;
 4746             }
 4747 
 4748 #define SET_COLOR_RANGE \
 4749 {\
 4750     red_low = (aVariation > search_red) ? 0 : search_red - aVariation;\
 4751     green_low = (aVariation > search_green) ? 0 : search_green - aVariation;\
 4752     blue_low = (aVariation > search_blue) ? 0 : search_blue - aVariation;\
 4753     red_high = (aVariation > 0xFF - search_red) ? 0xFF : search_red + aVariation;\
 4754     green_high = (aVariation > 0xFF - search_green) ? 0xFF : search_green + aVariation;\
 4755     blue_high = (aVariation > 0xFF - search_blue) ? 0xFF : search_blue + aVariation;\
 4756 }
 4757             
 4758             SET_COLOR_RANGE
 4759 
 4760             for (int j = 0; j < screen_pixel_count; ++j)
 4761             {
 4762                 if (right_to_left && bottom_to_top)
 4763                     i = screen_pixel_count - j - 1;
 4764                 else if (right_to_left)
 4765                     i = j / screen_width * screen_width + screen_width - j % screen_width - 1;
 4766                 else if (bottom_to_top)
 4767                     i = (screen_pixel_count - j - 1) / screen_width * screen_width + j % screen_width;
 4768                 else
 4769                     i = j;
 4770 
 4771                 // Note that screen pixels sometimes have a non-zero high-order byte.  But it doesn't
 4772                 // matter with the below approach, since that byte is not checked in the comparison.
 4773                 pixel = screen_pixel[i];
 4774                 red = GetBValue(pixel);   // Because pixel is in RGB vs. BGR format, red is retrieved with
 4775                 green = GetGValue(pixel); // GetBValue() and blue is retrieved with GetRValue().
 4776                 blue = GetRValue(pixel);
 4777                 if (red >= red_low && red <= red_high
 4778                     && green >= green_low && green <= green_high
 4779                     && blue >= blue_low && blue <= blue_high)
 4780                 {
 4781                     found = true;
 4782                     break;
 4783                 }
 4784             }
 4785         }
 4786 
 4787 fast_end:
 4788         // If found==false when execution reaches here, ErrorLevel is already set to the right value, so just
 4789         // clean up then return.
 4790         ReleaseDC(NULL, hdc);
 4791         if (sdc)
 4792         {
 4793             if (sdc_orig_select) // i.e. the original call to SelectObject() didn't fail.
 4794                 SelectObject(sdc, sdc_orig_select); // Probably necessary to prevent memory leak.
 4795             DeleteDC(sdc);
 4796         }
 4797         if (hbitmap_screen)
 4798             DeleteObject(hbitmap_screen);
 4799         if (screen_pixel)
 4800             free(screen_pixel);
 4801         else // One of the GDI calls failed and the search wasn't carried out.
 4802             goto error;
 4803 
 4804         // Otherwise, success.  Calculate xpos and ypos of where the match was found and adjust
 4805         // coords to make them relative to the position of the target window (rect will contain
 4806         // zeroes if this doesn't need to be done):
 4807         if (!aIsPixelGetColor && found)
 4808         {
 4809             if (output_var_x && !output_var_x->Assign((left + i%screen_width) - origin.x))
 4810                 return FAIL;
 4811             if (output_var_y && !output_var_y->Assign((top + i/screen_width) - origin.y))
 4812                 return FAIL;
 4813         }
 4814 
 4815         return g_ErrorLevel->Assign(found ? ERRORLEVEL_NONE : ERRORLEVEL_ERROR); // "0" indicates success; "1" indicates search completed okay, but didn't find it.
 4816     }
 4817 
 4818     // Otherwise (since above didn't return): fast_mode==false
 4819     // This old/slower method is kept  because fast mode will break older scripts that rely on
 4820     // which match is found if there is more than one match (since fast mode searches the
 4821     // pixels in a different order (horizontally rather than vertically, I believe).
 4822     // In addition, there is doubt that the fast mode works in all the screen color depths, games,
 4823     // and other circumstances that the slow mode is known to work in.
 4824 
 4825     // If the caller gives us inverted X or Y coordinates, conduct the search in reverse order.
 4826     // This feature was requested; it was put into effect for v1.0.25.06.
 4827     register int xpos, ypos;
 4828 
 4829     if (aVariation > 0)
 4830         SET_COLOR_RANGE
 4831 
 4832     for (xpos = aLeft  // It starts at aLeft even if right_to_left is true.
 4833         ; (right_to_left ? (xpos >= aRight) : (xpos <= aRight)) // Verified correct.
 4834         ; xpos += right_to_left ? -1 : 1)
 4835     {
 4836         for (ypos = aTop  // It starts at aTop even if bottom_to_top is true.
 4837             ; bottom_to_top ? (ypos >= aBottom) : (ypos <= aBottom) // Verified correct.
 4838             ; ypos += bottom_to_top ? -1 : 1)
 4839         {
 4840             pixel = GetPixel(hdc, xpos, ypos); // Returns a BGR value, not RGB.
 4841             if (aVariation < 1)  // User wanted an exact match.
 4842             {
 4843                 if (pixel == aColorBGR)
 4844                 {
 4845                     found = true;
 4846                     break;
 4847                 }
 4848             }
 4849             else  // User specified that some variation in each of the RGB components is allowable.
 4850             {
 4851                 red = GetRValue(pixel);
 4852                 green = GetGValue(pixel);
 4853                 blue = GetBValue(pixel);
 4854                 if (red >= red_low && red <= red_high && green >= green_low && green <= green_high
 4855                     && blue >= blue_low && blue <= blue_high)
 4856                 {
 4857                     found = true;
 4858                     break;
 4859                 }
 4860             }
 4861         }
 4862         // Check this here rather than in the outer loop's top line because otherwise the loop's
 4863         // increment would make xpos too big by 1:
 4864         if (found)
 4865             break;
 4866     }
 4867 
 4868     ReleaseDC(NULL, hdc);
 4869 
 4870     if (!found)
 4871         return g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // This value indicates "color not found".
 4872 
 4873     // Otherwise, this pixel matches one of the specified color(s).
 4874     // Adjust coords to make them relative to the position of the target window
 4875     // (rect will contain zeroes if this doesn't need to be done):
 4876     if (output_var_x && !output_var_x->Assign(xpos - origin.x))
 4877         return FAIL;
 4878     if (output_var_y && !output_var_y->Assign(ypos - origin.y))
 4879         return FAIL;
 4880     // Since above didn't return:
 4881     return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
 4882 
 4883 error:
 4884     return SetErrorLevelOrThrowStr(aIsPixelGetColor ? ERRORLEVEL_ERROR : ERRORLEVEL_ERROR2); // 2 means error other than "color not found".
 4885 }
 4886 
 4887 
 4888 
 4889 ResultType Line::ImageSearch(int aLeft, int aTop, int aRight, int aBottom, LPTSTR aImageFile)
 4890 // Author: ImageSearch was created by Aurelian Maga.
 4891 {
 4892     // Many of the following sections are similar to those in PixelSearch(), so they should be
 4893     // maintained together.
 4894     Var *output_var_x = ARGVAR1;  // Ok if NULL. RAW wouldn't be safe because load-time validation actually
 4895     Var *output_var_y = ARGVAR2;  // requires a minimum of zero parameters so that the output-vars can be optional. Also:
 4896     // Load-time validation has ensured that these are valid output variables (e.g. not built-in vars).
 4897 
 4898     // Set default results (output variables), in case of early return:
 4899     if (output_var_x)
 4900         output_var_x->Assign();  // Init to empty string regardless of whether we succeed here.
 4901     if (output_var_y)
 4902         output_var_y->Assign(); // Same.
 4903 
 4904     POINT origin = {0};
 4905     CoordToScreen(origin, COORD_MODE_PIXEL);
 4906     aLeft   += origin.x;
 4907     aTop    += origin.y;
 4908     aRight  += origin.x;
 4909     aBottom += origin.y;