-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathscript_autoit.cpp
2309 lines (2018 loc) · 88.3 KB
/
script_autoit.cpp
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
///////////////////////////////////////////////////////////////////////////////
//
// AutoIt
//
// Copyright (C)1999-2006:
// - Jonathan Bennett <jon@hiddensoft.com>
// - Others listed at http://www.autoitscript.com/autoit3/docs/credits.htm
// - Chris Mallett (support@autohotkey.com): various enhancements and
// adaptation of this file's functions to interface with AutoHotkey.
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h" // pre-compiled headers
#include <winsock.h> // for WSADATA. This also requires wsock32.lib to be linked in.
#include <tlhelp32.h> // For the ProcessExist routines.
#include <wininet.h> // For URLDownloadToFile().
#include "script.h"
#include "globaldata.h" // for g_ErrorLevel and probably other globals.
#include "window.h" // For ControlExist().
#include "application.h" // For SLEEP_WITHOUT_INTERRUPTION and MsgSleep().
ResultType Script::DoRunAs(char *aCommandLine, char *aWorkingDir, bool aDisplayErrors, bool aUpdateLastError, WORD aShowWindow
, Var *aOutputVar, PROCESS_INFORMATION &aPI, bool &aSuccess // Output parameters we set for caller, but caller must have initialized aSuccess to false.
, HANDLE &aNewProcess, char *aSystemErrorText) // Same. Caller must ensure aSystemErrorText is at least 512 in size.
{
/*
typedef BOOL (WINAPI *MyCreateProcessWithLogonW)(
LPCWSTR lpUsername, // user's name
LPCWSTR lpDomain, // user's domain
LPCWSTR lpPassword, // user's password
DWORD dwLogonFlags, // logon option
LPCWSTR lpApplicationName, // executable module name
LPWSTR lpCommandLine, // command-line string
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCWSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFOW lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInfo // process information
);
// Get a handle to the DLL module that contains CreateProcessWithLogonW
HINSTANCE hinstLib = LoadLibrary("advapi32");
if (!hinstLib)
{
if (aDisplayErrors)
ScriptError("RunAs: Missing advapi32.dll." ERR_ABORT);
return FAIL;
}
MyCreateProcessWithLogonW lpfnDLLProc = (MyCreateProcessWithLogonW)GetProcAddress(hinstLib, "CreateProcessWithLogonW");
if (!lpfnDLLProc)
{
FreeLibrary(hinstLib);
if (aDisplayErrors)
ScriptError("CreateProcessWithLogonW." ERR_ABORT); // Short msg since it probably never happens.
return FAIL;
}
// Set up wide char version that we need for CreateProcessWithLogon
// init structure for running programs (wide char version)
STARTUPINFOW wsi = {0};
wsi.cb = sizeof(STARTUPINFOW);
wsi.dwFlags = STARTF_USESHOWWINDOW;
wsi.wShowWindow = aShowWindow;
// The following are left initialized to 0/NULL (initialized earlier above):
//wsi.lpReserved = NULL;
//wsi.lpDesktop = NULL;
//wsi.lpTitle = NULL;
//wsi.cbReserved2 = 0;
//wsi.lpReserved2 = NULL;
// Convert to wide character format:
WCHAR command_line_wide[LINE_SIZE], working_dir_wide[MAX_PATH];
ToWideChar(aCommandLine, command_line_wide, LINE_SIZE); // Dest. size is in wchars, not bytes.
if (aWorkingDir && *aWorkingDir)
ToWideChar(aWorkingDir, working_dir_wide, MAX_PATH); // Dest. size is in wchars, not bytes.
else
*working_dir_wide = 0; // wide-char terminator.
if (lpfnDLLProc(mRunAsUser, mRunAsDomain, mRunAsPass, LOGON_WITH_PROFILE, 0
, command_line_wide, 0, 0, *working_dir_wide ? working_dir_wide : NULL, &wsi, &aPI))
{
aSuccess = true;
if (aPI.hThread)
CloseHandle(aPI.hThread); // Required to avoid memory leak.
aNewProcess = aPI.hProcess;
if (aOutputVar)
aOutputVar->Assign(aPI.dwProcessId);
}
else
GetLastErrorText(aSystemErrorText, 512, aUpdateLastError); // Caller has ensured that aSystemErrorText is at least this size.
FreeLibrary(hinstLib);
return OK;
*/
}
VarSizeType BIV_IPAddress(char *aBuf, char *aVarName)
{
/*
// aaa.bbb.ccc.ddd = 15, but allow room for larger IP's in the future.
#define IP_ADDRESS_SIZE 32 // The maximum size of any of the strings we return, including terminator.
if (!aBuf)
return IP_ADDRESS_SIZE - 1; // -1 since we're returning the length of the var's contents, not the size.
WSADATA wsadata;
if (WSAStartup(MAKEWORD(1, 1), &wsadata)) // Failed (it returns 0 on success).
{
*aBuf = '\0';
return 0;
}
char host_name[256];
gethostname(host_name, sizeof(host_name));
HOSTENT *lpHost = gethostbyname(host_name);
// au3: How many adapters have we?
int adapter_count = 0;
while (lpHost->h_addr_list[adapter_count])
++adapter_count;
int adapter_index = aVarName[11] - '1'; // A_IPAddress[1-4]
if (adapter_index >= adapter_count)
strcpy(aBuf, "0.0.0.0");
else
{
IN_ADDR inaddr;
memcpy(&inaddr, lpHost->h_addr_list[adapter_index], 4);
strlcpy(aBuf, (char *)inet_ntoa(inaddr), IP_ADDRESS_SIZE);
}
WSACleanup();
return (VarSizeType)strlen(aBuf);
*/
}
VarSizeType BIV_IsAdmin(char *aBuf, char *aVarName)
{
/*
if (!aBuf)
return 1; // The length of the string "1" or "0".
char result = '0'; // Default.
if (g_os.IsWin9x())
result = '1';
else
{
SC_HANDLE h = OpenSCManager(NULL, NULL, SC_MANAGER_LOCK);
if (h)
{
SC_LOCK lock = LockServiceDatabase(h);
if (lock)
{
UnlockServiceDatabase(lock);
result = '1'; // Current user is admin.
}
else
{
DWORD lastErr = GetLastError();
if (lastErr == ERROR_SERVICE_DATABASE_LOCKED)
result = '1'; // Current user is admin.
}
CloseServiceHandle(h);
}
}
aBuf[0] = result;
aBuf[1] = '\0';
return 1; // Length of aBuf.
*/
}
ResultType Line::PixelGetColor(int aX, int aY, char *aOptions)
{
/*
if (strcasestr(aOptions, "Slow")) // New mode for v1.0.43.10. Takes precedence over Alt mode.
return PixelSearch(aX, aY, aX, aY, 0, 0, aOptions, true); // It takes care of setting ErrorLevel and the output-var.
Var &output_var = *OUTPUT_VAR;
g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // Set default ErrorLevel.
output_var.Assign(); // Init to empty string regardless of whether we succeed here.
if (!(g.CoordMode & COORD_MODE_PIXEL)) // Using relative vs. screen coordinates.
{
// Convert from relative to absolute (screen) coordinates:
RECT rect;
if (!GetWindowRect(GetForegroundWindow(), &rect))
return OK; // Let ErrorLevel tell the story.
aX += rect.left;
aY += rect.top;
}
bool use_alt_mode = strcasestr(aOptions, "Alt") != NULL; // New mode for v1.0.43.10: Two users reported that CreateDC works better in certain windows such as SciTE, at least one some systems.
HDC hdc = use_alt_mode ? CreateDC("DISPLAY", NULL, NULL, NULL) : GetDC(NULL);
if (!hdc)
return OK; // Let ErrorLevel tell the story.
// Assign the value as an 32-bit int to match Window Spy reports color values.
// Update for v1.0.21: Assigning in hex format seems much better, since it's easy to
// look at a hex BGR value to get some idea of the hue. In addition, the result
// is zero padded to make it easier to convert to RGB and more consistent in
// appearance:
COLORREF color = GetPixel(hdc, aX, aY);
if (use_alt_mode)
DeleteDC(hdc);
else
ReleaseDC(NULL, hdc);
char buf[32];
sprintf(buf, "0x%06X", strcasestr(aOptions, "RGB") ? bgr_to_rgb(color) : color);
g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
return output_var.Assign(buf);
*/
}
ResultType Line::SplashTextOn(int aWidth, int aHeight, char *aTitle, char *aText)
{
/*
// Add some caption and frame size to window:
aWidth += GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
int min_height = GetSystemMetrics(SM_CYCAPTION) + (GetSystemMetrics(SM_CXFIXEDFRAME) * 2);
if (g_script.mIsAutoIt2)
{
// I think this is probably something like how AutoIt2 does things:
if (aHeight < min_height)
aHeight = min_height;
}
else // Use the new method that seems more friendly.
aHeight += min_height;
POINT pt = CenterWindow(aWidth, aHeight); // Determine how to center the window in the region that excludes the task bar.
// My: Probably not too much overhead to do this, though it probably would perform better to resize and
// "re-text" the existing window rather than recreating it like this:
DESTROY_SPLASH
// Doesn't seem necessary to have it owned by the main window, but neither
// does doing so seem to cause any harm. Feels safer to have it be
// an independent window. Update: Must make it owned by the parent window
// otherwise it will get its own task-bar icon, which is usually undesirable.
// In addition, making it an owned window should automatically cause it to be
// destroyed when it's parent window is destroyed:
g_hWndSplash = CreateWindowEx(WS_EX_TOPMOST, WINDOW_CLASS_SPLASH, aTitle, WS_DISABLED|WS_POPUP|WS_CAPTION
, pt.x, pt.y, aWidth, aHeight, g_hWnd, (HMENU)NULL, g_hInstance, NULL);
RECT rect;
GetClientRect(g_hWndSplash, &rect); // get the client size
// CREATE static label full size of client area.
HWND static_win = CreateWindowEx(0, "static", aText, WS_CHILD|WS_VISIBLE|SS_CENTER
, 0, 0, rect.right - rect.left, rect.bottom - rect.top, g_hWndSplash, (HMENU)NULL, g_hInstance, NULL);
if (!g_hFontSplash)
{
char default_font_name[65];
int CyPixels, nSize = 12, nWeight = FW_NORMAL;
HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT)); // Get Default Font Name
GetTextFace(hdc, sizeof(default_font_name) - 1, default_font_name); // -1 just in case, like AutoIt3.
CyPixels = GetDeviceCaps(hdc, LOGPIXELSY); // For Some Font Size Math
DeleteDC(hdc);
//strcpy(default_font_name,vParams[7].szValue()); // Font Name
//nSize = vParams[8].nValue(); // Font Size
//if ( vParams[9].nValue() >= 0 && vParams[9].nValue() <= 1000 )
// nWeight = vParams[9].nValue(); // Font Weight
g_hFontSplash = CreateFont(0-(nSize*CyPixels)/72,0,0,0,nWeight,0,0,0,DEFAULT_CHARSET,
OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,PROOF_QUALITY,FF_DONTCARE,default_font_name); // Create Font
// The font is deleted when by g_script's destructor.
}
SendMessage(static_win, WM_SETFONT, (WPARAM)g_hFontSplash, MAKELPARAM(TRUE, 0)); // Do Font
ShowWindow(g_hWndSplash, SW_SHOWNOACTIVATE); // Show the Splash
// Doesn't help with the brief delay in updating the window that happens when
// something like URLDownloadToFile is used immediately after SplashTextOn:
//InvalidateRect(g_hWndSplash, NULL, TRUE);
// But this does, but for now it seems unnecessary since the user can always do
// a manual sleep in the extremely rare cases this ever happens (even when it does
// happen, the window updates eventually, after the download starts, at least on
// my system. Update: Might as well do it since it's a little nicer this way
// (the text appears more quickly when the command after the splash is something
// that might keep our thread tied up and unable to check messages).
SLEEP_WITHOUT_INTERRUPTION(-1)
// UpdateWindow() would probably achieve the same effect as the above, but it feels safer to do
// the above because it ensures that our message queue is empty prior to returning to our caller.
return OK;
*/
}
ResultType Line::WinMenuSelectItem(char *aTitle, char *aText, char *aMenu1, char *aMenu2
, char *aMenu3, char *aMenu4, char *aMenu5, char *aMenu6, char *aMenu7
, char *aExcludeTitle, char *aExcludeText)
{
/*
// Set up a temporary array make it easier to traverse nested menus & submenus
// in a loop. Also add a NULL at the end to simplify the loop a little:
char *menu_param[] = {aMenu1, aMenu2, aMenu3, aMenu4, aMenu5, aMenu6, aMenu7, NULL};
g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // Set default ErrorLevel.
HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
if (!target_window)
return OK; // Let ErrorLevel tell the story.
HMENU hMenu = GetMenu(target_window);
if (!hMenu) // Window has no menu bar.
return OK; // Let ErrorLevel tell the story.
int menu_item_count = GetMenuItemCount(hMenu);
if (menu_item_count < 1) // Menu bar has no menus.
return OK; // Let ErrorLevel tell the story.
#define MENU_ITEM_IS_SUBMENU 0xFFFFFFFF
#define UPDATE_MENU_VARS(menu_pos) \
menu_id = GetMenuItemID(hMenu, menu_pos);\
if (menu_id == MENU_ITEM_IS_SUBMENU)\
menu_item_count = GetMenuItemCount(hMenu = GetSubMenu(hMenu, menu_pos));\
else\
{\
menu_item_count = 0;\
hMenu = NULL;\
}
UINT menu_id = MENU_ITEM_IS_SUBMENU;
char menu_text[1024];
bool match_found;
size_t this_menu_param_length, menu_text_length;
int pos, target_menu_pos;
char *this_menu_param;
for (int i = 0; ; ++i)
{
this_menu_param = menu_param[i]; // For performance and convenience.
if (!(this_menu_param && *this_menu_param))
break;
if (!hMenu) // The nesting of submenus ended prior to the end of the list of menu search terms.
return OK; // Let ErrorLevel tell the story.
this_menu_param_length = strlen(this_menu_param);
target_menu_pos = (this_menu_param[this_menu_param_length - 1] == '&') ? ATOI(this_menu_param) - 1 : -1;
if (target_menu_pos > -1)
{
if (target_menu_pos >= menu_item_count) // Invalid menu position (doesn't exist).
return OK; // Let ErrorLevel tell the story.
UPDATE_MENU_VARS(target_menu_pos)
}
else // Searching by text rather than numerical position.
{
for (match_found = false, pos = 0; pos < menu_item_count; ++pos)
{
menu_text_length = GetMenuString(hMenu, pos, menu_text, sizeof(menu_text) - 1, MF_BYPOSITION);
// v1.0.43.03: It's debatable, but it seems best to support locale's case insensitivity for
// menu items, since menu names tend to adapt to the user's locale. By contrast, things
// like process names (in the Process command) do not tend to change, so it seems best to
// have them continue to use stricmp(): 1) avoids breaking exisitng scripts; 2) provides
// consistent behavior across multiple locales; 3) performance.
match_found = !lstrcmpni(menu_text // This call is basically a strnicmp() that obeys locale.
, menu_text_length > this_menu_param_length ? this_menu_param_length : menu_text_length
, this_menu_param, this_menu_param_length);
//match_found = strcasestr(menu_text, this_menu_param);
if (!match_found)
{
// Try again to find a match, this time without the ampersands used to indicate
// a menu item's shortcut key:
StrReplace(menu_text, "&", "", SCS_SENSITIVE);
menu_text_length = strlen(menu_text);
match_found = !lstrcmpni(menu_text // This call is basically a strnicmp() that obeys locale.
, menu_text_length > this_menu_param_length ? this_menu_param_length : menu_text_length
, this_menu_param, this_menu_param_length);
//match_found = strcasestr(menu_text, this_menu_param);
}
if (match_found)
{
UPDATE_MENU_VARS(pos)
break;
}
} // inner for()
if (!match_found) // The search hierarchy (nested menus) specified in the params could not be found.
return OK; // Let ErrorLevel tell the story.
} // else
} // outer for()
// This would happen if the outer loop above had zero iterations due to aMenu1 being NULL or blank,
// or if the caller specified a submenu as the target (which doesn't seem valid since an app would
// next expect to ever receive a message for a submenu?):
if (menu_id == MENU_ITEM_IS_SUBMENU)
return OK; // Let ErrorLevel tell the story.
// Since the above didn't return, the specified search hierarchy was completely found.
PostMessage(target_window, WM_COMMAND, (WPARAM)menu_id, 0);
return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
*/
}
ResultType Line::Control(char *aCmd, char *aValue, char *aControl, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText)
// ATTACH_THREAD_INPUT has been tested to see if they help any of these work with controls
// in MSIE (whose Internet Explorer_TridentCmboBx2 does not respond to "Control Choose" but
// does respond to "Control Focus"). But it didn't help.
{
/*
g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // Set default since there are many points of return.
ControlCmds control_cmd = ConvertControlCmd(aCmd);
// Since command names are validated at load-time, this only happens if the command name
// was contained in a variable reference. Since that is very rare, just set ErrorLevel
// and return:
if (control_cmd == CONTROL_CMD_INVALID)
return OK; // Let ErrorLevel tell the story.
HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
if (!target_window)
return OK; // Let ErrorLevel tell the story.
HWND control_window = ControlExist(target_window, aControl); // This can return target_window itself for cases such as ahk_id %ControlHWND%.
if (!control_window)
return OK; // Let ErrorLevel tell the story.
HWND immediate_parent; // Possibly not the same as target_window since controls can themselves have children.
int control_id, control_index;
DWORD dwResult, new_button_state;
UINT msg, x_msg, y_msg;
RECT rect;
LPARAM lparam;
vk_type vk;
int key_count;
char temp_buf[32];
switch(control_cmd)
{
case CONTROL_CMD_CHECK: // au3: Must be a Button
case CONTROL_CMD_UNCHECK:
{ // Need braces for ATTACH_THREAD_INPUT macro.
new_button_state = (control_cmd == CONTROL_CMD_CHECK) ? BST_CHECKED : BST_UNCHECKED;
if (!SendMessageTimeout(control_window, BM_GETCHECK, 0, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
if (dwResult == new_button_state) // It's already in the right state, so don't press it.
break;
// MSDN docs for BM_CLICK (and au3 author says it applies to this situation also):
// "If the button is in a dialog box and the dialog box is not active, the BM_CLICK message
// might fail. To ensure success in this situation, call the SetActiveWindow function to activate
// the dialog box before sending the BM_CLICK message to the button."
ATTACH_THREAD_INPUT
SetActiveWindow(target_window == control_window ? GetNonChildParent(control_window) : target_window); // v1.0.44.13: Fixed to allow for the fact that target_window might be the control itself (e.g. via ahk_id %ControlHWND%).
if (!GetWindowRect(control_window, &rect)) // au3: Code to primary click the centre of the control
rect.bottom = rect.left = rect.right = rect.top = 0;
lparam = MAKELPARAM((rect.right - rect.left) / 2, (rect.bottom - rect.top) / 2);
PostMessage(control_window, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
PostMessage(control_window, WM_LBUTTONUP, 0, lparam);
DETACH_THREAD_INPUT
break;
}
case CONTROL_CMD_ENABLE:
EnableWindow(control_window, TRUE);
break;
case CONTROL_CMD_DISABLE:
EnableWindow(control_window, FALSE);
break;
case CONTROL_CMD_SHOW:
ShowWindow(control_window, SW_SHOWNOACTIVATE); // SW_SHOWNOACTIVATE has been seen in some example code for this purpose.
break;
case CONTROL_CMD_HIDE:
ShowWindow(control_window, SW_HIDE);
break;
case CONTROL_CMD_STYLE:
case CONTROL_CMD_EXSTYLE:
{
if (!*aValue)
return OK; // Seems best not to treat an explicit blank as zero. Let ErrorLevel tell the story.
int style_index = (control_cmd == CONTROL_CMD_STYLE) ? GWL_STYLE : GWL_EXSTYLE;
DWORD new_style, orig_style = GetWindowLong(control_window, style_index);
// +/-/^ are used instead of |&^ because the latter is confusing, namely that & really means &=~style, etc.
if (!strchr("+-^", *aValue)) // | and & are used instead of +/- to allow +/- to have their native function.
new_style = ATOU(aValue); // No prefix, so this new style will entirely replace the current style.
else
{
++aValue; // Won't work combined with next line, due to next line being a macro that uses the arg twice.
DWORD style_change = ATOU(aValue);
switch(aValue[-1])
{
case '+': new_style = orig_style | style_change; break;
case '-': new_style = orig_style & ~style_change; break;
case '^': new_style = orig_style ^ style_change; break;
}
}
if (new_style == orig_style) // v1.0.45.04: Ask for an unnecessary change (i.e. one that is already in effect) should not be considered an error.
return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
// Currently, BM_SETSTYLE is not done when GetClassName() says that the control is a button/checkbox/groupbox.
// This is because the docs for BM_SETSTYLE don't contain much, if anything, that anyone would ever
// want to change.
SetLastError(0); // Prior to SetWindowLong(), as recommended by MSDN.
if (SetWindowLong(control_window, style_index, new_style) || !GetLastError()) // This is the precise way to detect success according to MSDN.
{
// Even if it indicated success, sometimes it failed anyway. Find out for sure:
if (GetWindowLong(control_window, style_index) != orig_style) // Even a partial change counts as a success.
{
InvalidateRect(control_window, NULL, TRUE); // Quite a few styles require this to become visibly manifest.
return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
}
}
return OK; // Let ErrorLevel tell the story. As documented, DoControlDelay is not done for these.
}
case CONTROL_CMD_SHOWDROPDOWN:
case CONTROL_CMD_HIDEDROPDOWN:
// CB_SHOWDROPDOWN: Although the return value (dwResult) is always TRUE, SendMessageTimeout()
// will return failure if it times out:
if (!SendMessageTimeout(control_window, CB_SHOWDROPDOWN
, (WPARAM)(control_cmd == CONTROL_CMD_SHOWDROPDOWN ? TRUE : FALSE)
, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
break;
case CONTROL_CMD_TABLEFT:
case CONTROL_CMD_TABRIGHT: // must be a Tab Control
key_count = *aValue ? ATOI(aValue) : 1;
vk = (control_cmd == CONTROL_CMD_TABLEFT) ? VK_LEFT : VK_RIGHT;
lparam = (LPARAM)(vk_to_sc(vk) << 16);
for (int i = 0; i < key_count; ++i)
{
// DoControlDelay isn't done for every iteration because it seems likely that
// the Sleep(0) will take care of things.
PostMessage(control_window, WM_KEYDOWN, vk, lparam | 0x00000001);
SLEEP_WITHOUT_INTERRUPTION(0); // Au3 uses a Sleep(0).
PostMessage(control_window, WM_KEYUP, vk, lparam | 0xC0000001);
}
break;
case CONTROL_CMD_ADD:
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. !strnicmp for TListBox/TComboBox.
msg = CB_ADDSTRING;
else if (strcasestr(aControl, "List"))
msg = LB_ADDSTRING;
else
return OK; // Must be ComboBox or ListBox. Let ErrorLevel tell the story.
if (!SendMessageTimeout(control_window, msg, 0, (LPARAM)aValue, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
if (dwResult == CB_ERR || dwResult == CB_ERRSPACE) // General error or insufficient space to store it.
// CB_ERR == LB_ERR
return OK; // Let ErrorLevel tell the story.
break;
case CONTROL_CMD_DELETE:
if (!*aValue)
return OK;
control_index = ATOI(aValue) - 1;
if (control_index < 0)
return OK;
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. strnicmp for TListBox/TComboBox.
msg = CB_DELETESTRING;
else if (strcasestr(aControl, "List"))
msg = LB_DELETESTRING;
else
return OK; // Must be ComboBox or ListBox. Let ErrorLevel tell the story.
if (!SendMessageTimeout(control_window, msg, (WPARAM)control_index, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
if (dwResult == CB_ERR) // CB_ERR == LB_ERR
return OK; // Let ErrorLevel tell the story.
break;
case CONTROL_CMD_CHOOSE:
if (!*aValue)
return OK;
control_index = ATOI(aValue) - 1;
if (control_index < 0)
return OK; // Let ErrorLevel tell the story.
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. strnicmp for TListBox/TComboBox.
{
msg = CB_SETCURSEL;
x_msg = CBN_SELCHANGE;
y_msg = CBN_SELENDOK;
}
else if (strcasestr(aControl, "List"))
{
if (GetWindowLong(control_window, GWL_STYLE) & (LBS_EXTENDEDSEL|LBS_MULTIPLESEL))
msg = LB_SETSEL;
else // single-select listbox
msg = LB_SETCURSEL;
x_msg = LBN_SELCHANGE;
y_msg = LBN_DBLCLK;
}
else
return OK; // Must be ComboBox or ListBox. Let ErrorLevel tell the story.
if (msg == LB_SETSEL) // Multi-select, so use the cumulative method.
{
if (!SendMessageTimeout(control_window, msg, TRUE, control_index, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
}
else // ComboBox or single-select ListBox.
if (!SendMessageTimeout(control_window, msg, control_index, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
if (dwResult == CB_ERR) // CB_ERR == LB_ERR
return OK;
if ( !(immediate_parent = GetParent(control_window)) )
return OK;
if ( !(control_id = GetDlgCtrlID(control_window)) )
return OK;
if (!SendMessageTimeout(immediate_parent, WM_COMMAND, (WPARAM)MAKELONG(control_id, x_msg)
, (LPARAM)control_window, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK;
if (!SendMessageTimeout(immediate_parent, WM_COMMAND, (WPARAM)MAKELONG(control_id, y_msg)
, (LPARAM)control_window, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK;
// Otherwise break and do the end-function processing.
break;
case CONTROL_CMD_CHOOSESTRING:
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. strnicmp for TListBox/TComboBox.
{
msg = CB_SELECTSTRING;
x_msg = CBN_SELCHANGE;
y_msg = CBN_SELENDOK;
}
else if (strcasestr(aControl, "List"))
{
if (GetWindowLong(control_window, GWL_STYLE) & (LBS_EXTENDEDSEL|LBS_MULTIPLESEL))
msg = LB_FINDSTRING;
else // single-select listbox
msg = LB_SELECTSTRING;
x_msg = LBN_SELCHANGE;
y_msg = LBN_DBLCLK;
}
else
return OK; // Must be ComboBox or ListBox. Let ErrorLevel tell the story.
if (msg == LB_FINDSTRING) // Multi-select ListBox (LB_SELECTSTRING is not supported by these).
{
DWORD item_index;
if (!SendMessageTimeout(control_window, msg, -1, (LPARAM)aValue, SMTO_ABORTIFHUNG, 2000, &item_index)
|| item_index == LB_ERR
|| !SendMessageTimeout(control_window, LB_SETSEL, TRUE, item_index, SMTO_ABORTIFHUNG, 2000, &dwResult)
|| dwResult == LB_ERR) // Relies on short-circuit boolean.
return OK; // Let ErrorLevel tell the story.
}
else // ComboBox or single-select ListBox.
if (!SendMessageTimeout(control_window, msg, 1, (LPARAM)aValue, SMTO_ABORTIFHUNG, 2000, &dwResult)
|| dwResult == CB_ERR) // CB_ERR == LB_ERR
return OK; // Let ErrorLevel tell the story.
if ( !(immediate_parent = GetParent(control_window)) )
return OK;
if ( !(control_id = GetDlgCtrlID(control_window)) )
return OK;
if (!SendMessageTimeout(immediate_parent, WM_COMMAND, (WPARAM)MAKELONG(control_id, x_msg)
, (LPARAM)control_window, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK;
if (!SendMessageTimeout(immediate_parent, WM_COMMAND, (WPARAM)MAKELONG(control_id, y_msg)
, (LPARAM)control_window, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK;
// Otherwise break and do the end-function processing.
break;
case CONTROL_CMD_EDITPASTE:
if (!SendMessageTimeout(control_window, EM_REPLACESEL, TRUE, (LPARAM)aValue, SMTO_ABORTIFHUNG, 2000, &dwResult))
return OK; // Let ErrorLevel tell the story.
// Note: dwResult is not used by EM_REPLACESEL since it doesn't return a value.
break;
} // switch()
DoControlDelay; // Seems safest to do this for all of these commands.
return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
*/
}
ResultType Line::ControlGet(char *aCmd, char *aValue, char *aControl, char *aTitle, char *aText
, char *aExcludeTitle, char *aExcludeText)
{
/*
Var &output_var = *OUTPUT_VAR;
g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // Set default since there are many points of return.
ControlGetCmds control_cmd = ConvertControlGetCmd(aCmd);
// Since command names are validated at load-time, this only happens if the command name
// was contained in a variable reference. Since that is very rare, just set ErrorLevel
// and return:
if (control_cmd == CONTROLGET_CMD_INVALID)
return output_var.Assign(); // Let ErrorLevel tell the story.
HWND target_window = DetermineTargetWindow(aTitle, aText, aExcludeTitle, aExcludeText);
if (!target_window)
return output_var.Assign(); // Let ErrorLevel tell the story.
HWND control_window = ControlExist(target_window, aControl); // This can return target_window itself for cases such as ahk_id %ControlHWND%.
if (!control_window)
return output_var.Assign(); // Let ErrorLevel tell the story.
DWORD dwResult, index, length, item_length, start, end, u, item_count;
UINT msg, x_msg, y_msg;
int control_index;
char *cp, *dyn_buf, temp_buf[32];
switch(control_cmd)
{
case CONTROLGET_CMD_CHECKED: //Must be a Button
if (!SendMessageTimeout(control_window, BM_GETCHECK, 0, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return output_var.Assign();
output_var.Assign(dwResult == BST_CHECKED ? "1" : "0");
break;
case CONTROLGET_CMD_ENABLED:
output_var.Assign(IsWindowEnabled(control_window) ? "1" : "0");
break;
case CONTROLGET_CMD_VISIBLE:
output_var.Assign(IsWindowVisible(control_window) ? "1" : "0");
break;
case CONTROLGET_CMD_TAB: // must be a Tab Control
if (!SendMessageTimeout(control_window, TCM_GETCURSEL, 0, 0, SMTO_ABORTIFHUNG, 2000, &index) || index == -1) // Relies on short-circuit boolean order.
return output_var.Assign();
output_var.Assign(index + 1);
break;
case CONTROLGET_CMD_FINDSTRING:
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. strnicmp for TListBox/TComboBox.
msg = CB_FINDSTRINGEXACT;
else if (strcasestr(aControl, "List"))
msg = LB_FINDSTRINGEXACT;
else // Must be ComboBox or ListBox
return output_var.Assign(); // Let ErrorLevel tell the story.
if (!SendMessageTimeout(control_window, msg, 1, (LPARAM)aValue, SMTO_ABORTIFHUNG, 2000, &index)
|| index == CB_ERR) // CB_ERR == LB_ERR
return output_var.Assign();
output_var.Assign(index + 1);
break;
case CONTROLGET_CMD_CHOICE:
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. strnicmp for TListBox/TComboBox.
{
msg = CB_GETCURSEL;
x_msg = CB_GETLBTEXTLEN;
y_msg = CB_GETLBTEXT;
}
else if (strcasestr(aControl, "List"))
{
msg = LB_GETCURSEL;
x_msg = LB_GETTEXTLEN;
y_msg = LB_GETTEXT;
}
else // Must be ComboBox or ListBox
return output_var.Assign(); // Let ErrorLevel tell the story.
if (!SendMessageTimeout(control_window, msg, 0, 0, SMTO_ABORTIFHUNG, 2000, &index)
|| index == CB_ERR // CB_ERR == LB_ERR. There is no selection (or very rarely, some other type of problem).
|| !SendMessageTimeout(control_window, x_msg, (WPARAM)index, 0, SMTO_ABORTIFHUNG, 2000, &length)
|| length == CB_ERR) // CB_ERR == LB_ERR
return output_var.Assign(); // Above relies on short-circuit boolean order.
// In unusual cases, MSDN says the indicated length might be longer than it actually winds up
// being when the item's text is retrieved. This should be harmless, since there are many
// other precedents where a variable is sized to something larger than it winds up carrying.
// Set up the var, enlarging it if necessary. If the output_var is of type VAR_CLIPBOARD,
// this call will set up the clipboard for writing:
if (output_var.Assign(NULL, (VarSizeType)length) != OK) // It already displayed the error.
return FAIL;
if (!SendMessageTimeout(control_window, y_msg, (WPARAM)index, (LPARAM)output_var.Contents()
, SMTO_ABORTIFHUNG, 2000, &length)
|| length == CB_ERR) // Probably impossible given the way it was called above. Also, CB_ERR == LB_ERR. Relies on short-circuit boolean order.
{
output_var.Close(); // In case it's the clipboard.
return output_var.Assign(); // Let ErrorLevel tell the story.
}
output_var.Close(); // In case it's the clipboard.
output_var.Length() = length; // Update to actual vs. estimated length.
break;
case CONTROLGET_CMD_LIST:
if (!*aControl) // Fix for v1.0.46.11: If aControl is blank, the control ID came in via a WinTitle of "ahk_id xxx".
{
GetClassName(control_window, temp_buf, sizeof(temp_buf));
aControl = temp_buf;
}
if (!strnicmp(aControl, "SysListView32", 13)) // Tried strcasestr(aControl, "ListView") to get it to work with IZArc's Delphi TListView1, but none of the modes or options worked.
return ControlGetListView(output_var, control_window, aValue); // It will also set ErrorLevel to "success" if successful.
// This is done here as the special LIST sub-command rather than just being built into
// ControlGetText because ControlGetText already has a function for ComboBoxes: it fetches
// the current selection. Although ListBox does not have such a function, it seem best
// to consolidate both methods here.
if (strcasestr(aControl, "Combo")) // v1.0.42: Changed to strcasestr vs. strnicmp for TListBox/TComboBox.
{
msg = CB_GETCOUNT;
x_msg = CB_GETLBTEXTLEN;
y_msg = CB_GETLBTEXT;
}
else if (strcasestr(aControl, "List"))
{
msg = LB_GETCOUNT;
x_msg = LB_GETTEXTLEN;
y_msg = LB_GETTEXT;
}
else // Must be ComboBox or ListBox
return output_var.Assign(); // Let ErrorLevel tell the story.
if (!(SendMessageTimeout(control_window, msg, 0, 0, SMTO_ABORTIFHUNG, 5000, &item_count))
|| item_count < 1) // No items in ListBox/ComboBox or there was a problem getting the count.
return output_var.Assign(); // Let ErrorLevel tell the story.
// Calculate the length of delimited list of items. Length is initialized to provide enough
// room for each item's delimiter (the last item does not have a delimiter).
for (length = item_count - 1, u = 0; u < item_count; ++u)
{
if (!SendMessageTimeout(control_window, x_msg, u, 0, SMTO_ABORTIFHUNG, 5000, &item_length)
|| item_length == LB_ERR) // Note that item_length is legitimately zero for a blank item in the list.
return output_var.Assign(); // Let ErrorLevel tell the story.
length += item_length;
}
// In unusual cases, MSDN says the indicated length might be longer than it actually winds up
// being when the item's text is retrieved. This should be harmless, since there are many
// other precedents where a variable is sized to something larger than it winds up carrying.
// Set up the var, enlarging it if necessary. If the output_var is of type VAR_CLIPBOARD,
// this call will set up the clipboard for writing:
if (output_var.Assign(NULL, (VarSizeType)length, true, true) != OK)
return FAIL; // It already displayed the error.
for (cp = output_var.Contents(), length = item_count - 1, u = 0; u < item_count; ++u)
{
if (SendMessageTimeout(control_window, y_msg, (WPARAM)u, (LPARAM)cp, SMTO_ABORTIFHUNG, 5000, &item_length)
&& item_length != LB_ERR)
{
length += item_length; // Accumulate actual vs. estimated length.
cp += item_length; // Point it to the terminator in preparation for the next write.
}
//else do nothing, just consider this to be a blank item so that the process can continue.
if (u < item_count - 1)
*cp++ = '\n'; // Add delimiter after each item except the last (helps parsing loop).
// Above: In this case, seems better to use \n rather than pipe as default delimiter in case
// the listbox/combobox contains any real pipes.
}
output_var.Close(); // In case it's the clipboard.
output_var.Length() = (VarSizeType)length; // Update it to the actual length, which can vary from the estimate.
break;
case CONTROLGET_CMD_LINECOUNT: //Must be an Edit
// MSDN: "If the control has no text, the return value is 1. The return value will never be less than 1."
if (!SendMessageTimeout(control_window, EM_GETLINECOUNT, 0, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return output_var.Assign();
output_var.Assign(dwResult);
break;
case CONTROLGET_CMD_CURRENTLINE:
if (!SendMessageTimeout(control_window, EM_LINEFROMCHAR, -1, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return output_var.Assign();
output_var.Assign(dwResult + 1);
break;
case CONTROLGET_CMD_CURRENTCOL:
{
DWORD line_number;
// The dwResult from the first msg below is not useful and is not checked.
if ( !SendMessageTimeout(control_window, EM_GETSEL, (WPARAM)&start, (LPARAM)&end, SMTO_ABORTIFHUNG, 2000, &dwResult)
|| !SendMessageTimeout(control_window, EM_LINEFROMCHAR, (WPARAM)start, 0, SMTO_ABORTIFHUNG, 2000, &line_number) )
return output_var.Assign();
if (!line_number) // Since we're on line zero, the column number is simply start+1.
{
output_var.Assign(start + 1); // +1 to convert from zero based.
break; // Fall out of the switch so that ErrorLevel will be set to 0 (no error).
}
// Au3: Decrement the character index until the row changes. Difference between this
// char index and original is the column:
DWORD start_orig = start; // Au3: the character index
for (;;)
{
if (!SendMessageTimeout(control_window, EM_LINEFROMCHAR, (WPARAM)start, 0, SMTO_ABORTIFHUNG, 2000, &dwResult))
return output_var.Assign();
if (dwResult != line_number)
break;
--start;
}
output_var.Assign((int)(start_orig - start));
break;
}
case CONTROLGET_CMD_LINE:
if (!*aValue)
return output_var.Assign();
control_index = ATOI(aValue) - 1;
if (control_index < 0)
return output_var.Assign(); // Let ErrorLevel tell the story.
dyn_buf = (char *)_alloca(32768); // 32768 is the size Au3 uses for GETLINE and such.
*(LPINT)dyn_buf = 32768; // EM_GETLINE requires first word of string to be set to its size.
if ( !SendMessageTimeout(control_window, EM_GETLINE, (WPARAM)control_index, (LPARAM)dyn_buf, SMTO_ABORTIFHUNG, 2000, &dwResult)
|| !dwResult ) // due to the specified line number being greater than the number of lines in the edit control.
return output_var.Assign();
dyn_buf[dwResult] = '\0'; // Ensure terminated since the API might not do it in some cases.
output_var.Assign(dyn_buf);
break;
case CONTROLGET_CMD_SELECTED: // Must be an Edit.
// Note: The RichEdit controls of certain apps such as Metapad don't return the right selection
// with this technique. Au3 has the same problem with them, so for now it's just documented here
// as a limitation.
if (!SendMessageTimeout(control_window, EM_GETSEL, (WPARAM)&start, (LPARAM)&end, SMTO_ABORTIFHUNG, 2000, &dwResult))
return output_var.Assign();
// The above sets start to be the zero-based position of the start of the selection (similar for end).
// If there is no selection, start and end will be equal, at least in the edit controls I tried it with.
// The dwResult from the above is not useful and is not checked.
if (start == end) // Unlike Au3, it seems best to consider a blank selection to be a non-error.
{
output_var.Assign();
break; // Fall out of the switch so that ErrorLevel will be set to 0 (no error).
}
// Dynamic memory is used because must get all the control's text so that just the selected region
// can be cropped out and assigned to the output variable. Otherwise, output_var might
// have to be sized much larger than it would need to be:
if ( !SendMessageTimeout(control_window, WM_GETTEXTLENGTH, 0, 0, SMTO_ABORTIFHUNG, 2000, &length)
|| !length // Since the above didn't return for start == end, this is an error because we have a selection of non-zero length, but no text to go with it!
|| !(dyn_buf = (char *)malloc(length + 1)) ) // Relies on short-circuit boolean order.
return output_var.Assign();
if ( !SendMessageTimeout(control_window, WM_GETTEXT, (WPARAM)(length + 1), (LPARAM)dyn_buf, SMTO_ABORTIFHUNG, 2000, &length)
|| !length || end > length )
{
// The first check above is reveals a problem (ErrorLevel = 1) since the length
// is unexpectedly zero (above implied it shouldn't be). The second check is also
// a problem because the end of the selection should not be beyond length of text
// that was retrieved.
free(dyn_buf);
return output_var.Assign();
}
dyn_buf[end] = '\0'; // Terminate the string at the end of the selection.
output_var.Assign(dyn_buf + start);
free(dyn_buf);
break;
case CONTROLGET_CMD_STYLE:
// Seems best to always format as hex, since it has more human-readable meaning then:
sprintf(temp_buf, "0x%08X", GetWindowLong(control_window, GWL_STYLE));
output_var.Assign(temp_buf);
break;
case CONTROLGET_CMD_EXSTYLE:
// Seems best to always format as hex, since it has more human-readable meaning then:
sprintf(temp_buf, "0x%08X", GetWindowLong(control_window, GWL_EXSTYLE));
output_var.Assign(temp_buf);
break;
case CONTROLGET_CMD_HWND:
// The terminology "HWND" was chosen rather than "ID" to avoid confusion with a control's
// dialog ID (as retrieved by GetDlgCtrlID). This also reserves the word ID for possible
// use with the control's Dialog ID in future versions.
output_var.AssignHWND(control_window);
break;
}
// Note that ControlDelay is not done for the Get type commands, because it seems unnecessary.
return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
*/
}
ResultType Line::URLDownloadToFile(char *aURL, char *aFilespec)
{
/*
// Check that we have IE3 and access to wininet.dll
HINSTANCE hinstLib = LoadLibrary("wininet");
if (!hinstLib)
return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
typedef HINTERNET (WINAPI *MyInternetOpen)(LPCTSTR, DWORD, LPCTSTR, LPCTSTR, DWORD dwFlags);
typedef HINTERNET (WINAPI *MyInternetOpenUrl)(HINTERNET hInternet, LPCTSTR, LPCTSTR, DWORD, DWORD, LPDWORD);
typedef BOOL (WINAPI *MyInternetCloseHandle)(HINTERNET);
typedef BOOL (WINAPI *MyInternetReadFileEx)(HINTERNET, LPINTERNET_BUFFERS, DWORD, DWORD);
#ifndef INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY
#define INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 4