Programming can be comparatively easy when you can assume that nothing can go wrong. But as you've seen, even when everything in a DDE conversation proceeds as expected, complexities involving the creation and deletion of atoms and global memory blocks can be tricky.
DDE is further complicated by the potential for problems, and these problems are accentuated because there are two programs involved rather than just one. You've seen that whenever a PostMessage calls (indicating that the other program has unexpectedly terminated), you must clean up afterwards.
It is almost impossible to reach a point in DDE coding when you are absolutely certain that you've accounted for all possibilities of error—particularly when considering that the program your program is communicating with may have errant behavior. The only advice I can offer here is simply to do the best you can.