LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Help with threads and Windows messaging

I'm writing an application that communicates with another application via SendMessage.  Responses are returned using the WM_COPYDATA message which I interecpt with a callback installed as follows:

 

    static HWND ClientWindowHandle = 0;

    GetPanelAttribute (MainPanelHandle, ATTR_SYSTEM_WINDOW_HANDLE, (intptr_t *)&ClientWindowHandle);
    InstallWinMsgCallback (MainPanelHandle, WM_COPYDATA, ProcessWmCopyDataMessage, VAL_MODE_INTERCEPT, NULL, (intptr_t *)&ClientWindowHandle);

In some cases my applicaiton needs to send a message to the other app and wait for the repsonse to come back.  I've implemented this using a flag to indicate when the response has arrived, something like this:

 

    void GetInfoFromOtherApp(void)

    {

        // prepare message data

        ....

        SendMessage(ServerWindowHandle, WM_COPYDATA, (WPARAM)ClientWindowHandle, (LPARAM)&MessageData);

        WaitingForResponse = TRUE;

        while (WaitingForResponse)

        {

            ProcessSystemEvents();

        };

        // process response

    }

 

    int CVICALLBACK ProcessWmCopyDataMessage (int panelHandle, int message, unsigned int* wParam, unsigned int* lParam, void* callbackData)
    {

        // save data in buffer

       .....
       WaitingForResponse = FALSE;

    }

 

This works fine as long as I initiate the call to GetInfoFromOtherApp from a thread separate from the callback thread (e.g., on a UI event, timer event, etc.).  However, there are some cases where I need to send more messages once a reposnse arrives, something like this:

 

    int CVICALLBACK ProcessWmCopyDataMessage (int panelHandle, int message, unsigned int* wParam, unsigned int* lParam, void* callbackData)
    {

        // save data in buffer

        .....
        WaitingForResponse = FALSE;

        GetInfoFromOtherApp();

    }

 

My problem is that if I call GetInfoFromOtherApp while I'm in the callback thread, my while loop doesn't work because the callback is never invoked.  Put another way, apparently the callback can't be called reentrantly.

 

I'm not that familar with setting up separate threads in CVI, and I'm not even sure if that is the right way to handle this problem or if it is OK to call a Windows callback handler reentrantly.  I've tried to think of other ways to handle this, llike setting a timer in the first response and having the timer place the second call to GetInfroFromOtherApp, but that's pretty ugly (especially since I need to call GetInfroFromOtherApp about 30 times to get all the data I need).

 

I'd appreciate any suggestions on ways to attack this problem.

0 Kudos
Message 1 of 8
(4,700 Views)

Hey Tony -

 

A couple quick comments:

 

  1. Are you sure you have multiple threads?  The cases you mentioned (unless the timer event is from an Asynchronous Timer, and not the UI Timer) are actually all on the UI thread.  Unless you are explicitly creating threads (with CmtScheduleThreadPoolFunction or CreateThread), or using the Async Timer API, my guess is that your program is actually not multithreaded.
  2. The code that you present may just be a simplified version of what you actually have, but in the second case where you send another message, you're actually infinitely recursing.  You send the message and wait for the response.   While waiting, the internals of ProcessSytemEvents catch the WM_COPYDATA message, and pass it on to your handler.  This means that at this point, you're still not out of your while loop.  But while in the WM_COPYDATA handler, you call the GetInfo... function again, once again entering the loop.  If this is actually how your code looks, you should be able to see this by breaking into your program and looking at the stack trace in the variables window.  This would cause things to look like the callback isn't being called to exit the loop.

 

There doesn't seem to be anything about your setup that requires multiple threads.  All it looks like you need is a little different logic concerning when you exit the loop and when you call GetInfo... from within the WM_COPYDATA handler.

 

NickB

National Instruments

0 Kudos
Message 2 of 8
(4,697 Views)

NickB,

 

I haven't created any additional threads.  I just wasn't sure if the MW_COPYDATA callback was handled in the same thread as my own UI events.

 

The example is over simplified.  There is no infinite recursion.  What's really going on is that I ping the other app during my init.  Once I get the response through the WM_COPYDATA message, I need to get a bunch of data from the other app.  So the process looks something like this:

 

1) Send ping message

2) Start UI

