LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Using BroadcastSystemMessage to ping another application

I have a server application from a vendor that provides various services I need to use.  The vendor also provided a sample client application written in Borland C++ Builder.  I'm trying to implement the same message passing scheme they use in their client ina CVI program, but I can't get past the first step.

 

The Borland app first uses BroadcastSystemMessage to ping their server application.  Then it waits for a response from the server in the form of a WM_COPYDATA windows message.  The original Borland code for pinging the server looks something like this:

 

   UINT ping_id = RegisterWindowMessage("client_ping");
   DWORD dwApps = BSM_APPLICATIONS;
   BroadcastSystemMessage( BSF_NOHANG, &dwApps, ping_id, (WPARAM) m_client_handle, 0 );

 

The m_client_handle is the windows handle of the client application.  The server uses that handle to send it's reponse directly back to the client. My CVI implementation of the ping looks like this:

 

    GetPanelAttribute (panelHandle, ATTR_SYSTEM_WINDOW_HANDLE, (int *)&ClientWindowHandle);
    int PingId = RegisterWindowMessage("client_ping");
    DWORD dwApps = BSM_APPLICATIONS;
    int err = BroadcastSystemMessage(BSF_NOHANG, &dwApps, PingId, (WPARAM) ClientWindowHandle, 0);

BroadcastSystemMessage returns a 1 indicating that everything worked fine.

 

The next half of the ping is to catch the return message from the server.  The Borland app does this by implementing the call OnCopyData for it's main form.  As best as I can determine, this is essentially a predefined message handler for WM_COPYDATA.  I implemented this in CVI with the following code:

 

int main (int argc, char *argv[])
{
    ....
    // this is really the same call as before (i.e., I only get the handle once)

    GetPanelAttribute (panelHandle, ATTR_SYSTEM_WINDOW_HANDLE, (int *)&ClientWindowHandle);
    InstallWinMsgCallback (panelHandle, WM_COPYDATA, ProcessWmCopydataMessage, VAL_MODE_INTERCEPT, &panelHandle, &ClientWindowHandle);

    ....

}

 

int CVICALLBACK ProcessWmCopydataMessage (int panelHandle, int message, unsigned int* wParam, unsigned int* lParam, void* callbackData)
{
    PCOPYDATASTRUCT ServerInfo;

    if (message == WM_COPYDATA)
    {

        // process returned message
    }

}

 

The problem is that I never get a call to PrcoessWmCopydataMessage.  Am I doing something wrong in my setup?  In particular, am I retrieving the correct handle for my application and am I sending it correctly in the broadcast message?  Is there any spy tool that allows you to watch all windows messages and reponses so I can determine if the server is doing anything?

 

I tired writing my own dummy server just to verify that BroadcastSystemMessage works the way I expect.  The only problem is that I can't figure out how to catch the broadcast message in my dummy server app.  How do I install a callback that will catch broadcast messages?

 

One last thing: at first I couldn't get BroadcastServerMessage to compile.  It said it was not declared.  After a quick search on the forum, I added the following line before my inclusion of the windows header:

 

    #define _WIN32_WINDOWS 0x0410

It compiles now, but I'm concerned that by faking my Windows version I may have messed up something else.  I running Windows 7 BTW.

 

Tony G.

0 Kudos
Message 1 of 8
(6,753 Views)

Hey Tony -

 

I'm not sure why you're not able to receive the WM_COPYDATA message - it seems as if you are doing everything correctly.  However, I've written a simple example of what you've described.  There is a server application that waits for a broadcasted system message, and then sends a WM_COPYDATA message and exits.  The client app broadcasts the message and then waits for the WM_COPYDATA message.  Let me know if you have any questions about my example, or if this leads to new questions about your particular application.

 

NickB

National Instruments

0 Kudos
Message 2 of 8
(6,742 Views)

Nick,

 

I got your solution to work with my server app.  Thanks for the help!  I'm still not getting a reponse from the actual server.  I think there may be a problem with the "client_ping" string I'm passing to RegisterWindowMessage.  The Borland app creates this string through a series of macros, stringization operators, class constructors, etc.  It may be that somewhere in all that something unexpected is happening (i.e., an extra space is added, the string is defined using WCHAR, etc.).  And if the string doesn't match perfectly, then of course it won't get the same ID from RegisterWindowMessage as the server app.  I'm going to explorer this a little deeper and maybe ask the supplier of the server app for some help.

 

In the meantime, I have a few quesitons about the differences between our two programs:

 

1) After you get the window handle, you set the ATTR_SYSTEM_WINDOW_HANDLE of your button to the same handle.  What does that do?

2) When you call InstallWinMsgCalback you pass the window handle for both of the last two parameters.  I thought the second to last was supposed to be the panel handle.

3) You cast the window handles to intptr_t instead of HWND.  Is that just style or does it have some implications (like avoiding signed to unsigned conversion or something)?

 

Tony G.

 

0 Kudos
Message 3 of 8
(6,735 Views)

1) I actually set the ATTR_CALLBACK_DATA of the button to be the window handle.  This just lets me pass the pass the HWND through to the server with the BroadcastSystemMessage call on the button click.  You could make it global if you would like.

2) The second to last parameter is just a void * - you can pass almost anything you want through.  I just pass the hwnd through so I can use it in the callback function - such as to respond to the EVENT_NEWHANDLE message.

3) The type the fuction actually returns is an intptr_t.  I would rather store the output as what it actually is - an HWND.  I cast so that the user protection doesn't complain to me at runtime.

 

NickB

National Instruments

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

After reading my last post, I thought it would be worthwhile clarifying the first two points of my response.  The ATTR_CALLBACK_DATA for the button and the void *callbackData paramter to InstallWinMsgCallback allow you to define what comes through the void *callbackData parameter of the button's callback function, and the WinMsgCallback callback function.  I chose to pass the HWND through so that I could maintain it's local scope, but use it in the callback functions.

 

Let me know if you have any questions about this -

 

NickB

National Instruments

0 Kudos
Message 5 of 8
(6,698 Views)

Nick,

 

I understand what you mean.  Thanks for the clarificaiton.

 

BTW, any comments on my question about having to define the WIN32 variable to get BroadcastSystemMessage to compile?

 

Tony

0 Kudos
Message 6 of 8
(6,696 Views)

Nick,

 

Turns out the problem was with the vendor's server app.  They sent me a new version and it works perfectly. Still curious about the Windows version issue, though,

 

Tony G.

0 Kudos
Message 7 of 8
(6,687 Views)

Hey Tony -

 

The reason you get the compile error is because CVI does not define _WIN32_WINNT in the version of CVI you are using.  We expect the programmer to define it based on their own needs.  BroadcastSystemMessage is conditionally compiled so that it will not be defined if _WIN32_WINNT is not defined:

 

WinUser.h

#if defined(_WIN32_WINNT)
WINUSERAPI
long
WINAPI
BroadcastSystemMessageA(
...

 

The consequence of defining it to be 0x0410 is that you are stating your program is compatible with version NT 4.0 of Windows and later.  This means that you can use all functionality of the Windows OS that was available in Windows NT 4.0, but none of the newer functionality will be available to you.  If you try to run your program on a version of Windows older than Windows NT 4.0, the program will likely not run (this should not be an issue...)

 

Note that 0x0410 is not actually a defined Windows version.  From a recent version of the Interface to the Win32 API, you can read from sdkddkver.h:

#define _WIN32_WINNT_NT4     0x0400
#define _WIN32_WINNT_WIN2K   0x0500
...

 

This means that you are stating your program uses no Windows fuctionality more recent than Windows NT 4.0.  Here's some more good information on using these macros.

 

NickB

National Instruments

0 Kudos
Message 8 of 8
(6,675 Views)