stevedonovan / winapi

Minimal but useful Lua bindings to the Windows API
http://stevedonovan.github.com/winapi/api.html
Other
192 stars 40 forks source link

How to simulate a sequence of input #8

Closed starwing closed 11 years ago

starwing commented 11 years ago

Hi Steve,

I'm using winapi to do some automatic work with Qt-based program. what the I want to done is to simulate some input, just as:

  1. click windows pos at (xxx, yyy) (enter input zone)
  2. press a Ctrl-A (select all)
  3. press a Ctrl-V (paste)
  4. click windows pos at (zzz, aaa) (leave input zone), or press CR.

but I have tried several ways to do this, but none of them work correctly.

first, send_to_window only support keyboard event, so I had to use send_message: w:send_message(513, 0, 35_2^16+200) -- mouse down w:send_message(514, 0, 35_2^16+200) -- mouse up

this work correctly. but then I don't know how to send keys.

I have tried using send_to_window: W.send_to_window(17) W.send_to_window(65) W.send_to_window(65, true) -- ctrl-a W.send_to_window(86) W.send_to_window(86, true) -- ctrl-v W.send_to_window(17, true)

It only worked partly: some time I run the script, it just doesn't work, but sometime it works.

then I tried send message directly: w:send_message(513, 0, 35_2^16+200) -- mouse down w:send_message(514, 0, 35_2^16+200) -- mouse up w:send_message(256, 0, 17) -- press ctrl w:send_message(256, 0, 65) -- press A w:send_message(257, -1, 65) -- release A w:send_message(256, 0, 86) -- press V w:send_message(257, -1, 86) -- release V w:send_message(257, -1, 17) -- release ctrl

it just doesn't work.

So, how could I do this with winapi?

regards, Xavier Wang.

stevedonovan commented 11 years ago

Interesting problem; I see this comment in the source:

// The Windows SendInput() is a low-level function, and you have to
// simulate things like uppercase directly. Repeated characters need
// an explicit 'key up' keystroke to work.
// see http://stackoverflow.com/questions/2167156/sendinput-isnt-sending-the-correct-shifted-characters
// this is a case where we have to convert the parameter directly, since
// it may be an integer (virtual key code) or string of characters.

Does that reference help? If not, let me know...

starwing commented 11 years ago

you can see my code, I already send "key up" event in two sence. but it doesn't work.

I'll write a simple Qt program and a Lua script to show this issue.

starwing commented 11 years ago

I think we can try to make a function that support send mouse clicks with SendInput.

What about add a function named mouse_action and can used to simulate click or move?

stevedonovan commented 11 years ago

This could be a very useful feature for scripting/testing GUI apps. How did your send-keys go? Possible that the magic needed is a winapi.sleep between keystrokes? (Even a few ms to allow for task switching to take place)

starwing commented 11 years ago

I have tried sleep in both sence, but no luck. from 10ms to 50ms all do not work.

it seems SendInput is completely different with SendMessage, So using SendMessage to simulate mouse position may cause race condition. So I'm trying all use SendMessage (in correct form) or all use SendInput :-(

starwing commented 11 years ago

I have fixed this issue!!

Not the issue of winapi, because I simulate mouse message using SendMessage, but Should PostMessage, because We do not want remote application process this message.

change SendMessage to PostMessage fixed this issue.

Thank you very much! If I have extra time I will add a mouse_action function to winapi mode :-)

stevedonovan commented 11 years ago

Well, I think you should congratulate yourself for this one ;) I think a mouse_action function is a very good idea (send me the Lua code and I can write it in C), but I'm still curious why we can't send keystrokes using an API function designed for this ...

starwing commented 11 years ago

Okay, I already using post_message and send_to_window, that's all right. it's all my fault :-(

I think it should cover the function of SendInput when type equals INPUT_MOUSE.

and we have several actions: move left middle right xbutton1 xbutton2 wheel

we have several options: relative: for all action, relative coordinates. horizontal: for wheel, horizontal wheel. nocoalesce: for move, do not combine WM_MOVE message. up: for left, middle, up, xbuttonX, send a UP message instead of a DOWN message. virtual: for all action, using virtual desktop coordinates.

it seems that a event may have several options, so it can be designed as a option string set:

local x, y = win:get_position() winapi.mouse_action(x+120, y+30, "left") winapi.sleep(10) winapi.mouse_action(x+120, y+30, "left", "U")

a single true value can be used as "U" for same with send_to_window

I want a function mouse_to_window but that is diffcult :-(

starwing commented 11 years ago

btw, action can be ignored, winapi.mouse_action(x, y) means winapi.mouse_action(x, y, "left") and winapi.mouse_action(x, y, true) means: winapi.mouse_action(x, y, "left", true)