-
Notifications
You must be signed in to change notification settings - Fork 1
/
script.h
2687 lines (2439 loc) · 136 KB
/
script.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
AutoHotkey
Copyright 2003-2008 Chris Mallett (support@autohotkey.com)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef script_h
#define script_h
#include "stdafx.h" // pre-compiled headers
#include "defines.h"
#include "SimpleHeap.h" // for overloaded new/delete operators.
#include "keyboard_mouse.h" // for modLR_type
#include "var.h" // for a script's variables.
#include "WinGroup.h" // for a script's Window Groups.
#include "util.h" // for FileTimeToYYYYMMDD(), strlcpy()
#include "resources/resource.h" // For tray icon.
#ifdef AUTOHOTKEYSC
#include "lib/exearc_read.h"
#endif
#include "exports.h" // Naveen for addfile in script2.cpp
#include "os_version.h" // For the global OS_Version object
EXTERN_OSVER; // For the access to the g_os version object without having to include globaldata.h
EXTERN_G;
enum ExecUntilMode {NORMAL_MODE, UNTIL_RETURN, UNTIL_BLOCK_END, ONLY_ONE_LINE};
// It's done this way so that mAttribute can store a pointer or one of these constants.
// If it is storing a pointer for a given Action Type, be sure never to compare it
// for equality against these constants because by coincidence, the pointer value
// might just match one of them:
#define ATTR_NONE (void *)0 // Some places migh rely on this being zero.
#define ATTR_TRUE (void *)1
#define ATTR_LOOP_UNKNOWN (void *)1 // Same value as the above. // KEEP IN SYNC WITH BELOW.
#define ATTR_LOOP_IS_UNKNOWN_OR_NONE(attr) (attr <= ATTR_LOOP_UNKNOWN) // KEEP IN SYNC WITH ABOVE.
#define ATTR_LOOP_NORMAL (void *)2
#define ATTR_LOOP_FILEPATTERN (void *)3
#define ATTR_LOOP_REG (void *)4
#define ATTR_LOOP_READ_FILE (void *)5
#define ATTR_LOOP_PARSE (void *)6
typedef void *AttributeType;
enum FileLoopModeType {FILE_LOOP_INVALID, FILE_LOOP_FILES_ONLY, FILE_LOOP_FILES_AND_FOLDERS, FILE_LOOP_FOLDERS_ONLY};
enum VariableTypeType {VAR_TYPE_INVALID, VAR_TYPE_NUMBER, VAR_TYPE_INTEGER, VAR_TYPE_FLOAT
, VAR_TYPE_TIME , VAR_TYPE_DIGIT, VAR_TYPE_XDIGIT, VAR_TYPE_ALNUM, VAR_TYPE_ALPHA
, VAR_TYPE_UPPER, VAR_TYPE_LOWER, VAR_TYPE_SPACE};
#define ATTACH_THREAD_INPUT \
bool threads_are_attached = false;\
DWORD target_thread = GetWindowThreadProcessId(target_window, NULL);\
if (target_thread && target_thread != g_MainThreadID && !IsWindowHung(target_window))\
threads_are_attached = AttachThreadInput(g_MainThreadID, target_thread, TRUE) != 0;
// BELOW IS SAME AS ABOVE except it checks do_activate and also does a SetActiveWindow():
#define ATTACH_THREAD_INPUT_AND_SETACTIVEWINDOW_IF_DO_ACTIVATE \
bool threads_are_attached = false;\
DWORD target_thread;\
if (do_activate)\
{\
target_thread = GetWindowThreadProcessId(target_window, NULL);\
if (target_thread && target_thread != g_MainThreadID && !IsWindowHung(target_window))\
threads_are_attached = AttachThreadInput(g_MainThreadID, target_thread, TRUE) != 0;\
SetActiveWindow(target_window);\
}
#define DETACH_THREAD_INPUT \
if (threads_are_attached)\
AttachThreadInput(g_MainThreadID, target_thread, FALSE);
#define RESEED_RANDOM_GENERATOR \
{\
FILETIME ft;\
GetSystemTimeAsFileTime(&ft);\
init_genrand(ft.dwLowDateTime);\
}
// Notes about the below macro:
// One of the menus in the menu bar has been displayed, and the we know the user is is still in
// the menu bar, even moving to different menus and/or menu items, until WM_EXITMENULOOP is received.
// Note: It seems that when window's menu bar is being displayed/navigated by the user, our thread
// is tied up in a message loop other than our own. In other words, it's very similar to the
// TrackPopupMenuEx() call used to handle the tray menu, which is why g_MenuIsVisible can be used
// for both types of menus to indicate to MainWindowProc() that timed subroutines should not be
// checked or allowed to launch during such times. Also, "break" is used rather than "return 0"
// to let DefWindowProc()/DefaultDlgProc() take whatever action it needs to do for these.
// UPDATE: The value of g_MenuIsVisible is checked before changing it because it might already be
// set to MENU_TYPE_POPUP (apparently, TrackPopupMenuEx sometimes/always generates WM_ENTERMENULOOP).
// BAR vs. POPUP currently doesn't matter (as long as its non-zero); thus, the above is done for
// maintainability.
#define HANDLE_MENU_LOOP \
case WM_ENTERMENULOOP:\
if (!g_MenuIsVisible)\
g_MenuIsVisible = MENU_TYPE_BAR;\
break;\
case WM_EXITMENULOOP:\
g_MenuIsVisible = MENU_TYPE_NONE;\
break;
#define IS_PERSISTENT (Hotkey::sHotkeyCount || Hotstring::sHotstringCount || g_KeybdHook || g_MouseHook || g_persistent)
// Since WM_COMMAND IDs must be shared among all menus and controls, they are carefully conserved,
// especially since there are only 65,535 possible IDs. In addition, they are assigned to ranges
// to minimize the need that they will need to be changed in the future (changing the ID of a main
// menu item, tray menu item, or a user-defined menu item [by way of increasing MAX_CONTROLS_PER_GUI]
// is bad because some scripts might be using PostMessage/SendMessage to automate AutoHotkey itself).
// For this reason, the following ranges are reserved:
// 0: unused (possibly special in some contexts)
// 1: IDOK
// 2: IDCANCEL
// 3 to 1002: GUI window control IDs (these IDs must be unique only within their parent, not across all GUI windows)
// 1003 to 65299: User Defined Menu IDs
// 65300 to 65399: Standard tray menu items.
// 65400 to 65534: main menu items (might be best to leave 65535 unused in case it ever has special meaning)
enum CommandIDs {CONTROL_ID_FIRST = IDCANCEL + 1
, ID_USER_FIRST = MAX_CONTROLS_PER_GUI + 3 // The first ID available for user defined menu items. Do not change this (see above for why).
, ID_USER_LAST = 65299 // The last. Especially do not change this due to scripts using Post/SendMessage to automate AutoHotkey.
, ID_TRAY_FIRST, ID_TRAY_OPEN = ID_TRAY_FIRST
, ID_TRAY_HELP, ID_TRAY_WINDOWSPY, ID_TRAY_RELOADSCRIPT
, ID_TRAY_EDITSCRIPT, ID_TRAY_SUSPEND, ID_TRAY_PAUSE, ID_TRAY_EXIT
, ID_TRAY_LAST = ID_TRAY_EXIT // But this value should never hit the below. There is debug code to enforce.
, ID_MAIN_FIRST = 65400, ID_MAIN_LAST = 65534}; // These should match the range used by resource.h
#define GUI_INDEX_TO_ID(index) (index + CONTROL_ID_FIRST)
#define GUI_ID_TO_INDEX(id) (id - CONTROL_ID_FIRST) // Returns a small negative if "id" is invalid, such as 0.
#define GUI_HWND_TO_INDEX(hwnd) GUI_ID_TO_INDEX(GetDlgCtrlID(hwnd)) // Returns a small negative on failure (e.g. HWND not found).
// Notes about above:
// 1) Callers should call GuiType::FindControl() instead of GUI_HWND_TO_INDEX() if the hwnd might be a combobox's
// edit control.
// 2) Testing shows that GetDlgCtrlID() is much faster than looping through a GUI window's control array to find
// a matching HWND.
#define ERR_ABORT_NO_SPACES "The current thread will exit."
#define ERR_ABORT " " ERR_ABORT_NO_SPACES
#define WILL_EXIT "The program will exit."
#define OLD_STILL_IN_EFFECT "The script was not reloaded; the old version will remain in effect."
#define ERR_CONTINUATION_SECTION_TOO_LONG "Continuation section too long."
#define ERR_UNRECOGNIZED_ACTION "This line does not contain a recognized action."
#define ERR_NONEXISTENT_HOTKEY "Nonexistent hotkey."
#define ERR_NONEXISTENT_VARIANT "Nonexistent hotkey variant (IfWin)."
#define ERR_NONEXISTENT_FUNCTION "Call to nonexistent function."
#define ERR_EXE_CORRUPTED "EXE corrupted"
#define ERR_PARAM1_INVALID "Parameter #1 invalid"
#define ERR_PARAM2_INVALID "Parameter #2 invalid"
#define ERR_PARAM3_INVALID "Parameter #3 invalid"
#define ERR_PARAM4_INVALID "Parameter #4 invalid"
#define ERR_PARAM5_INVALID "Parameter #5 invalid"
#define ERR_PARAM6_INVALID "Parameter #6 invalid"
#define ERR_PARAM7_INVALID "Parameter #7 invalid"
#define ERR_PARAM8_INVALID "Parameter #8 invalid"
#define ERR_PARAM1_REQUIRED "Parameter #1 required"
#define ERR_PARAM2_REQUIRED "Parameter #2 required"
#define ERR_PARAM3_REQUIRED "Parameter #3 required"
#define ERR_PARAM4_OMIT "Parameter #4 should be omitted in this case."
#define ERR_PARAM2_MUST_BE_BLANK "Parameter #2 must be blank in this case."
#define ERR_PARAM3_MUST_BE_BLANK "Parameter #3 must be blank in this case."
#define ERR_PARAM4_MUST_BE_BLANK "Parameter #4 must be blank in this case."
#define ERR_INVALID_KEY_OR_BUTTON "Invalid key or button name"
#define ERR_MISSING_OUTPUT_VAR "Requires at least one of its output variables."
#define ERR_MISSING_OPEN_PAREN "Missing \"(\""
#define ERR_MISSING_OPEN_BRACE "Missing \"{\""
#define ERR_MISSING_CLOSE_PAREN "Missing \")\""
#define ERR_MISSING_CLOSE_BRACE "Missing \"}\""
#define ERR_MISSING_CLOSE_QUOTE "Missing close-quote" // No period after short phrases.
#define ERR_MISSING_COMMA "Missing comma" //
#define ERR_BLANK_PARAM "Blank parameter" //
#define ERR_BYREF "Caller must pass a variable to this ByRef parameter."
#define ERR_ELSE_WITH_NO_IF "ELSE with no matching IF"
#define ERR_OUTOFMEM "Out of memory." // Used by RegEx too, so don't change it without also changing RegEx to keep the former string.
#define ERR_MEM_LIMIT_REACHED "Memory limit reached (see #MaxMem in the help file)." ERR_ABORT
#define ERR_NO_LABEL "Target label does not exist."
#define ERR_MENU "Menu does not exist."
#define ERR_SUBMENU "Submenu does not exist."
#define ERR_WINDOW_PARAM "Requires at least one of its window parameters."
#define ERR_ON_OFF "Requires ON/OFF/blank"
#define ERR_ON_OFF_LOCALE "Requires ON/OFF/LOCALE"
#define ERR_ON_OFF_TOGGLE "Requires ON/OFF/TOGGLE/blank"
#define ERR_ON_OFF_TOGGLE_PERMIT "Requires ON/OFF/TOGGLE/PERMIT/blank"
#define ERR_TITLEMATCHMODE "Requires 1/2/3/Slow/Fast"
#define ERR_MENUTRAY "Supported only for the tray menu"
#define ERR_REG_KEY "Invalid registry root key"
#define ERR_REG_VALUE_TYPE "Invalid registry value type"
#define ERR_INVALID_DATETIME "Invalid YYYYMMDDHHMISS value"
#define ERR_MOUSE_BUTTON "Invalid mouse button"
#define ERR_MOUSE_COORD "X & Y must be either both absent or both present."
#define ERR_DIVIDEBYZERO "Divide by zero"
#define ERR_PERCENT "Must be between -100 and 100."
#define ERR_MOUSE_SPEED "Mouse speed must be between 0 and " MAX_MOUSE_SPEED_STR "."
#define ERR_VAR_IS_READONLY "Not allowed as an output variable."
//----------------------------------------------------------------------------------
void DoIncrementalMouseMove(int aX1, int aY1, int aX2, int aY2, int aSpeed);
DWORD ProcessExist9x2000(char *aProcess, char *aProcessName);
DWORD ProcessExistNT4(char *aProcess, char *aProcessName);
inline DWORD ProcessExist(char *aProcess, char *aProcessName = NULL)
{
return g_os.IsWinNT4() ? ProcessExistNT4(aProcess, aProcessName)
: ProcessExist9x2000(aProcess, aProcessName);
}
bool Util_Shutdown(int nFlag);
BOOL Util_ShutdownHandler(HWND hwnd, DWORD lParam);
void Util_WinKill(HWND hWnd);
enum MainWindowModes {MAIN_MODE_NO_CHANGE, MAIN_MODE_LINES, MAIN_MODE_VARS
, MAIN_MODE_HOTKEYS, MAIN_MODE_KEYHISTORY, MAIN_MODE_REFRESH};
ResultType ShowMainWindow(MainWindowModes aMode = MAIN_MODE_NO_CHANGE, bool aRestricted = true);
DWORD GetAHKInstallDir(char *aBuf);
struct InputBoxType
{
char *title;
char *text;
int width;
int height;
int xpos;
int ypos;
Var *output_var;
char password_char;
char *default_string;
DWORD timeout;
HWND hwnd;
};
struct SplashType
{
int width;
int height;
int bar_pos; // The amount of progress of the bar (it's position).
int margin_x; // left/right margin
int margin_y; // top margin
int text1_height; // Height of main text control.
int object_width; // Width of image.
int object_height; // Height of the progress bar or image.
HWND hwnd;
LPPICTURE pic; // For SplashImage.
HWND hwnd_bar;
HWND hwnd_text1; // MainText
HWND hwnd_text2; // SubText
HFONT hfont1; // Main
HFONT hfont2; // Sub
HBRUSH hbrush; // Window background color brush.
COLORREF color_bk; // The background color itself.
COLORREF color_text; // Color of the font.
};
// Use GetClientRect() to determine the available width so that control's can be centered.
#define SPLASH_CALC_YPOS \
int bar_y = splash.margin_y + (splash.text1_height ? (splash.text1_height + splash.margin_y) : 0);\
int sub_y = bar_y + splash.object_height + (splash.object_height ? splash.margin_y : 0); // i.e. don't include margin_y twice if there's no bar.
#define PROGRESS_MAIN_POS splash.margin_x, splash.margin_y, control_width, splash.text1_height
#define PROGRESS_BAR_POS splash.margin_x, bar_y, control_width, splash.object_height
#define PROGRESS_SUB_POS splash.margin_x, sub_y, control_width, (client_rect.bottom - client_rect.top) - sub_y
// From AutoIt3's InputBox. This doesn't add a measurable amount of code size, so the compiler seems to implement
// it efficiently (somewhat like a macro).
template <class T>
inline void swap(T &v1, T &v2) {
T tmp=v1;
v1=v2;
v2=tmp;
}
#define INPUTBOX_DEFAULT INT_MIN
ResultType InputBox(Var *aOutputVar, char *aTitle, char *aText, bool aHideInput
, int aWidth, int aHeight, int aX, int aY, double aTimeout, char *aDefault);
BOOL CALLBACK InputBoxProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID CALLBACK InputBoxTimeout(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime);
VOID CALLBACK DerefTimeout(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime);
BOOL CALLBACK EnumChildFindSeqNum(HWND aWnd, LPARAM lParam);
BOOL CALLBACK EnumChildFindPoint(HWND aWnd, LPARAM lParam);
BOOL CALLBACK EnumChildGetControlList(HWND aWnd, LPARAM lParam);
BOOL CALLBACK EnumMonitorProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM lParam);
BOOL CALLBACK EnumChildGetText(HWND aWnd, LPARAM lParam);
LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
bool HandleMenuItem(HWND aHwnd, WORD aMenuItemID, WPARAM aGuiIndex);
typedef UINT LineNumberType;
typedef WORD FileIndexType; // Use WORD to conserve memory due to its use in the Line class (adjacency to other members and due to 4-byte struct alignment).
#define ABSOLUTE_MAX_SOURCE_FILES 0xFFFF // Keep this in sync with the capacity of the type above. Actually it could hold 0xFFFF+1, but avoid the final item for maintainability (otherwise max-index won't be able to fit inside a variable of that type).
#define LOADING_FAILED UINT_MAX
// -2 for the beginning and ending g_DerefChars:
#define MAX_VAR_NAME_LENGTH (UCHAR_MAX - 2)
#define MAX_FUNCTION_PARAMS UCHAR_MAX // Also conserves stack space to support future attributes such as param default values.
#define MAX_DEREFS_PER_ARG 512
typedef WORD DerefLengthType; // WORD might perform better than UCHAR, but this can be changed to UCHAR if another field is ever needed in the struct.
typedef UCHAR DerefParamCountType;
class Func; // Forward declaration for use below.
struct DerefType
{
char *marker;
union
{
Var *var;
Func *func;
};
// Keep any fields that aren't an even multiple of 4 adjacent to each other. This conserves memory
// due to byte-alignment:
bool is_function; // This should be kept pure bool to allow easy determination of what's in the union, above.
DerefParamCountType param_count; // The actual number of parameters present in this function *call*. Left uninitialized except for functions.
DerefLengthType length; // Listed only after byte-sized fields, due to it being a WORD.
};
typedef UCHAR ArgTypeType; // UCHAR vs. an enum, to save memory.
#define ARG_TYPE_NORMAL (UCHAR)0
#define ARG_TYPE_INPUT_VAR (UCHAR)1
#define ARG_TYPE_OUTPUT_VAR (UCHAR)2
struct ArgStruct
{
ArgTypeType type;
bool is_expression; // Whether this ARG is known to contain an expression.
// Above are kept adjacent to each other to conserve memory (any fields that aren't an even
// multiple of 4, if adjacent to each other, consume less memory due to default byte alignment
// setting [which helps performance]).
WORD length; // Keep adjacent to above so that it uses no extra memory. This member was added in v1.0.44.14 to improve runtime performance. It relies on the fact that an arg's literal text can't be longer than LINE_SIZE.
char *text;
DerefType *deref; // Will hold a NULL-terminated array of var-deref locations within <text>.
};
// Some of these lengths and such are based on the MSDN example at
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/enumerating_registry_subkeys.asp:
// UPDATE for v1.0.44.07: Someone reported that a stack overflow was possible, implying that it only happens
// during extremely deep nesting of subkey names (perhaps a hundred or more nested subkeys). Upon review, it seems
// that the prior limit of 16383 for value-name-length is higher than needed; testing shows that a value name can't
// be longer than 259 (limit might even be 255 if API vs. RegEdit is used to create the name). Testing also shows
// that the total path name of a registry item (including item/value name but excluding the name of the root key)
// obeys the same limit.
#define MAX_REG_ITEM_LENGTH 259
#define REG_SUBKEY -2 // Custom type, not standard in Windows.
struct RegItemStruct
{
HKEY root_key_type, root_key; // root_key_type is always a local HKEY, whereas root_key can be a remote handle.
char subkey[MAX_REG_ITEM_LENGTH + 1]; // The branch of the registry where this subkey or value is located.
char name[MAX_REG_ITEM_LENGTH + 1]; // The subkey or value name.
DWORD type; // Value Type (e.g REG_DWORD). This is the length used by MSDN in their example code.
FILETIME ftLastWriteTime; // Non-initialized.
void InitForValues() {ftLastWriteTime.dwHighDateTime = ftLastWriteTime.dwLowDateTime = 0;}
void InitForSubkeys() {type = REG_SUBKEY;} // To distinguish REG_DWORD and such from the subkeys themselves.
RegItemStruct(HKEY aRootKeyType, HKEY aRootKey, char *aSubKey)
: root_key_type(aRootKeyType), root_key(aRootKey), type(REG_NONE)
{
*name = '\0';
// Make a local copy on the caller's stack so that if the current script subroutine is
// interrupted to allow another to run, the contents of the deref buffer is saved here:
strlcpy(subkey, aSubKey, sizeof(subkey));
// Even though the call may work with a trailing backslash, it's best to remove it
// so that consistent results are delivered to the user. For example, if the script
// is enumerating recursively into a subkey, subkeys deeper down will not include the
// trailing backslash when they are reported. So the user's own subkey should not
// have one either so that when A_ScriptSubKey is referenced in the script, it will
// always show up as the value without a trailing backslash:
size_t length = strlen(subkey);
if (length && subkey[length - 1] == '\\')
subkey[length - 1] = '\0';
}
};
struct LoopReadFileStruct
{
FILE *mReadFile, *mWriteFile;
char mWriteFileName[MAX_PATH];
#define READ_FILE_LINE_SIZE (64 * 1024) // This is also used by FileReadLine().
char mCurrentLine[READ_FILE_LINE_SIZE];
LoopReadFileStruct(FILE *aReadFile, char *aWriteFileName)
: mReadFile(aReadFile), mWriteFile(NULL) // mWriteFile is opened by FileAppend() only upon first use.
{
// Use our own buffer because caller's is volatile due to possibly being in the deref buffer:
strlcpy(mWriteFileName, aWriteFileName, sizeof(mWriteFileName));
*mCurrentLine = '\0';
}
};
typedef UCHAR ArgCountType;
#define MAX_ARGS 20 // Maximum number of args used by any command.
enum DllArgTypes {DLL_ARG_INVALID, DLL_ARG_STR, DLL_ARG_INT, DLL_ARG_SHORT, DLL_ARG_CHAR, DLL_ARG_INT64
, DLL_ARG_FLOAT, DLL_ARG_DOUBLE};
// Note that currently this value must fit into a sc_type variable because that is how TextToKey()
// stores it in the hotkey class. sc_type is currently a UINT, and will always be at least a
// WORD in size, so it shouldn't be much of an issue:
#define MAX_JOYSTICKS 16 // The maximum allowed by any Windows operating system.
#define MAX_JOY_BUTTONS 32 // Also the max that Windows supports.
enum JoyControls {JOYCTRL_INVALID, JOYCTRL_XPOS, JOYCTRL_YPOS, JOYCTRL_ZPOS
, JOYCTRL_RPOS, JOYCTRL_UPOS, JOYCTRL_VPOS, JOYCTRL_POV
, JOYCTRL_NAME, JOYCTRL_BUTTONS, JOYCTRL_AXES, JOYCTRL_INFO
, JOYCTRL_1, JOYCTRL_2, JOYCTRL_3, JOYCTRL_4, JOYCTRL_5, JOYCTRL_6, JOYCTRL_7, JOYCTRL_8 // Buttons.
, JOYCTRL_9, JOYCTRL_10, JOYCTRL_11, JOYCTRL_12, JOYCTRL_13, JOYCTRL_14, JOYCTRL_15, JOYCTRL_16
, JOYCTRL_17, JOYCTRL_18, JOYCTRL_19, JOYCTRL_20, JOYCTRL_21, JOYCTRL_22, JOYCTRL_23, JOYCTRL_24
, JOYCTRL_25, JOYCTRL_26, JOYCTRL_27, JOYCTRL_28, JOYCTRL_29, JOYCTRL_30, JOYCTRL_31, JOYCTRL_32
, JOYCTRL_BUTTON_MAX = JOYCTRL_32
};
#define IS_JOYSTICK_BUTTON(joy) (joy >= JOYCTRL_1 && joy <= JOYCTRL_BUTTON_MAX)
enum WinGetCmds {WINGET_CMD_INVALID, WINGET_CMD_ID, WINGET_CMD_IDLAST, WINGET_CMD_PID, WINGET_CMD_PROCESSNAME
, WINGET_CMD_COUNT, WINGET_CMD_LIST, WINGET_CMD_MINMAX, WINGET_CMD_CONTROLLIST, WINGET_CMD_CONTROLLISTHWND
, WINGET_CMD_STYLE, WINGET_CMD_EXSTYLE, WINGET_CMD_TRANSPARENT, WINGET_CMD_TRANSCOLOR
};
enum SysGetCmds {SYSGET_CMD_INVALID, SYSGET_CMD_METRICS, SYSGET_CMD_MONITORCOUNT, SYSGET_CMD_MONITORPRIMARY
, SYSGET_CMD_MONITORAREA, SYSGET_CMD_MONITORWORKAREA, SYSGET_CMD_MONITORNAME
};
enum TransformCmds {TRANS_CMD_INVALID, TRANS_CMD_ASC, TRANS_CMD_CHR, TRANS_CMD_DEREF
, TRANS_CMD_UNICODE, TRANS_CMD_HTML
, TRANS_CMD_MOD, TRANS_CMD_POW, TRANS_CMD_EXP, TRANS_CMD_SQRT, TRANS_CMD_LOG, TRANS_CMD_LN
, TRANS_CMD_ROUND, TRANS_CMD_CEIL, TRANS_CMD_FLOOR, TRANS_CMD_ABS
, TRANS_CMD_SIN, TRANS_CMD_COS, TRANS_CMD_TAN, TRANS_CMD_ASIN, TRANS_CMD_ACOS, TRANS_CMD_ATAN
, TRANS_CMD_BITAND, TRANS_CMD_BITOR, TRANS_CMD_BITXOR, TRANS_CMD_BITNOT
, TRANS_CMD_BITSHIFTLEFT, TRANS_CMD_BITSHIFTRIGHT
};
enum MenuCommands {MENU_CMD_INVALID, MENU_CMD_SHOW, MENU_CMD_USEERRORLEVEL
, MENU_CMD_ADD, MENU_CMD_RENAME, MENU_CMD_CHECK, MENU_CMD_UNCHECK, MENU_CMD_TOGGLECHECK
, MENU_CMD_ENABLE, MENU_CMD_DISABLE, MENU_CMD_TOGGLEENABLE
, MENU_CMD_STANDARD, MENU_CMD_NOSTANDARD, MENU_CMD_COLOR, MENU_CMD_DEFAULT, MENU_CMD_NODEFAULT
, MENU_CMD_DELETE, MENU_CMD_DELETEALL, MENU_CMD_TIP, MENU_CMD_ICON, MENU_CMD_NOICON
, MENU_CMD_CLICK, MENU_CMD_MAINWINDOW, MENU_CMD_NOMAINWINDOW
};
#define AHK_LV_SELECT 0x0100
#define AHK_LV_DESELECT 0x0200
#define AHK_LV_FOCUS 0x0400
#define AHK_LV_DEFOCUS 0x0800
#define AHK_LV_CHECK 0x1000
#define AHK_LV_UNCHECK 0x2000
#define AHK_LV_DROPHILITE 0x4000
#define AHK_LV_UNDROPHILITE 0x8000
// Although there's no room remaining in the BYTE for LVIS_CUT (AHK_LV_CUT) [assuming it's ever needed],
// it might be possible to squeeze more info into it as follows:
// Each pair of bits can represent three values (other than zero). But since only two values are needed
// (since an item can't be both selected an deselected simultaneously), one value in each pair is available
// for future use such as LVIS_CUT.
enum GuiCommands {GUI_CMD_INVALID, GUI_CMD_OPTIONS, GUI_CMD_ADD, GUI_CMD_MARGIN, GUI_CMD_MENU
, GUI_CMD_SHOW, GUI_CMD_SUBMIT, GUI_CMD_CANCEL, GUI_CMD_MINIMIZE, GUI_CMD_MAXIMIZE, GUI_CMD_RESTORE
, GUI_CMD_DESTROY, GUI_CMD_FONT, GUI_CMD_TAB, GUI_CMD_LISTVIEW, GUI_CMD_TREEVIEW, GUI_CMD_DEFAULT
, GUI_CMD_COLOR, GUI_CMD_FLASH
};
enum GuiControlCmds {GUICONTROL_CMD_INVALID, GUICONTROL_CMD_OPTIONS, GUICONTROL_CMD_CONTENTS, GUICONTROL_CMD_TEXT
, GUICONTROL_CMD_MOVE, GUICONTROL_CMD_MOVEDRAW, GUICONTROL_CMD_FOCUS, GUICONTROL_CMD_ENABLE, GUICONTROL_CMD_DISABLE
, GUICONTROL_CMD_SHOW, GUICONTROL_CMD_HIDE, GUICONTROL_CMD_CHOOSE, GUICONTROL_CMD_CHOOSESTRING
, GUICONTROL_CMD_FONT
};
enum GuiControlGetCmds {GUICONTROLGET_CMD_INVALID, GUICONTROLGET_CMD_CONTENTS, GUICONTROLGET_CMD_POS
, GUICONTROLGET_CMD_FOCUS, GUICONTROLGET_CMD_FOCUSV, GUICONTROLGET_CMD_ENABLED, GUICONTROLGET_CMD_VISIBLE
, GUICONTROLGET_CMD_HWND
};
typedef UCHAR GuiControls;
enum GuiControlTypes {GUI_CONTROL_INVALID // GUI_CONTROL_INVALID must be zero due to things like ZeroMemory() on the struct.
, GUI_CONTROL_TEXT, GUI_CONTROL_PIC, GUI_CONTROL_GROUPBOX
, GUI_CONTROL_BUTTON, GUI_CONTROL_CHECKBOX, GUI_CONTROL_RADIO
, GUI_CONTROL_DROPDOWNLIST, GUI_CONTROL_COMBOBOX
, GUI_CONTROL_LISTBOX, GUI_CONTROL_LISTVIEW, GUI_CONTROL_TREEVIEW
, GUI_CONTROL_EDIT, GUI_CONTROL_DATETIME, GUI_CONTROL_MONTHCAL, GUI_CONTROL_HOTKEY
, GUI_CONTROL_UPDOWN, GUI_CONTROL_SLIDER, GUI_CONTROL_PROGRESS, GUI_CONTROL_TAB, GUI_CONTROL_TAB2
, GUI_CONTROL_STATUSBAR}; // Kept last to reflect it being bottommost in switch()s (for perf), since not too often used.
enum ThreadCommands {THREAD_CMD_INVALID, THREAD_CMD_PRIORITY, THREAD_CMD_INTERRUPT, THREAD_CMD_NOTIMERS};
#define PROCESS_PRIORITY_LETTERS "LBNAHR"
enum ProcessCmds {PROCESS_CMD_INVALID, PROCESS_CMD_EXIST, PROCESS_CMD_CLOSE, PROCESS_CMD_PRIORITY
, PROCESS_CMD_WAIT, PROCESS_CMD_WAITCLOSE};
enum ControlCmds {CONTROL_CMD_INVALID, CONTROL_CMD_CHECK, CONTROL_CMD_UNCHECK
, CONTROL_CMD_ENABLE, CONTROL_CMD_DISABLE, CONTROL_CMD_SHOW, CONTROL_CMD_HIDE
, CONTROL_CMD_STYLE, CONTROL_CMD_EXSTYLE
, CONTROL_CMD_SHOWDROPDOWN, CONTROL_CMD_HIDEDROPDOWN
, CONTROL_CMD_TABLEFT, CONTROL_CMD_TABRIGHT
, CONTROL_CMD_ADD, CONTROL_CMD_DELETE, CONTROL_CMD_CHOOSE
, CONTROL_CMD_CHOOSESTRING, CONTROL_CMD_EDITPASTE};
enum ControlGetCmds {CONTROLGET_CMD_INVALID, CONTROLGET_CMD_CHECKED, CONTROLGET_CMD_ENABLED
, CONTROLGET_CMD_VISIBLE, CONTROLGET_CMD_TAB, CONTROLGET_CMD_FINDSTRING
, CONTROLGET_CMD_CHOICE, CONTROLGET_CMD_LIST, CONTROLGET_CMD_LINECOUNT, CONTROLGET_CMD_CURRENTLINE
, CONTROLGET_CMD_CURRENTCOL, CONTROLGET_CMD_LINE, CONTROLGET_CMD_SELECTED
, CONTROLGET_CMD_STYLE, CONTROLGET_CMD_EXSTYLE, CONTROLGET_CMD_HWND};
enum DriveCmds {DRIVE_CMD_INVALID, DRIVE_CMD_EJECT, DRIVE_CMD_LOCK, DRIVE_CMD_UNLOCK, DRIVE_CMD_LABEL};
enum DriveGetCmds {DRIVEGET_CMD_INVALID, DRIVEGET_CMD_LIST, DRIVEGET_CMD_FILESYSTEM, DRIVEGET_CMD_LABEL
, DRIVEGET_CMD_SETLABEL, DRIVEGET_CMD_SERIAL, DRIVEGET_CMD_TYPE, DRIVEGET_CMD_STATUS
, DRIVEGET_CMD_STATUSCD, DRIVEGET_CMD_CAPACITY};
enum WinSetAttributes {WINSET_INVALID, WINSET_TRANSPARENT, WINSET_TRANSCOLOR, WINSET_ALWAYSONTOP
, WINSET_BOTTOM, WINSET_TOP, WINSET_STYLE, WINSET_EXSTYLE, WINSET_REDRAW, WINSET_ENABLE, WINSET_DISABLE
, WINSET_REGION};
class Label; // Forward declaration so that each can use the other.
class Line
{
private:
#define SET_S_DEREF_BUF(ptr, size) sDerefBuf = ptr, sDerefBufSize = size
#define NULLIFY_S_DEREF_BUF \
{\
SET_S_DEREF_BUF(NULL, 0);\
if (sDerefBufSize > LARGE_DEREF_BUF_SIZE)\
--sLargeDerefBufs;\
}
static char *sDerefBuf; // Buffer to hold the values of any args that need to be dereferenced.
static size_t sDerefBufSize;
static int sLargeDerefBufs;
// Static because only one line can be Expanded at a time (not to mention the fact that we
// wouldn't want the size of each line to be expanded by this size):
static char *sArgDeref[MAX_ARGS];
static Var *sArgVar[MAX_ARGS];
ResultType EvaluateCondition();
ResultType PerformLoop(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine
, __int64 aIterationLimit, bool aIsInfinite);
ResultType PerformLoopFilePattern(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine
, FileLoopModeType aFileLoopMode, bool aRecurseSubfolders, char *aFilePattern);
ResultType PerformLoopReg(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine
, FileLoopModeType aFileLoopMode, bool aRecurseSubfolders, HKEY aRootKeyType, HKEY aRootKey, char *aRegSubkey);
ResultType PerformLoopParse(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine);
ResultType PerformLoopParseCSV(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine);
ResultType PerformLoopReadFile(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine, FILE *aReadFile, char *aWriteFileName);
ResultType Perform();
ResultType MouseGetPos(DWORD aOptions);
ResultType FormatTime(char *aYYYYMMDD, char *aFormat);
ResultType PerformAssign();
ResultType StringReplace();
ResultType StringSplit(char *aArrayName, char *aInputString, char *aDelimiterList, char *aOmitList);
ResultType SplitPath(char *aFileSpec);
ResultType PerformSort(char *aContents, char *aOptions);
ResultType GetKeyJoyState(char *aKeyName, char *aOption);
ResultType DriveSpace(char *aPath, bool aGetFreeSpace);
ResultType Drive(char *aCmd, char *aValue, char *aValue2);
ResultType DriveLock(char aDriveLetter, bool aLockIt);
ResultType DriveGet(char *aCmd, char *aValue);
ResultType SoundSetGet(char *aSetting, DWORD aComponentType, int aComponentInstance
, DWORD aControlType, UINT aMixerID);
ResultType SoundGetWaveVolume(HWAVEOUT aDeviceID);
ResultType SoundSetWaveVolume(char *aVolume, HWAVEOUT aDeviceID);
ResultType SoundPlay(char *aFilespec, bool aSleepUntilDone);
ResultType URLDownloadToFile(char *aURL, char *aFilespec);
ResultType FileSelectFile(char *aOptions, char *aWorkingDir, char *aGreeting, char *aFilter);
// Bitwise flags:
#define FSF_ALLOW_CREATE 0x01
#define FSF_EDITBOX 0x02
ResultType FileSelectFolder(char *aRootDir, char *aOptions, char *aGreeting);
ResultType FileGetShortcut(char *aShortcutFile);
ResultType FileCreateShortcut(char *aTargetFile, char *aShortcutFile, char *aWorkingDir, char *aArgs
, char *aDescription, char *aIconFile, char *aHotkey, char *aIconNumber, char *aRunState);
ResultType FileCreateDir(char *aDirSpec);
ResultType FileRead(char *aFilespec);
ResultType FileReadLine(char *aFilespec, char *aLineNumber);
ResultType FileAppend(char *aFilespec, char *aBuf, LoopReadFileStruct *aCurrentReadFile);
ResultType WriteClipboardToFile(char *aFilespec);
ResultType ReadClipboardFromFile(HANDLE hfile);
ResultType FileDelete();
ResultType FileRecycle(char *aFilePattern);
ResultType FileRecycleEmpty(char *aDriveLetter);
ResultType FileInstall(char *aSource, char *aDest, char *aFlag);
ResultType FileGetAttrib(char *aFilespec);
int FileSetAttrib(char *aAttributes, char *aFilePattern, FileLoopModeType aOperateOnFolders
, bool aDoRecurse, bool aCalledRecursively = false);
ResultType FileGetTime(char *aFilespec, char aWhichTime);
int FileSetTime(char *aYYYYMMDD, char *aFilePattern, char aWhichTime
, FileLoopModeType aOperateOnFolders, bool aDoRecurse, bool aCalledRecursively = false);
ResultType FileGetSize(char *aFilespec, char *aGranularity);
ResultType FileGetVersion(char *aFilespec);
ResultType IniRead(char *aFilespec, char *aSection, char *aKey, char *aDefault);
ResultType IniWrite(char *aValue, char *aFilespec, char *aSection, char *aKey);
ResultType IniDelete(char *aFilespec, char *aSection, char *aKey);
ResultType RegRead(HKEY aRootKey, char *aRegSubkey, char *aValueName);
ResultType RegWrite(DWORD aValueType, HKEY aRootKey, char *aRegSubkey, char *aValueName, char *aValue);
ResultType RegDelete(HKEY aRootKey, char *aRegSubkey, char *aValueName);
static bool RegRemoveSubkeys(HKEY hRegKey);
#define DESTROY_SPLASH \
{\
if (g_hWndSplash && IsWindow(g_hWndSplash))\
DestroyWindow(g_hWndSplash);\
g_hWndSplash = NULL;\
}
ResultType SplashTextOn(int aWidth, int aHeight, char *aTitle, char *aText);
ResultType Splash(char *aOptions, char *aSubText, char *aMainText, char *aTitle, char *aFontName
, char *aImageFile, bool aSplashImage);
ResultType ToolTip(char *aText, char *aX, char *aY, char *aID);
ResultType TrayTip(char *aTitle, char *aText, char *aTimeout, char *aOptions);
ResultType Transform(char *aCmd, char *aValue1, char *aValue2);
ResultType Input(); // The Input command.
#define SW_NONE -1
ResultType PerformShowWindow(ActionTypeType aActionType, char *aTitle = "", char *aText = ""
, char *aExcludeTitle = "", char *aExcludeText = "");
ResultType PerformWait();
ResultType WinMove(char *aTitle, char *aText, char *aX, char *aY
, char *aWidth = "", char *aHeight = "", char *aExcludeTitle = "", char *aExcludeText = "");
ResultType WinMenuSelectItem(char *aTitle, char *aText, char *aMenu1, char *aMenu2
, char *aMenu3, char *aMenu4, char *aMenu5, char *aMenu6, char *aMenu7
, char *aExcludeTitle, char *aExcludeText);
ResultType ControlSend(char *aControl, char *aKeysToSend, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText, bool aSendRaw);
ResultType ControlClick(vk_type aVK, int aClickCount, char *aOptions, char *aControl
, char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType ControlMove(char *aControl, char *aX, char *aY, char *aWidth, char *aHeight
, char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType ControlGetPos(char *aControl, char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType ControlGetFocus(char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType ControlFocus(char *aControl, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType ControlSetText(char *aControl, char *aNewText, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType ControlGetText(char *aControl, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType ControlGetListView(Var &aOutputVar, HWND aHwnd, char *aOptions);
ResultType Control(char *aCmd, char *aValue, char *aControl, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType ControlGet(char *aCommand, char *aValue, char *aControl, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType GuiControl(char *aCommand, char *aControlID, char *aParam3);
ResultType GuiControlGet(char *aCommand, char *aControlID, char *aParam3);
ResultType StatusBarGetText(char *aPart, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType StatusBarWait(char *aTextToWaitFor, char *aSeconds, char *aPart, char *aTitle, char *aText
, char *aInterval, char *aExcludeTitle, char *aExcludeText);
ResultType ScriptPostSendMessage(bool aUseSend);
ResultType ScriptProcess(char *aCmd, char *aProcess, char *aParam3);
ResultType WinSet(char *aAttrib, char *aValue, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText);
ResultType WinSetTitle(char *aTitle, char *aText, char *aNewTitle
, char *aExcludeTitle = "", char *aExcludeText = "");
ResultType WinGetTitle(char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType WinGetClass(char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType WinGet(char *aCmd, char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType WinGetControlList(Var &aOutputVar, HWND aTargetWindow, bool aFetchHWNDs);
ResultType WinGetText(char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType WinGetPos(char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
ResultType EnvGet(char *aEnvVarName);
ResultType SysGet(char *aCmd, char *aValue);
ResultType PixelSearch(int aLeft, int aTop, int aRight, int aBottom, COLORREF aColorBGR, int aVariation
, char *aOptions, bool aIsPixelGetColor);
ResultType ImageSearch(int aLeft, int aTop, int aRight, int aBottom, char *aImageFile);
ResultType PixelGetColor(int aX, int aY, char *aOptions);
static ResultType SetToggleState(vk_type aVK, ToggleValueType &ForceLock, char *aToggleText);
public:
// Keep any fields that aren't an even multiple of 4 adjacent to each other. This conserves memory
// due to byte-alignment:
ActionTypeType mActionType; // What type of line this is.
ArgCountType mArgc; // How many arguments exist in mArg[].
FileIndexType mFileIndex; // Which file the line came from. 0 is the first, and it's the main script file.
ArgStruct *mArg; // Will be used to hold a dynamic array of dynamic Args.
LineNumberType mLineNumber; // The line number in the file from which the script was loaded, for debugging.
AttributeType mAttribute;
Line *mPrevLine, *mNextLine; // The prev & next lines adjacent to this one in the linked list; NULL if none.
Line *mRelatedLine; // e.g. the "else" that belongs to this "if"
Line *mParentLine; // Indicates the parent (owner) of this line.
// Probably best to always use ARG1 even if other things have supposedly verfied
// that it exists, since it's count-check should make the dereference of a NULL
// pointer (or accessing non-existent array elements) virtually impossible.
// Empty-string is probably more universally useful than NULL, since some
// API calls and other functions might not appreciate receiving NULLs. In addition,
// always remembering to have to check for NULL makes things harder to maintain
// and more bug-prone. The below macros rely upon the fact that the individual
// elements of mArg cannot be NULL (because they're explicitly set to be blank
// when the user has omitted an arg in between two non-blank args). Later, might
// want to review if any of the API calls used expect a string whose contents are
// modifiable.
#define RAW_ARG1 (mArgc > 0 ? mArg[0].text : "")
#define RAW_ARG2 (mArgc > 1 ? mArg[1].text : "")
#define RAW_ARG3 (mArgc > 2 ? mArg[2].text : "")
#define RAW_ARG4 (mArgc > 3 ? mArg[3].text : "")
#define RAW_ARG5 (mArgc > 4 ? mArg[4].text : "")
#define RAW_ARG6 (mArgc > 5 ? mArg[5].text : "")
#define RAW_ARG7 (mArgc > 6 ? mArg[6].text : "")
#define RAW_ARG8 (mArgc > 7 ? mArg[7].text : "")
#define LINE_RAW_ARG1 (line->mArgc > 0 ? line->mArg[0].text : "")
#define LINE_RAW_ARG2 (line->mArgc > 1 ? line->mArg[1].text : "")
#define LINE_RAW_ARG3 (line->mArgc > 2 ? line->mArg[2].text : "")
#define LINE_RAW_ARG4 (line->mArgc > 3 ? line->mArg[3].text : "")
#define LINE_RAW_ARG5 (line->mArgc > 4 ? line->mArg[4].text : "")
#define LINE_RAW_ARG6 (line->mArgc > 5 ? line->mArg[5].text : "")
#define LINE_RAW_ARG7 (line->mArgc > 6 ? line->mArg[6].text : "")
#define LINE_RAW_ARG8 (line->mArgc > 7 ? line->mArg[7].text : "")
#define LINE_RAW_ARG9 (line->mArgc > 8 ? line->mArg[8].text : "")
#define NEW_RAW_ARG1 (aArgc > 0 ? new_arg[0].text : "") // Helps performance to use this vs. LINE_RAW_ARG where possible.
#define NEW_RAW_ARG2 (aArgc > 1 ? new_arg[1].text : "")
#define NEW_RAW_ARG3 (aArgc > 2 ? new_arg[2].text : "")
#define NEW_RAW_ARG4 (aArgc > 3 ? new_arg[3].text : "")
#define NEW_RAW_ARG5 (aArgc > 4 ? new_arg[4].text : "")
#define NEW_RAW_ARG6 (aArgc > 5 ? new_arg[5].text : "")
#define NEW_RAW_ARG7 (aArgc > 6 ? new_arg[6].text : "")
#define NEW_RAW_ARG8 (aArgc > 7 ? new_arg[7].text : "")
#define NEW_RAW_ARG9 (aArgc > 8 ? new_arg[8].text : "")
#define SAVED_ARG1 (mArgc > 0 ? arg[0] : "")
#define SAVED_ARG2 (mArgc > 1 ? arg[1] : "")
#define SAVED_ARG3 (mArgc > 2 ? arg[2] : "")
#define SAVED_ARG4 (mArgc > 3 ? arg[3] : "")
#define SAVED_ARG5 (mArgc > 4 ? arg[4] : "")
// For the below, it is the caller's responsibility to ensure that mArgc is
// large enough (either via load-time validation or a runtime check of mArgc).
// This is because for performance reasons, the sArgVar entry for omitted args isn't
// initialized, so may have an old/obsolete value from some previous command.
#define OUTPUT_VAR (*sArgVar) // ExpandArgs() has ensured this first ArgVar is always initialized, so there's never any need to check mArgc > 0.
#define ARGVARRAW1 (*sArgVar) // i.e. sArgVar[0], and same as OUTPUT_VAR (it's a duplicate to help readability).
#define ARGVARRAW2 (sArgVar[1]) // It's called RAW because its user is responsible for ensuring the arg exists by checking mArgc at loadtime or runtime.
#define ARGVAR1 ARGVARRAW1 // This first one doesn't need the check below because ExpandArgs() has ensured it's initialized.
#define ARGVAR2 (mArgc > 1 ? sArgVar[1] : NULL) // Caller relies on the check of mArgc because for performance,
#define ARGVAR3 (mArgc > 2 ? sArgVar[2] : NULL) // sArgVar[] isn't initialied for parameters the script
#define ARGVAR4 (mArgc > 3 ? sArgVar[3] : NULL) // omitted entirely from the end of the parameter list.
#define ARGVAR5 (mArgc > 4 ? sArgVar[4] : NULL)
#define ARGVAR6 (mArgc > 5 ? sArgVar[5] : NULL)
#define ARGVAR7 (mArgc > 6 ? sArgVar[6] : NULL)
#define ARGVAR8 (mArgc > 7 ? sArgVar[7] : NULL)
#define ARG1 sArgDeref[0] // These are the expanded/resolved parameters for the currently-executing command.
#define ARG2 sArgDeref[1] // They're populated by ExpandArgs().
#define ARG3 sArgDeref[2]
#define ARG4 sArgDeref[3]
#define ARG5 sArgDeref[4]
#define ARG6 sArgDeref[5]
#define ARG7 sArgDeref[6]
#define ARG8 sArgDeref[7]
#define ARG9 sArgDeref[8]
#define ARG10 sArgDeref[9]
#define ARG11 sArgDeref[10]
#define TWO_ARGS ARG1, ARG2
#define THREE_ARGS ARG1, ARG2, ARG3
#define FOUR_ARGS ARG1, ARG2, ARG3, ARG4
#define FIVE_ARGS ARG1, ARG2, ARG3, ARG4, ARG5
#define SIX_ARGS ARG1, ARG2, ARG3, ARG4, ARG5, ARG6
#define SEVEN_ARGS ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7
#define EIGHT_ARGS ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8
#define NINE_ARGS ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9
#define TEN_ARGS ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10
#define ELEVEN_ARGS ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, ARG11
// If the arg's text is non-blank, it means the variable is a dynamic name such as array%i%
// that must be resolved at runtime rather than load-time. Therefore, this macro must not
// be used without first having checked that arg.text is blank:
#define VAR(arg) ((Var *)arg.deref)
// Uses arg number (i.e. the first arg is 1, not 0). Caller must ensure that ArgNum >= 1 and that
// the arg in question is an input or output variable (since that isn't checked there):
#define ARG_HAS_VAR(ArgNum) (mArgc >= ArgNum && (*mArg[ArgNum-1].text || mArg[ArgNum-1].deref))
// Shouldn't go much higher than 400 since the main window's Edit control is currently limited
// to 64KB to be compatible with the Win9x limit. Avg. line length is probably under 100 for
// the vast majority of scripts, so 400 seems unlikely to exceed the buffer size. Even in the
// worst case where the buffer size is exceeded, the text is simply truncated, so it's not too bad:
#define LINE_LOG_SIZE 400 // See above.
static Line *sLog[LINE_LOG_SIZE];
static DWORD sLogTick[LINE_LOG_SIZE];
static int sLogNext;
#ifdef AUTOHOTKEYSC // Reduces code size to omit things that are unused, and helps catch bugs at compile-time.
static char *sSourceFile[1]; // Only need to be able to hold the main script since compiled scripts don't support dynamic including.
#else
static char **sSourceFile; // Will hold an array of strings.
static int sMaxSourceFiles; // Maximum number of items it can currently hold.
#endif
static int sSourceFileCount; // Number of items in the above array.
static void FreeDerefBufIfLarge();
ResultType ExecUntil(ExecUntilMode aMode, char **apReturnValue = NULL, Line **apJumpToLine = NULL);
// The following are characters that can't legally occur after an AND or OR. It excludes all unary operators
// "!~*&-+" as well as the parentheses chars "()":
#define EXPR_CORE "<>=/|^,:"
// The characters common to both EXPR_TELLTALES and EXPR_OPERAND_TERMINATORS:
#define EXPR_COMMON " \t" EXPR_CORE "*&~!()" // Space and Tab are included at the beginning for performance.
#define EXPR_COMMON_FORBIDDEN_BYREF "<>/|^,*&~!" // Omits space/tab because operators like := can have them. Omits colon because want to be able to pass a ternary byref. Omits = because colon is omitted (otherwise the logic is written in a way that wouldn't allow :=). Omits parentheses because a variable or assignment can be enclosed in them even though they're redundant.
#define CONTINUATION_LINE_SYMBOLS EXPR_CORE ".+-*&!?~" // v1.0.46.
// Characters whose presence in a mandatory-numeric param make it an expression for certain.
// + and - are not included here because legacy numeric parameters can contain unary plus or minus,
// e.g. WinMove, -%x%, -%y%:
#define EXPR_TELLTALES EXPR_COMMON "\""
// Characters that mark the end of an operand inside an expression. Double-quote must not be included:
#define EXPR_OPERAND_TERMINATORS EXPR_COMMON "+-"
#define EXPR_ALL_SYMBOLS EXPR_OPERAND_TERMINATORS "\"" // Excludes '.' and '?' since they need special treatment due to the present/future allowance of them inside the names of variable and functions.
#define EXPR_FORBIDDEN_BYREF EXPR_COMMON_FORBIDDEN_BYREF ".+-\"" // Dot is also included.
#define EXPR_ILLEGAL_CHARS "'\\;`{}" // Characters illegal in an expression.
// The following HOTSTRING option recognizer is kept somewhat forgiving/non-specific for backward compatibility
// (e.g. scripts may have some invalid hotstring options, which are simply ignored). This definition is here
// because it's related to continuation line symbols. Also, avoid ever adding "&" to hotstring options because
// it might introduce ambiguity in the differentiation of things like:
// : & x::hotkey action
// : & *::abbrev with leading colon::
#define IS_HOTSTRING_OPTION(chr) (isalnum(chr) || strchr("?*- \t", chr))
// The characters below are ordered with most-often used ones first, for performance:
#define DEFINE_END_FLAGS \
char end_flags[] = {' ', g_delimiter, '(', '\t', '<', '>', ':', '=', '+', '-', '*', '/', '!', '~', '&', '|', '^', '\0'}; // '\0' must be last.
// '?' and '.' are omitted from the above because they require special handling due to being permitted
// in the curruent or future names of variables and functions.
static bool StartsWithAssignmentOp(char *aStr) // RELATED TO ABOVE, so kept adjacent to it.
// Returns true if aStr begins with an assignment operator such as :=, >>=, ++, etc.
// For simplicity, this doesn't check that what comes AFTER an operator is valid. For example,
// :== isn't valid, yet is reported as valid here because it starts with :=.
// Caller is responsible for having omitted leading whitespace, if desired.
{
if (!(*aStr && aStr[1])) // Relies on short-circuit boolean order.
return false;
char cp0 = *aStr;
switch(aStr[1])
{
// '=' is listed first for performance, since it's the most common.
case '=': return strchr(":+-*.|&^/", cp0); // Covers :=, +=, -=, *=, .=, |=, &=, ^=, /= (9 operators).
case '+': // Fall through to below. Covers ++.
case '-': return cp0 == aStr[1]; // Covers --.
case '/': // Fall through to below. Covers //=.
case '>': // Fall through to below. covers >>=.
case '<': return cp0 == aStr[1] && aStr[2] == '='; // Covers <<=.
}
// Otherwise:
return false;
}
size_t ArgLength(int aArgNum);
Var *ResolveVarOfArg(int aArgIndex, bool aCreateIfNecessary = true);
ResultType ExpandArgs(VarSizeType aSpaceNeeded = VARSIZE_ERROR, Var *aArgVar[] = NULL);
VarSizeType GetExpandedArgSize(bool aCalcDerefBufSize, Var *aArgVar[]);
char *ExpandArg(char *aBuf, int aArgIndex, Var *aArgVar = NULL);
char *ExpandExpression(int aArgIndex, ResultType &aResult, char *&aTarget, char *&aDerefBuf
, size_t &aDerefBufSize, char *aArgDeref[], size_t aExtraSize);
ResultType Deref(Var *aOutputVar, char *aBuf);
static bool FileIsFilteredOut(WIN32_FIND_DATA &aCurrentFile, FileLoopModeType aFileLoopMode
, char *aFilePath, size_t aFilePathLength);
Label *GetJumpTarget(bool aIsDereferenced);
Label *IsJumpValid(Label &aTargetLabel);
HWND DetermineTargetWindow(char *aTitle, char *aText, char *aExcludeTitle, char *aExcludeText);
#ifndef AUTOHOTKEYSC
static int ConvertEscapeChar(char *aFilespec);
static size_t ConvertEscapeCharGetLine(char *aBuf, int aMaxCharsToRead, FILE *fp);
#endif // The functions above are not needed by the self-contained version.
// This is in the .h file so that it's more likely the compiler's cost/benefit estimate will
// make it inline (since it is called from only one place). Inline would be good since it
// is called frequently during script loading and is difficult to macro-ize in a way that
// retains readability.
static ArgTypeType ArgIsVar(ActionTypeType aActionType, int aArgIndex)
{
switch(aArgIndex)
{
case 0: // Arg #1
switch(aActionType)
{
case ACT_ASSIGN:
case ACT_ASSIGNEXPR:
case ACT_ADD:
case ACT_SUB:
case ACT_MULT:
case ACT_DIV:
case ACT_TRANSFORM:
case ACT_STRINGLEFT:
case ACT_STRINGRIGHT:
case ACT_STRINGMID:
case ACT_STRINGTRIMLEFT:
case ACT_STRINGTRIMRIGHT:
case ACT_STRINGLOWER:
case ACT_STRINGUPPER:
case ACT_STRINGLEN:
case ACT_STRINGREPLACE:
case ACT_STRINGGETPOS:
case ACT_GETKEYSTATE:
case ACT_CONTROLGETFOCUS:
case ACT_CONTROLGETTEXT:
case ACT_CONTROLGET:
case ACT_GUICONTROLGET:
case ACT_STATUSBARGETTEXT:
case ACT_INPUTBOX:
case ACT_RANDOM:
case ACT_INIREAD:
case ACT_REGREAD:
case ACT_DRIVESPACEFREE:
case ACT_DRIVEGET:
case ACT_SOUNDGET:
case ACT_SOUNDGETWAVEVOLUME:
case ACT_FILEREAD:
case ACT_FILEREADLINE:
case ACT_FILEGETATTRIB:
case ACT_FILEGETTIME:
case ACT_FILEGETSIZE:
case ACT_FILEGETVERSION:
case ACT_FILESELECTFILE:
case ACT_FILESELECTFOLDER:
case ACT_MOUSEGETPOS:
case ACT_WINGETTITLE:
case ACT_WINGETCLASS:
case ACT_WINGET:
case ACT_WINGETTEXT:
case ACT_WINGETPOS:
case ACT_SYSGET:
case ACT_ENVGET:
case ACT_CONTROLGETPOS:
case ACT_PIXELGETCOLOR:
case ACT_PIXELSEARCH:
case ACT_IMAGESEARCH:
case ACT_INPUT:
case ACT_FORMATTIME:
return ARG_TYPE_OUTPUT_VAR;
case ACT_SORT:
case ACT_SPLITPATH:
case ACT_IFINSTRING:
case ACT_IFNOTINSTRING:
case ACT_IFEQUAL:
case ACT_IFNOTEQUAL:
case ACT_IFGREATER:
case ACT_IFGREATEROREQUAL:
case ACT_IFLESS:
case ACT_IFLESSOREQUAL:
case ACT_IFBETWEEN:
case ACT_IFNOTBETWEEN:
case ACT_IFIN:
case ACT_IFNOTIN:
case ACT_IFCONTAINS:
case ACT_IFNOTCONTAINS:
case ACT_IFIS:
case ACT_IFISNOT:
return ARG_TYPE_INPUT_VAR;
}
break;
case 1: // Arg #2
switch(aActionType)
{
case ACT_STRINGLEFT:
case ACT_STRINGRIGHT:
case ACT_STRINGMID:
case ACT_STRINGTRIMLEFT:
case ACT_STRINGTRIMRIGHT:
case ACT_STRINGLOWER:
case ACT_STRINGUPPER:
case ACT_STRINGLEN:
case ACT_STRINGREPLACE:
case ACT_STRINGGETPOS:
case ACT_STRINGSPLIT:
return ARG_TYPE_INPUT_VAR;
case ACT_MOUSEGETPOS:
case ACT_WINGETPOS:
case ACT_CONTROLGETPOS:
case ACT_PIXELSEARCH:
case ACT_IMAGESEARCH:
case ACT_SPLITPATH:
case ACT_FILEGETSHORTCUT:
return ARG_TYPE_OUTPUT_VAR;
}
break;
case 2: // Arg #3
switch(aActionType)
{
case ACT_WINGETPOS:
case ACT_CONTROLGETPOS:
case ACT_MOUSEGETPOS:
case ACT_SPLITPATH:
case ACT_FILEGETSHORTCUT:
return ARG_TYPE_OUTPUT_VAR;
}