65.9K
CodeProject is changing. Read more.
Home

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

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (8 votes)

Mar 14, 2003

3 min read

viewsIcon

58072

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.