In the PROCsend_string, SendInput returns the number of character events that it has managed to pass to the window that has focus. It always returns zero.
Code: Select all
10 REM text-sender v2.01
20
30 REM define URL prefix, suffix and page no. variable
40 URL_prefix$ = "https://farmsubsidy.org/search/recipients?country=GB&year=2022&p="
50 URL_suffix$ = "&limit=100"
60 URL_complete$ = ""
70
80 REM define the specific window title referring to the website being scraped.
90 target_win_title$ = "Search recipients - Explore European Common Agricultural Policy"\
100 \ + " farm subsidy payments | FarmSubsidy.org - Profile 1 - Microsoft? Edge"
110
120 REM define Windows constants
130 SW_MINIMIZE = 6
140 SW_MAXIMIZE = 3
150 SW_RESTORE = 9
160
170
180
190 REM this library is needed for functions used to generate and examine the full list of
200 REM running processes - whether they're minimised or not.
210 REM https://www.bbcbasic.net/wiki/doku.php?id=listing_20other_20windows_20and_20applications
220 INSTALL @lib$+"CALLBACK"
230 SYS "LoadLibrary", "PSAPI.DLL" TO psapi%
240 SYS "GetProcAddress", psapi%, "GetModuleFileNameExA" TO `GetModuleFileNameEx`
250
260
270 DIM matching_whandles%(100)
280 DIM matching_pids%(100)
290 count_whandpids% = 0
300
310 REM check that we can list all processes
320 REM PROCwin_show_all_visible("", matching_whandles%(), matching_pids%(), count_whandpids%)
330
340 wh_edge% = 0
350
360
370 REM declare the structure for the SendInput function (defined here in C++)
380
390 REM typedef struct tagINPUT {
400 REM DWORD type;
410 REM union {
420 REM MOUSEINPUT mi;
430 REM KEYBDINPUT ki;
440 REM HARDWAREINPUT hi; };
450 REM } INPUT, *PINPUT, FAR* LPINPUT;
460
470
480 REM typedef struct tagKEYBDINPUT {
490 REM WORD wVk; // 2 bytes
500 REM WORD wScan; // 2 bytes
510 REM DWORD dwFlags; // 4 bytes
520 REM DWORD time; // 4 bytes
530 REM ULONG_PTR dwExtraInfo; // 8 bytes on 64 bit systems, 4 on 32 bit systems.
540 REM } KEYBDINPUT, *PKEYBDINPUT, FAR* LPKEYBDINPUT;
550
560
570 REM We're going to ignore the union and only define the keyboard component.
580 REM i.e.
590 REM DWORD type, WORD wVk, WORD wScan, DWORD dwFlags, DWORD time, ULONG_PTR dwExtraInfo}
600
610 REM We will need an array of 4 of these objects
620 DIM inputKBD{(3)type%, ki{wVk{lo&,hi&}, wScan{lo&,hi&}, dwFlags%, time%, dwExtraInfo%}}
630
640
650 REM define more Windows constants
660 winVK_LCONTROL = &A2 REM left Windows key
670 winKEYEVENTF_KEYUP = &2 REM when keyboard key is released
680 winINPUT_KEYBOARD = &1 REM presumably standard enumeration .. mouse would be 0; h/w would be 2
690
700
710 IF 1 THEN start_page% = 1: finish_page% = 1
720 errmsg$ = ""
730
740 REM .................................................................
750 REM invoke the main procedure
760 REM .................................................................
770
780
790 dummy% = FNscrape
800 PROClog("Result:" + STR$(dummy%) + " " + errmsg$)
810 END
820
830 REM main procedure
840 LOCAL rslt%
850 DEF FNscrape
860 count_whandpids% = 0
870 errmsg$=""
880
890 REM for simplicity, assume the Edge browser is already running
900 PROCwin_show_all_visible("msedge.exe", matching_whandles%(), matching_pids%(), count_whandpids%)
910 wh_edge% = matching_whandles%(count_whandpids%)
920
930 REM .................................................................
940 REM main loop over paginated output
950 REM .................................................................
960
970 FOR P% = start_page% TO finish_page%
980 REM give focus to the browser.
990 REM returns the handle of the window that it took focus from
1000 dummy% = FNwin_give_focus(wh_edge%)
1010
1020 REM construct URL from prefix, page no. and suffix
1030 URL_complete$ = URL_prefix$ + STR$(P%) + URL_suffix$
1040
1050 REM send it to the browser
1060 rslt% = FNsend_string(URL_complete$)
1070 IF rslt% <> 2*LEN(URL_complete$) THEN
1080 errmsg$ = "Error 7: Can't send URL to (Edge) browser (" + STR$(rslt%) + "/" + STR$(LEN(URL_complete$)) + ")"
1090 =7
1100 ENDIF
1110
1120 REM wait a couple of seconds
1130 WAIT 200
1140
1150 REM send Control+A {Mark All} to the browser
1160 rslt% = FNsend_control_char("A")
1170 IF rslt% <> 4 THEN
1180 errmsg$ = "Error 8: Unable to mark all text in (Edge) browser (" + STR$(rslt%) + "/4)"
1190 =8
1200 ENDIF
1210
1220 REM send Control+C {Copy All} to the browser
1230 REM which copies text to the clipboard.
1240 rslt% = FNsend_control_char("C")
1250 IF rslt% <> 4 THEN
1260 errmsg$ = "Error 9: Unable to copy all text in (Edge) browser (" + STR$(rslt%) + "/4)"
1270 =9
1280 ENDIF
1290
1300 NEXT P%
1310
1320 errmsg$ = "Success"
1330 =0
1340
1350
1360 REM The LPINPUT structure is defined as follows:
1370
1380 REM typedef struct tagINPUT {
1390 REM DWORD type;
1400 REM union
1410 REM {
1420 REM MOUSEINPUT mi;
1430 REM KEYBDINPUT ki;
1440 REM HARDWAREINPUT hi;
1450 REM };
1460 REM } INPUT, *PINPUT, FAR* LPINPUT;
1470
1480
1490 REM The KEYBDINPUT structure is defined like this:
1500
1510 REM typedef struct tagKEYBDINPUT {
1520 REM WORD wVk;
1530 REM WORD wScan;
1540 REM DWORD dwFlags;
1550 REM DWORD time;
1560 REM ULONG_PTR dwExtraInfo;
1570 REM } KEYBDINPUT, *PKEYBDINPUT, FAR* LPKEYBDINPUT;
1580
1590
1600 REM The sizes of these types are as follows:
1610 REM
1620 REM BBC BASIC equivalent
1630 REM DWORD 32-bits (unsigned) int%
1640 REM WORD 16-bits (unsigned?) word{lo&,hi&} 2 individual chars
1650 REM LONG 32-bits (signed) int%
1660 REM ULONG 32-bits? (unsigned) int%
1670 REM pointer 32-bits on a 32-bit o/s int%
1680 REM e.g. *PKEYBOARDINPUT
1690
1700 REM -----------------------------------------------------
1710 REM https://www.bbcbasic.co.uk/bbcwin/manual/bbcwin2.html#structarray
1720
1730 DEF FNsend_control_char(char$)
1740 LOCAL rslt%, size%, dummy%
1750 dummy% = FNwin_give_focus(wh_edge%)
1760
1770 inputKBD{(0)}.type% = winINPUT_KEYBOARD
1780 inputKBD{(0)}.ki.wVk.lo& = winVK_LCONTROL
1790 inputKBD{(0)}.ki.wVk.hi& = 0
1800 inputKBD{(0)}.ki.wScan.lo& = 0
1810 inputKBD{(0)}.ki.wScan.hi& = 0
1820 inputKBD{(0)}.ki.dwFlags% = 0
1830 inputKBD{(0)}.ki.time% = 0
1840 inputKBD{(0)}.ki.dwExtraInfo% = 0
1850
1860 inputKBD{(1)}.type% = winINPUT_KEYBOARD
1870 inputKBD{(1)}.ki.wVk.lo& = ASC(char$)
1880 inputKBD{(1)}.ki.wVk.hi& = 0
1890 inputKBD{(1)}.ki.wScan.lo& = 0
1900 inputKBD{(1)}.ki.wScan.hi& = 0
1910 inputKBD{(1)}.ki.dwFlags% = 0
1920 inputKBD{(1)}.ki.time% = 0
1930 inputKBD{(1)}.ki.dwExtraInfo% = 0
1940
1950 inputKBD{(2)}.type% = winINPUT_KEYBOARD
1960 inputKBD{(2)}.ki.wVk.lo& = ASC(char$)
1970 inputKBD{(2)}.ki.wVk.hi& = 0
1980 inputKBD{(2)}.ki.wScan.lo& = 0
1990 inputKBD{(2)}.ki.wScan.hi& = 0
2000 inputKBD{(2)}.ki.dwFlags% = winKEYEVENTF_KEYUP
2010 inputKBD{(2)}.ki.time% = 0
2020 inputKBD{(2)}.ki.dwExtraInfo% = 0
2030
2040 inputKBD{(3)}.type% = winINPUT_KEYBOARD
2050 inputKBD{(3)}.ki.wVk.lo& = winVK_LCONTROL
2060 inputKBD{(3)}.ki.wVk.hi& = 0
2070 inputKBD{(3)}.ki.wScan.lo& = 0
2080 inputKBD{(3)}.ki.wScan.hi& = 0
2090 inputKBD{(3)}.ki.dwFlags% = winKEYEVENTF_KEYUP
2100 inputKBD{(3)}.ki.time% = 0
2110 inputKBD{(3)}.ki.dwExtraInfo% = 0
2120
2130 REM https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
2140 size% = DIM(inputKBD{(0)})
2150 PROClog("size of inputKBD{}=" + STR$(size%))
2160 SYS "SendInput", 4, ^inputKBD{(0)}, size% TO rslt%
2170 REM SYS "SendInput", 4, inputKBD{(0)}, size% TO rslt%
2180 REM SYS "SendInput", 4, inputKBD{}, size% TO rslt%
2190 REM SYS "SendInput", 4, inputKBD{()}, size% TO rslt%
2200
2210 =rslt%
2220
2230
2240 REM -----------------------------------------------------
2250
2260 DEF FNsend_string(string$)
2270 REM loop calling SendInput
2280 LOCAL I%, rslt%, char$, size%, sum_rslt%, dummy%
2290
2300 sum_rslt% = 0
2310 dummy% = FNwin_give_focus(wh_edge%)
2320 FOR I% = 1 TO LEN(string$)
2330 char$ = MID$(string$,I%,1)
2340
2350 inputKBD{(0)}.type% = winINPUT_KEYBOARD
2360 inputKBD{(0)}.ki.wVk.lo& = ASC(char$)
2370 inputKBD{(0)}.ki.wVk.hi& = 0
2380 inputKBD{(0)}.ki.wScan.lo& = 0
2390 inputKBD{(0)}.ki.wScan.hi& = 0
2400 inputKBD{(0)}.ki.dwFlags% = 0
2410 inputKBD{(0)}.ki.time% = 0
2420 inputKBD{(0)}.ki.dwExtraInfo% = 0
2430
2440 inputKBD{(1)}.type% = winINPUT_KEYBOARD
2450 inputKBD{(1)}.ki.wVk.lo& = ASC(char$)
2460 inputKBD{(1)}.ki.wVk.hi& = 0
2470 inputKBD{(1)}.ki.wScan.lo& = 0
2480 inputKBD{(1)}.ki.wScan.hi& = 0
2490 inputKBD{(1)}.ki.dwFlags% = winKEYEVENTF_KEYUP
2500 inputKBD{(1)}.ki.time% = 0
2510 inputKBD{(1)}.ki.dwExtraInfo% = 0
2520
2530 REM https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
2540 size% = DIM(inputKBD{(0)})
2550 dummy% = FNwin_give_focus(wh_edge%)
2560 SYS "SendInput", 2, ^inputKBD{(0)}, size% TO rslt%
2570 REM SYS "SendInput", 2, inputKBD{(0)}, size% TO rslt%
2580 REM SYS "SendInput", 2, inputKBD{}, size% TO rslt%
2590 REM SYS "SendInput", 2, inputKBD{()}, size% TO rslt%
2600 sum_rslt% += rslt%
2610 IF rslt% <> 2 THEN
2620 PROClog("Error 11: send_string failed (" + STR$(rslt%) + "/2) Last-err=" + STR$(dummy%))
2630 EXIT FOR
2640 ENDIF
2650 NEXT I%
2660 =sum_rslt%
2670
2680 DEF FNwin_get_status(handle%)
2690 LOCAL bool_rslt%
2700 SYS "IsIconic", handle% TO bool_rslt%
2710 IF bool_rslt% THEN =SW_MINIMIZE ELSE =SW_MAXIMIZE
2720
2730
2740
2750 DEF FNwin_give_focus(handle%)
2760 LOCAL hw%
2770 SYS "SetFocus", handle% TO hw%
2780 = hw%
2790
2800
2810 DEF FNwin_has_focus(handle%)
2820 LOCAL hw%
2830 SYS "GetForegroundWindow" TO hw%
2840 IF hw% == handle% THEN
2850 =TRUE
2860 ELSE
2870 PROClog("Process " + STR$(hw%) + " has focus instead of Edge process " + STR$(handle%))
2880 =FALSE
2890
2900
2910 REM Check if a Microsoft Edge is running and has a VISIBLE (i.e. not HIDDEN) window
2920 DEF FNwin_is_edge_running
2930 PROCwin_show_all_visible("msedge.exe", matching_whandles%(), matching_pids%(), count_whandpids%)
2940 PROClog("No. of Edge windows=" + STR$(count_whandpids%))
2950 IF count_whandpids% > 0 THEN
2960 PROClog("pid of Edge win=" + STR$(matching_pids%(count_whandpids% - 1)))
2970 PROClog("whandle of Edge process=" + STR$(matching_whandles%(count_whandpids% - 1)))
2980 =matching_whandles%(count_whandpids% - 1)
2990 ELSE
3000 =0
3010 ENDIF
3020
3030
3040 DEF PROCwin_launch_edge_using_oscli
3050 LOCAL p1$, p2$
3060 REM PRINT "launching Edge"
3070 p1$ = "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
3080 REM run the child process in the background - achieved by using the trailing semicolon
3090 p2$ = "RUN " + p1$ + ";"
3100 REM PRINT p2$
3110 IF 0 THEN
3120 OSCLI p2$
3130 ENDIF
3140 ENDPROC
3150
3160
3170 DEF FNwin_maximise(handle%)
3180 SYS "ShowWindow", handle%, SW_MAXIMIZE
3190 =TRUE
3200
3210 DEF FNwin_minimise(handle%)
3220 SYS "ShowWindow", handle%, SW_MINIMIZE
3230 =TRUE
3240
3250 DEF FNwin_restore(handle%)
3260 SYS "ShowWindow", handle%, SW_RESTORE
3270 =TRUE
3280
3290 REM -------------------------------------------
3300
3310 REM examine ALL visible windows
3320 REM return of list of the pids where the window process-name contains the filter string
3330 REM code written by Michael Hicks
3340 REM https://www.bbcbasic.net/wiki/doku.php?id=listing_20other_20windows_20and_20applications
3350 REM where no filter has been specified, print out a list of ALL windows
3360 DEF PROCwin_show_all_visible(filter$, RETURN matching_whandles%(), RETURN matching_pids%(), RETURN count_whandpids%)
3370 LOCAL winnam$
3380
3390 Index% = 1
3400 lParam% = 0
3410 DIM hWnd%(2000)
3420 SYS FN_syscalls("EnumWindows"), FN_callback(FNwsav_enumfunc(),2), lParam%
3430 ret% = FN_sysresult
3440
3450 nMaxCount% = 255
3460 DIM lpWindowText% nMaxCount%, lpszFileName% nMaxCount%
3470 IF filter$ == "" THEN
3480 PROClog("List of visible windows with Process ID and image name")
3490 PROClog("======================================================")
3500 PROClog(" ")
3510 ENDIF
3520
3530 FOR i% = 1 TO Index%-1
3540 SYS "GetWindowText", hWnd%(i%), lpWindowText%, nMaxCount%
3550 SYS "IsWindowVisible", hWnd%(i%) TO vis%
3560 IF vis% AND $$lpWindowText% <> "" THEN
3570 SYS "GetWindowThreadProcessId", hWnd%(i%), ^pid%
3580 SYS "OpenProcess", &410, 0, pid% TO hprocess%
3590 SYS `GetModuleFileNameEx`, hprocess%, 0, lpszFileName%, nMaxCount%
3600 SYS "CloseHandle", hprocess%
3610 IF filter$ == "" THEN
3620 REM window title
3630 COLOUR 0: PROClog("* " + $$lpWindowText%)
3640 REM process_id process_name
3650 COLOUR 1: PROClog( " [pid=" + STR$(pid%) + "] " + $$lpszFileName%)
3660 ELSE
3670 winexe$ = $$lpszFileName%
3680 IF INSTR(winexe$, filter$) <> 0 THEN
3690 PROClog("==" + winexe$)
3700 matching_whandles%(count_whandpids%) = hWnd%(i%)
3710 matching_pids%(count_whandpids%) = pid%
3720 count_whandpids% += 1
3730 REM COLOUR 0: PRINT "* " $$lpWindowText%
3740 REM COLOUR 1: PRINT " [pid=" + STR$(pid%) + "] ", $$lpszFileName%
3750 ENDIF
3760 ENDIF
3770 ENDIF
3780 NEXT
3790 COLOUR 0
3800 ENDPROC
3810
3820 REM build a list of all current window handles
3830 DEF FNwsav_enumfunc(hwnd%, lparam%)
3840 hWnd%(Index%) = hwnd%
3850 Index% += 1
3860 = 1
3870
3880 DEF PROClog(s$)
3890 PRINT s$
3900 ENDPROC