简体   繁体   中英

Is it possible to create a message loop without creating a window in C++

I was working with some Bluetooth library and for some Bluetooth callback, it is necessary to use Windows message loop. But as per my requirement, I need to create a normal C++ program without any GUI. Is it possible to create a message loop without a window?

main(){
    Discovery disc;
    disc.startDiscovery();
}


Discovery::startDiscovery(){
  __hook(&CallBackFromLibrary::OnDiscoveryStarted, &obj, &Discovery::OnDiscoveryStarted);
  __hook(&CallBackFromLibrary::OnDiscoveryComplete, &obj, &Discovery::OnDiscoveryComplete);  
}


apiObject.discoverBluetoothDevices();

In this sample code, I should receive callbacks as OnDiscoveryStarted and OnDiscoveryComplete after calling apiObject.discoverBluetoothDevices().

Since they are using message loops for callbacks, I only got the callback on GUI application. How to receive the callback using message loops as the library documentation says message loops are required.

Yes, it's possible--Windows will associate a message queue with a thread when/if the thread tries to use one. There is a little bit of a race condition when doing this though. To post to the thread's message queue, you use PostThreadMessage . But, the thread won't have a message queue until it calls a function to attempt to read from the message queue (ie, Windows doesn't create a message queue for the thread until the thread tries to use it).

To prevent a race condition, you usually want to do something on this order:

  1. cal CreateEvent to create an event (unsignalled)
  2. call CreateThread , passing it the handle of the event to be passed to the new thread
  3. Have the parent wait for the event
  4. have the child call PeekMessage (not expecting any result, since the queue hasn't been created yet--but this forces its creation).
  5. Have the child signal the event
  6. Now the parent will resume, and can use PostThreadMessage , secure in the "knowledge" that the child has a message queue, so this will work.

Another possibility is for the child to create a window, but leave it hidden. The obvious advantage here is compatibility with code that expects to post/send messages to a normal message queue using SendMessage , PostMessage , SendMessageTimeout , and so on, instead of the special PostThreadMessage . The other obvious advantage is that it avoids the thread-message-queue dance outlined above.

When you get down to it, the primary characteristic of a Windows "window" isn't something on the display--it's a message queue, and what shows up on the display is just drawing that's done in response to some specific messages. A hidden window isn't a whole lot more than a message queue.

Message loop in non-GUI thread:

#include "stdafx.h"
#include <Windows.h>
#include <thread>
#include <iostream>
using namespace std;

void ThreadFunction()
{
    MSG msg;
    BOOL result;

    for (;;)
    {
        result = GetMessage(&msg, nullptr, 0, 0);

        if (result <= 0)
        {
            break;
        }

        cout << msg.message << " " << msg.wParam << " " << msg.lParam << endl;

        //TranslateMessage(&msg);
        //DispatchMessage(&msg);
    }
}

int main()
{
    thread t(ThreadFunction);
    HANDLE h = t.native_handle();
    DWORD dw = GetThreadId(h);

    PostThreadMessage(dw, WM_APP + 1, 1, 2);
    PostThreadMessage(dw, WM_APP + 2, 10, 20);
    PostThreadMessage(dw, WM_QUIT, 10, 20);

    t.join();
    return 0;
}

Update :

According to the comment, this code is not compiled in gcc. Trying to reproduce this in VC++, I found that the program doesn't work in x64. This updated solution hopefully solves both problems:

#include "stdafx.h"
#include <Windows.h>
#include <thread>
#include <iostream>
using namespace std;

DWORD threadID{};


void ThreadFunction(HANDLE event_handle)
{
    MSG msg;
    BOOL result;

    threadID = GetCurrentThreadId();
    SetEvent(event_handle);

    for (;;)
    {
        result = GetMessage(&msg, nullptr, 0, 0);

        if (result <= 0)
        {
            break;
        }

        cout << msg.message << " " << msg.wParam << " " << msg.lParam << endl;
    }
}

int main()
{
    HANDLE e = CreateEvent(nullptr, FALSE, FALSE, nullptr);

    thread t(ThreadFunction, e);

    WaitForSingleObject(e, INFINITE);
    CloseHandle(e);


    PostThreadMessage(threadID, WM_APP + 1, 1, 2);
    PostThreadMessage(threadID, WM_APP + 2, 10, 20);
    PostThreadMessage(threadID, WM_QUIT, 10, 20);

    t.join();
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM