7

I have created a small program which launches itself in a new desktop.

HDESK hDesktop = ::CreateDesktop(strDesktopName.c_str(),
                                    NULL, // Reserved
                                    NULL, // Reserved
                                    0, // DF_ALLOWOTHERACCOUNTHOOK
                                    GENERIC_ALL,
                                    NULL); // lpSecurity
::SetThreadDesktop(hDesktop);

Later on, started another application on that desktop using the following lines:

PROCESS_INFORMATION pi = { 0 };
STARTUPINFO         si = { 0 };

si.cb = sizeof(si);
si.lpDesktop = &strDesktop[0];
if (FALSE == ::CreateProcess(pathModuleName.file_string().c_str(), L"abc def", NULL, NULL,      FALSE, 0, NULL, NULL, &si, &pi))
    return false;

DWORD dwWaitRes = ::WaitForSingleObject(pi.hProcess, INFINITE);

pathModuleName is a self location obtained by GetModuleFileName(NULL).

The newly created application obtains a HWND to another window and sends window messages using the following commands:

// bring window to front
::SetForegroundWindow(hwnd);

// set focus so keyboard inputs will be caught
::SetFocus(hwnd);
::keybd_event(VK_MENU, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
...

So basically application A on desktop DEFAULT is starting application B on desktop X, which obtains an HWND to another application C started on the same desktop X.

My problem is that keyboard events coming from application B on desktop X are not being triggered in application C. Only if I use SwitchDesktop(B), then events are triggered and code is executed properly.

What am I missing?

igal k
  • 1,883
  • 2
  • 28
  • 57

1 Answers1

7

You are trying to simulate user input on a desktop that is not active on the physical console (screen, mouse, keyboard), which is not likely to work, and why SwitchDesktop() makes it work. According to the documentation:

SwitchDesktop function

Makes the specified desktop visible and activates it. This enables the desktop to receive input from the user.

keybd_event(), mouse_event(), SendInput(), they all simply generate and store input messages into the same input queue that the physical mouse/keyboard post their messages to. The input system does not know the difference between user input and synthesized input when dispatching the input messages to applications.

Raymond Chen touched on that in his blog:

How do I simulate input without SendInput?

SendInput operates at the bottom level of the input stack. It is just a backdoor into the same input mechanism that the keyboard and mouse drivers use to tell the window manager that the user has generated input. The SendInput function doesn't know what will happen to the input. That is handled by much higher levels of the window manager, like the components which hit-test mouse input to see which window the message should initially be delivered to.

He also posted a nice little diagram in another blog article showing where SendInput() sits in relation to the input queue:

When something gets added to a queue, it takes time for it to come out the front of the queue

diagram

Paul
  • 6,061
  • 6
  • 39
  • 70
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Another example of the same problem is that simulating input (and even some window messages) won't work as expected when the computer is locked. – Harry Johnston Jan 08 '15 at 21:42
  • Right, because when the computer is locked, it is displaying a separate (secure) desktop to the user, so only that desktop can process input events until the computer is unlocked. – Remy Lebeau Jan 09 '15 at 00:12
  • Thanks for the detailed response, I've heard there is s away by creating a non-interactive window station with an interactive desktop. doing that, I'm able to start a hidden process on a hidden desktop. – igal k Jan 09 '15 at 19:54
  • Has any of this changed with Windows 10? It would be nice to have an API call that we could specify the Desktop to SendInput to. – pcunite Aug 29 '15 at 18:18