"Isn't DDE all asynchronous anyway?" asks commenter KaiArnold.

It's mostly asynchronous, but not entirely.

You can read about how DDE works in MSDN, but since it seems people are reluctant to read the formal documentation, I'll repeat here the points relevant to the discussion.

The DDE process begins with a search for a service provider. This is done by broadcasting the WM_DDE_INITIATE message and collecting the responses. Each server that wishes to respond to the request sends back a WM_DDE_ACK message. The DDE client then chooses which of the servers it wishes to continue the conversation with (possible more than one). The remainder of the DDE conversation is carried out with posted messages, the details of which are not important here.

As you can see, everything in DDE is asynchronous with the exception of the WM_DDE_INITIATE. Why is WM_DDE_INITIATE synchronous?

Remember that DDE was developed back in the 16-bit days, when it was safe to broadcast messages. The initiate message and its WM_DDE_ACK replies are synchronous to ensure that the client doesn't have to wait indefinitely to build a list of servers. If it were asynchronous, then the client would post the WM_DDE_INITIATE and then wait "a while" to see if anybody responded. But how does it know when it should give up waiting and just go with whatever it has? What happens if a response comes in after the client already proceeded based on the assumption that that server was unavailable? What if a response comes in five minutes later, when the client had started a second DDE discovery query? Would that response have been to the first or the second discovery broadcast?

In particular, it is important for the client to know whether there are any servers out there at all, because the way the shell interprets DDE-based file associations is first to attempt a WM_DDE_INITIATE with the application and topic specified in the registration. If no server is found, then it launches the server manually and then tries to connect to the server via DDE a second time. (The second time should work, since the responsible server was explicitly launched!)