Page 1 of 3
MS SendInput
Posted: Mon Jul 03, 2017 10:25 am
by DGDanforth
WinApi does not export the Windows function "SendInput" present in USER32.dll so I created
a module that accesses that.
When I make a call to it I get the message (from show last error) of
"The parameter is incorrect."
Well there are 3 parameters being passed.
Code: Select all
IF MyUser32.SendInput(1, S.ADR(input), cbSize) = 0 THEN
MyWin.ShowLastError; Out.Ln;
input.Dump;
HALT(0);
END;
The first parameter specifies the number of records in the array "input" each of which is of size
What I am trying to do is pass a sequence of keystrokes to a preexisting game whose handle I am able to get.
The fact that the call to SendInput does not bomb says I really am calling the procedure.
Now comes my questions. the C code looks like
Code: Select all
kb.wVk = vk;
Input.type = INPUT_KEYBOARD;
Input.ki = kb;
::SendInput(1,&Input,sizeof(Input));
I am assuming the SYSTEM.ADR(input) does the same thing as &input. Is that correct?
Also SIZE(input) and sizeof(input) should yield the same number (in this case 16) bytes.
Before the call input.type = 1 (the value of INPUT_KEYBOARD), After the call stuff is
messed up
input.type = 6480556 (*initially 1*)
KEYBDINPUT.wVk = 452 (*initially ORD("R")*)
KEYBDINPUT.wScan = 13964 (*initially 0*)
KEYBDINPUT.dwFlags = 0 (*initially 0*)
KEYBDINPUT.time = 0 (*initially 0*)
KEYBDINPUT.dwExtraInfo = 0 (*initially 0*)
Is this an issue about [ccall]? What else could be going on?
-Doug Danforth
Re: MS SendInput
Posted: Mon Jul 03, 2017 10:40 am
by Josef Templ
Doug, can you post a complete code example including the MyXxx modules, everything.
Otherwise it is not possible to see what is going on.
My wild guess would be that the size parameter is wrong.
- Josef
Re: MS SendInput
Posted: Tue Jul 04, 2017 3:31 am
by DGDanforth
Josef,
This is probably more than you really want but for completeness ...
I've tried with and without [ccall] and get the same error. Which should it be?
Code: Select all
MODULE MyUser32 ["USER32.dll"];
IMPORT
S:=SYSTEM;
PROCEDURE [ccall] SendInput* ["USER32.dll", ""] (nInputs, pInputs, cbSize: INTEGER): INTEGER;
(*SendInput*)
END MyUser32.
Code: Select all
PROCEDURE GetGameHandle (title: SCHARS);
VAR className, windowName: WinApi.PtrSTR;
BEGIN
className := S.VAL(WinApi.PtrSTR, 0);
windowName := S.VAL(WinApi.PtrSTR, S.ADR(title[0]));
hWnd := WinApi.FindWindow(className, windowName);
ASSERT(hWnd # 0);
END GetGameHandle;
Code: Select all
PROCEDURE Attach*;
CONST false = 0;
CONST true = 1;
VAR fromPid, toPid, from, to, oldWnd: INTEGER;
BEGIN
GetGameHandle(title);
fromPid := WinApi.GetCurrentProcessId();
toPid := 1;
from := WinApi.GetCurrentThreadId();
to := WinApi.GetWindowThreadProcessId(hWnd, toPid);
(*
Out.String("fromPid = "); Out.Int(fromPid); Out.Ln;
Out.String("toPid = "); Out.Int(toPid); Out.Ln;
*)
ASSERT(WinApi.AttachThreadInput(from, to, true) # 0) ;
ASSERT(WinApi.SetForegroundWindow(hWnd) # 0) ;
ASSERT(WinApi.SetFocus(hWnd) # 0) ;
END Attach;
Code: Select all
PROCEDURE Detach*;
CONST false = 0;
CONST true = 1;
VAR fromPid, toPid, from, to: INTEGER;
BEGIN
fromPid := WinApi.GetCurrentProcessId();
toPid := 1;
from := WinApi.GetCurrentThreadId();
to := WinApi.GetWindowThreadProcessId(hWnd, toPid);
ASSERT(WinApi.AttachThreadInput(from, to, false) # 0) ;
(*
Out.String("fromPid = "); Out.Int(fromPid); Out.Ln;
Out.String("toPid = "); Out.Int(toPid); Out.Ln;
*)
END Detach;
Code: Select all
KEYBDINPUT = RECORD
wVk: SHORTINT;
wScan: SHORTINT;
dwFlags: INTEGER;
time: INTEGER;
dwExtraInfo: INTEGER;
END;
Code: Select all
PROCEDURE (VAR input: KEYBDINPUT) SetKeyDown (key: Key), NEW;
BEGIN
input.wVk := SHORT(ORD(key));
input.wScan := 0;
input.dwFlags :=0;
input.time := 0;
input.dwExtraInfo := 0
END SetKeyDown;
Code: Select all
INPUT = RECORD
type: INTEGER;
ki: KEYBDINPUT
END;
Code: Select all
PROCEDURE (VAR input: INPUT) Zero, NEW;
BEGIN
input.type := 0;
input.ki.Zero
END Zero;
Code: Select all
PROCEDURE (VAR input: KEYBDINPUT) Zero, NEW;
BEGIN
input.wVk := 0;
input.wScan := 0;
input.dwFlags := 0;
input.time := 0;
input.dwExtraInfo := 0
END Zero;
Code: Select all
PROCEDURE Test*;
VAR flag: BOOLEAN; input: INPUT; cbSize: INTEGER;
BEGIN
input.Zero;
Attach;
input.type := INPUT_KEYBOARD;
cbSize:= SIZE(KEYBDINPUT);
input.ki.SetKeyDown("R");
IF User32.SendInput(1, S.ADR(input), cbSize) = 0 THEN
MyWin.ShowLastError; Out.Ln;
input.Dump;
HALT(0);
END;
Detach
END Test;
Re: MS SendInput
Posted: Tue Jul 04, 2017 4:46 am
by DGDanforth
Small step.
I previously did not have my dump procedure when I tested the two forms of SendInput (with and without [ccall]).
I have tried those two forms again with dump and I see that with [ccall] I get garbage whereas without [ccall] the
returned parameters of KEYBDINPUT are clean. Hence no [ccall] seems to be correct.
Also I have tried using scan codes rather than virtual-key codes but I still get the same error message.
Code: Select all
PROCEDURE (VAR input: KEYBDINPUT) SetKeyDown (key: Key), NEW;
VAR Vk: INTEGER;
BEGIN
Vk := ORD(key);
input.wVk := 0;
input.wScan := SHORT(WinApi.MapVirtualKey(Vk, 0 ));
input.dwFlags :=0;
input.time := 0;
input.dwExtraInfo := 0
END SetKeyDown;
-Doug
Re: MS SendInput
Posted: Tue Jul 04, 2017 5:34 am
by DGDanforth
Perhaps this has something to do with c unions, of which I am not familiar.
I have assumed that the different record types simply share the same memory space.
The input.type = 1 then knows the space is a keyboard record and hence its length
should be 16. If that logic is wrong then what must I do?
Re: MS SendInput
Posted: Tue Jul 04, 2017 5:39 am
by DGDanforth
Also I found this on the web
Answer
Re: SendInput Parameter error Pin mvp Luc Pattyn 13-May-09 3:34
Go to ParentHi,
AFAIK there is a problem in your MOUSEINPUT struct: the extraInfo field is a pointer (IntPtr), not an int, and it takes 8B instead of 4B on X64.
BTW: there are a lot of such errors on the web, even on
www.pinvoke.net
Answer
Re: SendInput Parameter error Pin mvp Luc Pattyn 13-May-09 3:34
General
Re: SendInput Parameter error Pin member IceWater42 18-May-09 19:15
Go to ParentThanks Luc. There is no question you are correct.
I was aware of the 8b vs 4b difference. I guess what i don't understand is how 64-bit machines can run 32-bit DLLs. Why is it that the 32-bit DLL is not expecting 32-bit pointers?
Sorry for delay in getting back here.
Re: MS SendInput
Posted: Tue Jul 04, 2017 8:11 am
by Josef Templ
Since WinApi does not specify ccall anywhere, why should it be specified for SendInput?
All Windows dll calls and callbacks use stdcall, as far as I remember.
This is the default in BB dll modules.
WinApi already defines similar INPUT records (INPUT_RECORD, PtrINPUT_RECORD).
This can be used as a template for defining a union.
Care must be taken regarding the usage of BYTE, SHORTINT, and INTEGER.
This is also crucial.
In addition, all union variants must be specified in order to get the correct maximum size and alignment
for the union. A union simply overlays all variants and the size
and alignment follow from the maximum of all variants.
If MOUSEINPUT requires more memory (and I think it does) it will be ignored otherwise,
leading to a wrong size. In addition, the event type must also be counted for the size, I think.
I would use SIZE(INPUT) not SIZE(KEYBDINPUT).
It may be a good idea to include SendInput in the 1.7.1 WinApi.
- Josef
Re: MS SendInput
Posted: Wed Jul 05, 2017 5:23 am
by Josef Templ
Here is a possible coding of SendInput as an extension of WinApi:
Code: Select all
CONST
INPUT_MOUSE* = 0;
INPUT_KEYBOARD* = 1;
INPUT_HARDWARE* = 2;
KEYEVENTF_UNICODE* = {2};
KEYEVENTF_SCANCODE* = {3};
MOUSEEVENTF_XDOWN* = {7};
MOUSEEVENTF_XUP* = {8};
MOUSEEVENTF_WHEEL* = {11};
MOUSEEVENTF_HWHEEL* = {12};
MOUSEEVENTF_MOVE_NOCOALESCE* = {13};
MOUSEEVENTF_VIRTUALDESK* = {14};
TYPE
PtrMOUSEINPUT* = POINTER TO MOUSEINPUT;
MOUSEINPUT* = RECORD [untagged]
dx*: INTEGER;
dy*: INTEGER;
mouseData*: INTEGER;
dwFlags*: SET;
time*: INTEGER;
dwExtraInfo*: INTEGER;
END;
PtrKEYBDINPUT* = POINTER TO KEYBDINPUT;
KEYBDINPUT* = RECORD [untagged]
wVk*: SHORTINT;
wScan*: SHORTINT;
dwFlags*: SET;
time*: INTEGER;
dwExtraInfo*: INTEGER;
END;
PtrHARDWAREINPUT* = POINTER TO HARDWAREINPUT;
HARDWAREINPUT* = RECORD [untagged]
uMsg*: INTEGER ;
wParamL*: SHORTINT;
wParamH*: SHORTINT;
END;
PtrINPUT* = POINTER TO INPUT;
INPUT* = RECORD [untagged]
type*: INTEGER;
u*: RECORD [union]
mi*: MOUSEINPUT;
ki*: KEYBDINPUT;
hi*: HARDWAREINPUT;
END
END;
PROCEDURE SendInput* ["USER32.dll", ""] (nInputs: INTEGER; pInputs: POINTER TO ARRAY [untagged] OF INPUT; cbSize: INTEGER): INTEGER;
(*END SendInput;*)
For testing set the cursor to the traget position and execute TestSendInput.Do:
Code: Select all
MODULE TestSendInput;
IMPORT WinApi;
PROCEDURE Do*;
VAR inp: ARRAY 1 OF WinApi.INPUT; res: INTEGER;
BEGIN
inp[0].type := WinApi.INPUT_KEYBOARD;
inp[0].u.ki.wVk := ORD("9");
inp[0].u.ki.wScan := 0;
inp[0].u.ki.dwFlags := {}; (*WinApi.KEYEVENTF_KEYUP*)
inp[0].u.ki.time := 0;
inp[0].u.ki.dwExtraInfo := 0;
res := WinApi.SendInput(1, inp, SIZE(WinApi.INPUT));
ASSERT(res > 0)
END Do;
END TestSendInput.
- Josef
Re: MS SendInput
Posted: Wed Jul 05, 2017 12:47 pm
by DGDanforth
Josef,
Thank you very much!
I see there were quite a number of things I overlooked and of which
I was unaware. I now need to study and test your code.
-Doug
Re: MS SendInput
Posted: Wed Jul 05, 2017 12:51 pm
by DGDanforth
Josef Templ wrote:Since WinApi does not specify ccall anywhere, why should it be specified for SendInput?
All Windows dll calls and callbacks use stdcall, as far as I remember.
...
It may be a good idea to include SendInput in the 1.7.1 WinApi.
- Josef
From the sourcecode of WinApi.odc
Code: Select all
PROCEDURE [ccall] wsprintfA* ["USER32.dll", ""] (p0: PtrSTR; p1: PtrSTR): INTEGER;
(*END wsprintfA;*)
Some procedures do and others don't use [ccall]
I agree that SendInput should be part of 1.7.1 WinApi
-Doug