1

I am trying to register an invisible window in a Win32 console application. My goal is to listen for Raw Input in the WindowProc to (1) display it on the console, and (2) perform additional computation e.g. sending information over a Web socket. I followed this CodeProject article, but my WNDCLASSEX registration seems to fail.

Here is the code that I have:

approach 1 -- registration does not seem to work

My main function

WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.lpfnWndProc = NVTouch_WindowProc;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.lpszClassName = L"myclass";
bool isClassRegistered = false;
isClassRegistered =  RegisterClassEx(&wndclass);
if (isClassRegistered) //1
{
    HWND window = CreateWindow(wndclass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(0), NULL);
    if (window)
    {
        ShowWindow(window, SW_SHOWDEFAULT);
        MSG msg;
        while (GetMessage(&msg, 0, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

My WindowProc function:

static LRESULT CALLBACK NVTouch_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    std::cout << "window Proc";
    bool registrationStatus = false;
    switch (message)
    {
    case WM_CREATE:
        registrationStatus  = registerTouchpadForInput();
        break;
    case WM_DESTROY:
        std::cout << "destroying application.";
        PostQuitMessage(0);
        break;
    case WM_INPUT:
        std::cout << "input!";
        break;
    default:
        std::cout << "default.";
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

When I put breakpoints in Visual Studio 2019, I notice my code jumps from //1 to //2 with the registration status being false.

I had a similar implementation based on this article where registration was successful, but I wasn't able to listen for the WM_INPUT messages.

approach 2 -- window creation works, but WM_INPUT messages are not read

int main()
{
    WNDCLASSEX wndclass = {
        sizeof(WNDCLASSEX),
        CS_DBLCLKS,
        NVTouch_WindowProc,
        0,
        0,
        GetModuleHandle(0),
        LoadIcon(0,IDI_APPLICATION),
        LoadCursor(0,IDC_ARROW),
        HBRUSH(COLOR_WINDOW + 1),
        0,
        L"myclass",
        LoadIcon(0,IDI_APPLICATION)
    };
    bool isClassRegistered = false;
    isClassRegistered =  RegisterClassEx(&wndclass);
    if (isClassRegistered)
    {
        std::cout << "window class registered!";
        HWND window = CreateWindowEx(0, L"myclass", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
        if (window)
        {
            ShowWindow(window, SW_SHOWDEFAULT);
            MSG msg;
            while (GetMessage(&msg, 0, 0, 0))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }
}

I want to ideally register the class and display Raw Input on the console (approach 1), but being able to do that modifying approach 2 code would also work.

Why is my registration failing in approach 1? Why am I not able to listen for WM_INPUT messages in approach 2?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    You need to zeroise `wndclass` before using it and/or set all the values in it not leave them floating. – Richard Critten May 29 '20 at 14:50
  • 1
    Mind the [RegisterRawInputDevices](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerrawinputdevices) part in the [WM_INPUT](https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-input) docs. – dxiv May 29 '20 at 18:04
  • Yes. I have a function to RegisterRawInput, which works. I did not include it hear for brevity. It is called in the registerTouchpadForInput() function. – Venkatesh Potluri May 30 '20 at 00:13
  • Probably not the issue, but you are leaking resources. From [WM_INPUT](https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-input): *"The application must call [DefWindowProc](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowprocw) so the system can perform cleanup."* – IInspectable May 30 '20 at 06:41

1 Answers1

1

In approach 1:

You need to initialize wndclass:

WNDCLASSEX wndclass = { 0 };

Otherwise, the uninitialized part defaults to random values(Undefined behavior, usually like 0xcccccccc), and RegisterClassEx will failed with error code: 87.

In approach 2:

You initialized all the members in wndclass, so the RegisterClassEx succeed. I cannot reproduce the issue with approach 2, The problem may be in the registerTouchpadForInput.

Noticed that this function has no parameters, but RegisterRawInputDevices need to set RAWINPUTDEVICE.hwndTarget, maybe you have not set the target window, if you set to NULL, according to the document, you must focus your keyboard on your window To receive the message.

In addition, in approach 2, the program will create a visible window. For generating an invisible window, create a Message-Only Windows as in approach 1.

sample:

#include <windows.h>
#include <iostream>

using namespace std;
BOOL registerTouchpadForInput(HWND hWnd)
{
    RAWINPUTDEVICE rid;
    rid.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK;
    rid.usUsagePage = 1;                            // raw keyboard data only
    rid.usUsage = 6;
    rid.hwndTarget = hWnd;
    return RegisterRawInputDevices(&rid, 1, sizeof(rid));
}
static LRESULT CALLBACK NVTouch_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    std::cout << "window Proc";
    BOOL  registrationStatus = false;
    switch (message)
    {
    case WM_CREATE:
        registrationStatus = registerTouchpadForInput(hwnd);
        break;
    case WM_DESTROY:
        std::cout << "destroying application.";
        PostQuitMessage(0);
        break;
    case WM_INPUT:
        std::cout << "input!";
        return DefWindowProc(hwnd, message, wParam, lParam);
    default:
        std::cout << "default.";
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

int main()
{
    WNDCLASSEX wndclass = {
        sizeof(WNDCLASSEX),
        CS_DBLCLKS,
        NVTouch_WindowProc,
        0,
        0,
        GetModuleHandle(0),
        LoadIcon(0,IDI_APPLICATION),
        LoadCursor(0,IDC_ARROW),
        HBRUSH(COLOR_WINDOW + 1),
        0,
        L"myclass",
        LoadIcon(0,IDI_APPLICATION)
    };
    bool isClassRegistered = false;
    isClassRegistered = RegisterClassEx(&wndclass);
    if (isClassRegistered)
    {
        std::cout << "window class registered!";
        //HWND window = CreateWindowEx(0, L"myclass", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
        HWND window = CreateWindow(wndclass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(0), NULL);
        if (window)
        {
            ShowWindow(window, SW_SHOWDEFAULT);
            MSG msg;
            while (GetMessage(&msg, 0, 0, 0))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }
}
Drake Wu
  • 6,927
  • 1
  • 7
  • 30
  • Hi,@Venkatesh Potluri, is this sample helpful to you? Please feel free to [accept](https://stackoverflow.com/help/accepted-answer) the answer if it does solve the issue. – Drake Wu Jun 03 '20 at 01:50
  • You were right. I was setting the hwndTarget to NULL. I interpreted the documentation as setting it to null would follow keyboard focus and give me input messages from the window having the keyboard focus. Interestingly though, setting the hwndTarget to NULL resulted in a Win32 error of invalid parameter. making that changes results in my approach one working, and your example shows how to do this in approach two. Thanks! – Venkatesh Potluri Jun 03 '20 at 22:34