NO

Author Topic: SendInput on a remote process?  (Read 4211 times)

Offline tony74

  • Member
  • *
  • Posts: 35
SendInput on a remote process?
« on: October 16, 2024, 09:56:41 PM »
I'm trying to do a workaround for an app that doesn't have any command line options.

In this case, I would need to 'tab' to a checkbox, click it, then 'tab' to a 'start' button and click that - in a remote running process, that I spawned from my main program using ShellExecute, since CreateProcess would wait 'till it closed, which doesn't let me operate on it (which may not be possible, anyway).

Sounds like one of those 'You can't get there from here' situations.

Maybe this isn't a doable thing.
Maybe I'm not using SendInput correctly (after all, I have no experience with it).

But, MrAutoHotkey does it from his runtime, so I still hold out hope I can do this internally without resorting to any additional external processes ( himmel, I'm *already* an 'external' process! ).

There's no error message from SendInput.
But here's what appears on the command line after I exit:
Code: [Select]
C:\Programming\proto>"BlockInput function (winuser.h) - Win32 apps _ Microsoft Learn_files"

Which is a tab in my browser...
So it looks like SendInput is, indeed, working on at least *one* remote process...
Just not the one I want it to.

Here's what I do after the external program is up and running:
Code: [Select]
void robodim(void)
{
    char buf[1024];
    INPUT inputs[6] = {0};
    ZeroMemory(inputs, sizeof(inputs));
   
    //Press tab
    inputs[0].type = INPUT_KEYBOARD;
    inputs[0].ki.wVk = VK_TAB;
   
    //lift key
    inputs[1].type = INPUT_KEYBOARD;
    inputs[1].ki.wVk = VK_TAB;
    inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;

    //press tab agin
    inputs[2].type = INPUT_KEYBOARD;
    inputs[2].ki.wVk = VK_TAB;
   
    //lift key
    inputs[3].type = INPUT_KEYBOARD;
    inputs[3].ki.wVk = VK_TAB;
    inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;

    //press spacebar to check
    inputs[4].type = INPUT_KEYBOARD;
    inputs[4].ki.wVk = VK_SPACE;
   
    //lift key
    inputs[5].type = INPUT_KEYBOARD;
    inputs[5].ki.wVk = VK_SPACE;
    inputs[5].ki.dwFlags = KEYEVENTF_KEYUP;


    UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
    if (uSent != ARRAYSIZE(inputs))
    {
        sprintf(buf,"SendInput failed: 0x%x\n", GetLastError());
        MessageBox(NULL,buf,"",MB_OK);
    }
}

After which I exit.

That's all I got, Thanks.

Offline MrBcx

  • Global Moderator
  • Member
  • *****
  • Posts: 195
    • Bcx Basic to C/C++ Translator
Re: SendInput on a remote process?
« Reply #1 on: October 17, 2024, 06:19:17 AM »
Here's a different look at the problem.

When compiled and run at a command prompt, it will launch NOTEPAD

Code: [Select]

#include <windows.h> 
 
void MySendkey (unsigned char vKey)
{
  keybd_event(vKey,0,KEYEVENTF_EXTENDEDKEY,0);
  keybd_event(vKey,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
}

 

int main(int argc, char *argv[])
{
  MySendkey(VK_N);
  MySendkey(VK_O);
  MySendkey(VK_T);
  MySendkey(VK_E);
  MySendkey(VK_P);
  MySendkey(VK_A);
  MySendkey(VK_D);
  MySendkey(VK_RETURN);
  return EXIT_SUCCESS;   
}

Bcx Basic to C/C++ Translator
https://www.BcxBasicCoders.com

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2122
Re: SendInput on a remote process?
« Reply #2 on: October 17, 2024, 12:37:57 PM »
Should remote app have to have a focus before filling keyboard buffer ?
May the source be with you

Offline MrBcx

  • Global Moderator
  • Member
  • *****
  • Posts: 195
    • Bcx Basic to C/C++ Translator
Re: SendInput on a remote process?
« Reply #3 on: October 17, 2024, 03:29:37 PM »
Should remote app have to have a focus before filling keyboard buffer ?

Yes ... absolutely.
Bcx Basic to C/C++ Translator
https://www.BcxBasicCoders.com

Offline tony74

  • Member
  • *
  • Posts: 35
Re: SendInput on a remote process?
« Reply #4 on: October 17, 2024, 11:26:57 PM »
@MrBCX

The example differs a bit from my use-case.

The program I'm using launches an external program through ShellExecute.
ShellExecute, rather than CreateProcess, is used so the call, as much as possible, emulates a user having called the external from Explorer or the desktop.

The external program does have focus upon execution, as far as I know.
Now that the external is up and running, my program attempts to sendkeys, but I don't know that in sending the keys, the focus doesn't change back to my program rather than the external?

In any case, the keys don't get through to the intended external.

I enumerated the windows and got a handle for the child-window that has the checkbox and start button I need to click.
I haven't yet, but I could setfocus on the retrieved window handle, just to be sure, but what then?

I could sendmessage, but I still haven't found the two control handles I need. Would winSpy be the next step?
Would those handles or ID change if run from another machine?

So many questions, I know. Just looking for a way-in at the moment, trying not to go 'off the rails' following a wrong path.

Thanks to both of you for having a look.

Offline tony74

  • Member
  • *
  • Posts: 35
Re: SendInput on a remote process?
« Reply #5 on: October 18, 2024, 04:05:50 AM »
Well, to update, it looks like this is a QT window, so the qwidgit wrapper and 'signals' might throw a new wrinkle into the mix.
I'll try enumerating child windows of the child window tomorrow and see what happens.

WinSpy didn't come up with anything for controls, so we'll see.
And, yeah, these queries have to happen live for each start because handles change every run, which is expected.

Offline John Z

  • Member
  • *
  • Posts: 865
Re: SendInput on a remote process?
« Reply #6 on: October 18, 2024, 05:00:07 PM »
Hi tony74,

I've also used SendInput on occasion.  It stuffs the keyboard but does not change focus to any other window.  It is important that the window you want to communicate with has the current focus SetFocus(some HWND), without that it probably is not monitoring the keyboard because without focus the keys are meant for some other window.

Also there can be issue with process security levels, MS says "This function fails when it is blocked by UIPI. Note that neither GetLastError nor the return value will indicate the failure was caused by UIPI blocking."  So if QT is as admin and your process is lower it will fail too.

Is this 'QT' program generally available? 

You might try CreateProcess rather than ShellExecute which can have issues with COM processes - or maybe add
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) to your code just in case, and ShellExecute ensures running at the same security level.

Your comment about WinSpy is curious - it didn't work at all?

John Z

Offline John Z

  • Member
  • *
  • Posts: 865
Re: SendInput on a remote process?
« Reply #7 on: October 19, 2024, 02:11:37 AM »
For CreateProcess you will want to pay attention to this from MS docs

“ The calling thread can use the WaitForInputIdle function to wait until the new process has finished its initialization and is waiting for user input with no input pending. This can be useful for synchronization between parent and child processes, because CreateProcess returns without waiting for the new process to finish its initialization. For example, the creating process would use WaitForInputIdle before trying to find a window associated with the new process.”

John Z

Offline tony74

  • Member
  • *
  • Posts: 35
Re: SendInput on a remote process?
« Reply #8 on: October 20, 2024, 07:42:04 PM »
I've since changed my thinking on this because of the way this particular external program works.
Here's why:
 
When run, the external program in question processes any files it finds in it's 'files' directory.
After having processed it's files, the program *doesn't exit*, in case the user has other chores for it.

There would be no guaranteed way for me to know when it had completed file-processing.
The 'files' folder remains untouched, no way to monitor any changes there.
No indication of processing-progress that I can access.
The user may want to use it for other functions before exiting.

In this case, spawning the program via CreateProcess, letting the user interact with it, exit it, and continuing when CreateProcess returns (WaitForSingleObject) , seems the most sensible approach.

 As much as I would have liked to have made this a seamless process for the user, I can see that abrogating user-control of the spawned process should be avoided in this case.

@John Z : good advice on  WaitForInputIdle, will definitely use it. Thanks!
I normally use CreateProcess, I went with ShellExecute earlier to emulate user-desktop click.
WinSpy didn't return any control handles (all 0). Otherwise, it worked as usual.

@MrBCX: MySendkeys() is very cool! A lot better than the MS example I used. Thanks!