passing an array of structures to a Win API function

Discussions related to network, internet and socket programming; also to serial, parallel, Bluetooth, USB etc.
artfizz
Posts: 21
Joined: Wed 11 Dec 2024, 17:15

passing an array of structures to a Win API function

Post by artfizz »

I am trying to call SendInput. I have defined a structure in BBC BASIC.
SendInput requires a structure LPINPUT like this in C++:

Code: Select all

typedef struct tagINPUT {
DWORD   type;
union
{
MOUSEINPUT      mi;
KEYBDINPUT      ki;
HARDWAREINPUT   hi;
};
} INPUT, *PINPUT, FAR* LPINPUT;

 The KEYBDINPUT structure is defined like this in C++:

typedef struct tagKEYBDINPUT {
WORD    wVk;
WORD    wScan;
DWORD   dwFlags;
DWORD   time;
ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT, FAR* LPKEYBDINPUT;
I have defined a 4-element array of structures in BBC BASIC:

Code: Select all

DIM inputKBD{(3)type%, ki{wVk{lo&,hi&}, wScan{lo&,hi&}, dwFlags%,      time%,          dwExtraInfo%}}
I can pass this to a function like this:

Code: Select all

rslt% = FNvalidate_params(4, inputKBD{()}, size%)
but none of these system calls work:

Code: Select all

SYS "SendInput", 4, ^inputKBD{(0)}, size% TO rslt%
SYS "SendInput", 4, inputKBD{(0)}, size% TO rslt%
SYS "SendInput", 4, inputKBD{}, size% TO rslt%
SYS "SendInput", 4, inputKBD{()}, size% TO rslt%
The 3rd one gives 'No such variable'.
The 4th one gives 'Bad use of array'.

Any suggestions about where I am going wrong will be appreciated!
Last edited by artfizz on Tue 11 Mar 2025, 17:40, edited 2 times in total.
Richard Russell
Posts: 272
Joined: Tue 18 Jun 2024, 09:32

Re: passing an array of structures to a Win API function

Post by Richard Russell »

artfizz wrote: Tue 11 Mar 2025, 11:53 Any suggestions about where I am going wrong will be appreciated!
I can't see anything wrong with this, it is syntactically valid and should pass a pointer to the first structure as required:

Code: Select all

SYS "SendInput", 4, inputKBD{(0)}, size% TO rslt%
So when you say it "doesn't work" do you get an error message, or does it simply not have the effect you expected?

There's a Wiki article, which you may well have seen, showing how to use SendInput to automate mouse actions, so there's no obvious reason why the keyboard equivalent shouldn't work.

Can you list a minimal, but complete, program illustrating what isn't working?
artfizz
Posts: 21
Joined: Wed 11 Dec 2024, 17:15

Re: passing an array of structures to a Win API function

Post by artfizz »

I don't know how I missed your explanation about using the mouse with SendInput!
https://www.bbcbasic.net/wiki/doku.php? ... _20actions
but I found your tutorial on using structures with Win API:
https://www.bbcbasic.co.uk/bbcwin/manua ... tructarray

I've cut my program down to 400 lines! (Sorry!)
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
Last edited by artfizz on Tue 11 Mar 2025, 17:42, edited 2 times in total.
artfizz
Posts: 21
Joined: Wed 11 Dec 2024, 17:15

Re: passing an array of structures to a Win API function

Post by artfizz »

That routine to list process ids and window handles was written by Michael Hicks, listed here:
https://www.bbcbasic.net/wiki/doku.php? ... plications
Richard Russell
Posts: 272
Joined: Tue 18 Jun 2024, 09:32

Re: passing an array of structures to a Win API function

Post by Richard Russell »

artfizz wrote: Tue 11 Mar 2025, 16:42 I've cut my program down to 400 lines! (Sorry!)
Eek, I'm afraid that's far too big for me to try. A Minimal Reproducible Example should ideally be much smaller.
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.
Can't you distil the program down to just that part?
artfizz
Posts: 21
Joined: Wed 11 Dec 2024, 17:15

Re: passing an array of structures to a Win API function

Post by artfizz »

The program is getting smaller. It's only 54 lines - if you don't count the routine to look up a window handle.
Thanks again for your help.

Code: Select all

   10 INSTALL @lib$+"CALLBACK"
   20 SYS "LoadLibrary", "PSAPI.DLL" TO psapi%
   30 SYS "GetProcAddress", psapi%, "GetModuleFileNameExA" TO `GetModuleFileNameEx`
   40 
   50 p$ = "RUN C:/Windows/system32/notepad.exe;"
   60 OSCLI p$
   70 
   80 wh_notepad% = FNwin_get_handle("Notepad.exe")
   90 dummy% = 0
  100 SYS "SetFocus", wh_notepad% TO dummy%
  110 
  120 REM We need an array of 4 objects to pass to SendInput
  130 DIM inputKBD{(3)type%, ki{wVk{lo&,hi&}, wScan{lo&,hi&}, dwFlags%, time%, dwExtraInfo%}}
  140 
  150 _VK_LCONTROL = &A2         REM left Windows key
  160 _KEYEVENTF_KEYUP = &2      REM when keyboard key is released
  170 _INPUT_KEYBOARD = &1
  180 
  190 dummy% = FNsend_string("abc")
  200 END
  210 
  220 DEF FNsend_string(string$)
  230 LOCAL I%, rslt%, char$, size%, sum_rslt%, dummy%
  240 sum_rslt% = 0
  250 FOR I% = 1 TO LEN(string$)
  260   char$ = MID$(string$,I%,1)
  270   
  280   inputKBD{(0)}.type% = _INPUT_KEYBOARD
  290   inputKBD{(0)}.ki.wVk.lo& = ASC(char$)
  300   inputKBD{(0)}.ki.wVk.hi& = 0
  310   inputKBD{(0)}.ki.wScan.lo& = 0
  320   inputKBD{(0)}.ki.wScan.hi& = 0
  330   inputKBD{(0)}.ki.dwFlags% = 0
  340   inputKBD{(0)}.ki.time% = 0
  350   inputKBD{(0)}.ki.dwExtraInfo% = 0
  360   
  370   inputKBD{(1)}.type% = _INPUT_KEYBOARD
  380   inputKBD{(1)}.ki.wVk.lo& = ASC(char$)
  390   inputKBD{(1)}.ki.wVk.hi& = 0
  400   inputKBD{(1)}.ki.wScan.lo& = 0
  410   inputKBD{(1)}.ki.wScan.hi& = 0
  420   inputKBD{(1)}.ki.dwFlags% = _KEYEVENTF_KEYUP
  430   inputKBD{(1)}.ki.time% = 0
  440   inputKBD{(1)}.ki.dwExtraInfo% = 0
  450   
  460   size% = DIM(inputKBD{(0)})
  470   SYS "SendInput", 2, ^inputKBD{(0)}, size% TO rslt%
  480   sum_rslt% += rslt%
  490   IF rslt% <> 2 THEN
  500     PRINT "Error "; rslt%; " characters sent"
  510     STOP
  520   ENDIF
  530 NEXT I%
  540 =sum_rslt%
  550 
  560 REM return the window handle where the process-name contains the specified string
  570 REM code from article written by Michael Hicks
  590 REM https://www.bbcbasic.net/wiki/doku.php?id=listing_20other_20windows_20and_20applications
  600 
  610 REM where no filter has been specified, print out a list of ALL windows
  620 DEF FNwin_get_handle(exe_name$)
  630 LOCAL winnam$
  640 
  650 Index% = 1
  660 lParam% = 0
  670 DIM hWnd%(2000)
  680 SYS FN_syscalls("EnumWindows"), FN_callback(FNwsav_enumfunc(),2), lParam%
  690 ret% = FN_sysresult
  700 
  710 nMaxCount% = 255
  720 DIM lpWindowText% nMaxCount%, lpszFileName% nMaxCount%
  730 IF exe_name$ == "" THEN
  740   PRINT "List of visible windows with Process ID and image name"
  750   PRINT "======================================================"
  760   PRINT " "
  770 ENDIF
  780 
  790 FOR i% = 1 TO Index%-1
  800   SYS "GetWindowText", hWnd%(i%), lpWindowText%, nMaxCount%
  810   SYS "IsWindowVisible", hWnd%(i%) TO vis%
  820   IF vis% AND $$lpWindowText% <> "" THEN
  830     SYS "GetWindowThreadProcessId", hWnd%(i%), ^pid%
  840     SYS "OpenProcess", &410, 0, pid% TO hprocess%
  850     SYS `GetModuleFileNameEx`, hprocess%, 0, lpszFileName%, nMaxCount%
  860     SYS "CloseHandle", hprocess%
  870     IF exe_name$ == "" THEN
  880       REM window title
  890       COLOUR 0: PRINT "* "; $$lpWindowText%)
  900       REM process_id      process_name
  910       COLOUR 1: PRINT  "  [pid="; STR$(pid%) + "] "; $$lpszFileName%)
  920     ELSE
  930       winexe$ = $$lpszFileName%
  940       IF INSTR(winexe$, exe_name$) <> 0 THEN
  950         = hWnd%(i%)
  960       ENDIF
  970     ENDIF
  980   ENDIF
  990 NEXT
 1000 COLOUR 0
 1010 =0
 1020 
 1030 REM build a list of all current window handles
 1040 DEF FNwsav_enumfunc(hwnd%, lparam%)
 1050 hWnd%(Index%) = hwnd%
 1060 Index% += 1
 1070 = 1
Richard Russell
Posts: 272
Joined: Tue 18 Jun 2024, 09:32

Re: passing an array of structures to a Win API function

Post by Richard Russell »

artfizz wrote: Tue 11 Mar 2025, 20:01 Thanks again for your help.

Code: Select all

  470   SYS "SendInput", 2, ^inputKBD{(0)}, size% TO rslt%
The problem is the value of size%. What you haven't taken into account is that the INPUT structure contains a union of the MOUSEINPUT, KEYBDINPUT and HARDWAREINPUT structures, so its size is the largest of these, which happens to be MOUSEINPUT. You are passing only the size of KEYBDINPUT (plus the size of type) which is too small.

Change the code to this and it will work:

Code: Select all

  470   SYS "SendInput", 2, inputKBD{(0)}, 28 TO rslt%
Of course you could define the MOUSEINPUT structure purely to find its size, which might be more 'elegant', but just plugging in the known size as a constant (28) works fine.
artfizz
Posts: 21
Joined: Wed 11 Dec 2024, 17:15

Re: passing an array of structures to a Win API function

Post by artfizz »

Thank you! A brilliant analysis.