forked from AutoHotkey/AutoHotkey
/
script.h
2971 lines (2657 loc) · 140 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-2009 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.
#include "script_object.h"
#include "Debugger.h"
#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;
#define MAX_THREADS_LIMIT UCHAR_MAX // Uses UCHAR_MAX (255) because some variables that store a thread count are UCHARs.
#define MAX_THREADS_DEFAULT 10 // Must not be higher than above.
#define EMERGENCY_THREADS 2 // This is the number of extra threads available after g_MaxThreadsTotal has been reached for the following to launch: hotkeys/etc. whose first line is something important like ExitApp or Pause. (see #MaxThreads documentation).
#define MAX_THREADS_EMERGENCY (g_MaxThreadsTotal + EMERGENCY_THREADS)
#define TOTAL_ADDITIONAL_THREADS (EMERGENCY_THREADS + 2) // See below.
// Must allow two beyond EMERGENCY_THREADS: One for the AutoExec/idle thread and one so that ExitApp()
// can run even when MAX_THREADS_EMERGENCY has been reached.
// Explanation: If/when AutoExec() finishes, although it no longer needs g_array[0] (not even
// AutoExecSectionTimeout() needs it because it either won't be called or it will return early),
// at least the following might still use g_array[0]:
// 1) Threadless (fast-mode) callbacks that have no controlling script thread; see RegisterCallbackCStub().
// 2) g_array[0].IsPaused indicates whether the script is in a paused state while idle.
// In addition, it probably simplifies the code not to reclaim g_array[0]; e.g. ++g and --g can be done
// unconditionally when creating new threads.
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 might rely on this being zero.
#define ATTR_TRUE (void *)1
typedef void *AttributeType;
typedef int FileLoopModeType;
#define FILE_LOOP_INVALID 0
#define FILE_LOOP_FILES_ONLY 1
#define FILE_LOOP_FOLDERS_ONLY 2
#define FILE_LOOP_RECURSE 4
#define FILE_LOOP_FILES_AND_FOLDERS (FILE_LOOP_FILES_ONLY | 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
, VAR_TYPE_OBJECT, VAR_TYPE_BYREF}; // v2
#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);\
}
// 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 _T("The current thread will exit.")
#define ERR_ABORT _T(" ") ERR_ABORT_NO_SPACES
#define WILL_EXIT _T("The program will exit.")
#define OLD_STILL_IN_EFFECT _T("The script was not reloaded; the old version will remain in effect.")
#define ERR_CONTINUATION_SECTION_TOO_LONG _T("Continuation section too long.")
#define ERR_UNRECOGNIZED_ACTION _T("This line does not contain a recognized action.")
#define ERR_NONEXISTENT_HOTKEY _T("Nonexistent hotkey.")
#define ERR_NONEXISTENT_VARIANT _T("Nonexistent hotkey variant (IfWin).")
#define ERR_NONEXISTENT_FUNCTION _T("Call to nonexistent function.")
#define ERR_UNRECOGNIZED_DIRECTIVE _T("Unknown directive.")
#define ERR_EXE_CORRUPTED _T("EXE corrupted")
#define ERR_INVALID_VALUE _T("Invalid value.")
#define ERR_PARAM_INVALID _T("Invalid parameter(s).")
#define ERR_PARAM1_INVALID _T("Parameter #1 invalid.")
#define ERR_PARAM2_INVALID _T("Parameter #2 invalid.")
#define ERR_PARAM3_INVALID _T("Parameter #3 invalid.")
#define ERR_PARAM4_INVALID _T("Parameter #4 invalid.")
#define ERR_PARAM5_INVALID _T("Parameter #5 invalid.")
#define ERR_PARAM6_INVALID _T("Parameter #6 invalid.")
#define ERR_PARAM7_INVALID _T("Parameter #7 invalid.")
#define ERR_PARAM8_INVALID _T("Parameter #8 invalid.")
#define ERR_PARAM1_REQUIRED _T("Parameter #1 required")
#define ERR_PARAM2_REQUIRED _T("Parameter #2 required")
#define ERR_PARAM3_REQUIRED _T("Parameter #3 required")
#define ERR_PARAM4_OMIT _T("Parameter #4 should be omitted in this case.")
#define ERR_PARAM2_MUST_BE_BLANK _T("Parameter #2 must be blank in this case.")
#define ERR_PARAM3_MUST_BE_BLANK _T("Parameter #3 must be blank in this case.")
#define ERR_PARAM4_MUST_BE_BLANK _T("Parameter #4 must be blank in this case.")
#define ERR_MISSING_OUTPUT_VAR _T("Requires at least one of its output variables.")
#define ERR_MISSING_OPEN_PAREN _T("Missing \"(\"")
#define ERR_MISSING_OPEN_BRACE _T("Missing \"{\"")
#define ERR_MISSING_CLOSE_PAREN _T("Missing \")\"")
#define ERR_MISSING_CLOSE_BRACE _T("Missing \"}\"")
#define ERR_MISSING_CLOSE_BRACKET _T("Missing \"]\"") // L31
#define ERR_UNEXPECTED_CLOSE_PAREN _T("Unexpected \")\"")
#define ERR_UNEXPECTED_CLOSE_BRACKET _T("Unexpected \"]\"")
#define ERR_UNEXPECTED_CLOSE_BRACE _T("Unexpected \"}\"")
#define ERR_UNEXPECTED_COMMA _T("Unexpected comma")
#define ERR_BAD_AUTO_CONCAT _T("Missing space or operator before this.")
#define ERR_MISSING_CLOSE_QUOTE _T("Missing close-quote") // No period after short phrases.
#define ERR_MISSING_COMMA _T("Missing comma") //
#define ERR_MISSING_PARAM_NAME _T("Missing parameter name.")
#define ERR_PARAM_REQUIRED _T("Missing a required parameter.")
#define ERR_TOO_MANY_PARAMS _T("Too many parameters passed to function.") // L31
#define ERR_TOO_FEW_PARAMS _T("Too few parameters passed to function.") // L31
#define ERR_BAD_OPTIONAL_PARAM _T("Expected \":=\"")
#define ERR_ELSE_WITH_NO_IF _T("ELSE with no matching IF")
#define ERR_UNTIL_WITH_NO_LOOP _T("UNTIL with no matching LOOP")
#define ERR_CATCH_WITH_NO_TRY _T("CATCH with no matching TRY")
#define ERR_FINALLY_WITH_NO_PRECEDENT _T("FINALLY with no matching TRY or CATCH")
#define ERR_BAD_JUMP_INSIDE_FINALLY _T("Jumps cannot exit a FINALLY block.")
#define ERR_EXPECTED_BLOCK_OR_ACTION _T("Expected \"{\" or single-line action.")
#define ERR_OUTOFMEM _T("Out of memory.") // Used by RegEx too, so don't change it without also changing RegEx to keep the former string.
#define ERR_EXPR_TOO_LONG _T("Expression too complex")
#define ERR_TOO_MANY_REFS ERR_EXPR_TOO_LONG // No longer applies to just var/func refs. Old message: "Too many var/func refs."
#define ERR_NO_LABEL _T("Target label does not exist.")
#define ERR_MENU _T("Menu does not exist.")
#define ERR_SUBMENU _T("Submenu does not exist.")
#define ERR_WINDOW_PARAM _T("Requires at least one of its window parameters.")
#define ERR_MENUTRAY _T("Supported only for the tray menu")
#define ERR_MOUSE_COORD _T("X & Y must be either both absent or both present.")
#define ERR_DIVIDEBYZERO _T("Divide by zero")
#define ERR_VAR_IS_READONLY _T("Not allowed as an output variable.")
#define ERR_INVALID_CHAR _T("This character is not allowed here.")
#define ERR_INVALID_DOT _T("Ambiguous or invalid use of \".\"")
#define ERR_UNQUOTED_NON_ALNUM _T("Unquoted literals may only consist of alphanumeric characters/underscore.")
#define ERR_DUPLICATE_DECLARATION _T("Duplicate declaration.")
#define ERR_INVALID_CLASS_VAR _T("Invalid class variable declaration.")
#define ERR_INVALID_LINE_IN_CLASS_DEF _T("Expected assignment or class/method definition.")
#define ERR_INVALID_GUI_NAME _T("Invalid Gui name.")
#define ERR_INVALID_OPTION _T("Invalid option.") // Generic message used by the Gui system.
#define ERR_MUST_DECLARE _T("This variable must be declared.")
#define ERR_REMOVE_THE_PERCENT _T("If this variable was not intended to be dynamic, remove the % symbols from it.")
#define ERR_DYNAMIC_TOO_LONG _T("This dynamically built variable name is too long. ") ERR_REMOVE_THE_PERCENT
#define ERR_DYNAMIC_BLANK _T("This dynamic variable is blank. ") ERR_REMOVE_THE_PERCENT
#define ERR_HOTKEY_IF_EXPR _T("Parameter #2 must match an existing #If expression.")
#define ERR_EXCEPTION _T("An exception was thrown.")
#define ERR_EXPR_EVAL _T("Error evaluating expression. There may be a syntax error.")
#define ERR_NO_OBJECT _T("No object to invoke.")
#define ERR_NO_MEMBER _T("Unknown property or method.")
#define ERR_NO_GUI _T("No default GUI.")
#define ERR_PCRE_EXEC _T("PCRE execution error.")
#define WARNING_USE_UNSET_VARIABLE _T("This variable has not been assigned a value.")
#define WARNING_LOCAL_SAME_AS_GLOBAL _T("This local variable has the same name as a global variable.")
#define WARNING_USE_ENV_VARIABLE _T("An environment variable is being accessed; see #NoEnv.")
//----------------------------------------------------------------------------------
void DoIncrementalMouseMove(int aX1, int aY1, int aX2, int aY2, int aSpeed);
DWORD ProcessExist9x2000(LPTSTR aProcess);
#ifdef CONFIG_WINNT4
DWORD ProcessExistNT4(LPTSTR aProcess);
#endif
inline DWORD ProcessExist(LPTSTR aProcess)
{
return
#ifdef CONFIG_WINNT4
g_os.IsWinNT4() ? ProcessExistNT4(aProcess) :
#endif
ProcessExist9x2000(aProcess);
}
DWORD GetProcessName(DWORD aProcessID, LPTSTR aBuf, DWORD aBufSize, bool aGetNameOnly);
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(LPTSTR aBuf);
struct InputBoxType
{
LPTSTR title;
LPTSTR text;
int width;
int height;
int xpos;
int ypos;
Var *output_var;
TCHAR password_char;
bool set_password_char;
LPTSTR default_string;
DWORD timeout;
HWND hwnd;
HFONT font;
};
// 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;
}
// The following functions are used in GUI DPI scaling, so that
// GUIs designed for a 96 DPI setting (i.e. using absolute coords
// or explicit widths/sizes) can continue to run with mostly no issues.
static inline int DPIScale(int x)
{
extern int g_ScreenDPI;
return MulDiv(x, g_ScreenDPI, 96);
}
static inline int DPIUnscale(int x)
{
extern int g_ScreenDPI;
return MulDiv(x, 96, g_ScreenDPI);
}
#define INPUTBOX_DEFAULT INT_MIN
ResultType InputBoxParseOptions(LPTSTR aOptions, InputBoxType &aInputBox);
ResultType InputBox(Var *aOutputVar, LPTSTR aTitle, LPTSTR aText, LPTSTR aOptions, LPTSTR aDefault);
INT_PTR CALLBACK InputBoxProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID CALLBACK InputBoxTimeout(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
VOID CALLBACK DerefTimeout(HWND hWnd, UINT uMsg, UINT_PTR 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, HWND aGuiHwnd);
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;
// Traditionally DerefType was used to hold var and func references, which are parsed at an
// early stage, but when the capability to nest expressions between percent signs was added,
// it became necessary to pre-parse more. All non-numeric operands are represented in it.
enum DerefTypeType : BYTE
{
DT_VAR, // Variable reference, including built-ins.
DT_DOUBLE, // Marks the end of a double-deref.
DT_STRING, // Segment of text in a text arg (delimited by '%').
DT_QSTRING, // Segment of text in a quoted string (delimited by '%').
DT_WORDOP, // Word operator: and, or, not, new.
// DerefType::is_function() requires that these are last:
DT_FUNC, // Function call.
DT_VARIADIC // Variadic function call.
};
class Func; // Forward declaration for use below.
struct DerefType
{
LPTSTR marker;
union
{
Var *var; // DT_VAR
Func *func; // DT_FUNC
DerefType *next; // DT_STRING
SymbolType symbol; // DT_WORDOP
};
// Keep any fields that aren't an even multiple of 4 adjacent to each other. This conserves memory
// due to byte-alignment:
DerefTypeType type;
bool is_function() { return type >= DT_FUNC; }
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.
typedef WORD ArgLengthType; // Relies on the fact that an arg's literal text can't be longer than LINE_SIZE.
#define ARG_TYPE_NORMAL (UCHAR)0
#define ARG_TYPE_INPUT_VAR (UCHAR)1
#define ARG_TYPE_OUTPUT_VAR (UCHAR)2
#define ARGMAP_END_MARKER ((ArgLengthType)~0) // ExpressionToPostfix() may rely on this being greater than any possible arg character offset.
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]).
ArgLengthType 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.
LPTSTR text;
DerefType *deref; // Will hold a NULL-terminated array of operands/word-operators pre-parsed by ParseDerefs()/ParseOperands().
ExprTokenType *postfix; // An array of tokens in postfix order. Also used for ACT_(NOT)BETWEEN to store pre-converted binary integers.
};
#define BIF_DECL_PARAMS ResultType &aResult, ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount
// The following macro is used for definitions and declarations of built-in functions:
#define BIF_DECL(name) void name(BIF_DECL_PARAMS)
#define BIF_DECL_GUICTRL(name) void name(BIF_DECL_PARAMS, GuiControlType& control)
// 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:
// FIX FOR v1.0.48:
// OLDER (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 BUT ONLY within the RegEdit GUI. RegEdit seems capable of importing subkeys whose names
// (even without any value name appended) are longer than 259 characters (see comments higher above).
#define MAX_REG_ITEM_SIZE 1024 // Needs to be greater than 260 (see comments above), but I couldn't find any documentation at MSDN or the web about the max length of a subkey name. One example at MSDN RegEnumKeyEx() uses MAX_KEY_LENGTH=255 and MAX_VALUE_NAME=16383, but clearly MAX_KEY_LENGTH should be larger.
#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.
TCHAR subkey[MAX_REG_ITEM_SIZE]; // The branch of the registry where this subkey or value is located.
TCHAR name[MAX_REG_ITEM_SIZE]; // The subkey or value name.
DWORD type; // Value Type (e.g REG_DWORD).
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, LPTSTR 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:
tcslcpy(subkey, aSubKey, _countof(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 = _tcslen(subkey);
if (length && subkey[length - 1] == '\\')
subkey[length - 1] = '\0';
}
};
class TextStream; // TextIO
struct LoopReadFileStruct
{
TextStream *mReadFile, *mWriteFile;
TCHAR mWriteFileName[MAX_PATH];
#define READ_FILE_LINE_SIZE (64 * 1024)
TCHAR mCurrentLine[READ_FILE_LINE_SIZE];
LoopReadFileStruct(TextStream *aReadFile, LPTSTR 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:
tcslcpy(mWriteFileName, aWriteFileName, _countof(mWriteFileName));
*mCurrentLine = '\0';
}
};
// TextStream flags for LoadIncludedFile (script files) and file-reading loops.
// Do not lock read/write: older versions used fopen(), which is implicitly permissive.
#define DEFAULT_READ_FLAGS (TextStream::READ | TextStream::EOL_CRLF | TextStream::EOL_ORPHAN_CR | TextStream::SHARE_READ | TextStream::SHARE_WRITE)
typedef UCHAR ArgCountType;
#define MAX_ARGS 20 // Maximum number of args used by any command.
enum DllArgTypes {
DLL_ARG_INVALID
, DLL_ARG_ASTR
, DLL_ARG_INT
, DLL_ARG_SHORT
, DLL_ARG_CHAR
, DLL_ARG_INT64
, DLL_ARG_FLOAT
, DLL_ARG_DOUBLE
, DLL_ARG_WSTR
, DLL_ARG_STR = UorA(DLL_ARG_WSTR, DLL_ARG_ASTR)
, DLL_ARG_xSTR = UorA(DLL_ARG_ASTR, DLL_ARG_WSTR) // To simplify some sections.
}; // Some sections might rely on DLL_ARG_INVALID being 0.
// 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, WINGET_CMD_PROCESSPATH
};
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.
typedef UCHAR GuiControls;
enum GuiControlTypes {GUI_CONTROL_INVALID // GUI_CONTROL_INVALID must be zero due to things like ZeroMemory() on the struct.
, GUI_CONTROL_LABEL, 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_ACTIVEX, GUI_CONTROL_LINK, GUI_CONTROL_CUSTOM, 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 _T("LBNAHR")
enum ProcessCmds {PROCESS_CMD_INVALID, PROCESS_CMD_EXIST, PROCESS_CMD_CLOSE
, 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_SERIAL, DRIVEGET_CMD_TYPE, DRIVEGET_CMD_STATUS
, DRIVEGET_CMD_STATUSCD, DRIVEGET_CMD_CAPACITY, DRIVEGET_CMD_SPACEFREE};
enum WinSetAttributes {WINSET_INVALID, WINSET_TRANSPARENT, WINSET_TRANSCOLOR, WINSET_ALWAYSONTOP
, WINSET_STYLE, WINSET_EXSTYLE, WINSET_ENABLED, WINSET_REGION};
class Label; // Forward declaration so that each can use the other.
class Line
{
private:
ResultType EvaluateCondition();
bool EvaluateLoopUntil(ResultType &aResult);
ResultType Line::PerformLoop(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil
, __int64 aIterationLimit, bool aIsInfinite);
ResultType Line::PerformLoopFilePattern(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil
, FileLoopModeType aFileLoopMode, bool aRecurseSubfolders, LPTSTR aFilePattern);
ResultType PerformLoopReg(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil
, FileLoopModeType aFileLoopMode, bool aRecurseSubfolders, HKEY aRootKeyType, HKEY aRootKey, LPTSTR aRegSubkey);
ResultType PerformLoopParse(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil);
ResultType Line::PerformLoopParseCSV(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil);
ResultType PerformLoopReadFile(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil, TextStream *aReadFile, LPTSTR aWriteFileName);
ResultType PerformLoopWhile(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine); // Lexikos: ACT_WHILE.
ResultType PerformLoopFor(ExprTokenType *aResultToken, bool &aContinueMainLoop, Line *&aJumpToLine, Line *aUntil); // Lexikos: ACT_FOR.
ResultType Perform();
friend BIF_DECL(BIF_PerformAction);
ResultType MouseGetPos(DWORD aOptions);
ResultType FormatTime(LPTSTR aYYYYMMDD, LPTSTR aFormat);
ResultType StringReplace();
ResultType SplitPath(LPTSTR aFileSpec);
ResultType PerformSort(LPTSTR aContents, LPTSTR aOptions);
ResultType GetKeyJoyState(LPTSTR aKeyName, LPTSTR aOption);
ResultType DriveSpace(LPTSTR aPath, bool aGetFreeSpace);
ResultType Drive(LPTSTR aCmd, LPTSTR aValue, LPTSTR aValue2);
ResultType DriveLock(TCHAR aDriveLetter, bool aLockIt);
ResultType DriveGet(LPTSTR aCmd, LPTSTR aValue);
ResultType SoundSetGet(LPTSTR aSetting, LPTSTR aComponentType, LPTSTR aControlType, LPTSTR aDevice);
ResultType SoundSetGet2kXP(LPTSTR aSetting, DWORD aComponentType, int aComponentInstance
, DWORD aControlType, LPTSTR aDevice);
ResultType SoundSetGetVista(LPTSTR aSetting, DWORD aComponentType, int aComponentInstance
, DWORD aControlType, LPTSTR aDevice);
ResultType SoundPlay(LPTSTR aFilespec, bool aSleepUntilDone);
ResultType Download(LPTSTR aURL, LPTSTR aFilespec);
ResultType FileSelect(LPTSTR aOptions, LPTSTR aWorkingDir, LPTSTR aGreeting, LPTSTR aFilter);
// Bitwise flags:
#define FSF_ALLOW_CREATE 0x01
#define FSF_EDITBOX 0x02
#define FSF_NONEWDIALOG 0x04
ResultType DirSelect(LPTSTR aRootDir, LPTSTR aOptions, LPTSTR aGreeting);
ResultType FileGetShortcut(LPTSTR aShortcutFile);
ResultType FileCreateShortcut(LPTSTR aTargetFile, LPTSTR aShortcutFile, LPTSTR aWorkingDir, LPTSTR aArgs
, LPTSTR aDescription, LPTSTR aIconFile, LPTSTR aHotkey, LPTSTR aIconNumber, LPTSTR aRunState);
ResultType FileCreateDir(LPTSTR aDirSpec);
ResultType FileRead(LPTSTR aFilespec);
ResultType FileAppend(LPTSTR aFilespec, LPTSTR aBuf, LoopReadFileStruct *aCurrentReadFile);
ResultType WriteClipboardToFile(LPTSTR aFilespec);
ResultType ReadClipboardFromFile(HANDLE hfile);
ResultType FileDelete();
ResultType FileRecycle(LPTSTR aFilePattern);
ResultType FileRecycleEmpty(LPTSTR aDriveLetter);
ResultType FileInstall(LPTSTR aSource, LPTSTR aDest, LPTSTR aFlag);
ResultType FileGetAttrib(LPTSTR aFilespec);
int FileSetAttrib(LPTSTR aAttributes, LPTSTR aFilePattern, FileLoopModeType aOperateOnFolders
, bool aDoRecurse, bool aCalledRecursively = false);
ResultType FileGetTime(LPTSTR aFilespec, TCHAR aWhichTime);
int FileSetTime(LPTSTR aYYYYMMDD, LPTSTR aFilePattern, TCHAR aWhichTime
, FileLoopModeType aOperateOnFolders, bool aDoRecurse, bool aCalledRecursively = false);
ResultType FileGetSize(LPTSTR aFilespec, LPTSTR aGranularity);
ResultType FileGetVersion(LPTSTR aFilespec);
ResultType IniRead(LPTSTR aFilespec, LPTSTR aSection, LPTSTR aKey, LPTSTR aDefault);
ResultType IniWrite(LPTSTR aValue, LPTSTR aFilespec, LPTSTR aSection, LPTSTR aKey);
ResultType IniDelete(LPTSTR aFilespec, LPTSTR aSection, LPTSTR aKey);
ResultType RegRead(HKEY aRootKey, LPTSTR aRegSubkey, LPTSTR aValueName);
ResultType RegWrite(DWORD aValueType, HKEY aRootKey, LPTSTR aRegSubkey, LPTSTR aValueName, LPTSTR aValue);
ResultType RegDelete(HKEY aRootKey, LPTSTR aRegSubkey, LPTSTR aValueName);
static LONG RegRemoveSubkeys(HKEY hRegKey);
ResultType SetRegView(LPTSTR aView);
ResultType ToolTip(LPTSTR aText, LPTSTR aX, LPTSTR aY, LPTSTR aID);
ResultType TrayTip(LPTSTR aTitle, LPTSTR aText, LPTSTR aTimeout, LPTSTR aOptions);
ResultType Input(); // The Input command.
#define SW_NONE -1
ResultType PerformShowWindow(ActionTypeType aActionType, LPTSTR aTitle = _T(""), LPTSTR aText = _T("")
, LPTSTR aExcludeTitle = _T(""), LPTSTR aExcludeText = _T(""));
ResultType PerformWait();
ResultType WinMove(LPTSTR aTitle, LPTSTR aText, LPTSTR aX, LPTSTR aY
, LPTSTR aWidth = _T(""), LPTSTR aHeight = _T(""), LPTSTR aExcludeTitle = _T(""), LPTSTR aExcludeText = _T(""));
ResultType MenuSelect(LPTSTR aTitle, LPTSTR aText, LPTSTR aMenu1, LPTSTR aMenu2
, LPTSTR aMenu3, LPTSTR aMenu4, LPTSTR aMenu5, LPTSTR aMenu6, LPTSTR aMenu7
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlSend(LPTSTR aControl, LPTSTR aKeysToSend, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText, bool aSendRaw);
ResultType ControlClick(vk_type aVK, int aClickCount, LPTSTR aOptions, LPTSTR aControl
, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlMove(LPTSTR aControl, LPTSTR aX, LPTSTR aY, LPTSTR aWidth, LPTSTR aHeight
, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlGetPos(LPTSTR aControl, LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlGetFocus(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlFocus(LPTSTR aControl, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlSetText(LPTSTR aControl, LPTSTR aNewText, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlGetText(LPTSTR aControl, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlGetListView(Var &aOutputVar, HWND aHwnd, LPTSTR aOptions);
ResultType Control(LPTSTR aCmd, LPTSTR aValue, LPTSTR aControl, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ControlGet(LPTSTR aCommand, LPTSTR aValue, LPTSTR aControl, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType StatusBarGetText(LPTSTR aPart, LPTSTR aTitle, LPTSTR aText
, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType StatusBarWait(LPTSTR aTextToWaitFor, LPTSTR aSeconds, LPTSTR aPart, LPTSTR aTitle, LPTSTR aText
, LPTSTR aInterval, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType ScriptPostSendMessage(bool aUseSend);
ResultType WinSetTitle(LPTSTR aTitle, LPTSTR aText, LPTSTR aNewTitle
, LPTSTR aExcludeTitle = _T(""), LPTSTR aExcludeText = _T(""));
ResultType WinGetTitle(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType WinGetClass(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType WinGetText(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType WinGetPos(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
ResultType EnvGet(LPTSTR aEnvVarName);
ResultType PixelSearch(int aLeft, int aTop, int aRight, int aBottom, COLORREF aColorRGB, int aVariation
, LPTSTR aOptions, bool aIsPixelGetColor);
ResultType ImageSearch(int aLeft, int aTop, int aRight, int aBottom, LPTSTR aImageFile);
ResultType PixelGetColor(int aX, int aY, LPTSTR aOptions);
static ResultType SetToggleState(vk_type aVK, ToggleValueType &ForceLock, LPTSTR aToggleText);
public:
#define SET_S_DEREF_BUF(ptr, size) Line::sDerefBuf = ptr, Line::sDerefBufSize = size
#define NULLIFY_S_DEREF_BUF \
{\
SET_S_DEREF_BUF(NULL, 0);\
if (sDerefBufSize > LARGE_DEREF_BUF_SIZE)\
--sLargeDerefBufs;\
}
#define PRIVATIZE_S_DEREF_BUF \
LPTSTR our_deref_buf = Line::sDerefBuf;\
size_t our_deref_buf_size = Line::sDerefBufSize;\
SET_S_DEREF_BUF(NULL, 0) // For detecting whether ExpandExpression() caused a new buffer to be created.
#define DEPRIVATIZE_S_DEREF_BUF \
if (our_deref_buf)\
{\
if (Line::sDerefBuf)\
{\
free(Line::sDerefBuf);\
if (Line::sDerefBufSize > LARGE_DEREF_BUF_SIZE)\
--Line::sLargeDerefBufs;\
}\
SET_S_DEREF_BUF(our_deref_buf, our_deref_buf_size);\
}
//else the original buffer is NULL, so keep any new sDerefBuf that might have been created (should
// help avg-case performance).
static LPTSTR 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 LPTSTR sArgDeref[MAX_ARGS];
static Var *sArgVar[MAX_ARGS];
// 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.
LineNumberType mLineNumber; // The line number in the file from which the script was loaded, for debugging.
ArgStruct *mArg; // Will be used to hold a dynamic array of dynamic Args.
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.
#ifdef CONFIG_DEBUGGER
Breakpoint *mBreakpoint;
#endif
// Probably best to always use ARG1 even if other things have supposedly verified
// 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 : _T(""))
#define RAW_ARG2 (mArgc > 1 ? mArg[1].text : _T(""))
#define RAW_ARG3 (mArgc > 2 ? mArg[2].text : _T(""))
#define RAW_ARG4 (mArgc > 3 ? mArg[3].text : _T(""))
#define RAW_ARG5 (mArgc > 4 ? mArg[4].text : _T(""))
#define RAW_ARG6 (mArgc > 5 ? mArg[5].text : _T(""))
#define RAW_ARG7 (mArgc > 6 ? mArg[6].text : _T(""))
#define RAW_ARG8 (mArgc > 7 ? mArg[7].text : _T(""))
#define LINE_RAW_ARG1 (line->mArgc > 0 ? line->mArg[0].text : _T(""))
#define LINE_RAW_ARG2 (line->mArgc > 1 ? line->mArg[1].text : _T(""))
#define LINE_RAW_ARG3 (line->mArgc > 2 ? line->mArg[2].text : _T(""))
#define LINE_RAW_ARG4 (line->mArgc > 3 ? line->mArg[3].text : _T(""))
#define LINE_RAW_ARG5 (line->mArgc > 4 ? line->mArg[4].text : _T(""))
#define LINE_RAW_ARG6 (line->mArgc > 5 ? line->mArg[5].text : _T(""))
#define LINE_RAW_ARG7 (line->mArgc > 6 ? line->mArg[6].text : _T(""))
#define LINE_RAW_ARG8 (line->mArgc > 7 ? line->mArg[7].text : _T(""))
#define LINE_RAW_ARG9 (line->mArgc > 8 ? line->mArg[8].text : _T(""))
#define NEW_RAW_ARG1 (aArgc > 0 ? new_arg[0].text : _T("")) // Helps performance to use this vs. LINE_RAW_ARG where possible.
#define NEW_RAW_ARG2 (aArgc > 1 ? new_arg[1].text : _T(""))
#define NEW_RAW_ARG3 (aArgc > 2 ? new_arg[2].text : _T(""))
#define NEW_RAW_ARG4 (aArgc > 3 ? new_arg[3].text : _T(""))
#define NEW_RAW_ARG5 (aArgc > 4 ? new_arg[4].text : _T(""))
#define NEW_RAW_ARG6 (aArgc > 5 ? new_arg[5].text : _T(""))
#define NEW_RAW_ARG7 (aArgc > 6 ? new_arg[6].text : _T(""))
#define NEW_RAW_ARG8 (aArgc > 7 ? new_arg[7].text : _T(""))
#define NEW_RAW_ARG9 (aArgc > 8 ? new_arg[8].text : _T(""))
#define SAVED_ARG1 (mArgc > 0 ? arg[0] : _T(""))
#define SAVED_ARG2 (mArgc > 1 ? arg[1] : _T(""))
#define SAVED_ARG3 (mArgc > 2 ? arg[2] : _T(""))
#define SAVED_ARG4 (mArgc > 3 ? arg[3] : _T(""))
#define SAVED_ARG5 (mArgc > 4 ? arg[4] : _T(""))
// 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
#define ARGVARRAW3 (sArgVar[2]) // 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 initialized 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 LPTSTR sSourceFile[1]; // Only need to be able to hold the main script since compiled scripts don't support dynamic including.
#else
static LPTSTR *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, ExprTokenType *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 _T("<>=/|^,:")
// The characters common to both EXPR_TELLTALES and EXPR_OPERAND_TERMINATORS:
#define EXPR_COMMON _T(" \t") EXPR_CORE _T("*&~!()[]{}") // Space and Tab are included at the beginning for performance. L31: Added [] for array-like syntax.
#define CONTINUATION_LINE_SYMBOLS EXPR_CORE _T(".+-*&!?~") // 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 _T("\"")
// Characters that mark the end of an operand inside an expression. Double-quote must not be included:
#define EXPR_OPERAND_TERMINATORS_EX_DOT EXPR_COMMON _T("%+-?\n") // L31: Used in a few places where '.' needs special treatment.
#define EXPR_OPERAND_TERMINATORS EXPR_OPERAND_TERMINATORS_EX_DOT _T(".") // L31: Used in expressions where '.' is always an operator.
#define EXPR_ALL_SYMBOLS EXPR_OPERAND_TERMINATORS _T("\"'")
#define EXPR_ILLEGAL_CHARS _T("\\;`@#$") // 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) (cisalnum(chr) || _tcschr(_T("?*- \t"), chr))
// The characters below are ordered with most-often used ones first, for performance:
#define DEFINE_END_FLAGS \
LPTSTR end_flags = _T(" ,(\t<>:=+-*/!~&|^[.?");
// L31: Added '[' for standalone ObjSet/Get to work as ACT_EXPRESSION. "Get" is allowed for simplicity and for future use with functions-as-values (e.g. varContainingFunc[]).
// L34: Added '.' and changed dot handling to fix x.=y, improve support in other areas, catch more errors and give slightly better error messages.
#define ArgLength(aArgNum) ArgIndexLength((aArgNum)-1)
#define ArgToDouble(aArgNum) ArgIndexToDouble((aArgNum)-1)
#define ArgToInt64(aArgNum) ArgIndexToInt64((aArgNum)-1)
#define ArgToInt(aArgNum) (int)ArgToInt64(aArgNum) // Benchmarks show that having a "real" ArgToInt() that calls ATOI vs. ATOI64 (and ToInt() vs. ToInt64()) doesn't measurably improve performance.
#define ArgToUInt(aArgNum) (UINT)ArgToInt64(aArgNum) // Similar to what ATOU() does.
__int64 ArgIndexToInt64(int aArgIndex);
double ArgIndexToDouble(int aArgIndex);
size_t ArgIndexLength(int aArgIndex);
ResultType ExpandArgs(ExprTokenType *aResultTokens = NULL);
VarSizeType GetExpandedArgSize(Var *aArgVar[]);
LPTSTR ExpandExpression(int aArgIndex, ResultType &aResult, ExprTokenType *aResultToken
, LPTSTR &aTarget, LPTSTR &aDerefBuf, size_t &aDerefBufSize, LPTSTR aArgDeref[], size_t aExtraSize
, Var **aArgVar = NULL);
ResultType ExpressionToPostfix(ArgStruct &aArg);
ResultType EvaluateHotCriterionExpression(LPTSTR aHotkeyName); // L4: Called by MainWindowProc to handle an AHK_HOT_IF_EXPR message.
ResultType ValueIsType(ExprTokenType &aResultToken, ExprTokenType &aValue, LPTSTR aValueStr, ExprTokenType &aType, LPTSTR aTypeStr);
ResultType Deref(Var *aOutputVar, LPTSTR aBuf);
static bool FileIsFilteredOut(WIN32_FIND_DATA &aCurrentFile, FileLoopModeType aFileLoopMode
, LPTSTR aFilePath, size_t aFilePathLength);
Label *GetJumpTarget(bool aIsDereferenced);
Label *GetJumpTarget(bool aIsDereferenced, Func *aFunc);
Label *IsJumpValid(Label &aTargetLabel, bool aSilent = false);
BOOL IsOutsideAnyFunctionBody();
BOOL CheckValidFinallyJump(Line* jumpTarget);
static HWND DetermineTargetWindow(LPTSTR aTitle, LPTSTR aText, LPTSTR aExcludeTitle, LPTSTR aExcludeText);
// 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_ASSIGNEXPR:
case ACT_DEREF:
case ACT_STRINGLOWER:
case ACT_STRINGUPPER:
case ACT_STRINGREPLACE:
case ACT_GETKEYSTATE:
case ACT_CONTROLGETFOCUS:
case ACT_CONTROLGETTEXT:
case ACT_CONTROLGET:
case ACT_STATUSBARGETTEXT:
case ACT_INPUTBOX:
case ACT_RANDOM:
case ACT_INIREAD:
case ACT_REGREAD:
case ACT_DRIVEGET:
case ACT_SOUNDGET:
case ACT_FILEREAD:
case ACT_FILEGETATTRIB:
case ACT_FILEGETTIME:
case ACT_FILEGETSIZE:
case ACT_FILEGETVERSION:
case ACT_FILESELECT:
case ACT_DIRSELECT:
case ACT_MOUSEGETPOS:
case ACT_WINGETTITLE:
case ACT_WINGETCLASS:
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:
case ACT_FOR:
case ACT_CATCH:
case ACT_SORT:
return ARG_TYPE_OUTPUT_VAR;
}
break;
case 1: // Arg #2
switch(aActionType)
{
case ACT_MOUSEGETPOS:
case ACT_WINGETPOS:
case ACT_CONTROLGETPOS:
case ACT_PIXELSEARCH:
case ACT_IMAGESEARCH:
case ACT_SPLITPATH:
case ACT_FILEGETSHORTCUT:
case ACT_FOR:
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;
}
break;
case 3: // Arg #4
switch(aActionType)
{
case ACT_WINGETPOS:
case ACT_CONTROLGETPOS:
case ACT_MOUSEGETPOS:
case ACT_SPLITPATH:
case ACT_FILEGETSHORTCUT:
case ACT_RUN:
case ACT_RUNWAIT:
return ARG_TYPE_OUTPUT_VAR;
}
break;
case 4: // Arg #5
if (aActionType == ACT_STRINGREPLACE)
return ARG_TYPE_OUTPUT_VAR;
// Otherwise, fall through to below: