Author: Wolfgang Büscher (DL4YHF)
Rev/Date: 2004-10-02 (YYYY-MM-DD)
Master copy: yhf_comm\yhf_comm_info.htm
This document describes a simple method how WIN32-applications can communicate with each other, using a simple message-based method. It was originally used in some of the author's hobbyist projects, without having to dig in the depths of OLE, COM/DCOM etc. Sending and receiving a windows message is very simple, so the WM_COPYDATA message is used to send blocks of data from one application ("program") to another was the basic idea. Without large overhead, the "message-based protocol" described in this document is open for future extensions too. But beware: At the moment this is only a description but not a specification, which means I may decide to change parts of the protocol in future (I'm open to suggestions)..
The "protocol" described in this document was originally implemented in "C"
in a module called YHF_Comm
. The sourcecodes and a simple
test suite can be downloaded from the author's website. If you read this
document online, you can download the sourcecodes from
this link: msg_tester_sources.zip. Unpack
them while maintaining the directory structure, so you don't have to modify
the pathnames in the C++Builder project file. On the author's PC, the source
modules were located in directories c:\CBproj\YHF_tools and c:\CBproj\MsgTester
. There is a short description of the "Message Tester" in the zipped archive
(MsgTester\readme.txt) which describes how to send and receive WM_COPYDATA
messages with that tool (it may be helpful for you, if you are a programmer
and want to implement the "protocol" in your own software too).
For general info about sending and receiving WM_COPYDATA messages in your own program, regardless of the programming language, search the net for "WM_COPYDATA message" which should take you to the official documentation. You may already have the "Win32 programmer's reference" in a file named Win32.hlp on your harddisk (this file is distributed with many programming languages, Borland C++Builder is only one of them).
Here just the basics about sending and receiving WM_COPYDATA messages in your application (partly copied from Win32.hlp) :
Definition of the WM_COPYDATA message
The "lParam" parameter points to a COPYDATASTRUCT structure that contains the data to be passed.
An application must use the SendMessage function to send this message, not
the PostMessage function.
The data being passed must not contain pointers or other references to objects
not accessible to the application receiving the data.
While this message is being sent, the referenced data must not be changed
by another thread of the sending process.
The receiving application should consider the data read-only. The pcds parameter
is valid only during the processing of the message. The receiving application
should not free the memory referenced by pcds. If the receiving application
must access the data after SendMessage returns, it must copy the data into
a local buffer.
The COPYDATASTRUCT structure contains data to be passed to another application by the WM_COPYDATA message.
typedef struct {
DWORD dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT;
Members of the COPYDATASTRUCT :
Here is a code snippet from YHF_COMM.CPP, subroutine YHF_SendCommandMessage,
which shows how to send a WM_COPYDATA message to another application. The
parameter pCmd
is a pointer to a T_YHF_COMMAND structure (actually
a "union"), which may contain a lot of different data formats, and also the
application specific 2-character module-
and command-identifier (we'll come back
to these ID's in one of the next chapters). The characters (module-id and
command) are combined into a single 32-bit value using a special union defined
as follows (in "C"):
typedef union { // 8/16/32-bit - adressable "long"-data type
BYTE b[4];
WORD w[2];
INT32 l;
LWORD ul;
} T_YHF_BLONG;
The four characters are written into the "b"-components of this union (byte-by-byte), and read as a single 32-bit value ("ul") from it. If your programming language does not support unions, you may alternatively combine the 4 characters into a single 32-bit integer manually, using bit-shift operations. The 1st character (=byte(0)) will be the least significant byte, the 4th character the most significant byte because all 80x86-"Intel"-compatible CPU's use the "Little Endian" format. "Little Endian" means that the low-order byte of the number is stored in memory at the lowest address.
The "cu" component of the T_YHF_COMMAND
struct is such a
byte-adressable longword type.
COPYDATASTRUCT cds; // declare a variable with type copy-data-struct" (windows API)
// Fill the members of the COPYDATASTRUCT.
// Input: pCmd = pointer to a T_YHF_COMMAND struct
// First copy the 4 characters for module + command into dwData
cds.dwData = pCmd->cu.bl.ul; // combine 4 characters in a single WORD
cds.cbData = pCmd->lDataSize; // count of bytes in data block
cds.lpData = pCmd->du.bData; // pointer to data block
// now SEND the message ("SEND", not "POST" !)
return SendMessage(
(HWND)hDestWindow, // HWND hWnd = handle of destination window
WM_COPYDATA, // UINT Msg = message to send
(WPARAM)MyWindowHandle, // HANDLE OF SENDING WINDOW
(LPARAM)&cds ); // 2nd msg parameter = pointer to COPYDATASTRUCT
The return value will be 0(zero) if the message could not be delivered. Everything else means "ok". MS suggests to use only 0 or 1 for the 32-bit return value but the return value may be used for other purposes also (for example, several bits in the return value may provide information if a response message will be sent).
Sounds easy so far ? One problem to be solved is, we need to know the "handle" of the destination window. This is a numeric value which will be assigned by the operating system when starting a program (you never know which "handle" your window will have, it only remains valid through the window's lifetime). But there are several ways to retrieve this value; one of them will be explained in the next chapter.
Because the handle of the sending window is passed to the receiving window (alias "application" in this case), the receiver will know how to send an "answering message" to the caller if necessary. More about handling received messages follows later.
To send a windows message from "our own" program to "another" (already running) program, we need to know the other program's window handle. All "window handles" are assigned by the operating system during runtime, so the handle values cannot be hard-coded in the sending subroutine.
In the module YHF_COMM, the following method is used to retrieve the other application's window handle: The FindWindow() function is used for this purpose. Look at this sourcecode snippet from YHF_FindWindowByName() :
// The FindWindow function retrieves the handle to the top-level window
// whose class name and/or window name match the specified strings.
// This function does not search child windows.
//
long lResult = (long)FindWindow(
pszWindowName, // pointer to class name, for example "TSpectrumLab"
NULL ); // LPCTSTR lpWindowName = pointer to window name
If the above function succeeds, lResult will be non-zero, and it can be used later as the hDestWindow parameter for sending a message to the other program .
< to be continued >
To receive WM_COPYDATA messages in your own application, you must add a message handler in the message processing loop. How to achieve this, depends on your programming language - you may do a web search on "Windows Message Handler". Here just a few starting points:
In the message handler, check if the message code is WM_COPYDATA, and -if so- convert the message parameter "LParam" into a pointer to a COPYDATASTRUCT. Then you can let YHF_HandleCopydataMessage() do the rest, or write your own handler for it. The basic principle is as follows:
First split the dwData parameter of the message into four characters. The first two characters are the "module", the second two the "command". The following modules existed at the time of this writing (feel free to extend this table ;-) :
Predefined Module Identifiers for DL4YHF's inter-application message handler | |
Module Identifier | Meaning |
CL | Command Line Interpreter |
AU | Audio Data |
The third and fourth character in the message's dwData parameter contain the "command". They are specific to the "module ID". Here, for example, a few "commands" understood by the Command Line Interpreter built inside DL4YHF's Spectrum Lab:
Command Identifiers for the command line interpreter in DL4YHF's Spectrum Lab | |
Command Identifier | Meaning |
CF | Calculate Function (with result returned in response message) |
EC | Execute Command (no result, will not send a response message) |
A practical example: To stop the spectrum analyser, send the following message..
COPYDATASTRUCT cds;
// Set module ID "CL" + command ID "EC" :
cds.dwData = ('C')+((DWORD)'L'>>8)+((DWORD)'E'>>16)+((DWORD)'C'>>24);
cds.lpData = "spectrum.pause=1"; // command sent to SpecLab's interpreter
cds.cbData = strlen((char*)cds.lpData); // count of bytes in data block
SendMessage(hDestWindow,WM_COPYDATA,(WPARAM)MyWindowHandle,(LPARAM)&cds);
--... ...-- ...-.-