2

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.

krishnakumarcn
  • 3,959
  • 6
  • 39
  • 69
  • 1
    I think it's possible. What's your (example, as a [mcve]) code for creating a message loop? – user202729 Aug 21 '18 at 06:43
  • A message loop is not dependent on a window, but a window is dependent on a message loop. But what kind of messaging is the library using that doesn't involve sending messages to a window? Only 2 possibilities I can think of - thread messages, or an apartment-threaded ActiveX/COM object.. – Remy Lebeau Aug 21 '18 at 07:34
  • 1
    In all likelihood that Bluetooth library creates a hidden window. Very common. So you have to pump a message loop yourself to keep it going, no different from what you'd do if you create a window yourself. – Hans Passant Aug 21 '18 at 08:21
  • That or it's designed to work with or without a window, and prefers common code for both use cases (which seems reasonable). I've also written libraries before that use (and, to be fair, also provide) a message loop without necessarily any threading: it makes for some easy interactions between components. – Lightness Races in Orbit Aug 21 '18 at 10:09

2 Answers2

6

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 (i.e., 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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • "*To prevent a race condition, you usually want to do something on this order*" - an event object via `CreateEvent()`, or a Condition Variable via `InitializeConditionVariable()` or `std::condition_variable`, would be more appropriate to use than a mutex. You can't signal a mutex. – Remy Lebeau Aug 21 '18 at 15:59
  • How can I hide the window? – kittu Jun 16 '20 at 18:03
  • @kittu: `ShowWindow(yourWindow, SW_HIDE)` will hide a window. – Jerry Coffin Jun 16 '20 at 22:40
3

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;
}
Alex F
  • 42,307
  • 41
  • 144
  • 212
  • I get error: `error: invalid conversion from 'std::thread::native_handle_type' {aka 'unsigned int'} to 'HANDLE' {aka 'void*'} [-fpermissive] HANDLE h = t.native_handle();` – kittu Jun 16 '20 at 12:32
  • Cannot reproduce this in VC++ compiler. But I see that this code doesn't work in x64. I will post updated solution later. – Alex F Jun 16 '20 at 13:46
  • Thanks I will check it out – kittu Jun 16 '20 at 14:39