Once a DDE conversation has been established, the client can retrieve the value of a data item from the server by issuing the WM_DDE_REQUEST message, or the client can submit a data-item value to the server by issuing the WM_DDE_POKE message.
To retrieve an item from the server, the client sends the server a WM_DDE_REQUEST message specifying the item and format to retrieve, as follows:
if ((atomItem = GlobalAddAtom(szItemName)) != 0) {
if (!PostMessage(hwndServerDDE,
WM_DDE_REQUEST,
hwndClientDDE,
MAKELONG(CF_TEXT, atomItem)))
GlobalDeleteAtom(atomItem);
}
if (atomItem == 0) {
.
. /* error handling */
.
}
In this example, the client specifies the clipboard format CF_TEXT as the preferred format for the requested data item.
The receiver (server) of the WM_DDE_REQUEST message typically must delete the item atom, but if the PostMessage call itself fails, the client must delete the atom.
If the server has access to the requested item and can render it in the requested format, the server copies the item value as a global shared-memory object and sends the client a WM_DDE_DATA message, as follows:
/*
* Allocate the size of the DDE data header, plus the data: a
* string,<CR><LF><NULL>. The byte for the string's terminating
* null character is counted by DDEDATA.Value[1].
*/
if (!(hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
(LONG) sizeof(DDEDATA) + strlen(szItemValue) + 2)))
return;
if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) {
GlobalFree(hData);
return;
}
.
.
.
lpData->cfFormat = CF_TEXT;
lstrcpy((LPSTR) lpData->Value, (LPSTR) szItemValue);
/* Each line of CF_TEXT data is terminated by CR/LF.*/
lstrcat((LPSTR) lpData->Value, (LPSTR) "\r\n");
GlobalUnlock(hData);
if ((atomItem = GlobalAddAtom((LPSTR) szItemName)) != 0) {
if (!PostMessage(hwndClientDDE,
WM_DDE_DATA,
hwndServerDDE,
MAKELONG(hData, atomItem))) {
GlobalFree(hData);
GlobalDeleteAtom(atomItem);
}
}
if (atomItem == 0) {
.
. /* error handling */
.
}
In this example, the server application allocates a memory object to contain the data item. The memory is allocated with the GMEM_DDESHARE option, so that the server and client applications can share the memory. After allocating the memory object, the server application locks the object so it can obtain the object's address. The data object is initialized as a DDEDATA structure.
The server application then sets the cfFormat member of the structure to CF_TEXT to inform the client application that the data is in text format. In response, the client copies the value of the requested data into the Value member of the DDEDATA structure.
After the server has filled the data object, the server unlocks the data. It then creates a global atom containing the name of the data item.
Finally, the server issues the WM_DDE_DATA message by calling the PostMessage function. The handle of the data object and the atom containing the item name are contained in the lParam parameter of the message.
If the server cannot satisfy the request, it sends the client a negative WM_DDE_ACK message, as follows:
/* negative acknowledgment */
PostMessage(hwndClientDDE,
WM_DDE_ACK,
hwndServerDDE,
MAKELONG(0, atomItem));
Upon receiving a WM_DDE_DATA message, the client processes the data-item value as appropriate. Then, if the fAckReq bit specified in the WM_DDE_DATA message is 1, the client must send the server a positive WM_DDE_ACK message, as follows:
hData = LOWORD(lParam); /* of WM_DDE_DATA message */
atomItem = HIWORD(lParam);
if (!(lpDDEData = (DDEDATA FAR*) GlobalLock(hData))
|| (lpDDEData->cfFormat != CF_TEXT)) {
PostMessage(hwndServerDDE,
WM_DDE_ACK,
hwndClientDDE,
MAKELONG(0, atomItem)); /* negative ACK */
}
/* Copy data from lpDDEData here.*/
if (lpDDEData->fAckReq) {
PostMessage(hwndServerDDE,
WM_DDE_ACK,
hwndClientDDE,
MAKELONG(0x8000, atomItem)); /* positive ACK */
}
bRelease = lpDDEData->fRelease;
GlobalUnlock(hData);
if (bRelease)
GlobalFree(hData);
In this example, the client examines the format of the data; if the format is not CF_TEXT (or if the client cannot lock the memory for the data), the client sends a negative WM_DDE_ACK message to indicate that it cannot process the data.
If it can process the data, the client examines the fAckReq member of the DDEDATA structure to determine whether the server requested that it be informed that the client received and processed the data successfully. If the server did request this information, the client sends the server a positive WM_DDE_ACK message.
The client saves the value of the fRelease member before unlocking the data object, because unlocking the data invalidates the pointer to the data. The client then examines the flag value to determine whether the server application requested the client to free the global memory containing the data; the client acts accordingly.
Upon receiving a negative WM_DDE_ACK message, the client may ask for the same item value again, specifying a different clipboard format. Typically, a client will first ask for the most complex format it can support, and then step down if necessary through progressively simpler formats until it finds one the server can provide.
If the server supports the Formats item of the System topic, the client can determine once what clipboard formats the server supports, instead of determining them each time the client requests an item. For more information about the System topic, see Section 22.5, “The System Topic.”
The client may send an item value to the server by using the WM_DDE_POKE message. The client renders the item to be sent and sends the WM_DDE_POKE message, as follows:
if (!(hPokeData
= GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
(LONG) sizeof(DDEPOKE) + lstrlen(szValue) + 2)))
return;
if (!(lpPokeData
= (DDEPOKE FAR*) GlobalLock(hPokeData))) {
GlobalFree(hPokeData);
return;
}
lpPokeData->fRelease = TRUE;
lpPokeData->cfFormat = CF_TEXT;
lstrcpy((LPSTR) lpPokeData->Value, (LPSTR) szValue);
/* Each line of CF_TEXT data is terminated by CR/LF.*/
lstrcat((LPSTR) lpPokeData->Value, (LPSTR) "\r\n");
GlobalUnlock(hPokeData);
if ((atomItem = GlobalAddAtom((LPSTR) szItem)) != 0) {
.
.
.
if (!PostMessage(hwndServerDDE,
WM_DDE_POKE,
hwndClientDDE,
MAKELONG(hPokeData, atomItem))) {
GlobalDeleteAtom(atomItem);
GlobalFree(hPokeData);
}
}
if (atomItem == 0) {
.
. /* error handling */
.
}
Note that sending data by using a WM_DDE_POKE message is essentially the same as sending it by using a WM_DDE_DATA message, except that WM_DDE_POKE is sent from the client to the server.
If the server is able to accept the data-item value in the format in which it was rendered by the client, the server processes the item value as appropriate and sends the client a positive WM_DDE_ACK message. If it is unable to process the item value, because of format or other reasons, the server sends the client a negative WM_DDE_ACK message.
hPokeData = LOWORD(lParam);
atomItem = HIWORD(lParam);
GlobalGetAtomName(atomItem, szItemName, ITEM_NAME_MAX_SIZE);
if (!(lpPokeData = (DDEPOKE FAR*) GlobalLock(hPokeData))
|| lpPokeData->cfFormat != CF_TEXT
|| !IsItemSupportedByServer(szItemName))) {
PostMessage(hwndClientDDE,
WM_DDE_ACK,
hwndServerDDE,
MAKELONG(0, atomItem)); /* neg ACK */
}
lstrcpy(szItemValue, lpPokeData->Value); /* copies value */
bRelease = lpPokeData->fRelease;
GlobalUnlock(hPokeData);
if (bRelease) {
GlobalFree(hPokeData);
}
PostMessage(hwndClientDDE,
WM_DDE_ACK,
hwndServerDDE,
MAKELONG(0x8000, atomItem)); /* positive ACK */
In this example, the server calls the GlobalGetAtomName function to retrieve the name of the item sent by the client. The server then determines whether it supports the item and whether the item is rendered in the correct format (CF_TEXT). If not, or if the server cannot lock the memory for the data, it sends a negative acknowledgment back to the client application.