home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk12 / ddeml / dmgwndp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-22  |  58.0 KB  |  1,840 lines

  1. /****************************** Module Header ******************************\
  2. *
  3. * Module Name: DMGWNDP.C
  4. *
  5. * This module contains all the window procs for the DDE manager.
  6. *
  7. * Created: 12/23/88 sanfords
  8. *
  9. * Copyright (c) 1988, 1989  Microsoft Corporation
  10. \***************************************************************************/
  11.  
  12. #include "ddemlp.h"
  13.  
  14.  
  15. ULONG defid = QID_SYNC;
  16. XFERINFO defXferInfo = {
  17.         &defid,
  18.         1L,
  19.         XTYP_INIT,
  20.         DDEFMT_TEXT,
  21.         0L,
  22.         0L,
  23.         0L,
  24.         NULL
  25. };
  26.  
  27. void InitAck(HWND hwnd, PCLIENTINFO pci, HWND hwndServer, PDDEINIT pddei);
  28. MRESULT ClientXferReq(PXFERINFO pXferInfo, PCLIENTINFO pci, HWND hwnd);
  29. USHORT SendClientReq(PXADATA pXad, HWND hwndServer, HWND hwnd, PAPPINFO pai);
  30. void DoClientDDEmsg(PCLIENTINFO pci, HWND hwnd, USHORT msg, HWND hwndFrom,
  31.         PDDESTRUCT pddes);
  32. BOOL fExpectedMsg(PXADATA pXad, PDDESTRUCT pddes, USHORT msg, PCLIENTINFO pci);
  33. BOOL AdvanceXaction(HWND hwnd, PCLIENTINFO pci, PXADATA pXad,
  34.         PDDESTRUCT pddes, USHORT msg, PUSHORT pErr);
  35. MRESULT ClientXferRespond(PCLIENTINFO pci, PXADATA pXad, PUSHORT pErr);
  36. void FrameInitConv(HWND hwndClient, PDDEINIT pddei);
  37.  
  38. /*
  39.  * ----------------CLIENT SECTION------------------
  40.  *
  41.  * Each client conversation has associated with it a window and a queue.
  42.  * A conversation has one synchronous transaction and may have many
  43.  * asynchronous transactions.  A transaction is differientiated by its
  44.  * state and other pertinant data.  A transaction may be synchronous,
  45.  * asynchronous, (initiated by DdeClientXfer()), or it may be external,
  46.  * (initiated by an advise loop.)
  47.  *
  48.  * A transaction is active if it is in the middle of tranfer, otherwise
  49.  * it is shutdown.  A shutdown transaction is either successful or
  50.  * failed.  When an asynchronous transaction shuts down, the client
  51.  * is notified via the callback function. (XTYP_XFERCOMPLETE)
  52.  *
  53.  * The synchronous transaction, when active, is in a timeout loop which
  54.  * can shut-down the transaction at the end of a predefined time period.
  55.  * Shutdown synchronous transactions imediately transfer their information
  56.  * to the client application by returning to DdeClientXfer().
  57.  *
  58.  * asynchronous transactions remain in the client queue until removed
  59.  * by the client application via DdeCheckQueue().  
  60.  *
  61.  * external transactions take place when the client is in an advise
  62.  * data loop.  These transactions pass through the callback function to
  63.  * the client to be accepted.(XTYP_ADVDATA)
  64.  */
  65.  
  66.  
  67. /***************************** Private Function ****************************\
  68. * MRESULT EXPENTRY ClientWndProc(hwnd, msg, mp1, mp2);
  69. *
  70. *   This window controls a single DDE conversation from the CLIENT side.
  71. *   If closed, it will automaticly abort any conversationn in progress.
  72. *   It maintains an internal list of any extra WM_DDEINITIATEACK messages
  73. *   it receives so that it can be queried later about this information.
  74. *   Any extra WM_DDEINITIATEACK messages comming in will be immediately
  75. *   terminated.
  76. *   It also maintains an internal list of all items which currently are
  77. *   in active ADVISE loops.
  78. *
  79. * History:
  80. *   Created     12/16/88    Sanfords
  81. \***************************************************************************/
  82. MRESULT EXPENTRY ClientWndProc(hwnd, msg, mp1, mp2)
  83. HWND hwnd;
  84. USHORT msg;
  85. MPARAM mp1;
  86. MPARAM mp2;
  87. {
  88.     register PCLIENTINFO pci;
  89.     PAPPINFO pai;
  90.     MRESULT mrData;
  91.     PDDESTRUCT pddes;
  92.  
  93.     pci = (PCLIENTINFO)WinQueryWindowULong(hwnd, QWL_USER);
  94.  
  95.     switch (msg) {
  96.     case WM_CREATE:
  97.         /*
  98.          * allocate and initialize the client window info.
  99.          */
  100.         pai = GetCurrentAppInfo(FALSE);
  101.         SemEnter();
  102.         pci = (PCLIENTINFO)FarAllocMem(pai->hheapApp, sizeof(CLIENTINFO));
  103.         SemLeave();
  104.         if (pci == NULL) {
  105.             pai->LastError = DMGERR_MEMORY_ERROR;
  106.             return(1);          /* aboart creation - low memory */
  107.         }
  108.         WinSetWindowULong(hwnd, QWL_USER, (ULONG)pci);
  109.         pci->ci.pai = pai;
  110.         pci->ci.xad.state = CONVST_NULL;
  111.         pci->ci.xad.pXferInfo = &defXferInfo;
  112.         pci->ci.fs = 0;
  113.         pci->ci.hwndPartner = NULL;
  114.         pci->ci.hszServerApp = NULL;
  115.         pci->ci.hszTopic = NULL;
  116.         pci->pQ = NULL;    /* don't create until we need one */
  117.         if (!(pci->ci.pAdviseList = CreateLst(pai->hheapApp, sizeof(ADVLI)))) {
  118.             FarFreeMem(pai->hheapApp, (PBYTE)pci, sizeof(CLIENTINFO));
  119.             pai->LastError = DMGERR_MEMORY_ERROR;
  120.             return(1);          /* aboart creation - low memory */
  121.         }
  122.         break;
  123.  
  124.     case UMCL_INITIATE:
  125.         if (pci->ci.xad.state == CONVST_NULL) {
  126.             return(ClientInitiate(hwnd, (PINITINFO)mp1, pci));
  127.         }
  128.         break;
  129.  
  130.     case WM_DDE_INITIATEACK:
  131.         InitAck(hwnd, pci, mp1, mp2);
  132.         DosFreeSeg(SELECTOROF(mp2));
  133.         return(1);
  134.         break;
  135.  
  136.     case WM_DESTROY:
  137.         SemCheckOut();
  138.         if (pci->ci.fs & ST_CONNECTED) {
  139.             /*
  140.              * stop any advises in progress
  141.              */
  142.             if (pci->ci.fs & ST_ADVISE) {
  143.                 pddes = AllocDDESel(0, 0, 0, 0L, NULL);
  144.                 MyDdePostMsg(pci->ci.hwndPartner, hwnd, WM_DDE_UNADVISE,
  145.                         (PMYDDES)pddes, pci->ci.pai, MDPM_FREEHDATA);
  146.             }
  147.             WinSendMsg(hwnd, UMCL_TERMINATE, 0L, 0L);
  148.             /*
  149.              * decrement the use counts on hszs we are done with.
  150.              */
  151.             FreeHsz(pci->ci.hszServerApp);
  152.             FreeHsz(pci->ci.hszTopic);
  153.         }
  154.         
  155.         DestroyLst(pci->ci.pAdviseList);
  156.         
  157.         SemEnter();
  158.         DestroyQ(pci->pQ);
  159.         FarFreeMem(pci->ci.pai->hheapApp, (PBYTE)pci, sizeof(CLIENTINFO));
  160.         SemLeave();
  161.         break;
  162.  
  163.     case UMCL_TERMINATE:
  164.         /*
  165.          * terminates any conversation in progress
  166.          */
  167.         if (pci->ci.fs & ST_CONNECTED) {
  168.             pci->ci.fs = pci->ci.fs & ~ST_CONNECTED;
  169.             pci->ci.xad.state = CONVST_TERMINATED;
  170.             if (WinIsWindow(DMGHAB, pci->ci.hwndPartner))
  171.                 WinDdePostMsg(pci->ci.hwndPartner, hwnd, WM_DDE_TERMINATE, 0L, FALSE);
  172.         }
  173.         break;
  174.  
  175.     case UMCL_XFER:
  176.         if (!(pci->ci.fs & ST_CONNECTED)) {
  177.             pci->ci.pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  178.             return(0);
  179.         }
  180.         return(ClientXferReq((PXFERINFO)mp1, pci, hwnd));
  181.         break;
  182.  
  183.     case WM_DDE_DATA:
  184.     case WM_DDE_ACK:
  185.         DoClientDDEmsg(pci, hwnd, msg, (HWND)mp1, (PDDESTRUCT)mp2);
  186.         break;
  187.  
  188.     case WM_DDE_TERMINATE:
  189.         SemCheckOut();
  190.         /*
  191.          * only respond if this is for us.
  192.          */
  193.         if ((HWND)mp1 != pci->ci.hwndPartner) {
  194.            DosFreeSeg(SELECTOROF(mp2));
  195.            break;
  196.         }
  197.         WinSendMsg(hwnd, UMCL_TERMINATE, 0L, 0L);
  198.         DosFreeSeg(SELECTOROF(mp2));
  199.         break;
  200.  
  201.     case UM_QUERY:
  202.         /*
  203.          * LOUSHORT(mp1) = info index.
  204.          * mp2 = pData.     If pData==0, return data else copy into pData.
  205.          */
  206.         switch (LOUSHORT(mp1)) {
  207.         case Q_STATUS:
  208.              mrData = (MRESULT)pci->ci.fs;
  209.              break;
  210.  
  211.         case Q_CLIENT:
  212.              mrData = TRUE;
  213.              break;
  214.  
  215.         case Q_APPINFO:
  216.              mrData = pci->ci.pai;
  217.              break;
  218.  
  219.         case Q_APPNAME:
  220.              mrData = *(PHSZ)PTOPPILEITEM(pci->ci.pai->pAppNamePile);
  221.              break;
  222.              
  223.         case Q_ALL:
  224.              mrData = (MRESULT)(CLIENTINFO FAR *)pci;
  225.              break;
  226.         }
  227.         if (mp2 == 0)
  228.             return(mrData);
  229.         else
  230.             *(MRESULT FAR *)mp2 = mrData;
  231.         return(1);
  232.         break;
  233.  
  234.     default:
  235.         return(WinDefWindowProc(hwnd, msg, mp1, mp2));
  236.         break;
  237.     }
  238.     return(0);
  239. }
  240.  
  241.  
  242.  
  243. /*
  244.  * Client response to a WM_DDE_INITIATEACK message when expected.
  245.  */
  246. void InitAck(hwnd, pci, hwndServer, pddei)
  247. HWND hwnd;
  248. PCLIENTINFO pci;
  249. HWND hwndServer;
  250. PDDEINIT pddei;
  251. {
  252.     SemCheckOut();
  253.     
  254.     if (pci->ci.fs & ST_CONNECTED) {
  255.         /*
  256.          * extra server - spawn another client window.  (we assume we
  257.          * will only get extras if enumerating.)
  258.          */
  259.         AssertF(WinQueryWindow(hwnd, QW_PARENT, FALSE) != pci->ci.pai->hwndDmg,
  260.             "Improper client spawn")
  261.         if (hwndServer != pci->ci.hwndPartner) {
  262.             WinSendMsg(hwndServer, WM_DDE_TERMINATE, hwnd, 0L);
  263.             GetDDEClientWindow(WinQueryWindow(hwnd, QW_PARENT, FALSE),
  264.                     hwndServer, hwndServer, pci->ci.hszServerApp,
  265.                     pci->ci.hszTopic, &pci->ci.cc);
  266.         }
  267.         return;
  268.     }
  269.  
  270.     if (pci->ci.xad.state != CONVST_INIT1) 
  271.         return;
  272.         
  273.     /*
  274.      * first one back... lock in!
  275.      */
  276.     pci->ci.hwndPartner = hwndServer;
  277.     pci->ci.xad.state = CONVST_CONNECTED;
  278.     pci->ci.fs |= ST_CONNECTED;
  279.     if (WinQueryWindowPtr(hwndServer, QWP_PFNWP) == ServerWndProc) 
  280.         pci->ci.fs |= ST_INTRADLL;
  281.     
  282.     /*
  283.      * If the connection was made using a wild app name, we want to
  284.      * hack in an apropriate name so QueryConvInfo can give the app
  285.      * something useful to refer to this guy as.
  286.      *
  287.      * - the protocol is little help here.
  288.      */
  289.     if (pci->ci.hszServerApp == 0) {
  290.         if (WinQueryWindowPtr(hwndServer, QWP_PFNWP) == ServerWndProc) {
  291.             /*
  292.              * one of ours! simple.
  293.              */
  294.             pci->ci.hszServerApp = (PAPPINFO)WinSendMsg(pci->ci.hwndPartner,
  295.                     UM_QUERY, (MPARAM)Q_APPNAME, 0L);
  296.         } else {
  297.             /*
  298.              * Try the psz in pddei.  Maybe the server set it properly
  299.              * before returning it.
  300.              */
  301.             if (!(pci->ci.hszServerApp =
  302.                     GetHsz(PSZAPP(pddei), pci->ci.cc.idCountry,
  303.                             pci->ci.cc.usCodepage, TRUE))) {
  304.                         
  305.                 PSZ pszT;
  306.                 USHORT cb;
  307.                 
  308.                 /*
  309.                  * WORST CASE:
  310.                  * Until a better way is found, we set the hszServerApp to
  311.                  * the title of the frame window.
  312.                  */
  313.                 if (pszT = FarAllocMem(pci->ci.pai->hheapApp,
  314.                         cb = WinQueryWindowTextLength(pci->ci.hwndFrame) + 1)) {
  315.                     WinQueryWindowText(pci->ci.hwndFrame, cb, (PSZ)pszT);
  316.                     pci->ci.hszServerApp = GetHsz(pszT, pci->ci.cc.idCountry,
  317.                             pci->ci.cc.usCodepage, TRUE);
  318.                     FarFreeMem(pci->ci.pai->hheapApp, (PBYTE)pszT, cb);
  319.                 }
  320.             }
  321.         }
  322.     }
  323.     /*
  324.      * Now what if the topic was wild?
  325.      */
  326.     if (pci->ci.hszTopic == 0) {
  327.         if (WinQueryWindowPtr(hwndServer, QWP_PFNWP) == ServerWndProc) {
  328.             /*
  329.              * one of ours! simple.
  330.              */
  331.             pci->ci.hszTopic = (PAPPINFO)WinSendMsg(pci->ci.hwndPartner,
  332.                     UM_QUERY, (MPARAM)Q_TOPIC, 0L);
  333.         } else {
  334.             /*
  335.              * Try the psz in pddei.  Maybe the server set it properly
  336.              * before returning it.  If this doesn't work were out of
  337.              * luck, keep it wild.
  338.              */
  339.             pci->ci.hszServerApp = GetHsz(PSZAPP(pddei), pci->ci.cc.idCountry,
  340.                     pci->ci.cc.usCodepage, TRUE);
  341.         }                
  342.     }
  343. }
  344.  
  345.  
  346.  
  347. /***************************** Private Function ****************************\
  348. * Processes a client transfer request issued by one of the ClientXfer
  349. * functions.  This may be synchronous or asynchronous.
  350. *
  351. * History:
  352. *   Created     9/1/89    Sanfords
  353. \***************************************************************************/
  354. MRESULT ClientXferReq(pXferInfo, pci, hwnd)
  355. PXFERINFO pXferInfo;
  356. PCLIENTINFO pci;
  357. HWND hwnd;
  358. {
  359.     PCQDATA pcqd;
  360.     MRESULT retVal;
  361.  
  362.     if (pXferInfo->ulTimeout == TIMEOUT_ASYNC) {
  363.         /*
  364.          * add a client queue item to track this transaction and return
  365.          * the ID.
  366.          */
  367.         if (pci->pQ == NULL)
  368.             pci->pQ = CreateQ(sizeof(CQDATA));
  369.         if (pci->pQ == NULL) {
  370.             pci->ci.pai->LastError = DMGERR_MEMORY_ERROR;
  371.             return(0);
  372.         }
  373.         pcqd = (PCQDATA)Addqi(pci->pQ);
  374.         if (pcqd == NULL) {
  375.             pci->ci.pai->LastError = DMGERR_MEMORY_ERROR;
  376.             return(0);
  377.         }
  378.         CopyBlock((PBYTE)pXferInfo, (PBYTE)&pcqd->XferInfo, sizeof(XFERINFO));
  379.         pcqd->xad.state = CONVST_CONNECTED;
  380.         pcqd->xad.pddes = 0;
  381.         pcqd->xad.LastError = DMGERR_NO_ERROR;
  382.         pcqd->xad.pXferInfo = &pcqd->XferInfo;
  383.         /*
  384.          * Get transaction started - if it fails, quit now.
  385.          */
  386.         if ((pcqd->xad.LastError = SendClientReq(&pcqd->xad,
  387.                 pci->ci.hwndPartner, hwnd, pci->ci.pai)) == DMGERR_SERVER_DIED) {
  388.             pci->ci.fs = pci->ci.fs & ~ST_CONNECTED;
  389.             Deleteqi(pci->pQ, MAKEID(pcqd));
  390.             pci->ci.pai->LastError = DMGERR_SERVER_DIED;
  391.             return(0);
  392.         }
  393.         return((MRESULT)MAKEID(pcqd));
  394.     }
  395.     
  396.     /*
  397.      * if not quiesent, yet synchronous, tell him were busy.
  398.      * (this case could happen on a recursive call.)
  399.      */
  400.     if (pci->ci.xad.state != CONVST_CONNECTED) {
  401.         pci->ci.pai->LastError = DMGERR_BUSY;
  402.         return(0);
  403.     }
  404.     /*
  405.      * Set this so messages comming in during the conversation know whats up
  406.      */
  407.     pci->ci.xad.pXferInfo = pXferInfo;
  408.     
  409.     if ((pci->ci.pai->LastError = SendClientReq(&pci->ci.xad,
  410.             pci->ci.hwndPartner, hwnd, pci->ci.pai)) == DMGERR_SERVER_DIED) {
  411.         pci->ci.fs = pci->ci.fs & ~ST_CONNECTED;
  412.     }
  413.     
  414.     if (pci->ci.pai->LastError != DMGERR_NO_ERROR)
  415.         return(0);
  416.     /*
  417.      *  reset the LastError here so we know if we had problems while
  418.      *  in the modal loop.
  419.      */
  420.     pci->ci.pai->LastError = DMGERR_NO_ERROR;
  421.     /*
  422.      * timeout - modal loop.
  423.      */
  424.     if (!timeout(pci->ci.pai, pXferInfo->ulTimeout, hwnd)) {
  425.         /*
  426.          * reentrency or client has unregistered
  427.          */
  428.         return(0);
  429.     }
  430.     /*
  431.      * check results - lasterror already set by timeout().
  432.      * Synchronous conversation must be reset to quiesent by the time we
  433.      * give up.
  434.      */
  435.     if (pci->ci.xad.state == CONVST_INCOMPLETE) {
  436.         pci->ci.xad.state = CONVST_CONNECTED;
  437.         return(0);
  438.     }
  439.  
  440.     retVal = ClientXferRespond(pci, &pci->ci.xad, &pci->ci.pai->LastError);
  441.     if (pci->ci.xad.state == CONVST_INCOMPLETE) 
  442.         pci->ci.xad.state = CONVST_CONNECTED;
  443.         
  444.     return(retVal);
  445. }
  446.  
  447.  
  448.  
  449.      
  450. /***************************** Private Function ****************************\
  451. * This routine sends the apropriate initiation messages for starting a
  452. * client request according to the transaction data given.
  453. *
  454. * History:
  455. *   Created     9/1/89    Sanfords
  456. \***************************************************************************/
  457. USHORT SendClientReq(pXad, hwndServer, hwnd, pai)     
  458. PXADATA pXad;
  459. HWND hwndServer;
  460. HWND hwnd;
  461. PAPPINFO pai;
  462. {
  463.     USHORT fsStatus = 0;
  464.     USHORT msg;
  465.     BOOL fCopy;
  466.     PDDESTRUCT pddes;
  467.     
  468.     switch (pXad->pXferInfo->usType) {
  469.     case XTYP_REQUEST:
  470.         msg = WM_DDE_REQUEST;
  471.         pXad->state = CONVST_REQSENT;
  472.         fCopy = FALSE;
  473.         break;
  474.  
  475.     case XTYP_POKE:
  476.         msg = WM_DDE_POKE;
  477.         pXad->state = CONVST_POKESENT;
  478.         fCopy = TRUE;
  479.         break;
  480.  
  481.     case XTYP_EXEC:
  482.         msg = WM_DDE_EXECUTE;
  483.         pXad->state = CONVST_EXECSENT;
  484.         fCopy = TRUE;
  485.         break;
  486.  
  487.     case XTYP_ADVSTART:
  488.     case XTYP_ADVSTART | XTYPF_NODATA:
  489.     case XTYP_ADVSTART | XTYPF_ACKREQ:
  490.     case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
  491.         fsStatus = pXad->pXferInfo->usType & (DDE_FACKREQ | DDE_FNODATA);
  492.         msg = WM_DDE_ADVISE;
  493.         pXad->state = CONVST_ADVSENT;
  494.         fCopy = FALSE;
  495.         break;
  496.  
  497.     case XTYP_ADVSTOP:
  498.         msg = WM_DDE_UNADVISE;
  499.         pXad->state = CONVST_UNADVSENT;
  500.         fCopy = FALSE;
  501.         break;
  502.  
  503.     default:
  504.         return(DMGERR_INVALIDPARAMETER);
  505.         break;
  506.     }
  507.     
  508.     /*
  509.      * Send transfer
  510.      */
  511.     if ((pddes = AllocDDESel(fsStatus, pXad->pXferInfo->usFmt,
  512.             pXad->pXferInfo->hszItem, fCopy ? pXad->pXferInfo->cb : 0, NULL))
  513.             == 0) {
  514.         pXad->state = CONVST_CONNECTED;
  515.         return(DMGERR_MEMORY_ERROR);
  516.     }
  517.  
  518.     if (fCopy)
  519.         CopyHugeBlock((PBYTE)pXad->pXferInfo->pData, DDES_PABDATA(pddes),
  520.                 pXad->pXferInfo->cb);
  521.  
  522.     if (WinIsWindow(DMGHAB, hwndServer)) {
  523.         if (!MyDdePostMsg(hwndServer, hwnd, msg, (PMYDDES)pddes, pai, MDPM_FREEHDATA)) {
  524.             pXad->state = CONVST_CONNECTED;
  525.             return(DMGERR_POSTMSG_FAILED);
  526.         }
  527.     } else {
  528.         /*
  529.          * We lost the server, we are TERMINATED arnold!
  530.          */
  531.         pXad->state = CONVST_TERMINATED;
  532.         FreeData((PMYDDES)pddes, pai);
  533.         return(DMGERR_SERVER_DIED);
  534.     }
  535.     return(DMGERR_NO_ERROR);
  536. }
  537.  
  538.  
  539.  
  540.  
  541.  
  542. /***************************** Private Function ****************************\
  543. * This handles client window processing of WM_DDE_ACK and WM_DDE_DATA msgs.
  544. * On exit pddes is freed.
  545. *
  546. * History:
  547. *   Created     9/1/89    Sanfords
  548. \***************************************************************************/
  549. void DoClientDDEmsg(pci, hwnd, msg, hwndFrom, pddes)
  550. PCLIENTINFO pci;
  551. HWND hwnd;
  552. USHORT msg;
  553. HWND hwndFrom;
  554. PDDESTRUCT pddes;
  555. {
  556.     PCQDATA pqd;
  557.     int i;
  558.     HSZ hszItem;
  559.     PADVLI pAdviseItem;
  560.     
  561.     /*
  562.      * make sure its for us.
  563.      */
  564.     if (hwndFrom != pci->ci.hwndPartner || !(pci->ci.fs & ST_CONNECTED)) {
  565.         FreeData((PMYDDES)pddes, pci->ci.pai);
  566.         return;
  567.     }
  568.     /*
  569.      * Check if it fits the synchronous transaction data
  570.      */
  571.     if (fExpectedMsg(&pci->ci.xad, pddes, msg, pci)) {
  572.         if (AdvanceXaction(hwnd, pci, &pci->ci.xad, pddes, msg,
  573.                 &pci->ci.pai->LastError))
  574.             WinPostMsg(hwnd, WM_TIMER, (MPARAM)TID_TIMEOUT, 0L);
  575.         return;
  576.     }
  577.      
  578.     /*
  579.      * See if it fits any asynchronous transaction data - if any exist
  580.      */
  581.     if (pci->pQ != NULL && pci->pQ->pqiHead != NULL) {
  582.         SemEnter();
  583.         pqd = (PCQDATA)pci->pQ->pqiHead;
  584.         /*
  585.          * cycle from oldest to newest.
  586.          */
  587.         for (i = pci->pQ->cItems; i; i--) {
  588.             pqd = (PCQDATA)pqd->next;
  589.             if (!fExpectedMsg(&pqd->xad, pddes, msg, pci))
  590.                 continue;
  591.             if (AdvanceXaction(hwnd, pci, &pqd->xad, pddes, msg,
  592.                     &pqd->xad.LastError)) {
  593.                 ClientXferRespond(pci, &pqd->xad, &pqd->xad.LastError);
  594.                 SemLeave();
  595.                 MakeCallback(pci->ci.pai, hwnd, (HSZ)0L, (HSZ)0L, 0,
  596.                         XTYP_XFERCOMPLETE, (HDMGDATA)MAKEID(pqd),
  597.                         0, 0, hwndFrom);
  598.                 return;
  599.             }
  600.             SemLeave();
  601.             return;
  602.         }
  603.         SemLeave();
  604.     }
  605.     /*
  606.      * It doesn't fit anything, check for an advise data message.
  607.      */
  608.     if (msg == WM_DDE_DATA) {
  609.         hszItem = GetHszItem((PMYDDES)pddes, &pci->ci.cc, TRUE);
  610.         if (pAdviseItem = (PADVLI)FindAdvList(pci->ci.pAdviseList, hszItem,
  611.                 pddes->usFormat)) {
  612.             MakeCallback(pci->ci.pai, (HCONV)hwnd, pci->ci.hszTopic,
  613.                 hszItem, pddes->usFormat, XTYP_ADVDATA, pddes, msg,
  614.                 pddes->fsStatus, pci->ci.hwndPartner);
  615.         } else {
  616.             FreeHsz(hszItem);
  617.         }
  618.         return;
  619.     }
  620.     /*
  621.      * throw it away
  622.      */
  623.     FreeData((PMYDDES)pddes, pci->ci.pai);
  624.     return;
  625. }
  626.  
  627.  
  628.  
  629. /***************************** Private Function ****************************\
  630. * This routine matches a conversation transaction with a DDE message.  If
  631. * the state, usType, format, itemname dde structure data and the message
  632. * received all agree, TRUE is returned.
  633. *
  634. * History:
  635. *   Created     9/1/89    Sanfords
  636. \***************************************************************************/
  637. BOOL fExpectedMsg(pXad, pddes, msg, pci)
  638. PXADATA pXad;
  639. PDDESTRUCT pddes;
  640. USHORT msg;
  641. PCLIENTINFO pci;
  642. {
  643.     HSZ hsz = 0;
  644.     BOOL fRet = FALSE;
  645.     
  646.     if (!(pXad->state > CONVST_INIT1 &&
  647.             pddes->usFormat == pXad->pXferInfo->usFmt &&
  648.             (hsz = GetHszItem((PMYDDES)pddes, &pci->ci.cc, TRUE)) ==
  649.             pXad->pXferInfo->hszItem)) {
  650.         goto Exit;
  651.     }
  652.     switch (pXad->state) {
  653.     case CONVST_REQSENT:
  654.         if (msg == WM_DDE_DATA && !(pddes->fsStatus & DDE_FRESPONSE))
  655.             /*
  656.              * Not data in response to a request!
  657.              */
  658.             break;
  659.         fRet = (msg == WM_DDE_ACK || msg == WM_DDE_DATA);
  660.         break;
  661.         
  662.     case CONVST_POKESENT:
  663.     case CONVST_EXECSENT:
  664.     case CONVST_ADVSENT:
  665.     case CONVST_UNADVSENT:
  666.         fRet = (msg == WM_DDE_ACK);
  667.         break;
  668.     }
  669.     
  670. Exit:    
  671.     FreeHsz(hsz);
  672.     return(fRet);
  673. }
  674.  
  675.  
  676.  
  677. /***************************** Private Function ****************************\
  678. * This function assumes that msg is an apropriate message for the transaction
  679. * referenced by pXad.  It acts on msg as apropriate.  pddes is the DDESTRUCT
  680. * associated with msg.
  681. *
  682. * Returns fSuccess ie: transaction is ready to close up.
  683. *
  684. * History:
  685. *   Created     9/1/89    Sanfords
  686. \***************************************************************************/
  687. BOOL AdvanceXaction(hwnd, pci, pXad, pddes, msg, pErr)
  688. HWND hwnd;
  689. PCLIENTINFO pci;
  690. PXADATA pXad;
  691. PDDESTRUCT pddes;
  692. USHORT msg;
  693. PUSHORT pErr;
  694. {
  695.     switch (msg) {
  696.     case WM_DDE_ACK:
  697.         switch (pXad->state) {
  698.         case CONVST_ADVSENT:
  699.         case CONVST_EXECSENT:
  700.         case CONVST_POKESENT:
  701.         case CONVST_REQSENT:
  702.         case CONVST_UNADVSENT:
  703.             if (pddes->fsStatus & DDE_FACK) {
  704.                 /*
  705.                  * handle successes
  706.                  */
  707.                 switch (pXad->state) {
  708.                 case CONVST_POKESENT:
  709.                     pXad->state = CONVST_POKEACKRCVD;
  710.                     break;
  711.  
  712.                 case CONVST_EXECSENT:
  713.                     pXad->state = CONVST_EXECACKRCVD;
  714.                     break;
  715.  
  716.                 case CONVST_ADVSENT:
  717.                     pXad->state = CONVST_ADVACKRCVD;
  718.                     break;
  719.  
  720.                 case CONVST_UNADVSENT:
  721.                     pXad->state = CONVST_UNADVACKRCVD;
  722.                     break;
  723.  
  724.                 case CONVST_REQSENT:
  725.                     /*
  726.                      * requests are not expected to send a +ACK.  only
  727.                      * -ACK or data.  We ignore a +ACK to a request.
  728.                      */
  729.                     FreeData((PMYDDES)pddes, pci->ci.pai);
  730.                     return(FALSE);
  731.                 }
  732.             } else {
  733.                 /*
  734.                  * handle the expected ACK failures.
  735.                  */
  736.                 *pErr = DMGERR_NOTPROCESSED;
  737.                 if (pddes->fsStatus & DDE_FBUSY)
  738.                     *pErr = DMGERR_BUSY;
  739.                 pXad->state = CONVST_INCOMPLETE;
  740.             }
  741.         }
  742.         FreeData((PMYDDES)pddes, pci->ci.pai);
  743.         return(TRUE);
  744.         break;
  745.  
  746.     case WM_DDE_DATA:
  747.         switch (pXad->state) {
  748.         case CONVST_REQSENT:
  749.             /*
  750.              * send an ack if requested - we dare not return the given
  751.              * pddes because it may be a data item sent to several
  752.              * clients and we would mess up the fsStatus word for
  753.              * all processes involved.
  754.              */
  755.             if (pddes->fsStatus & DDE_FACKREQ) {
  756.                 MyDdePostMsg(pci->ci.hwndPartner, hwnd, WM_DDE_ACK,
  757.                         (PMYDDES)AllocDDESel(DDE_FACK, pddes->usFormat,
  758.                         pXad->pXferInfo->hszItem, 0L, NULL),
  759.                         pci->ci.pai, MDPM_FREEHDATA);
  760.             }
  761.             pXad->state = CONVST_DATARCVD;
  762.             /*
  763.              * We do NOT free the selector here yet because it will be 
  764.              * given to the client via pXad->pddes.
  765.              */
  766.             pXad->pddes = pddes;
  767.             return(TRUE);
  768.             break;
  769.         }
  770.     }
  771.     return(FALSE);
  772. }
  773.  
  774.  
  775.     
  776. /***************************** Private Function ****************************\
  777. * This function assumes that a client transfer request has been completed -
  778. * or should be completed by the time this is called.
  779. *
  780. * pci contains general client info
  781. * pXad contains the transaction info
  782. * pErr points to where to place the LastError code.
  783. *
  784. * Returns 0 on failure
  785. * Returns TRUE or a Data Selector on success.
  786. * On failure, the conversation is left in a CONVST_INCOMPLETE state.
  787. * On success, the conversation is left in a CONVST_CONNECTED state.
  788. *
  789. * History:
  790. *   Created     9/1/89    Sanfords
  791. \***************************************************************************/
  792. MRESULT ClientXferRespond(pci, pXad, pErr)
  793. PCLIENTINFO pci;
  794. PXADATA pXad;
  795. PUSHORT pErr;
  796. {
  797.     if (pXad->state == CONVST_INCOMPLETE) 
  798.         return(0);
  799.         
  800.     switch (pXad->pXferInfo->usType) {
  801.     case XTYP_REQUEST:
  802.         if (pXad->state != CONVST_DATARCVD) {
  803.             if (*pErr == DMGERR_NO_ERROR)
  804.                 *pErr = DMGERR_DATAACKTIMEOUT;
  805.             goto failexit;
  806.         }
  807.         pXad->state = CONVST_CONNECTED;
  808.         return(pXad->pddes);
  809.         break;
  810.  
  811.     case XTYP_POKE:
  812.         if (pXad->state != CONVST_POKEACKRCVD) {
  813.             if (*pErr == DMGERR_NO_ERROR)
  814.                 *pErr = DMGERR_POKEACKTIMEOUT;
  815.             goto failexit;
  816.         }
  817.         pXad->state = CONVST_CONNECTED;
  818.         return(TRUE);
  819.         break;
  820.  
  821.     case XTYP_EXEC:
  822.         if (pXad->state != CONVST_EXECACKRCVD) {
  823.             if (*pErr == DMGERR_NO_ERROR)
  824.                 *pErr = DMGERR_EXECACKTIMEOUT;
  825.             goto failexit;
  826.         }
  827.         pXad->state = CONVST_CONNECTED;
  828.         return(TRUE);
  829.         break;
  830.  
  831.     case XTYP_ADVSTART:
  832.     case XTYP_ADVSTART | XTYPF_NODATA:
  833.     case XTYP_ADVSTART | XTYPF_ACKREQ:
  834.     case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
  835.         if (pXad->state != CONVST_ADVACKRCVD) {
  836.             if (*pErr == DMGERR_NO_ERROR)
  837.                 *pErr = DMGERR_ADVACKTIMEOUT;
  838.             goto failexit;
  839.         }
  840.         if (!AddAdvList(pci->ci.pAdviseList, pXad->pXferInfo->hszItem,
  841.                 pXad->pXferInfo->usType & (DDE_FACKREQ | DDE_FNODATA),
  842.                 pXad->pXferInfo->usFmt)) {
  843.             pXad->state = CONVST_INCOMPLETE;
  844.             pci->ci.pai->LastError = DMGERR_MEMORY_ERROR;
  845.             return(FALSE);
  846.         } else {
  847.             pXad->state = CONVST_CONNECTED;
  848.             pci->ci.fs |= ST_ADVISE;
  849.             return(TRUE);
  850.         }
  851.         break;
  852.  
  853.     case XTYP_ADVSTOP:
  854.         if (pXad->state != CONVST_UNADVACKRCVD) {
  855.             if (*pErr == DMGERR_NO_ERROR)
  856.                 *pErr = DMGERR_UNADVACKTIMEOUT;
  857.             goto failexit;
  858.         }
  859.         if (!DeleteAdvList(pci->ci.pAdviseList, pXad->pXferInfo->hszItem,
  860.                 pXad->pXferInfo->usFmt))
  861.             pci->ci.fs &= ~ST_ADVISE;
  862.         pXad->state = CONVST_CONNECTED;
  863.         return(TRUE);
  864.         break;
  865.  
  866.     }
  867.     
  868. failexit:
  869.     pXad->state = CONVST_INCOMPLETE;
  870.     return(0);
  871. }
  872.  
  873.  
  874.  
  875.  
  876.  
  877.  
  878. /*
  879.  * ----------------------------SERVER SECTION--------------------------------
  880.  */
  881.  
  882.  
  883.  
  884. /***************************** Public  Function ****************************\
  885. * MRESULT EXPENTRY ServerWndProc(hwnd, msg, mp1, mp2)
  886. * HWND hwnd;
  887. * USHORT msg;
  888. * MPARAM mp1;
  889. * MPARAM mp2;
  890. *
  891. * DESCRIPTION:
  892. *   This processes DDE conversations from the server end.
  893. *   It stores internal information and acts much like a state machine.
  894. *   If closed, it will automaticly abort any conversation in progress.
  895. *   It also maintains an internal list of all items which currently are
  896. *   in active ADVISE loops.
  897. * PUBDOC START
  898. *   These server windows have the feature that a conversation can be
  899. *   re-initiated with them by a client.  The Client merely terminates
  900. *   the conversation and then re-initiates by using a SendMsg to this
  901. *   window.  This allows a client to change the topic of the conversation
  902. *   or to pass the conversation on to another client window without
  903. *   loosing the server it initiated with.   This is quite useful for
  904. *   wild initiates.
  905. * PUBDOC END
  906. *
  907. * History:
  908. * 10/18/89 sanfords Added hack to make hszItem==0L when offszItem==offabData.
  909. * 1/4/89   sanfords created 
  910. \***************************************************************************/
  911. MRESULT EXPENTRY ServerWndProc(hwnd, msg, mp1, mp2)
  912. HWND hwnd;
  913. USHORT msg;
  914. MPARAM mp1;
  915. MPARAM mp2;
  916. {
  917. #define PDDES ((PDDESTRUCT)mp2)    
  918.     register PSERVERINFO psi;
  919.     MPARAM mrData;
  920.     PADVLI pAdviseItem;
  921.     PSZ pszApp, pszTopic;
  922.     HSZ hsz;
  923.     USHORT cchApp, cchTopic;
  924.     USHORT usType;
  925.     HDMGDATA hDmgData = 0L;
  926.     BOOL fResult;
  927.     
  928.  
  929.     psi = (PSERVERINFO)WinQueryWindowULong(hwnd, QWL_USER);
  930.  
  931.     switch (msg) {
  932.     case WM_DDE_REQUEST:
  933.     case WM_DDE_ACK:
  934.     case WM_DDE_ADVISE:
  935.     case WM_DDE_UNADVISE:
  936.     case WM_DDE_POKE:
  937.     case WM_DDE_EXECUTE:
  938.         /*
  939.          * only respond if this is for us.
  940.          */
  941.         if ((HWND)mp1 != psi->ci.hwndPartner || !(psi->ci.fs & ST_CONNECTED)) {
  942.             FreeData((PMYDDES)mp2, psi->ci.pai);
  943.             return(0);
  944.         }
  945.     }
  946.  
  947.     switch (msg) {
  948.     case WM_CREATE: {
  949.             PAPPINFO pai;
  950.             
  951.             /*
  952.              * allocate and initialize the server window info.
  953.              */
  954.             pai = GetCurrentAppInfo(FALSE);
  955.             SemEnter();
  956.  
  957.             if (!(psi = (PSERVERINFO)FarAllocMem(pai->hheapApp, sizeof(SERVERINFO))))
  958.                 goto LowMem;
  959.             FillBlock((PBYTE)&psi->ci, sizeof(COMMONINFO), 0);
  960.             if (!(psi->ci.pAdviseList = CreateLst(pai->hheapApp, sizeof(ADVLI)))) {
  961.                 FarFreeMem(pai->hheapApp, (PBYTE)psi, sizeof(SERVERINFO));
  962. LowMem:                        
  963.                 pai->LastError = DMGERR_MEMORY_ERROR;
  964.                 SemLeave();
  965.                 return(1);          /* abort creation - low memory */
  966.             }
  967.             SemLeave();
  968.             psi->ci.pai = pai;
  969.             psi->ci.xad.state = CONVST_NULL;
  970.             WinSetWindowULong(hwnd, QWL_USER, (ULONG)psi);
  971.         }
  972.         break;
  973.  
  974.     case UMSR_INITIATE:
  975.         /*
  976.          * This was sent by the subclassed frame of the server app.
  977.          * The frame has already queried the server for permission
  978.          * to create this window.
  979.          *
  980.          * If mp2 is NULL, this is a hot server window waiting for
  981.          * a WM_DDE_INITIATE.
  982.          */
  983. #define pii ((PINITINFO)mp1)         
  984.         IncHszCount(psi->ci.hszServerApp = pii->hszAppName);
  985.         IncHszCount(psi->ci.hszTopic = pii->hszTopic);
  986.         psi->ci.hwndPartner = (HWND)mp2;
  987.         psi->ci.hwndFrame = FindFrame(psi->ci.hwndPartner);
  988.         psi->ci.cc.fsContext = pii->pCC->fsContext;
  989.         psi->ci.cc.idCountry = pii->pCC->idCountry;
  990.         psi->ci.cc.usCodepage = pii->pCC->usCodepage;
  991.         psi->ci.fs |= ST_CONNECTED;
  992.         psi->ci.xad.state = CONVST_CONNECTED;
  993.         
  994.         SemEnter();
  995.         pszApp = pszFromHsz(psi->ci.hszServerApp, &cchApp);
  996.         pszTopic = pszFromHsz(psi->ci.hszTopic, &cchTopic);
  997.         SemLeave();
  998.  
  999.         if (mp2)
  1000.             WinDdeRespond((HWND)mp2, hwnd, pszApp, pszTopic);
  1001.         
  1002.         SemEnter();
  1003.         FarFreeMem(hheapDmg, (PBYTE)pszApp, cchApp);
  1004.         FarFreeMem(hheapDmg, (PBYTE)pszTopic, cchTopic);
  1005.         SemLeave();
  1006.         
  1007.         return(1);
  1008. #undef pii        
  1009.         break;
  1010.  
  1011.     case WM_DDE_INITIATE:
  1012.         /*
  1013.          * This will happen when a client tries to re-initiate a conversation
  1014.          * with this server.  We allow about 10 seconds after termination
  1015.          * for another client to connect specifically with this window.
  1016.          * This allows a client to swap windows on its end of the conversation.
  1017.          */
  1018.  
  1019.         if (psi->ci.xad.state == CONVST_TERMINATED &&
  1020.                 (psi->ci.hszServerApp == GetHsz(PSZAPP(mp2), psi->ci.cc.idCountry,
  1021.                         psi->ci.cc.usCodepage, FALSE))) {
  1022.                     
  1023.             WinStopTimer(DMGHAB, hwnd, TID_SELFDESTRUCT);
  1024.             hsz = psi->ci.hszTopic;
  1025.             psi->ci.hszTopic = GetHsz(PSZTOPIC(mp2), psi->ci.cc.idCountry,
  1026.                     psi->ci.cc.usCodepage, TRUE);
  1027.             FreeHsz(hsz);
  1028.             psi->ci.hwndPartner = (HWND)mp1;
  1029.             psi->ci.hwndFrame = FindFrame(psi->ci.hwndPartner);
  1030.             psi->ci.fs |= ST_CONNECTED;
  1031.             psi->ci.xad.state = CONVST_CONNECTED;
  1032.             if (WinQueryWindowPtr(psi->ci.hwndPartner, QWP_PFNWP) == ClientWndProc) 
  1033.                 psi->ci.fs |= ST_INTRADLL;
  1034.             WinDdeRespond((HWND)mp1, hwnd, PSZAPP(mp2), PSZTOPIC(mp2));
  1035.             fResult = TRUE;
  1036.         } else
  1037.             fResult = FALSE;
  1038.             
  1039.         FreeData((PMYDDES)mp2, psi->ci.pai);
  1040.         return(fResult);
  1041.         break;
  1042.  
  1043.     case WM_DDE_TERMINATE:
  1044.         /*
  1045.          * only respond if this is for us.
  1046.          */
  1047.         if ((HWND)mp1 != psi->ci.hwndPartner) 
  1048.             break;
  1049.         /* fall through */
  1050.  
  1051.     case UMSR_TERMINATE:
  1052.         /*
  1053.          * terminates any conversation in progress
  1054.          * Note that we keep around all the other conversation data so
  1055.          * a later re-connection is possible.
  1056.          */
  1057.         if (psi->ci.fs & ST_CONNECTED) {
  1058.             psi->ci.fs &= ~ST_CONNECTED;
  1059.             psi->ci.xad.state = CONVST_TERMINATED;
  1060.             if (WinIsWindow(DMGHAB, psi->ci.hwndPartner))
  1061.                 WinDdePostMsg(psi->ci.hwndPartner, hwnd, WM_DDE_TERMINATE, 0L, FALSE);
  1062.         }
  1063.         if (psi->ci.fs & ST_ADVISE) {
  1064.             FlushLst(psi->ci.pAdviseList);
  1065.             psi->ci.fs &= ~ST_ADVISE;
  1066.         }
  1067.         /*
  1068.          * Mr. Phelps, if this window isn't reconnected within 10 odd
  1069.          * seconds, it will self-destruct.  This gives the client time
  1070.          * to reconnect with another client window.  This often happens
  1071.          * with wild initiates.
  1072.          */
  1073.         WinStartTimer(DMGHAB, hwnd, TID_SELFDESTRUCT, 0xa000);
  1074.         break;
  1075.  
  1076.     case WM_TIMER:
  1077.         if (LOUSHORT(mp1) == TID_SELFDESTRUCT && !(psi->ci.fs & ST_CONNECTED))
  1078.             DestroyWindow(hwnd);
  1079.         break;
  1080.  
  1081.     case WM_DESTROY:
  1082.         SemCheckOut();
  1083.         /*
  1084.          * Send ourselves a terminate and free local data.
  1085.          */
  1086.         WinSendMsg(hwnd, UMSR_TERMINATE, 0L, 0L);
  1087.         MakeCallback(psi->ci.pai, hwnd, psi->ci.hszTopic, (HSZ)NULL, 0, XTYP_TERM,
  1088.                 0L, 0, 0, psi->ci.hwndPartner);
  1089.         SemEnter();
  1090.         DestroyLst(psi->ci.pAdviseList);
  1091.         FreeHsz(psi->ci.hszServerApp);
  1092.         FreeHsz(psi->ci.hszTopic);
  1093.         FarFreeMem(psi->ci.pai->hheapApp, (PBYTE)psi, sizeof(SERVERINFO));
  1094.         SemLeave();
  1095.         break;
  1096.  
  1097.     case WM_DDE_REQUEST:
  1098.         usType = XTYP_REQUEST;
  1099.         goto Callback;
  1100.         
  1101.     case WM_DDE_EXECUTE:
  1102.         usType = XTYP_EXEC;
  1103.         hDmgData = mp2;
  1104.         goto Callback;
  1105.         
  1106.     case WM_DDE_POKE:
  1107.         usType = XTYP_POKE;
  1108.         hDmgData = mp2;
  1109.         goto Callback;
  1110.         
  1111.     case WM_DDE_ADVISE:
  1112.         usType = XTYP_ADVSTART; /* set ST_ADVISE AFTER app oks advise loop */
  1113.         goto Callback;
  1114.         
  1115.     case WM_DDE_UNADVISE:
  1116.         /*
  1117.          * Terminate the advise now, but notify the server in callback so
  1118.          * messages don't get out of order.
  1119.          */
  1120.         if (!DeleteAdvList(psi->ci.pAdviseList,
  1121.                 GetHszItem(mp2, &psi->ci.cc, FALSE),
  1122.                 PDDES->usFormat))
  1123.             psi->ci.fs &= ~ST_ADVISE;
  1124.         usType = XTYP_ADVSTOP;
  1125.         goto Callback;
  1126.         
  1127.     case WM_DDE_ACK:
  1128.         /*
  1129.          * This is an ack in response to the FACKREQ bit being set.
  1130.          * See if this refers to one of the advise loops.
  1131.          */
  1132.         if ((pAdviseItem = FindAdvList(psi->ci.pAdviseList,
  1133.                 GetHszItem(mp2, &psi->ci.cc, FALSE),
  1134.                 PDDES->usFormat)) &&
  1135.                 (pAdviseItem->fsStatus & DDE_FACKREQ)) {
  1136.             /*
  1137.              * Update advise loop status - no longer waiting for an ack.
  1138.              */
  1139.             pAdviseItem->fsStatus &= ~ADVST_WAITING;
  1140.             if (pAdviseItem->fsStatus & ADVST_CHANGED) {
  1141.                 pAdviseItem->fsStatus |= ADVST_POSTED; 
  1142.                 /*
  1143.                  * The client is out of date.  Send the data
  1144.                  * again (simulate a post advise call).
  1145.                  * Don't bother the server with ACK info.
  1146.                  */
  1147.                 MakeCallback(psi->ci.pai, (HCONV)hwnd, psi->ci.hszTopic,
  1148.                         pAdviseItem->hszItem, 
  1149.                         pAdviseItem->usFmt, XTYP_ADVREQ, 
  1150.                         0L, UMSR_POSTADVISE,
  1151.                         pAdviseItem->fsStatus & ~DDE_FRESERVED,
  1152.                         psi->ci.hwndPartner);
  1153.                 FreeData((PMYDDES)mp2, psi->ci.pai);
  1154.                 return(0);
  1155.             }
  1156.         }
  1157.         usType = XTYP_ACK;
  1158.         hDmgData = PDDES->fsStatus;
  1159.         
  1160. Callback:        
  1161.         MakeCallback(psi->ci.pai, (HCONV)hwnd, psi->ci.hszTopic,
  1162. #if 0
  1163.                 /*
  1164.                  * hack for EXCEL which makes its items and data equal for
  1165.                  * execute acks which SHOULD use NULL as the item name.
  1166.                  */
  1167.                 (PDDES->offszItemName == PDDES->offabData) ?
  1168.                     0L :
  1169. #endif
  1170.                 GetHszItem((PMYDDES)mp2, &psi->ci.cc, TRUE),
  1171.                 PDDES->usFormat, usType,
  1172.                 hDmgData, msg, PDDES->fsStatus,
  1173.                 psi->ci.hwndPartner);
  1174.         /*
  1175.          * now free the incomming selector IF it wasn't passed on to
  1176.          * MakeCallback as hDmgData.
  1177.          */
  1178.         if (hDmgData != mp2)
  1179.             FreeData((PMYDDES)mp2, psi->ci.pai);
  1180.         break;
  1181.         
  1182.     case UMSR_POSTADVISE:
  1183.         /*
  1184.          * This message came from DdePostAdvise()
  1185.          *
  1186.          * Advise loops are tricky because of the desireable FACKREQ feature
  1187.          * of DDE.  The advise loop list holds information in its fsStatus
  1188.          * field to maintain the state of the advise loop.
  1189.          *
  1190.          * if the ADVST_POSTED bit is set, it means that the server already
  1191.          * has an ADVREQ message in its callback queue.  This prevents
  1192.          * unnecessary ADVREQ messages from getting thrown into the callback
  1193.          * queue.
  1194.          *
  1195.          * if the ADVST_WAITING bit is set, the server is still waiting for
  1196.          * the client to give it the go-ahead for more data with an
  1197.          * ACK message on this item. (FACKREQ is set)  Without a go-ahead,
  1198.          * the server will not send any more advise data to the client but
  1199.          * will instead set the ADVST_CHANGED bit which will cause another
  1200.          * WM_DDE_DATA message to be sent to the client as soon as the
  1201.          * go-ahead ACK is received.  This keeps the client up to date
  1202.          * but never overloads it.
  1203.          */
  1204.  
  1205.         if (!(psi->ci.fs & ST_ADVISE) ||
  1206.                 !(pAdviseItem = FindAdvList(psi->ci.pAdviseList, (HSZ)mp1, 0)))
  1207.             break;
  1208.  
  1209.         do {
  1210.             /*
  1211.              * for each format for this item that has an advise loop:
  1212.              */
  1213.             if (pAdviseItem->fsStatus & ADVST_POSTED)
  1214.                 continue;
  1215.                 
  1216.             if ((pAdviseItem->fsStatus & DDE_FACKREQ) &&
  1217.                     (pAdviseItem->fsStatus & ADVST_WAITING)) {
  1218.                 /*
  1219.                  * if the client has not yet finished with the last data
  1220.                  * we gave him, just update the advise loop status
  1221.                  * instead of sending data now.
  1222.                  */
  1223.                 pAdviseItem->fsStatus |= ADVST_CHANGED;
  1224.                 continue;
  1225.             }
  1226.             if (pAdviseItem->fsStatus & DDE_FNODATA) {
  1227.                 /*
  1228.                  * In the nodata case, we don't bother the server.  Just
  1229.                  * pass the client an apropriate DATA message.
  1230.                  */
  1231.                 MyDdePostMsg(psi->ci.hwndPartner, hwnd, WM_DDE_DATA,
  1232.                         (PMYDDES)AllocDDESel(pAdviseItem->fsStatus & ~(DDE_FNODATA | DDE_FACKREQ),
  1233.                         pAdviseItem->usFmt, (HSZ)mp1, 0L, 0),
  1234.                         psi->ci.pai, MDPM_FREEHDATA);
  1235.                 continue;
  1236.             }
  1237.             /*
  1238.              * Otherwise, lets get the data from the server.
  1239.              */
  1240.             pAdviseItem->fsStatus |= ADVST_POSTED;
  1241.             MakeCallback(psi->ci.pai, (HCONV)hwnd, psi->ci.hszTopic,
  1242.                     (HSZ)mp1, pAdviseItem->usFmt, XTYP_ADVREQ,
  1243.                     0, UMSR_POSTADVISE,
  1244.                     pAdviseItem->fsStatus & (DDE_FACKREQ | DDE_FNODATA),
  1245.                     psi->ci.hwndPartner);
  1246.         } while (pAdviseItem = FindNextAdv(pAdviseItem, (HSZ)mp1));
  1247.         break;
  1248.  
  1249.     case UM_QUERY:
  1250.         /*
  1251.          * LOUSHORT(mp1) = info index.
  1252.          * mp2 = pData.     If pData==0, return data else copy into pData.
  1253.          */
  1254.         switch (LOUSHORT(mp1)) {
  1255.         case Q_STATUS:
  1256.              mrData = (MRESULT)psi->ci.fs;
  1257.              break;
  1258.  
  1259.         case Q_CLIENT:
  1260.              mrData = FALSE;
  1261.              break;
  1262.  
  1263.         case Q_APPINFO:
  1264.              mrData = psi->ci.pai;
  1265.              break;
  1266.               
  1267.         case Q_APPNAME:
  1268.              mrData = psi->ci.hszServerApp;
  1269.              break;
  1270.  
  1271.         case Q_TOPIC:
  1272.              mrData = psi->ci.hszTopic;
  1273.              break;
  1274.              
  1275.         case Q_ALL:
  1276.              mrData = (MRESULT)(SERVERINFO FAR *)psi;
  1277.              break;
  1278.         }
  1279.         if (mp2 == 0)
  1280.             return(mrData);
  1281.         else
  1282.             *(MRESULT FAR *)mp2 = mrData;
  1283.         return(1);
  1284.         break;
  1285.  
  1286.     default:
  1287.         return(WinDefWindowProc(hwnd, msg, mp1, mp2));
  1288.         break;
  1289.     }
  1290.     return(0);
  1291. #undef PDDES    
  1292. }
  1293.  
  1294.  
  1295.  
  1296.  
  1297. /*
  1298.  * This assumes hwnd is a DDE window.  It tries to locate the proper
  1299.  * top-level frame window that this window is associated with useing
  1300.  * process and thread IDs.
  1301.  */
  1302. HWND FindFrame(
  1303. HWND hwnd)
  1304. {
  1305.     PID pid, pidFrame;
  1306.     TID tid, tidFrame;
  1307.     HWND hwndMaybe = NULL;
  1308.     HWND hwndBetter = NULL;
  1309.     HWND hwndFrame;
  1310.     HENUM hEnum;
  1311.     ULONG ul;
  1312.     
  1313.     WinQueryWindowProcess(hwnd, &pid, &tid);
  1314.     hEnum = WinBeginEnumWindows(HWND_DESKTOP);
  1315.     while (hwndFrame = WinGetNextWindow(hEnum)) {
  1316.         /*
  1317.          * for all top level windows ...
  1318.          */
  1319.         ul = (ULONG)WinSendMsg(hwndFrame, WM_QUERYFRAMEINFO, 0L, 0L);
  1320.         if (FI_FRAME & ul) {
  1321.             /*
  1322.              * that are frames ...
  1323.              */
  1324.             WinQueryWindowProcess(hwndFrame, &pidFrame, &tidFrame);
  1325.             if (pidFrame == pid) {
  1326.                 /*
  1327.                  * in this process - maybe - ...
  1328.                  */
  1329.                 hwndMaybe = hwndFrame;
  1330.                 if (tidFrame == tid) {
  1331.                     /*
  1332.                      * in this thread - better - ...
  1333.                      */
  1334.                     hwndBetter = hwndFrame;
  1335.                     if (WinQueryWindowPtr(hwndFrame, QWP_PFNWP) ==
  1336.                             subframeWndProc) {
  1337.                         /*
  1338.                          * that are subclassed by us - certainly!
  1339.                          */
  1340.                         hwndBetter = hwndFrame;
  1341.                         break;
  1342.                     }
  1343.                 }
  1344.             }
  1345.         }
  1346.     }
  1347.     WinEndEnumWindows(hEnum);
  1348.     return(hwndBetter ? hwndBetter : hwndMaybe);
  1349. }
  1350.  
  1351.  
  1352.  
  1353.  
  1354. /***************************** Private Function ****************************\
  1355. * This routine handles server message replys.  This may have been called
  1356. * immediately in the case of enabled callbacks, or may have been called
  1357. * via DdeEnableCallback in which case the server action has been
  1358. * delayed.  QReply is responsible for freeing the pddes given as well as
  1359. * the pcbi->hDmgData and pcbi->hszItem.
  1360. *
  1361. * History:
  1362. *   Created     9/12/89    Sanfords
  1363. *   6/12/90 sanfords    Added checks for HDATA ownership.
  1364. \***************************************************************************/
  1365. void QReply(pcbi, pddes)
  1366. PCBLI pcbi;
  1367. PDDESTRUCT pddes;   /* hDataRet */
  1368. {
  1369.     PSERVERINFO psi;
  1370.     PADVLI pAdviseItem;
  1371.     USHORT fsStatus, msg;
  1372.  
  1373.     if ((pcbi->usType & XCLASS_MASK) == XCLASS_NOTIFICATION)
  1374.         return;
  1375.  
  1376.     SemCheckOut();
  1377.     psi = WinSendMsg(pcbi->hConv, UM_QUERY, (MPARAM)Q_ALL, 0L);
  1378.     
  1379.     switch (pcbi->msg) {
  1380.     case UMSR_POSTADVISE:
  1381.         /*
  1382.          * The NODATA case never gets here.
  1383.          */
  1384.         if ((psi) &&
  1385.                 (pAdviseItem = FindAdvList(psi->ci.pAdviseList, pcbi->hszItem,
  1386.                 pcbi->usFmt))) {
  1387.             pAdviseItem->fsStatus &= ~ADVST_POSTED;
  1388.             if (pddes) {
  1389.                 pAdviseItem->fsStatus &= ~ADVST_CHANGED;
  1390.                 MyDdePostMsg(pcbi->hConvPartner, pcbi->hConv, WM_DDE_DATA,
  1391.                         (PMYDDES)pddes, psi->ci.pai, MDPM_FREEHDATA);
  1392.                 if (pAdviseItem->fsStatus & DDE_FACKREQ) 
  1393.                     pAdviseItem->fsStatus |= ADVST_WAITING;
  1394.             }
  1395.         }
  1396.         break;
  1397.             
  1398.     case WM_DDE_REQUEST:
  1399.         if (pddes) {
  1400.             pddes->fsStatus = (pcbi->fsStatus & DDE_FACKREQ) | DDE_FRESPONSE;
  1401.             msg = WM_DDE_DATA;
  1402.         } else {
  1403.             /*
  1404.              * send a -ACK
  1405.              */
  1406.             pddes = AllocDDESel(((USHORT)pddes & DDE_FAPPSTATUS) |
  1407.                     ((USHORT)pddes & DDE_FBUSY ? DDE_FBUSY : DDE_NOTPROCESSED),
  1408.                     pcbi->usFmt, pcbi->hszItem, 0L, NULL);
  1409.             msg = WM_DDE_ACK;
  1410.         }
  1411.         MyDdePostMsg(pcbi->hConvPartner, pcbi->hConv, msg, (PMYDDES)pddes,
  1412.                 psi->ci.pai, MDPM_FREEHDATA);
  1413.         break;
  1414.         
  1415.     case WM_DDE_POKE:
  1416.     case WM_DDE_EXECUTE:
  1417.         /*
  1418.          * pddes is supposed to be the proper DDE_ constants to return.
  1419.          * we just stick them in the given pddes (hDmgData) and return
  1420.          * it as an ACK.  This frees pcbi->hDmgData in the process.
  1421.          */
  1422.         ((PDDESTRUCT)pcbi->hDmgData)->fsStatus = 
  1423.                 (USHORT)pddes & ~DDE_FRESERVED;
  1424.         MyDdePostMsg(pcbi->hConvPartner, pcbi->hConv, WM_DDE_ACK,
  1425.                 (PMYDDES)pcbi->hDmgData, psi->ci.pai, MDPM_FREEHDATA);
  1426.         break;
  1427.         
  1428.     case WM_DDE_ADVISE:
  1429.         /*
  1430.          * pddes is fStartAdvise
  1431.          * If DDE_FACK, we add the item to the advise loop
  1432.          * list and +ACK else we -ACK.
  1433.          */
  1434.         if ((BOOL)pddes) {
  1435.             psi = (PSERVERINFO)WinQueryWindowULong(pcbi->hConv, QWL_USER);
  1436.             if (AddAdvList(psi->ci.pAdviseList, pcbi->hszItem,
  1437.                     pcbi->fsStatus & (DDE_FNODATA | DDE_FACKREQ),
  1438.                     pcbi->usFmt) == NULL) {
  1439.                 psi->ci.pai->LastError = DMGERR_MEMORY_ERROR;
  1440.                 fsStatus = DDE_NOTPROCESSED;
  1441.             } else {
  1442.                 psi->ci.fs |= ST_ADVISE;
  1443.                 fsStatus = DDE_FACK;
  1444.             }
  1445.         } else {
  1446.             fsStatus = DDE_NOTPROCESSED;
  1447.         }
  1448.         goto AckBack;
  1449.         break;
  1450.  
  1451.     case WM_DDE_UNADVISE:
  1452.         fsStatus = DDE_FACK;
  1453.         goto AckBack;
  1454.         break;
  1455.     
  1456.     case WM_DDE_DATA:
  1457.         /*
  1458.          * must be an advise data item for the CLIENT or maybe some requested
  1459.          * data mistakenly sent here due to the client queue being flushed.
  1460.          * pddes is fsStatus.
  1461.          *
  1462.          * send an ack back if requested.
  1463.          */
  1464.         if (pcbi->fsStatus & DDE_FACKREQ) {
  1465.             /*
  1466.              * Clean up the status incase the app is messed up.
  1467.              */
  1468.             fsStatus = (USHORT)pddes & ~DDE_FRESERVED;
  1469.             if (fsStatus & (DDE_NOTPROCESSED | DDE_FBUSY))
  1470.                 fsStatus &= ~DDE_FACK;
  1471. AckBack:
  1472.             MyDdePostMsg(pcbi->hConvPartner, pcbi->hConv, WM_DDE_ACK,
  1473.                 (PMYDDES)AllocDDESel(fsStatus, pcbi->usFmt, pcbi->hszItem, 0L, 0),
  1474.                 psi->ci.pai, MDPM_FREEHDATA);
  1475.         }
  1476.         break;
  1477.     }
  1478. }
  1479.  
  1480.  
  1481.  
  1482. /*
  1483.  * ----------------FRAME SECTION------------------
  1484.  *
  1485.  * A frame window exists on behalf of every registered thread.  It
  1486.  * handles conversation initiation and therefore issues callbacks
  1487.  * to the server app as needed to notify or query the server app.
  1488.  * The callback queue is always bypassed for these synchronous
  1489.  * events.
  1490.  */
  1491.  
  1492. /***************************** Private Function ****************************\
  1493. * MRESULT EXPENTRY subframeWndProc(hwnd, msg, mp1, mp2)
  1494. * HWND hwnd;
  1495. * USHORT msg;
  1496. * MPARAM mp1;
  1497. * MPARAM mp2;
  1498. *
  1499. * This routine takes care of setting up server windows as needed to respond
  1500. * to incomming WM_DDE_INTIIATE messages.  It is subclassed from the top
  1501. * level frame of the server application.
  1502. *
  1503. * History:  created 12/20/88    sanfords
  1504. \***************************************************************************/
  1505. MRESULT EXPENTRY subframeWndProc(hwnd, msg, mp1, mp2)
  1506. HWND hwnd;
  1507. USHORT msg;
  1508. MPARAM mp1;
  1509. MPARAM mp2;
  1510. {
  1511.     PAPPINFO pai;
  1512.  
  1513.     pai = GetCurrentAppInfo(FALSE);
  1514.     
  1515.     switch (msg) {
  1516.     case UM_REGISTER:
  1517.     case UM_UNREGISTER:
  1518.         /*
  1519.          * we pass notification messages through this proc so we can make the
  1520.          * xfer call within the correct thread's context.
  1521.          */
  1522.         MakeCallback(pai, (HCONV)0, (HSZ)0, (HSZ)mp1, 0,
  1523.                 msg == UM_REGISTER ? XTYP_REGISTER : XTYP_UNREGISTER,
  1524.                 (HDMGDATA)mp2, msg, 0, 0L);
  1525.         return(0);
  1526.         break;
  1527.  
  1528.     case WM_DDE_INITIATE:
  1529.         FrameInitConv((HWND)mp1, (PDDEINIT)mp2);
  1530.         FreeData((PMYDDES)mp2, pai);
  1531.         break;
  1532.  
  1533.     default:
  1534.         return((*lpfnFrameWndProc)(hwnd, msg, mp1, mp2));
  1535.         break;
  1536.     }
  1537. }
  1538.  
  1539.  
  1540.  
  1541. void FrameInitConv(hwndClient, pddei)
  1542. HWND hwndClient;
  1543. PDDEINIT pddei;
  1544. {
  1545.     PAPPINFO pai, paiClient;
  1546.     INITINFO ii;
  1547.     HSZPAIR hp[2];
  1548.     PHSZPAIR php;
  1549.     HSZ hsz = 0;
  1550.     HDMGDATA hDataCC;
  1551.     PDDESTRUCT pddes;
  1552.     HWND hwndServer;
  1553.     CONVCONTEXT cc;
  1554.     BOOL fWild;
  1555.  
  1556.     if (!CheckSel(SELECTOROF(pddei))) {
  1557.         AssertF(FALSE, "Invalid DDEINIT selector");
  1558.         return;
  1559.     }
  1560.     
  1561.     SemCheckOut();
  1562.     
  1563.     pai = GetCurrentAppInfo(FALSE);
  1564.     /*
  1565.      * If we are filtering and no app names are registered, quit.
  1566.      */
  1567.     if ((pai->afCmd & DMGCMD_FILTERINITS) &&
  1568.             QPileItemCount(pai->pAppNamePile) == 0) 
  1569.         return;
  1570.         
  1571.     /*
  1572.      * filter out inits from ourselves and other agents (if we are an agent)
  1573.      */
  1574.     if (WinQueryWindowPtr(hwndClient, QWP_PFNWP) == ClientWndProc) {
  1575.         paiClient = WinSendMsg(hwndClient, UM_QUERY, (MPARAM)Q_APPINFO, 0L);
  1576.         if (paiClient == pai)
  1577.             return;
  1578.             
  1579.         if ((pai->afCmd & DMGCMD_AGENT) && (paiClient->afCmd & DMGCMD_AGENT)) 
  1580.             return;
  1581.     }
  1582.  
  1583.     /*
  1584.      * make sure ii.pCC is set up right.
  1585.      */
  1586.     if (pddei->cb >= sizeof(DDEINIT) && pddei->offConvContext) {
  1587.         /*
  1588.          * new dde init structure!
  1589.          */
  1590.         ii.pCC = DDEI_PCONVCONTEXT(pddei);
  1591.     } else {
  1592.         ii.pCC = &cc;
  1593.         cc.cb = sizeof(CONVCONTEXT);
  1594.         cc.idCountry = syscc.country;
  1595.         cc.usCodepage = syscc.codepage;
  1596.         cc.fsContext = 0;
  1597.     }
  1598.  
  1599.  
  1600.     hp[0].hszApp = GetHsz(PSZAPP(pddei), ii.pCC->idCountry,
  1601.             ii.pCC->usCodepage, TRUE);
  1602.             
  1603.     /*
  1604.      * filter out unwanted app names.
  1605.      */
  1606.     if (hp[0].hszApp && (pai->afCmd & DMGCMD_FILTERINITS) &&
  1607.             !FindPileItem(pai->pAppNamePile, CmppHsz, (PBYTE)&hp[0].hszApp, 0)) {
  1608.         FreeHsz(hp[0].hszApp);
  1609.         return;    
  1610.     }
  1611.  
  1612.     hp[0].hszTopic = GetHsz(PSZTOPIC(pddei), ii.pCC->idCountry,
  1613.             ii.pCC->usCodepage, TRUE);
  1614.             
  1615.     hp[1].hszApp = hp[1].hszTopic = 0L;
  1616.  
  1617.     fWild = (hp[0].hszApp == 0L || hp[0].hszTopic == 0);
  1618.  
  1619.     hDataCC = PutData((PBYTE)ii.pCC, (ULONG)sizeof(CONVCONTEXT), 0L, (HSZ)NULL,
  1620.             0, 0, pai);
  1621.  
  1622.     if (hDataCC == NULL)
  1623.         goto CheckOut;
  1624.         
  1625.     pddes = (PDDESTRUCT)DoCallback(pai, NULL, hp[0].hszTopic,
  1626.                     hp[0].hszApp, 0, (fWild ? XTYP_WILDINIT : XTYP_INIT),
  1627.                     hDataCC);
  1628.  
  1629.     if (pddes == NULL)
  1630.         goto CheckOut;
  1631.  
  1632.     FindPileItem(pai->pHDataPile, CmpULONG, (PBYTE)&hDataCC, FPI_DELETE);
  1633.     DosFreeSeg(SELECTOROF(hDataCC));
  1634.     
  1635.     if (fWild) {
  1636.         php = (PHSZPAIR)DDES_PABDATA(pddes);
  1637.     } else {
  1638.         php = &hp[0];
  1639.         pddes = NULL;
  1640.     }
  1641.         
  1642.     /*
  1643.      * now php points to a 0 terminated list of hszpairs to respond to.
  1644.      */
  1645.     SemEnter();
  1646.     while (QuerylatomLength((LATOM)php->hszApp) &&
  1647.             QuerylatomLength((LATOM)php->hszTopic)) {
  1648.         SemLeave();
  1649.         if ((hwndServer = CreateServerWindow(pai, php->hszTopic)) == 0)
  1650.             break;
  1651.         /*
  1652.          * have the server respond
  1653.          */
  1654.         ii.hszAppName = php->hszApp;
  1655.         ii.hszTopic = php->hszTopic;
  1656.         WinSendMsg(hwndServer, UMSR_INITIATE, (MPARAM)&ii, hwndClient);
  1657.  
  1658.         /*
  1659.          * confirm initialization to server app
  1660.          */
  1661.         DoCallback(pai, (HCONV)hwndServer, php->hszTopic, php->hszApp,
  1662.                 0, XTYP_INIT_CONFIRM, 0L);
  1663.             
  1664.         php++;
  1665.         SemEnter();
  1666.     }
  1667.     SemLeave();
  1668.     SemCheckOut();
  1669. CheckOut:    
  1670.     FreeHsz(hp[0].hszApp);
  1671.     FreeHsz(hp[0].hszTopic);
  1672.     if (fWild)
  1673.         FreeData((PMYDDES)pddes, pai);
  1674. }
  1675.  
  1676. HWND CreateServerWindow(
  1677. PAPPINFO pai,
  1678. HSZ hszTopic)
  1679. {
  1680.     HWND hwndTSvr, hwndServer;
  1681.     
  1682.     SemCheckOut();
  1683.     /*
  1684.      * locate or make a Topic server window...
  1685.      */
  1686.     if ((hwndTSvr =
  1687.             HwndFromHsz(hszTopic, pai->pSvrTopicList)) == 0) {
  1688.         /*
  1689.          * NO - make one.
  1690.          */
  1691.         if ((hwndTSvr = WinCreateWindow(pai->hwndDmg, SZDEFCLASS, "", 0L,
  1692.                 0, 0, 0, 0, (HWND)NULL, HWND_BOTTOM, WID_SVRTOPIC,
  1693.                 0L, 0L)) == 0L) {
  1694.             pai->LastError = DMGERR_PMWIN_ERROR;
  1695.             return(NULL);
  1696.         }
  1697.         AddHwndHszList(hszTopic, hwndTSvr, pai->pSvrTopicList);
  1698.     }
  1699.     
  1700.     /*
  1701.      * Create the server window
  1702.      */
  1703.     if ((hwndServer = WinCreateWindow(hwndTSvr, SZSERVERCLASS, "", 0L,
  1704.             0, 0, 0, 0, (HWND)NULL, HWND_BOTTOM, WID_SERVER, 0L, 0L)) == 0L) {
  1705.         pai->LastError = DMGERR_PMWIN_ERROR;
  1706.         return(NULL);
  1707.     }
  1708.     return(hwndServer);
  1709. }
  1710.  
  1711. /*
  1712.  * main application window - parent of all others in app.
  1713.  *
  1714.  * 6/12/90 sanfords     Fixed semaphore bug
  1715.  */
  1716. MRESULT EXPENTRY DmgWndProc(hwnd, msg, mp1, mp2)
  1717. HWND hwnd;
  1718. USHORT msg;
  1719. MPARAM mp1;
  1720. MPARAM mp2;
  1721. {
  1722. #define pai ((PAPPINFO)mp1)
  1723.     PCBLI pli, pliNext;
  1724.     BOOL fException;
  1725.     HDMGDATA hDataRet;
  1726.  
  1727.     hwnd;
  1728.     mp2;
  1729.         
  1730.     switch (msg) {
  1731.     case UM_CHECKCBQ:
  1732.         /*
  1733.          * We consider everything to be blocked if we are in a client
  1734.          * transfer modal loop.   This prevents recursive timeout
  1735.          * calls.
  1736.          */
  1737.         if (pai->hwndTimer)
  1738.             return(0);
  1739.             
  1740.         /*
  1741.          * This is where we actually do callbacks.  We do them via this
  1742.          * window proc so that we can asynchronously institute callbacks
  1743.          * via a PostMsg().
  1744.          */
  1745.         SemCheckOut();
  1746.         SemEnter();
  1747.         /*
  1748.          * process all enabled conversation callbacks.
  1749.          */
  1750.         for (pli = (PCBLI)pai->plstCB->pItemFirst; pli; pli = (PCBLI)pliNext) {
  1751.             pliNext = (PCBLI)pli->next;
  1752.             fException = FindLstItem(pai->plstCBExceptions, CmpULONG, (PLITEM)pli)
  1753.                     == NULL ? FALSE : TRUE;
  1754.             if (fException == pai->fEnableCB)
  1755.                 continue; /* blocked */
  1756.  
  1757.             pai->cInCallback++;
  1758.             SemLeave();
  1759.             /*
  1760.              * make the actual callback here.
  1761.              */
  1762.             hDataRet = DoCallback(pai, pli->hConv, pli->hszTopic,
  1763.                     pli->hszItem, pli->usFmt, pli->usType, pli->hDmgData);
  1764.             SemEnter();
  1765.             if (pai->cInCallback > 0)   /* test incase exlst processing messed it up */
  1766.                 pai->cInCallback--;
  1767.  
  1768.             /*
  1769.              * If the callback resulted in a BLOCK, disable this conversation.
  1770.              */
  1771.             if (hDataRet == CBR_BLOCK && !(pli->usType & XTYPF_NOBLOCK)) {
  1772.                 SemLeave();
  1773.                 DdeEnableCallback(pli->hConv, FALSE);
  1774.                 SemEnter();
  1775.                 continue;
  1776.             } else {
  1777.                 /*
  1778.                  * otherwise finish processing the callback.
  1779.                  */
  1780.                 if (WinIsWindow(DMGHAB, pli->hConvPartner)) {
  1781.                     SemLeave();
  1782.                     QReply(pli, (PDDESTRUCT)hDataRet);
  1783.                     SemEnter();
  1784.                 }
  1785.                 RemoveLstItem(pai->plstCB, (PLITEM)pli);
  1786.             }
  1787.         }
  1788.         SemLeave();
  1789.         return(0);
  1790.         break;
  1791.  
  1792.     default:
  1793.         WinDefWindowProc(hwnd, msg, mp1, mp2);
  1794.         break;
  1795.     }
  1796. #undef pai
  1797. }
  1798.  
  1799.  
  1800. HDMGDATA DoCallback(
  1801. PAPPINFO pai,
  1802. HCONV hConv,
  1803. HSZ hszTopic,
  1804. HSZ hszItem,
  1805. USHORT usFmt,
  1806. USHORT usType,
  1807. HDMGDATA hDmgData)
  1808. {
  1809.     HDMGDATA hDataRet;
  1810.     
  1811.     AssertF(IncHszCount(hszTopic) && FreeHsz(hszTopic), "Bad hszTopic on callback");
  1812.     AssertF(IncHszCount(hszItem) && FreeHsz(hszItem), "Bad hszItem on callback");
  1813.  
  1814.     if (usType & XCLASS_DATAIN) {
  1815.         AssertF(CheckSel(SELECTOROF(hDmgData)), "invalid callback data handle");
  1816.         ((PMYDDES)hDmgData)->fs |= HDATA_READONLY;
  1817.     }
  1818.     
  1819. #ifdef CRUISER    
  1820.     if (pai->afCmd & DMGCMD_32BIT)
  1821.         hDataRet = ThkCallback(hConv, hszTopic, hszItem, usFmt, usType, hDmgData,
  1822.                 pai->pfnCallback);
  1823.     else
  1824. #endif    
  1825.         hDataRet = (*pai->pfnCallback)(hConv, hszTopic, hszItem, usFmt, usType,
  1826.                 hDmgData);
  1827.  
  1828.     if (usType & XCLASS_DATA && CheckSel(SELECTOROF(hDataRet)) > sizeof(MYDDES) &&
  1829.             ((PMYDDES)hDataRet)->magic == MYDDESMAGIC) {
  1830.         if (((PMYDDES)hDataRet)->pai != pai) {
  1831.             AssertF(FALSE, "hData from callback not created by same thread");
  1832.             pai->LastError = DMGERR_DLL_USAGE;
  1833.             hDataRet = NULL;
  1834.         }
  1835.     }
  1836.     return(hDataRet);
  1837. }
  1838.  
  1839.  
  1840.