Initiating the DDE Conversation

I've chosen to initiate the DDE conversation by sending WndProc a user-defined message (which I've called WM_USER_INITIATE) after the UpdateWindow call in WinMain. Normally a client would initiate the conversation in response to a menu command.

In response to this user-defined message, WndProc calls GlobalAddAtom to create atoms for the application name of the server (”DdePop“) and the topic name (”US_Population“). WndProc broadcasts the WM_DDE_INITIATE message by calling SendMessage with a 0xFFFF window handle.

As we've seen, a server that scores a match with the application and topic atoms is required to send a WM_DDE_ACK message back to the client. Because this message is sent using SendMessage rather than posted, the client will receive the WM_DDE_ACK message before the original SendMessage call with the WM_DDE_INITIATE message has returned. WndProc handles the WM_DDE_ACK message by storing the window handle of the server in the variable hwndServer and deleting the atoms that accompany the message.

If a client broadcasts a WM_DDE_INITIATE message with NULL application or topic names, then it must be prepared to receive multiple WM_DDE_ACK messages from each of the servers that can satisfy the request. In this case, the client must decide which server to use. The others must be posted WM_DDE_TERMINATE messages to terminate the conversation.

It is possible that hwndServer will still be NULL after the WM_DDE_INITIATE SendMessage call. This means that DDEPOP is not running under Windows. In this case, WndProc attempts to execute DDEPOP by calling WinExec. The WinExec call searches the current directory and the PATH environment variable to load DDEPOP. WndProc then again broadcasts the WM_DDE_INITIATE message. If hwndServer is still NULL, then WndProc displays a message box notifying the user of the problem.

Next, for each of the states listed in the pop structure, WndProc allocates a DDEADVISE structure by calling GlobalAlloc with the GMEM_DDESHARE flag. The fAckReq (”acknowledgment requested“) flag is set to TRUE (indicating that the server should post WM_DDE_DATA messages with the fAckReq field in the DDEDATA field set to NULL). The fDeferUpd flag is set to FALSE (indicating a hot link rather than a warm link), and the cfFormat field is set to CF_TEXT. GlobalAddAtom adds an atom for the two-letter state abbreviation.

This structure and the atom are passed to the server when SHOWPOP posts the WM_DDE_ADVISE message. If the PostMessage call fails (which might happen if DDEPOP is suddenly terminated), then SHOWPOP frees the memory block, deletes the atom, and exits the loop.

Otherwise, SHOWPOP waits for a WM_DDE_ACK message by calling PeekMessage. As the DDE documentation indicates, the client deletes the atom accompanying the message, and also frees the global memory block if the client responds with a negative acknowledgment.

It's quite likely that this WM_DDE_ACK message from the client will be followed by a WM_DDE_DATA message for the item. For this reason, SHOWPOP calls PeekMessage and DispatchMessage to extract any DDE messages from the message queue and dispatch them to WndProc.