1

First the big picture: I'm trying to synchronize two external devices through a console program on Windows 7 (64-bit). That is, Device 1 should trigger Device 2. (For the curious, Device 1 is the NI USB-6525, and I'm using its Change Detection function.)

I'm using GetMessage() in a while loop to wait for a message that's triggered by Device 1. (Since I didn't write the software for that hardware, I can't change the fact that I have to read this message.) Once that message is read, it is dispatched. This dispatch results in a callback to a function that performs a measurement using Device 2 and sets measurementComplete to true. Once the callback returns, the loop ends. Then I perform cleanup and the application exits.

The problem is, the user should also be able to abort while waiting for the message, for example by pressing a key. I tried to implement a check to see whether the received message came from the other thread or the keyboard, but it never recognizes keyboard input:

#include <cstdio>
#include <windows.h>

using namespace std;

bool measurementComplete = false;
BOOL bRet = 0;
MSG threadMessage;

signed long __cdecl callbackFunction(type1 param1, type2 param2) // (pseudo args)
{
    measurementComplete = 1;
    performMeasurement();
    return 0;
}

int main (int argc, char* argv[])
{
    /* Load libraries, set up hardware,
       call NI function that looks for signal from Device 1 in separate thread
       and sends a Windows message upon signal detection */

    while (!measurementComplete) { // measurement has not yet been performed
        // wait for message
        puts("Waiting for message.");
        if ((bRet = GetMessage(&threadMessage, NULL, 0, 0)) != 0) { // if message available
            if (bRet == -1) {
                puts("Error: GetMessage() returned -1. The program will now exit.");
                break;
            } else {
                DispatchMessage(&threadMessage);
                if ((TranslateMessage(&threadMessage)) != 0) // if character message (indicates key press)
                    break;
            }
        }
        puts("Message handled.");
    }

/* perform cleanup */
return 0;
}

I'm new to the Windows API, so I'm not that familiar with it. I'm programming in the Code::Blocks 13.12 IDE and using the GCC. I don't have MFC or any paid products like that from Microsoft, so I can't use MFC functions or classes. Unfortunately, many of the answers I found to similar questions included MFC functions.

From my research on this issue, it seems that the keyboard messages might not have a window to go to. I tried to create a message-only window such as described here, but I always get Error 18: There are no more files upon calling CreateWindowEx(). I can provide that code if requested, but I'm not even sure that I really need to create a window. When I run FindWindowEx(HWND_MESSAGE, NULL, NULL, NULL);, I can see that such a window already exists. I don't know whether the window that was found by that function is one that was somehow automatically created by my binary or whether it's a message-only window that was created by another program running on my computer. Besides, don't I already have a window (the console window)?

Does anyone have any tips for how to direct keyboard input to the messaging system in my console application? Any help would be greatly appreciated.

Community
  • 1
  • 1
Cerran
  • 2,087
  • 2
  • 21
  • 33
  • 2
    That's not possible. The console window is managed by a completely different executable, you can't mess with it. Use ReadConsoleInput() or _getch(). – Hans Passant Jan 15 '14 at 14:15
  • @HansPassant: why not an answer? Explanation and hints in just two lines are still perfectly valid answer, even if so short ;) – quetzalcoatl Jan 15 '14 at 14:18
  • Hi, thanks for your response. Unfortunately, GetMessage() is a blocking function, so I wanted to react to *either* the signal event or a key press with that function. I could use PeekMessage() in the loop instead, but then I'd be busy-idling. I could pause for some time in that loop, but detecting the signal message immediately is key to my synchronization. I understand now that I won't be able to detect input to the console window; would it then make more sense to create my own (message-only) window for that purpose? Or is there another way of handling the situation that I haven't considered? – Cerran Jan 15 '14 at 14:24
  • @HansPassant So, `kbhit()` and `PeekMessage()`? (Or switch to windows executable with a normal message pump?) –  Jan 15 '14 at 14:25

2 Answers2

2

Indeed, 'console app' does not have a 'window', hence they can't receive any messages (in a easy way).

If I remember correctly, you actually can write code that will create an invisible window with message pump, so that all 'messages' will be dispatched. *)

But then, you've got tons of window-related code to research, write and mantain. It's much easier to start with a 'windows app' and hide its window on startup. You will get all the messagepump code for free, from the project code template. From a windowed-app you can also allocate and open a console window, if you like that UI style.

*) You may start a new windowed-app project and peek inside to see how the window is registered with a 'wndproc' routine that will handle the messages. At some point of your main() code, you will need to perform just the same actions that the empty 'windowed' project performs. Also, remember that you will need to actually enter the dispatch loop and that this will block your thread until the dispatchloop is shutdown (with PostQuitMessage, etc) - typically when app gets close "signal" (note that usually it is your code that has to listen and react to that "signals", and you have to call PostQuitMessage when you decide to quit).

