MS SendInput

The open discussion of hosting, forum, web-sites, community organisation and open voting

MS SendInput

Postby DGDanforth » Mon Jul 03, 2017 10:25 am

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
Code: Select all
cbSize:= SIZE(KEYBDINPUT);


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
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Re: MS SendInput

Postby Josef Templ » Mon Jul 03, 2017 10:40 am

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
Josef Templ
 
Posts: 222
Joined: Tue Sep 17, 2013 6:50 am

Re: MS SendInput

Postby DGDanforth » Tue Jul 04, 2017 3:31 am

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
      Key =    SHORTCHAR;

Code: Select all
      SCHARS =   ARRAY OF SHORTCHAR;

Code: Select all
      hWnd:   INTEGER;

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;
   
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Re: MS SendInput

Postby DGDanforth » Tue Jul 04, 2017 4:46 am

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
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Re: MS SendInput

Postby DGDanforth » Tue Jul 04, 2017 5:34 am

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?
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Re: MS SendInput

Postby DGDanforth » Tue Jul 04, 2017 5:39 am

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.
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Re: MS SendInput

Postby Josef Templ » Tue Jul 04, 2017 8:11 am

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
Josef Templ
 
Posts: 222
Joined: Tue Sep 17, 2013 6:50 am

Re: MS SendInput

Postby Josef Templ » Wed Jul 05, 2017 5:23 am

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
Josef Templ
 
Posts: 222
Joined: Tue Sep 17, 2013 6:50 am

Re: MS SendInput

Postby DGDanforth » Wed Jul 05, 2017 12:47 pm

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
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Re: MS SendInput

Postby DGDanforth » Wed Jul 05, 2017 12:51 pm

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
User avatar
DGDanforth
 
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.

Next

Return to Brainstorming

Who is online

Users browsing this forum: No registered users and 0 guests