Search code examples
c++node.jswinapiv8libuv

How do I pump window messages in a nodejs addon?


In a Windows nodejs addon, I've created a window for the purpose of receiving messages.

Handle<Value> MakeMessageWindow(const Arguments &args) { // exposed to JS
    ...
    CreateWindow(L"ClassName", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0);
    ...
}

I have a wndproc function.

Local<Function> wndProc;
LRESULT APIENTRY WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // pack up the arguments into Local<Value> argv
    wndProc->Call(Context::GetCurrent()->Global(), 3, argv);
}

Now I need to pump messages. Normally, you'd do something like

MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) 
{
     TranslateMessage(&msg);
     DispatchMessage(&msg);
}

...but that won't work since it would just block the v8 event loop.

How do I pump Windows messages in a manner that won't block v8 and allows me to invoke a JS function when my window receives messages?

I presume libuv will play a role, but I'm unsure exactly how to safely invoke a JS function from C running on a separate thread, especially since uv_async_send is not guaranteed to invoke a callback every time you call it, and I need to ensure that my JS callback is called every time a window message is received.


Solution

  • My mistake was trying to create the window on the V8 thread. Instead, uv_thread_create should be used to call a function that creates the window on a new thread and then proceeds to do its own message pump loop.

    The wndproc function then needs to save received messages into a queue in a thread-safe manner and then use uv_async_send to notify the V8 thread that messages have arrived.

    A function on the V8 thread (which was passed to uv_async_init) is then invoked after messages are enqueued. The function (thread-safely) pops each pending message off the queue and invokes the JS callback.