Search code examples
c++winapiwindow-messages

Newly created desktop doesn't receive keyboard events


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?


Solution

  • 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