You cannot escape that, there is no magic here! Something has to listen for messages. If you want the messages to pass and also have some parallel work, you will need either:

  • two threads, one for message pump, one for job, with all the synchronisation fuss to communicate between them safely
  • or, just one thread running the message pump, and then REWRITE THE JOB to be asynchronous with notifications through messages, so any notifs/callbacks are handled by the messageloop too

Pick what you dislike the least :)

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • Thanks for the response. I'm not worried about the message loop (if that's what you meant by "dispatch loop" blocking. Everything in callbackFunction() is actually run back in the first thread (the same thread as all the other code I posted), and that's perfect because I only ever want it to run once before the program exits. Following your advice, I just created a new "Win32 GUI project" in Code::Blocks and am reviewing the code there. – Cerran Jan 15 '14 at 14:39
  • 1
    @Cerran I have to say this "run once before exit" makes me uneasy. The windows message loop is designed to process all the messages. For instance, in a normal windows app, there are WM_QUIT and WM_EXIT messages that have to do with exit and cleanup. Just issuing say `exit(0)` may be abnormal program termination. –  Jan 15 '14 at 14:52
  • @ebyrob Ok, I'm still getting used to this whole messaging thing, but that's something I can certainly implement. I think I can move my cleanup code to the end of the callback and then add PostQuitMessage() just before my callback returns. I just meant that I only want my callback function (which performs a measurement) to run once before the program exits. In the future, I may change this to make it more user friendly, but I want to get it working this way first as a proof-of-concept for the synchronization. – Cerran Jan 15 '14 at 15:10
  • 1
    I'm also heavily confused with the "run once" thing. Either you mis-explained something, or you are trying to achieve something 'unnatural'. As to the "dispatch loop", I used a proper term. Dispatch loop and message loop are the same thing. It is because the loop is structured like this: `loop{read message, translate it, dispatch it}`. The most important parts are 'read' and 'dispatch' and this forms the alternate name. "Dispatching" is a phase when the chain of wndproc handlers inspects the message and call the various handlers according to the type of the message. – quetzalcoatl Jan 15 '14 at 15:11
  • @quetzalcoatl No worries, I wasn't correcting you; since I'm new to the Windows API and had only seen the term "message loop" used thus far, I just wanted to confirm that we were talking about the same thing. The "run once" idea is that any time someone wants to perform a synchronized measurement, they execute this program. Since there will be times when someone will want to perform several consecutive measurements, I plan to change this later. Does that help? Is there something wrong with that? – Cerran Jan 15 '14 at 15:31
  • No, nothing wrong. It's just that usually the message-loop is the last thing that your 'main' executes, and after quitting the loop program usually terminates. That's the standard/typical implementation that you will also find in the template code. Of course, you can do it just like you said now, that's just 'main' and a loop, pure code. If I got it well, you would now listen to keyboard messages to detect the "stop-and-measure NOW" keyboard signal (say, SPACE key) and you'd probably PostQuitMessage upon such event to terminate the loop and do the measurement? (continued..) – quetzalcoatl Jan 15 '14 at 15:39
  • It's absolutely ok. But consider also not breaking the loop at all. If you'd replace the "when SPACE then PostQuitMessage" with your "measurement code from the end of the program" then you'd instantly get the many-measurement version, just like that. Well, checking ESC button to be able to quit the program would help then too ;) Anyways, it's all ok with your idea, provided that you really want to perform the measurement once and only after the SPACE. Actually, you can probably get it too (and much simplier) with plain console and ENTER key - `while(readline() == "") print(measure())` etc. – quetzalcoatl Jan 15 '14 at 15:43
  • Hmm, I have to see if I can clarify my original question. What happens is this: When Device 1 sends a signal, I need to start the measurement. How do I know when Device 1 sends a signal? It creates a message. So I'm waiting for that message with GetMessage. If that message is received, the program should run the measurement and then exit. But what happens if the user changes his mind or realizes the signal will never come? He should be able to exit the program (without performing a measurement) by pressing a key, and that keypress should result in a message that will be caught by GetMessage. – Cerran Jan 15 '14 at 16:21
  • Basically, I was trying to do something like this: while (GetMessage()) { if (TranslateMessage()) // if keyboard input break; DispatchMessage(); } [TranslateMessage](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644955%28v=vs.85%29.aspx)'s return is non-zero if the message is WM_KEYDOWN. This is probably neither the most elegant nor the most fool-proof solution, but I thought it would work. But first I need to figure out how to route messages originating from keyboard input to GetMessage. – Cerran Jan 15 '14 at 16:32
  • `figure out how to route messages originating from keyboard input to GetMessage` - you don't do that. The Windows sytem does it. System receives keyboard events (from devices), packs them into messages, and then pushes that messages to the message queue of your window (visible or not), from which your message loop reads them via Peek/GetMessage. Other sources like async network or disk I/O or screen-button clicks can send their messages to your app's queue too. Of course you can also sent extra messages to that queue whenever you want, but for listening to the keyboard it is not needed. – quetzalcoatl Jan 15 '14 at 17:01
  • @quetzalcoatl I did some testing. It seems an "invisible window" in console app may only get keyboard events when the invisible window has focus? –  Jan 15 '14 at 18:36
  • That does not surprise me at all and I expected that, but frandly I forgot to mention that problem. See http://stackoverflow.com/questions/5065817/listen-for-a-key-when-the-application-is-not-focused and http://www.codeproject.com/Articles/9351/Background-applications-listening-for-keyboard-act and of course [SetWindowHookEx](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990.aspx). Sorry for mixing C# into discussion, but it is easy to find info there. From first link - `RegisterHotKey`. From second link - `ListeningWindow` see how listener via RAWINPUTDEVICE. etc. – quetzalcoatl Jan 15 '14 at 18:52
  • @ebyrob: All the above solutions still require to provide a HWND for targetting the receiver, so an invisible window is still required to pump/dispatch notification messages. The RAWINPUTDEVICE might be close to DirectX DInput, but it still uses messages, while DX would probably not. – quetzalcoatl Jan 15 '14 at 18:57
  • 1
    @quetzalcoatl there is a newer interface for disabled computer users and GUI testers that can get such information (at least in C#). However, at this level I think "just spin and use `kbhit()`" will win out. –  Jan 15 '14 at 19:17
0

Here is a normal message loop taken from the MSDN site:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644928%28v=vs.85%29.aspx

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
} 

Ignore the scary fact that BOOL has values other than true and false in play here, and notice that the return of GetMessage() is the outermost control of when the message loop will quit. That is natural for Win32. Putting something else around it (like a test for whether some measurement has been taken or not) disrupts this simple pattern.

Note: As has been discussed earlier, it's more than acceptable (and expected) to have your DispatchMessage() implementation raise a message that will kick off application exit. (Say by issuing PostQuitMessage() on the main thread)

Finally: With no filters in a Win32 app, you should certainly be getting plenty of keyboard messages coming out of GetMessage(). Which isn't to say TranslateMessage() and/or DispatchMessage() default implementations might not be hiding them from you further in.

detecting the signal message immediately is key to my synchronization

Seems like that's going to be dicey from anything approaching a real-time standpoint since those windows messages, which are apparently your only interface, get queued up with any and all GUI messages on the system.

Basically, timing is tough: How to make thread sleep less than a millisecond on Windows

Community
  • 1
  • 1
  • If cerran wants as 'real-time' as possible, then I'm quite sure i.e. DirectX DInput and its callbacks have better timing than win-messages. Also, let's check the null hypothesis: cerran wants to precisely **synchronize measurements** with **keyboard**? Like three people at three machines are to press space simultaneously and get 3 readings? Unless you heavily trained (Quake, etc;)), a typical quick reflex delay of ~150-200ms will still be far worse than context switching and all win-messaging.. – quetzalcoatl Jan 15 '14 at 17:09
  • @quetzalcoatl I don't think he's using keyboard for sychronization. 2 devices at 2 locations are sitting there. One fires a signal which is translated to a WINDOWS event for some reason. The other device would like to be notified through some API right away that this happened. I believe the keyboard event was just a secondary input that could cancel the whole process or modify it. –  Jan 15 '14 at 17:21
  • Your understanding of the situation is correct, ebyrob. The keyboard is not used for synchronization; I'd like to use it to exit the program in a normal way. Right now, there are only two ways to exit the program: send a signal from Device 1 or ctrl+c. – Cerran Jan 15 '14 at 18:13
  • I agree that my design of breaking out of the message loop is apparently abnormal for such an application. I don't yet know how to make the receipt of a message such as WM_KEYDOWN trigger the execution of PostQuitMessage. Also, it seems that doing so will require a second thread, and I have no experience in multi-threading, so that's another challenge to overcome. I really appreciate the guidance, though, and I'll see if I can get any further using the template Win32 app code tomorrow. Unfortunately I can't access the code any longer today. – Cerran Jan 15 '14 at 18:23