home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / d / d020_1_4 / 5.ddi / DDE / CLIDDE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-01  |  20.3 KB  |  724 lines

  1. /****************************************************************************
  2.  
  3.     MODULE: CLIDDE.C
  4.  
  5.     PURPOSE: Processes incoming and outgoing DDE messages
  6.  
  7.  
  8. ****************************************************************************/
  9.  
  10. #include "windows.h"
  11.  
  12. #include "dde.h"
  13. #include "clires.h"
  14. #include "client.h"
  15. #include <string.h>
  16.  
  17. #define DEFAULT_ACK_TIME_OUT_MILLISEC 10000
  18. static int nAckTimeOut;
  19.  
  20. static BOOL   bInInitiate = FALSE;
  21.  
  22. BOOL NEAR AwaitingAck(HWND);
  23.  
  24.  
  25. /****************************************************************************
  26.  
  27.     FUNCTION: AwaitingAck
  28.  
  29.     PURPOSE:  Inform user if acknowledgement is required before further 
  30.               action.
  31.  
  32. ****************************************************************************/
  33. BOOL NEAR AwaitingAck(hwndClientDDE)
  34.     HWND hwndClientDDE;
  35. {
  36.     if (GetConvPendingAck(hwndClientDDE) == NONE)
  37.     {
  38.         return (FALSE);
  39.     }
  40.     MessageBox(hwndMain,
  41.         "Previous DDE operation must be acknowledged first",
  42.         "Client",
  43.         MB_ICONEXCLAMATION | MB_OK);
  44.     return (TRUE);
  45. }
  46.  
  47.  
  48.  
  49. /****************************************************************************
  50.  
  51.     FUNCTION: ClientAcknowledge
  52.  
  53.     PURPOSE:  Called when client application receives WM_DDE_ACK message
  54.           or WM_TIMER message (time out on wait for ACK).
  55.  
  56. ****************************************************************************/
  57. void ClientAcknowledge(hwndClientDDE, hwndServerDDE, lParam, bTimeOut)
  58.     HWND hwndClientDDE;
  59.     HWND hwndServerDDE;
  60.     LONG lParam;    /* lParam of WM_DDE_ACK message */
  61.     BOOL bTimeOut;  /* TRUE if NACK is due to time-out */
  62. {
  63.     enum PENDINGACK ePendingAck;
  64.     char szApplication[APP_MAX_SIZE+1];
  65.     char szTopic[TOPIC_MAX_SIZE+1];
  66.     char szItem[ITEM_MAX_SIZE+1];
  67.     char message[80];
  68.  
  69.     ePendingAck = GetConvPendingAck(hwndClientDDE);
  70.     SetConvPendingAck(hwndClientDDE, NONE);
  71.     KillTimer(hwndClientDDE, hwndServerDDE);
  72.  
  73.     if (bInInitiate)
  74.     {
  75.         GlobalGetAtomName(LOWORD(lParam),
  76.             szApplication,
  77.             APP_MAX_SIZE);
  78.         GlobalGetAtomName(HIWORD(lParam),
  79.             szTopic,
  80.             TOPIC_MAX_SIZE);
  81.     if (!AddConv(hwndClientDDE, hwndServerDDE, szApplication, szTopic))
  82.         {
  83.         MessageBox(hwndMain,
  84.         "Maximum conversation count exceeded",
  85.                 "Client",
  86.                 MB_ICONEXCLAMATION | MB_OK);
  87.         }
  88.     /*
  89.     GlobalDeleteAtom(LOWORD(lParam));
  90.         GlobalDeleteAtom(HIWORD(lParam));
  91.     */
  92.         return;
  93.     }
  94.     if ((ePendingAck == ADVISE) && (LOWORD(lParam) & 0x8000))
  95.     {    /* received positive ACK in response to ADVISE */
  96.         GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_MAX_SIZE);
  97.     AddItemToConv(hwndClientDDE, szItem);
  98.  
  99.     /* Conversation item is established: now get current value */
  100.     /* and update screen                      */
  101.     SendRequest(hwndClientDDE, hwndServerDDE, szItem);
  102.     InvalidateRect(hwndMain, NULL, TRUE);
  103.     }
  104.     if ((ePendingAck == UNADVISE) && (LOWORD(lParam) & 0x8000))
  105.     {    /* received positive ACK in response to UNADVISE */
  106.         GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_MAX_SIZE);
  107.     RemoveItemFromConv(hwndClientDDE, szItem);
  108.     InvalidateRect(hwndMain, NULL, TRUE);
  109.     }
  110.     if (!(LOWORD(lParam) & 0x8000))    /* NACK */
  111.     {
  112.         strcpy(message, "DDE ");
  113.         strcat(message, ePendingAck == ADVISE?    "ADVISE "
  114.                       : ePendingAck == UNADVISE?  "UNADVISE "
  115.                       : ePendingAck == POKE?      "POKE "
  116.                       : ePendingAck == REQUEST?   "REQUEST "
  117.               : ePendingAck == EXECUTE?   "EXECUTE "
  118.                       : " ");
  119.         strcat(message, bTimeOut? "acknowledge time out"
  120.                                 : "operation failed");
  121.     MessageBox(hwndMain,
  122.             message,
  123.             "Client",
  124.             MB_ICONEXCLAMATION | MB_OK); 
  125.     }
  126.     switch (ePendingAck)
  127.     {
  128.     case ADVISE:
  129.     case UNADVISE:
  130.     case POKE:
  131.     case REQUEST:
  132.         if (HIWORD(lParam))  /* will not be available for time-out */
  133.         GlobalDeleteAtom(HIWORD(lParam));
  134.         break;
  135.     case EXECUTE:
  136.         GlobalFree(HIWORD(lParam)); /* hCommand (execute string) */
  137.         break;
  138.     }
  139.     return;
  140. }
  141.  
  142.  
  143.  
  144. /****************************************************************************
  145.  
  146.     FUNCTION: ClientReceiveData
  147.  
  148.     PURPOSE:  Called when client application receives WM_DDE_DATA message.
  149.  
  150. ****************************************************************************/
  151. void ClientReceiveData(hwndClientDDE, hwndServerDDE, lParam)
  152.     HWND  hwndClientDDE;
  153.     HWND  hwndServerDDE;
  154.     LONG  lParam;
  155. {
  156.     DDEDATA FAR * lpDDEData;
  157.     char          szItem[ITEM_MAX_SIZE+1];
  158.     BOOL          bRelease;
  159.     BOOL          bAck;
  160.  
  161.     if (IsConvInTerminateState(hwndClientDDE, hwndServerDDE))
  162.     { /* Terminate in progress: do not receive data */
  163.         GlobalFree(LOWORD(lParam));
  164.         GlobalDeleteAtom(HIWORD(lParam));
  165.         return;
  166.     }
  167.  
  168.     if (GetConvPendingAck(hwndClientDDE) == REQUEST)
  169.     {
  170.     SetConvPendingAck(hwndClientDDE, NONE);
  171.     KillTimer(hwndClientDDE, hwndServerDDE);
  172.     }
  173.  
  174.     if (!(lpDDEData = (DDEDATA FAR *)GlobalLock(LOWORD(lParam)))
  175.         || (lpDDEData->cfFormat != CF_TEXT))
  176.     {
  177.     PostMessage(hwndServerDDE,
  178.             WM_DDE_ACK,
  179.         hwndClientDDE,
  180.             MAKELONG(0, HIWORD(lParam)));  /* Negative ACK */
  181.     }
  182.     bAck = FALSE;
  183.     if (IsInRequestDlg())
  184.     {
  185.     /* Update REQUEST dialog box value */
  186.     RequestSatisfied(lpDDEData->Value);
  187.         bAck = TRUE;
  188.     }
  189.     else 
  190.     {
  191.         GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_MAX_SIZE);
  192.     bAck = SetConvItemValue(hwndClientDDE, szItem, lpDDEData->Value);
  193.     }
  194.     if (lpDDEData->fAckReq)
  195.     {
  196.     /* return ACK or NACK */
  197.     PostMessage(hwndServerDDE,
  198.             WM_DDE_ACK,
  199.         hwndClientDDE,
  200.         MAKELONG( (bAck? 0x8000:0), HIWORD(lParam)));
  201.     }
  202.     bRelease = lpDDEData->fRelease;
  203.     GlobalUnlock(LOWORD(lParam));
  204.     if (bRelease)
  205.         GlobalFree(LOWORD(lParam));
  206.     return;
  207. }
  208.             
  209.  
  210. /****************************************************************************
  211.  
  212.     FUNCTION: ClientTerminate
  213.  
  214.     PURPOSE:  Called when client application receives WM_DDE_TERMINATE
  215.           message.
  216.  
  217. ****************************************************************************/
  218. void ClientTerminate(hwndClientDDE, hwndServerDDE)
  219.     HWND  hwndClientDDE;
  220.     HWND  hwndServerDDE;
  221. {
  222.     if (!IsConvInTerminateState(hwndClientDDE, hwndServerDDE))
  223.     { /* Server has requested terminate: respond with terminate */
  224.     PostMessage(hwndServerDDE, WM_DDE_TERMINATE, hwndClientDDE, 0L);
  225.     }
  226.     RemoveConv(hwndClientDDE, hwndServerDDE);
  227.     if (!IsHwndClientDDEUsed(hwndClientDDE))
  228.     DestroyWindow(hwndClientDDE);
  229.     InvalidateRect(hwndMain, NULL, TRUE);
  230.     return;
  231. }
  232.  
  233.  
  234.  
  235. /****************************************************************************
  236.  
  237.     FUNCTION: DDEWndProc
  238.  
  239.     PURPOSE:  Handles all DDE messages received by the client application.
  240.  
  241. ****************************************************************************/
  242. long FAR PASCAL DDEWndProc(hwnd, message, wParam, lParam)
  243.     HWND      hwnd;
  244.     unsigned  message;
  245.     WORD      wParam;
  246.     LONG      lParam;
  247. {
  248.     switch (message)
  249.     {
  250.     case WM_DDE_ACK:
  251.         ClientAcknowledge(hwnd,(HWND)wParam, lParam, FALSE);
  252.         return (0L);
  253.  
  254.     case WM_TIMER:
  255.         /* Negative ACK because of time out */
  256.         ClientAcknowledge(hwnd,(HWND)wParam, 0L, TRUE);
  257.         return (0L);
  258.  
  259.      case WM_DDE_DATA:
  260.          ClientReceiveData(hwnd,(HWND)wParam, lParam);
  261.          return (0L);
  262.  
  263.      case WM_DDE_TERMINATE:
  264.           ClientTerminate(hwnd,(HWND)wParam);
  265.           return (0L);
  266.  
  267.      default:
  268.           return (DefWindowProc(hwnd, message, wParam, lParam));
  269.     }
  270. }
  271.  
  272.  
  273.  
  274. /****************************************************************************
  275.  
  276.     FUNCTION: DoPasteLink
  277.  
  278.     PURPOSE:  Get conversation information (app/topic/item) from clipboard.
  279.           Initiate conversation, if not already initiated.
  280.           Then, request server to advise the specified item.
  281.  
  282.           Note, the standard clipboard format registered with the
  283.           name "Link" is as follows:
  284.  
  285.           <app> <null> <topic> <null> <item> <null> <null>
  286.  
  287. ****************************************************************************/
  288. void DoPasteLink(void)
  289. {
  290.  
  291.     HANDLE hData;
  292.     LPSTR  lpData;
  293.     HWND   hwndClientDDE;
  294.     HWND   hwndServerDDE;
  295.     char   szApplication[APP_MAX_SIZE+1];
  296.     char   szTopic[TOPIC_MAX_SIZE+1];
  297.     char   szItem[ITEM_MAX_SIZE+1];
  298.     int    nBufLen;
  299.    
  300.     if (OpenClipboard(hwndMain))
  301.     {
  302.        if (!(hData = GetClipboardData(cfLink)) ||
  303.       !(lpData = GlobalLock(hData)))
  304.        {
  305.           CloseClipboard();
  306.       return;
  307.        }
  308.  
  309.  
  310.        /* Parse clipboard data */
  311.        if ((nBufLen = lstrlen(lpData)) >= APP_MAX_SIZE)
  312.        {
  313.         CloseClipboard();
  314.         GlobalUnlock(hData);
  315.         return;
  316.        }
  317.        lstrcpy(szApplication, lpData);
  318.        lpData += (nBufLen+1); /* skip over null */
  319.        if ((nBufLen = lstrlen(lpData)) >= TOPIC_MAX_SIZE)
  320.        {
  321.         CloseClipboard();
  322.         GlobalUnlock(hData);
  323.         return;
  324.        }
  325.        lstrcpy(szTopic, lpData);
  326.        lpData += (nBufLen+1); /* skip over null */
  327.        if ((nBufLen = lstrlen(lpData)) >= ITEM_MAX_SIZE)
  328.        {
  329.         CloseClipboard();
  330.         GlobalUnlock(hData);
  331.         return;
  332.        }
  333.        lstrcpy(szItem, lpData);
  334.  
  335.        GlobalUnlock(hData);
  336.        CloseClipboard();
  337.  
  338.        if (hwndClientDDE = FindConvGivenAppTopic(szApplication, szTopic))
  339.        {   /* app/topic conversation already started */
  340.        if (DoesAdviseAlreadyExist(hwndClientDDE, szItem))
  341.            MessageBox(hwndMain,"Advisory already established",
  342.                    "Client", MB_ICONEXCLAMATION | MB_OK);
  343.            else
  344.            hwndServerDDE = GetHwndServerDDE(hwndClientDDE);
  345.            SendAdvise(hwndClientDDE, hwndServerDDE, szItem);
  346.        }
  347.        else
  348.        {   /* must initiate new conversation first */
  349.        SendInitiate(szApplication, szTopic);
  350.        if (hwndClientDDE = FindConvGivenAppTopic(szApplication, szTopic))
  351.        {
  352.         hwndServerDDE = GetHwndServerDDE(hwndClientDDE);
  353.         SendAdvise(hwndClientDDE, hwndServerDDE, szItem);
  354.        }
  355.        }
  356.     }
  357.  
  358.     return;
  359. }
  360.  
  361.  
  362.  
  363. /****************************************************************************
  364.  
  365.     FUNCTION: InitAckTimeOut
  366.  
  367.     PURPOSE:  Get DDE timeout value from win.ini.  Value is in milliseconds.
  368.  
  369. ****************************************************************************/
  370. void InitAckTimeOut(void)
  371. {
  372.  
  373.    /* Finds value in win.ini section corresponding to application name */
  374.  
  375.    nAckTimeOut = GetPrivateProfileInt("Client",
  376.                    "DdeTimeOut",
  377.                    DEFAULT_ACK_TIME_OUT_MILLISEC,
  378.                                "client.ini");
  379.    return;
  380. }
  381.  
  382. /****************************************************************************
  383.  
  384.     FUNCTION: SendAdvise
  385.  
  386.     PURPOSE:  Send advise message to server.
  387.  
  388.  
  389. ****************************************************************************/
  390. void SendAdvise(hwndClientDDE, hwndServerDDE, szItem)
  391.     HWND  hwndClientDDE;
  392.     HWND  hwndServerDDE;
  393.     char * szItem;
  394. {
  395.     ATOM            atomItem;
  396.     HANDLE          hOptions;
  397.     DDEADVISE FAR * lpOptions;
  398.  
  399.     /* don't send another message requiring an ACK until first message */
  400.     /* is acknowledged                               */
  401.     if (AwaitingAck(hwndClientDDE))
  402.         return;
  403.  
  404.     if (!(hOptions 
  405.           = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (LONG)sizeof(DDEADVISE))))
  406.         return;
  407.     if (!(lpOptions
  408.           = (DDEADVISE FAR *)GlobalLock(hOptions)))
  409.     {
  410.     GlobalFree(hOptions);
  411.         return;
  412.     }
  413.     lpOptions->cfFormat = CF_TEXT;
  414.     lpOptions->fAckReq = TRUE;
  415.     lpOptions->fDeferUpd = FALSE;
  416.     GlobalUnlock(hOptions);
  417.     atomItem = GlobalAddAtom((LPSTR)szItem);
  418.     SetConvPendingAck(hwndClientDDE, ADVISE);
  419.     SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
  420.     if (!PostMessage(hwndServerDDE,
  421.             WM_DDE_ADVISE, 
  422.         hwndClientDDE,
  423.             MAKELONG(hOptions, atomItem)))
  424.     {
  425.         GlobalDeleteAtom(atomItem);
  426.         GlobalFree(hOptions);
  427.     }
  428.     return;
  429. }
  430.  
  431.  
  432.  
  433. /****************************************************************************
  434.  
  435.     FUNCTION: SendExecute
  436.  
  437.     PURPOSE:  Send execute string to server.
  438.  
  439.  
  440. ****************************************************************************/
  441. void SendExecute(hwndClientDDE, hwndServerDDE, szExecuteString)
  442.     HWND  hwndClientDDE;
  443.     HWND  hwndServerDDE;
  444.     char * szExecuteString;
  445. {
  446.     HANDLE    hExecuteString;
  447.     LPSTR    lpExecuteString;
  448.  
  449.     /* don't send another message requiring an ACK until first message */
  450.     /* is acknowledged                               */
  451.     if (AwaitingAck(hwndClientDDE))
  452.         return;
  453.  
  454.     if (!(hExecuteString
  455.           = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 
  456.             (DWORD)lstrlen(szExecuteString) + 1)))
  457.         return;
  458.     if (!(lpExecuteString
  459.       = GlobalLock(hExecuteString)))
  460.     {
  461.     GlobalFree(hExecuteString);
  462.         return;
  463.     }
  464.     lstrcpy(lpExecuteString, szExecuteString);
  465.     GlobalUnlock(hExecuteString);
  466.     SetConvPendingAck(hwndClientDDE, EXECUTE);
  467.     SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
  468.     if (!PostMessage(hwndServerDDE,
  469.         WM_DDE_EXECUTE,
  470.         hwndClientDDE,
  471.         MAKELONG(0, hExecuteString)))
  472.     {
  473.     GlobalFree(hExecuteString);
  474.     }
  475.     return;
  476. }
  477.  
  478.  
  479.  
  480. /****************************************************************************
  481.  
  482.     FUNCTION: SendInitiate
  483.  
  484.     PURPOSE:  Sends initiate message to all windows.  By the time this
  485.           function returns, all servers matching the app/topic will
  486.           have acknowledged, and this client applicaiton will have
  487.           temporarily registered the new conversations.   If more
  488.           than one server responded, then this client application
  489.           asks the user which conversation to keep; all other
  490.           conversations will then be terminated.   This function
  491.           returns the handle of the hidden DDE window used to
  492.           initiate the conversation with server(s).
  493.  
  494. ****************************************************************************/
  495. HWND SendInitiate(szApplication, szTopic)
  496.     char * szApplication;
  497.     char * szTopic;
  498. {
  499.     HWND  hwndClientDDE;
  500.     ATOM  atomApplication;
  501.     ATOM  atomTopic;
  502.  
  503.     if (!(hwndClientDDE = CreateWindow(
  504.         "ClientDDEWndClass",
  505.         "ClientDDE",
  506.         WS_CHILD,    /* not visible */
  507.         0, 0, 0, 0, /* no position or dimensions */
  508.         hwndMain,    /* parent */
  509.         NULL,    /* no menu */
  510.         hInst,
  511.         NULL)))
  512.     {
  513.     return (NULL);
  514.     }
  515.  
  516.     atomApplication
  517.         = *szApplication == 0 ? NULL : GlobalAddAtom((LPSTR)szApplication);
  518.     atomTopic
  519.         = *szTopic == 0 ? NULL : GlobalAddAtom((LPSTR)szTopic);
  520.  
  521.     /* flag bIniInitiate is queried when client processes the server's ACK */
  522.     bInInitiate = TRUE;
  523.     SendMessage(-1, 
  524.         WM_DDE_INITIATE, 
  525.     hwndClientDDE,
  526.         MAKELONG(atomApplication, atomTopic));
  527.     bInInitiate = FALSE;
  528.     if (atomApplication != NULL)
  529.         GlobalDeleteAtom(atomApplication);
  530.     if (atomTopic != NULL)
  531.         GlobalDeleteAtom(atomTopic);
  532.     return (hwndClientDDE);
  533. }
  534.  
  535. /****************************************************************************
  536.  
  537.     FUNCTION: SendPoke
  538.  
  539.     PURPOSE:  Send poke message to server.
  540.  
  541.  
  542. ****************************************************************************/
  543. void SendPoke(hwndClientDDE, hwndServerDDE, szItem, szValue)
  544.     HWND  hwndClientDDE;
  545.     HWND  hwndServerDDE;
  546.     char * szItem;
  547.     char * szValue;
  548. {
  549.     ATOM        atomItem;
  550.     HANDLE      hPokeData;
  551.     DDEPOKE FAR * lpPokeData;
  552.  
  553.     /* don't send another message requiring an ACK until first message */
  554.     /* is acknowledged                               */
  555.     if (AwaitingAck(hwndClientDDE))
  556.         return;
  557.  
  558.     /* Allocate size of DDE data header, plus the data:  a string   */
  559.     /* terminated by <CR> <LF> <NULL>.  The <NULL> is counted by    */
  560.     /* by DDEPOKE.Value[1].                                         */
  561.  
  562.     if (!(hPokeData
  563.           = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 
  564.                         (LONG)sizeof(DDEPOKE) + lstrlen(szValue) + 2)))
  565.         return;
  566.     if (!(lpPokeData
  567.           = (DDEPOKE FAR*)GlobalLock(hPokeData)))
  568.     {
  569.     GlobalFree(hPokeData);
  570.         return;
  571.     }
  572.     lpPokeData->fRelease = TRUE;
  573.     lpPokeData->cfFormat = CF_TEXT;
  574.     lstrcpy((LPSTR)lpPokeData->Value, (LPSTR)szValue);
  575.     /* each line of CF_TEXT data is terminated by CR/LF */
  576.     lstrcat((LPSTR)lpPokeData->Value, (LPSTR)"\r\n");
  577.     GlobalUnlock(hPokeData);
  578.     atomItem = GlobalAddAtom((LPSTR)szItem);
  579.     SetConvPendingAck(hwndClientDDE, POKE);
  580.     SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
  581.     if (!PostMessage(hwndServerDDE,
  582.             WM_DDE_POKE,
  583.         hwndClientDDE,
  584.             MAKELONG(hPokeData, atomItem)))
  585.     {
  586.         GlobalDeleteAtom(atomItem);
  587.         GlobalFree(hPokeData);
  588.     }
  589.     return;
  590. }
  591.  
  592.  
  593.  
  594. /****************************************************************************
  595.  
  596.     FUNCTION: SendRequest
  597.  
  598.     PURPOSE:  Send request message to server.
  599.  
  600.  
  601. ****************************************************************************/
  602. void SendRequest(hwndClientDDE, hwndServerDDE, szItem)
  603.     HWND  hwndClientDDE;
  604.     HWND  hwndServerDDE;
  605.     char * szItem;
  606. {
  607.     ATOM  atomItem;
  608.  
  609.     /* don't send another message requiring an ACK until first message */
  610.     /* is acknowledged                               */
  611.     if (AwaitingAck(hwndClientDDE))
  612.         return;
  613.  
  614.     atomItem = GlobalAddAtom((LPSTR)szItem);
  615.     SetConvPendingAck(hwndClientDDE, REQUEST);
  616.     SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
  617.     if (!PostMessage(hwndServerDDE,
  618.             WM_DDE_REQUEST,
  619.         hwndClientDDE,
  620.             MAKELONG(CF_TEXT,atomItem)))
  621.     {
  622.         GlobalDeleteAtom(atomItem);
  623.     }
  624.     return;
  625. }
  626.  
  627.  
  628. /****************************************************************************
  629.  
  630.     FUNCTION: SendTerminate
  631.  
  632.     PURPOSE:  Send terminate message to server.
  633.  
  634. ****************************************************************************/
  635. void SendTerminate(hwndClientDDE, hwndServerDDE)
  636.     HWND  hwndClientDDE;
  637.     HWND  hwndServerDDE;
  638. {
  639.     MSG msg;
  640.     LONG lTimeOut;
  641.  
  642.     SetConvInTerminateState(hwndClientDDE, hwndServerDDE);
  643.     PostMessage(hwndServerDDE, WM_DDE_TERMINATE, hwndClientDDE, 0L);
  644.     return;
  645. }
  646.  
  647.  
  648.  
  649.  
  650. /****************************************************************************
  651.  
  652.     FUNCTION: SendUnadvise
  653.  
  654.     PURPOSE:  Send unadvise message to server.
  655.  
  656.  
  657. ****************************************************************************/
  658. void SendUnadvise(hwndClientDDE, hwndServerDDE, szItem)
  659.     HWND  hwndClientDDE;
  660.     HWND  hwndServerDDE;
  661.     char * szItem;
  662. {
  663.     ATOM  atomItem;
  664.  
  665.     /* don't send another message requiring an ACK until first message */
  666.     /* is acknowledged                               */
  667.     if (AwaitingAck(hwndClientDDE))
  668.         return;
  669.  
  670.     atomItem = GlobalAddAtom((LPSTR)szItem);
  671.     SetConvPendingAck(hwndClientDDE, UNADVISE);
  672.     SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
  673.     if (!PostMessage(hwndServerDDE,
  674.             WM_DDE_UNADVISE, 
  675.         hwndClientDDE,
  676.             MAKELONG(0,atomItem)))
  677.     {
  678.         GlobalDeleteAtom(atomItem);
  679.     }
  680.     return;
  681. }
  682.  
  683.  
  684. /****************************************************************************
  685.  
  686.     FUNCTION: TerminateConversations
  687.  
  688.     PURPOSE:  Processes WM_DESTROY message, terminates all conversations.
  689.  
  690. ****************************************************************************/
  691. void TerminateConversations(void)
  692. {
  693.    HWND  hwndClientDDE;
  694.    HWND  hwndServerDDE;
  695.    LONG  lTimeOut;
  696.    MSG   msg;
  697.  
  698.  
  699.    /* Terminate each active conversation */
  700.    hwndClientDDE = NULL;
  701.    while (hwndClientDDE = GetNextConv(hwndClientDDE))
  702.    {
  703.     hwndServerDDE = GetHwndServerDDE(hwndClientDDE);
  704.     if (IsWindow(hwndServerDDE)) /* if server window still alive */
  705.         SendTerminate(hwndClientDDE, hwndServerDDE);
  706.    }
  707.  
  708.    /* Wait for all conversations to terminate or for time out */
  709.    lTimeOut = GetTickCount() + (LONG)nAckTimeOut;
  710.    while (PeekMessage(&msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
  711.    {
  712.          DispatchMessage (&msg);
  713.      if (msg.message == WM_DDE_TERMINATE)
  714.      {
  715.          if (!AtLeastOneConvActive())
  716.          break;
  717.      }
  718.          if (GetTickCount() > lTimeOut)
  719.              break;
  720.    }
  721.  
  722.    return;
  723. }
  724.