3) On ping response call the GetInfo function multiple times

 

Each message sent to the other app includes some ID info specifying what data to get and the same ID is returned with the response so I know what data was recevied (I didn't show all this is the example code).  My WM_COPYDATA callback copies the data to temporary storage and sets the flag so that the GetInfo function can then process that data.  Once that data is processed, another call is issued to GetInfo.  This continues until all the info has been received, at which point we return from the initial WM_COPYDATA callback that started this while process. 

 

To restate my problem, here is what I've found:

 

1) If I call my GetInfo function from a button event, then the WM_COPYDATA callback is called while I'm sitting in my while loop and everything works perfectly.

2) If I call my GetInfo function from the WM_COPYDATA callback then the callback does not get called while I'm sitting in my while loop and my program freezes.

 

I verified the problem in two ways.  First I placed a breakpoint in the WM_COPYDATA callback.  The breakpoint is hit once during the ping response but never again.  Once it got stuck in the while loop wiating for the second callback, I paused the code and then set the execution point to the end of the function that processes the ping response (i.e., the end of the WM_COPYDATA callback).  As soon as that function exited, I hit the callback breakpoint.

 

0 Kudos
Message 3 of 8
(4,662 Views)

@tonyg 614 wrote:

2) If I call my GetInfo function from the WM_COPYDATA callback then the callback does not get called while I'm sitting in my while loop and my program freezes.

 


 

I'm sure someone from NI will correct me if I'm wrong, but I'm pretty sure that the CVI callback mechanism is such that it cannot be called from within itself - it must terminate before it can be re-invoked. You need to re-arrange your code accordingly.

 

JR

0 Kudos
Message 4 of 8
(4,649 Views)

JR,

 

That's the conclusion I've been hoping to avoid.  The one thing I don't get is that if everything in my app is handled in the same thread, then why does it work if I call my GetInfo function from a buton callback but not when I call it from the WM_COPYDATA callback?  If both callbacks are handled in the same thread, then neither way should work or both should work.

0 Kudos
Message 5 of 8
(4,640 Views)

After looking into this a little bit further (and refreshing myself on the details of BroadcastSystemMessage), I think I might be able to explain what's happening in such as way that you can resolove it in your code.  The short answer is that you don't need the while loop.  BroadcastSystemMessage will not return until the server has sent you a message back, and you have in turn processed that message. 

 

Lets break that down a little bit.

 

BroadcastSystemMessage *sends* (not posts) a message to all applications.  We know that the SendMessage function is serial - meaning it does not return until the thread owning the window the message is sent to processes the message.  This means that BroadcastSystemMessage will not return until all applications either respond to the message or timeout (depending on the flags you pass for first parameter).

 

When the server receives the message you've broadcast, it calls SendMessage (..., WM_COPYDATA, ..., ...) back to your window.  Meanwhile, your client is still inside of BroadcastSystemMessage, which is likely (who knows how Microsoft actually implements it...) inside SendMessage.  When you read the documentation for SendMessage, you see that nonqueued messages (sent messages) will be processed by the implementation of SendMessage, which means that your WM_COPYDATA handler will actually be called by the internals of BroadcastSystemMessage.  Thus, there is no need to wait for a response from the server - this is done implicitly by BroadcastSystemMessage.

 

Hopefully you will be able to see this by stepping through your code.  I'm hopeful that understanding how this works will allow you to architect your code in a way that solves the issue for you.

 

NickB

National Instruments

0 Kudos
Message 6 of 8
(4,630 Views)

As I've thought a little more about this, I've realized it may be unwise to depend upon the server responding to your broadcast message synchronously.  It is entirely possible that the server spins off a new thread to respond to your message, which would necessitate you waiting upon a response.  In light of this, I would recommend setting the Waiting... flag to true *before* you broadcast the system message.  This is because, as I mentioned in my previous post, it's entirely possible that the value of the flag is set to false while you're still inside the BroadcastSystemMessage call.

 

NickB

National Instruments

0 Kudos
Message 7 of 8
(4,599 Views)

NickB,

 

Thanks for all your feedback.  In the end I implemented a one-shot timer which I enable after I get the ping response.  The timer event then handles all the init.  That's working well (so far).

 

Thanks again!

 

TonyG

0 Kudos
Message 8 of 8
(4,585 Views)