0

I have tested making a default window, and I have tested a message-only window both on a separate thread from the main thread.

The goal is to receive WM_INPUT from HID devices from outside the main thread. The reason is that the main thread loop will run at a much slower rate than the needed rate to process inputs.

Both windows seem to work so long as the main window of the main thread isn't the active window.

What are the possible solutions for this issue?


Edit:

I didn't add the code because it is basically boilerplate code, but since many commented wanting to see some code, I will add it as it might really be helpful.

//FIRST: CLASS REGISTRATION:

WNDCLASSEXW wcex;
    
wchar_t local_name[] = L"ClassGenericName";
wcsncat(local_name, 
    std::to_wstring(GetCurrentThreadId()).c_str(), 
    std::to_wstring(GetCurrentThreadId()).size());
//You can ignore naming process as it was made to make more than one window for more than one thread, just for testing purposes.
    
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINAPITEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINAPITEST);
wcex.lpszClassName = local_name;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
RegisterClassExW(&wcex);
    
hWnd = CreateWindowEx(0, wcex.lpszClassName, L"no title", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0);
    
ShowWindow(hWnd, 3);//Testing, I switch it on and off.
UpdateWindow(hWnd); 
    
    
//SECOND: DEVICE REGISTRATION:
    
RAWINPUTDEVICE RID;
RID.usUsagePage = 0x01;
RID.usUsage = 0x05;
RID.dwFlags = 0;
RID.hwndTarget = 0;
RegisterRawInputDevices(&RID, 1, sizeof(RID));
    
//THIRD: THREAD FUNCTION MESSAGE LOOP.
    
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINAPITEST));
MSG msg;
while (GetMessageW(&msg, nullptr, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
    
//FOURTH: WINDOW PROCEDURE MESSAGE PROCESSING.
    
//I don't think I need to add this as it is boilerplate and tested to work all the time. If wanted I'd add it tho.
    
//FIVE: Thanks for helpful comments. My bad for not adding code ahead of time.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Physician
  • 483
  • 2
  • 7
  • Message loops don't have a "rate" at which they run, so I'm not understanding that part of your question. Only one window can receive raw input for a given device class, per the docs for RegisterRawInputDevice. The WM_INPUT message wParam tells you whether the app is in the foreground or not, which implies that it can receive input when not the active window. More help might be available if you share your code that calls RegisterRawInputDevice. – Paul Dempsey Apr 13 '23 at 18:33
  • Any thread can create windows and a message loop. `WM_INPUT` doesn't care which thread receives it, so if your worker thread is not receiving messages when the main window is active then there is something wrong with your code (which you did not show). Please provide a [mcve]. Are you specifying the `RIDEV_INPUTSINK` flag when registering the devices? – Remy Lebeau Apr 13 '23 at 18:54
  • @PaulDempsey Sorry for not providing enough details. The main loop rate is part of the Unreal Engine main loop, and it has a specific rate depending on the amount of work done, and on the speed of rendering thread job as well. So I can't touch that loop to make it faster. – Physician Apr 13 '23 at 19:15
  • @RemyLebeau Sorry Remy, my bad I haven't provided good details, I edited the question. – Physician Apr 13 '23 at 19:16
  • Perhaps Unreal Engine handles `WM_ACTIVATE` by calling `RegisterRawInputDevice` and replacing your registration. – Ben Voigt Apr 13 '23 at 19:17
  • @BenVoigt I am coding a completely winapi style code within UE4. I spawn the threads and create the windows all in non-UE4 style or API. So I don't think UE4 could effect the behavior, but I might be mistaking. – Physician Apr 13 '23 at 19:19
  • @BenVoigt Raw Input doesn't allow you to intercept input (unlike low-level keyboard/mouse hooks). As such, there is no concept of *"replacing"* a client. Raw input events are delivered to **all** clients, all the time. – IInspectable Apr 13 '23 at 19:39
  • @IInspectable: That contradicts [Paul's statement](https://stackoverflow.com/questions/76008562/can-a-window-receive-messages-without-being-the-active-window?noredirect=1#comment134056011_76008562) that "Only one window can receive raw input for a given device class". Obviously he meant "only one at a time". – Ben Voigt Apr 13 '23 at 22:02
  • 3
    @Physician your 2nd `Edit` should be [posted as an answer](https://stackoverflow.com/help/self-answer) instead. However, I do see one problem with the remaining code. You have a buffer overflow on `local_name` as it is not large enough to hold your string and thread id. Its size is only 17 chars (including the null terminator), you need to increase that to at least 27 chars, or else use `std::wstring` instead, eg: `std::wstring local_name = L"ClassGenericName" + std::to_wstring(GetCurrentThreadId()); ... wcex.lpszClassName = local_name.c_str();` – Remy Lebeau Apr 13 '23 at 22:52
  • @RemyLebeau great point, I will change it all as that was a very quick attempt at getting it works. Thanks – Physician Apr 14 '23 at 09:13
  • @BenVoigt *"Obviously he meant "only one at a time"."* - I'm not going to speculate what anyone meant. Regardless, that statement would be equally wrong. The actual contract is: No more than one window per device class, **per process**, at any given time. Regardless, the OP got what they asked for, and since found out that what they asked for wasn't what they needed. – IInspectable Apr 14 '23 at 16:38
  • @IInspectable: Since OP's code is running in the Unreal engine process, just on a different thread, the "one at a time" constraint would apply. – Ben Voigt Apr 14 '23 at 17:25

1 Answers1

1

Solved: the problem was in setting the target window handle for HID when registering. I set it to null, which makes the active window become the target. Instead, I should set it to the separate thread's window. I did and it worked.

Physician
  • 483
  • 2
  • 7