Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building up a Client-Server-Architecture between a 32-Bit-app and a 16-Bit-app using WM_COPYDATA instead of a thunk

0.00/5 (No votes)
13 Mar 2003 1  
This article describes how to use the Windows Message WM_COPYDATA if one of the communicating apps is a 32-Bit-app, the other one a 16-bit app. It's really easy, but as far is I know, there has been no detailed description of this problem up till now.

First attempts and ideas

If you read the MSDN help about WM_COYDATA you can see that it is possible to use it in order to communicate between 16-bit and 32-bit apps, but not how. My Problem was the following: I had to include a 16-bit-dll into a 32-bit-Application, as there is no newer version of the dll available. If you really want to solve this problem, you have to either build a real thunk using the thunk compiler (see other articles for details) or cheat a little using the function QT_Thunk() which has been described on the web several times. Both ideas are rather complicated since you have to write at least a few lines of assembly code. I didn't dare to do this, because I thought it would be too complicated.

My next approach to the problem was to build up a DDE-Client-Server-Architecture. But also this possibility seemed to be too great an effort. When I first saw the documentation of WM_COPYDATA I thought it couldn't work between 16-bit and 32-Bit apps and first tried another thing: I let two apps communicate via Windows Messages and sent pointers pointing to data arrays via this channel. This lead to the problem of the translation between flat 32-bit Pointers and generic far 16-bit-Pointers. I advise you to never try a thing like that, it's really awful!

How it works

At last I tried just to use WM_COPYDATA, even though I thought there were still two problems to solve, which I saw no solution for: The first one is the same problem I encountered trying to use my own Windows Messages. I thought it would be necessary to translate the pointers. But to my surprise this seems to be done automatically. The second problem I expected was the following: I thought it would not be possible to get access to a pointer outside the adress space of an application by directly dereferecing it. Normally this leads to Windows exceptions, but it does not in the case of using WM_COPYDATA. The pointer you send by this message is allowed to be dereferenced by some magic way.

So what do you have to do? I can only answer this question for my special case. I used Visual C++ 6.0 for the 32-bit-app and Visual C++ 1.52 for the 16-bit-App on Windows 2000. As my solution works for running the 16-bit-app in both a shared and a seperate virtual dos machine, I guess that it also works on Windows 98 / ME, but I have not tested that. In order to have the two apps communicate I have them get handles to each other using the API-Function FindWindow(NULL,"name"). When they have handles, they can send messages to each other using the API-Function (NOT the CWnd-member) SendMessage(). In order to avoid timing problems you should never use PostMessage() instead of SendMessage(). It's a good idea to choose a small set of common messages for the two apps and have them register by the API-Function RegisterWindowMessage(). I only built up a data transfer from 16-bit to 32-bit but not vice versa. For that pupose I had to include the following piece of code in my 16-bit-app, as WM_COPYDATA and COPYDATASTRUCT are not defined in the headers of the old 16-bit-API:

// taken from 32-bit-file winuser.h and modified slightly ///////////
/////////////////////////////////////////////////////////////////////

#define WM_COPYDATA 0x004A

typedef struct tagCOPYDATASTRUCT {
DWORD dwData; //an obsolete parameter for additional information
DWORD cbData; //number of bytes to be transferred
LPVOID lpData; //pointer to the data array to be transferred
} COPYDATASTRUCT, *PCOPYDATASTRUCT;

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////


In order to transfer data I just had to create an instance of the COPYDATASTRUCT (let's call it cpstruct), assign values to its members and then send the message WM_COPYDATA like

::SendMessage(handle, WM_COPYDATA, 0, (LPARAM) (LPVOID) &cpstruct);
The receiving 32-bit-app needn't do any pointer translation and can directly get access to the data by dereferencing the pointer in the MessageHandler. The MessageHandler for WM_COPYDATA in the 32-bit-app looks like:

BOOL CCCDClient::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
memcpy(m_pReceiveData, pCopyDataStruct->lpData,
pCopyDataStruct->cbData);
return CFrameWnd::OnCopyData(pWnd, pCopyDataStruct);
}


It is necessary to copy the data (e.g. using memcpy() as shown here) within the MessageHandler. I first tried to store the pointer and copy the data in a different function, but this doesn't work. So as you can see it's quite easy to communicate and exchange data between 16-bit and 32-bit-apps.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here