home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / SAMPLES / DDEML / CLIENT / DDE.C_ / DDE.C
Encoding:
C/C++ Source or Header  |  1993-02-08  |  24.1 KB  |  645 lines

  1. /***************************************************************************
  2.  *                                                                         *
  3.  *  MODULE      : dde.c                                                    *
  4.  *                                                                         *
  5.  *  PURPOSE     : Contains routines for handling of DDE interaction with   *
  6.  *                DDEML.                                                   *
  7.  *                                                                         *
  8.  ***************************************************************************/
  9. #include "ddemlcl.h"
  10. #include <string.h>
  11. #include <memory.h>
  12. #include "infoctrl.h"
  13.  
  14. char szT[100];
  15.  
  16. /****************************************************************************
  17.  *                                                                          *
  18.  *  FUNCTION   : CreateXactionWindow()                                      *
  19.  *                                                                          *
  20.  *  PURPOSE    : Creates a transaction window for the given transaction     *
  21.  *               under the given conversation window.                       *
  22.  *                                                                          *
  23.  *  RETURNS    : TRUE  - If successful.                                     *
  24.  *               FALSE - otherwise.                                         *
  25.  *                                                                          *
  26.  ****************************************************************************/
  27. HWND CreateXactionWindow(
  28. HWND hwndMDI,
  29. XACT *pxact)
  30. {
  31.     PSTR pszFmt, pszItem;
  32.     PSTR pData;
  33.     HWND hwnd;
  34.  
  35.     pszItem = GetHSZName(pxact->hszItem);
  36.     pszFmt = GetFormatName(pxact->wFmt);
  37.     pData = GetTextData(pxact->hDdeData);
  38.  
  39.     /*
  40.      *   ┌type/opts─────── ITEM ───────────────ret─┐    GWW_WUSER=pxact
  41.      *   │                                         │
  42.      *   │                                         │
  43.      *   │                                         │
  44.      *   │                                         │
  45.      *   │                 DATA                    │
  46.      *   │                                         │
  47.      *   │                                         │
  48.      *   │                                         │
  49.      *   │                                         │
  50.      *   └state/error───── FORMAT ──────────Result─┘
  51.      */
  52.     hwnd = CreateInfoCtrl((LPSTR)pData,
  53.             (int)SendMessage(hwndMDI, UM_GETNEXTCHILDX, 0, 0L),
  54.             (int)SendMessage(hwndMDI, UM_GETNEXTCHILDY, 0, 0L),
  55.             200, 100, hwndMDI, hInst,
  56.             Type2String(pxact->wType, pxact->fsOptions), pszItem, NULL,
  57.             "Starting", (LPSTR)pszFmt, NULL,
  58.             ICSTY_SHOWFOCUS, 0, (DWORD)(LPSTR)pxact);
  59.     MyFree(pszItem);
  60.     MyFree(pszFmt);
  61.     MyFree(pData);
  62.     return(hwnd);
  63. }
  64.  
  65.  
  66.  
  67.  
  68. /****************************************************************************
  69.  *                                                                          *
  70.  *  FUNCTION   : ProcessTransaction()                                       *
  71.  *                                                                          *
  72.  *  PURPOSE    : Processes synchronous transactions entirely and starts     *
  73.  *               async transactions.  Transaction attempts result in a      *
  74.  *               transaction window being created which displays the state  *
  75.  *               or results of the transaction.  (the callback function     *
  76.  *               updates these windows as it gets calls) Transaction        *
  77.  *               windows stay around until abandoned by the user or until   *
  78.  *               the conversation is disconnected.  Advise Data and Advise  *
  79.  *               Stop transactions are special.  We don't create a new      *
  80.  *               window if the associated advise start transaction window   *
  81.  *               can be found.                                              *
  82.  *                                                                          *
  83.  *  RETURNS    : TRUE  - If successful.                                     *
  84.  *               FALSE - otherwise.                                         *
  85.  *                                                                          *
  86.  ****************************************************************************/
  87. BOOL ProcessTransaction(
  88. XACT *pxact)
  89. {
  90.     CONVINFO ci;
  91.     HWND hwndInfoCtrl = 0;
  92.  
  93.     /* create transaction window to show we tried (except in ADVSTOP case) */
  94.  
  95.     pxact = (XACT *)memcpy(MyAlloc(sizeof(XACT)), (PSTR)pxact, sizeof(XACT));
  96.     ci.cb = sizeof(CONVINFO);
  97.     DdeQueryConvInfo(pxact->hConv, (DWORD)QID_SYNC, &ci); // ci.hUser==hConv
  98.     if (pxact->wType == XTYP_ADVSTOP) {
  99.         hwndInfoCtrl = FindAdviseChild((HWND)ci.hUser, pxact->hszItem,
  100.                 pxact->wFmt);
  101.         if (hwndInfoCtrl) {
  102.             SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UL,
  103.                     (DWORD)(LPSTR)Type2String(pxact->wType, pxact->fsOptions));
  104.             DdeFreeStringHandle(idInst, pxact->hszItem);
  105.         }
  106.     }
  107.     /*
  108.      * If we still need to create a transaction window, do so here.
  109.      */
  110.     if (!hwndInfoCtrl) {
  111.         hwndInfoCtrl = CreateXactionWindow((HWND)ci.hUser, pxact);
  112.         if (!hwndInfoCtrl) {
  113.             MyFree(pxact);
  114.             return 0;
  115.         }
  116.         SetFocus(hwndInfoCtrl);
  117.     }
  118.     /*
  119.      * Disable callbacks for this conversation now if the XOPT_DISABLEFIRST
  120.      * option is set.  This tests disabling asynchronous transactions
  121.      * before they are completed.
  122.      */
  123.     if (pxact->fsOptions & XOPT_DISABLEFIRST)
  124.         DdeEnableCallback(idInst, pxact->hConv, EC_DISABLE);
  125.     /*
  126.      * Adjust the timeout for asynchronous transactions.
  127.      */
  128.     if (pxact->fsOptions & XOPT_ASYNC)
  129.     pxact->ulTimeout = (DWORD)TIMEOUT_ASYNC;
  130.  
  131.     /*
  132.      * start transaction with DDEML here
  133.      */
  134.     pxact->ret = DdeClientTransaction((LPBYTE)pxact->hDdeData, (DWORD)-1,
  135.             pxact->hConv, pxact->hszItem, pxact->wFmt,
  136.             pxact->wType,
  137.             pxact->ulTimeout, (LPDWORD)&pxact->Result);
  138.  
  139.     /*
  140.      * show return value in transaction window
  141.      */
  142.     wsprintf(szT, "ret=%lx", pxact->ret);
  143.     SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UR, (DWORD)(LPSTR)szT);
  144.  
  145.     /*
  146.      * show result or ID value in transaction window
  147.      */
  148.     wsprintf(szT, pxact->fsOptions & XOPT_ASYNC ? "ID=%ld" :
  149.             "result=0x%lx", pxact->Result);
  150.     SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LR, (DWORD)(LPSTR)szT);
  151.  
  152.     if ((pxact->fsOptions & XOPT_ASYNC) && pxact->ret) {
  153.         /*
  154.          * asynchronous successful start - link transaction to window.
  155.          */
  156.         DdeSetUserHandle(pxact->hConv, pxact->Result, (DWORD)hwndInfoCtrl);
  157.  
  158.         /*
  159.          * Abandon started async transaction after initiated if
  160.          * XOPT_ABANDONAFTERSTART is chosen.  This tests the mid-transaction
  161.          * abandoning code.
  162.          */
  163.         if (pxact->fsOptions & XOPT_ABANDONAFTERSTART)
  164.             DdeAbandonTransaction(idInst, pxact->hConv, pxact->Result);
  165.         /*
  166.          * show actual status
  167.          */
  168.         ci.cb = sizeof(CONVINFO);
  169.         DdeQueryConvInfo(pxact->hConv, pxact->Result, &ci);
  170.         SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL,
  171.                 (DWORD)(LPSTR)State2String(ci.wConvst));
  172.     } else {
  173.         /*
  174.          * Synchronous transactions are completed already so pass on to
  175.          * CompleteTransaction right away.
  176.          */
  177.         CompleteTransaction(hwndInfoCtrl, pxact);
  178.     }
  179.     return TRUE;
  180. }
  181.  
  182.  
  183.  
  184.  
  185.  
  186. /****************************************************************************
  187.  *                                                                          *
  188.  *  FUNCTION   : CompleteTransaction()                                      *
  189.  *                                                                          *
  190.  *  PURPOSE    : This handles completed synchronous and asynchronous        *
  191.  *               transactions as well as failed attempted transactions.     *
  192.  *                                                                          *
  193.  *  RETURNS    : TRUE  - If successful.                                     *
  194.  *               FALSE - otherwise.                                         *
  195.  *                                                                          *
  196.  ****************************************************************************/
  197. VOID CompleteTransaction(
  198. HWND hwndInfoCtrl,
  199. XACT *pxact)
  200. {
  201.     PSTR psz;
  202.  
  203.     if (pxact->ret) {
  204.         /*
  205.          * Successful transaction case
  206.          */
  207.         SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL,
  208.                 (DWORD)(LPSTR)"Completed");
  209.  
  210.         if (pxact->wType == XTYP_REQUEST) {
  211.             /*
  212.              * Show resulting data
  213.              */
  214.             psz = GetTextData((HDDEDATA)pxact->ret);
  215.             SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_CENTER,
  216.                     (DWORD)(LPSTR)psz);
  217.             MyFree(psz);
  218.             /*
  219.              * free returned data since it is displayed.
  220.              */
  221.             DdeFreeDataHandle(pxact->ret);
  222.             pxact->ret = 0L;
  223.             SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UR, NULL);
  224.         }
  225.     } else {
  226.         /*
  227.          * failed - show error result.
  228.          */
  229.         SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL,
  230.                 (DWORD)(LPSTR)Error2String(DdeGetLastError(idInst)));
  231.     }
  232.     pxact->fsOptions |= XOPT_COMPLETED;
  233. }
  234.  
  235.  
  236.  
  237.  
  238. /****************************************************************************
  239.  *                                                                          *
  240.  *  FUNCTION   : DdeCallback()                                              *
  241.  *                                                                          *
  242.  *  PURPOSE    : This handles all callbacks from the DDEML.  This handles   *
  243.  *               updating of the associated conversation and any special    *
  244.  *               testing cases such as blocking callbacks etc.              *
  245.  *                                                                          *
  246.  *               For the most part, clients only handle advise data and     *
  247.  *               asynchronous transaction completion here.                  *
  248.  *                                                                          *
  249.  *  RETURNS    : Results vary depending on transaction type.                *
  250.  *                                                                          *
  251.  ****************************************************************************/
  252. HDDEDATA EXPENTRY __export DdeCallback(
  253. WORD wType,
  254. WORD wFmt,
  255. HCONV hConv,
  256. HSZ hsz1,
  257. HSZ hsz2,
  258. HDDEDATA hData,
  259. DWORD lData1,
  260. DWORD lData2)
  261. {
  262.     HWND hwnd;
  263.     CONVINFO ci;
  264.     XACT *pxact;
  265.  
  266.     if (hConv) {
  267.         /*
  268.          * update conversation status if it changed.
  269.          */
  270.         MYCONVINFO *pmci;
  271.  
  272.         ci.cb = sizeof(CONVINFO);
  273.     if (!DdeQueryConvInfo(hConv,(DWORD) QID_SYNC, &ci) || (!IsWindow((HWND)ci.hUser))) {
  274.             /*
  275.              * This conversation does not yet have a corresponding MDI window
  276.              * or is disconnected.
  277.              */
  278.             return 0;
  279.         }
  280.         if (pmci = (MYCONVINFO *)GetWindowWord((HWND)ci.hUser, 0)) {
  281.             if (pmci->ci.wStatus != ci.wStatus ||
  282.                     pmci->ci.wConvst != ci.wConvst ||
  283.                     pmci->ci.wLastError != ci.wLastError) {
  284.                 /*
  285.                  * Things have changed, updated the conversation window.
  286.                  */
  287.                 InvalidateRect((HWND)ci.hUser, NULL, TRUE);
  288.             }
  289.             if (ci.wConvst & ST_INLIST) {
  290.                 /*
  291.                  * update the associated list window (if any) as well.
  292.                  */
  293.                 if (hwnd = FindListWindow(ci.hConvList))
  294.                     InvalidateRect(hwnd, NULL, TRUE);
  295.             }
  296.         }
  297.     }
  298.  
  299.     /*
  300.      * handle special block on next callback option here.  This demonstrates
  301.      * the CBR_BLOCK feature.
  302.      */
  303.     if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) {
  304.         fBlockNextCB = FALSE;
  305.         return(CBR_BLOCK);
  306.     }
  307.  
  308.     /*
  309.      * handle special termination here.  This demonstrates that at any time
  310.      * a client can drop a conversation.
  311.      */
  312.     if (fTermNextCB && hConv && wType != XTYP_DISCONNECT) {
  313.         fTermNextCB = FALSE;
  314.         MyDisconnect(hConv);
  315.         return(0);
  316.     }
  317.  
  318.     /*
  319.      * Now we begin sort out what to do.
  320.      */
  321.     switch (wType) {
  322.     case XTYP_REGISTER:
  323.     case XTYP_UNREGISTER:
  324.         /*
  325.          * This is where the client would insert code to keep track of
  326.          * what servers are available.  This could cause the initiation
  327.          * of some conversations.
  328.          */
  329.         break;
  330.  
  331.     case XTYP_DISCONNECT:
  332.         if (fAutoReconnect) {
  333.             /*
  334.              * attempt a reconnection
  335.              */
  336.             if (hConv = DdeReconnect(hConv)) {
  337.                 AddConv(ci.hszServiceReq, ci.hszTopic, hConv, FALSE);
  338.                 return 0;
  339.             }
  340.         }
  341.  
  342.         /*
  343.          * update conv window to show its new state.
  344.          */
  345.         SendMessage((HWND)ci.hUser, UM_DISCONNECTED, 0, 0);
  346.         return 0;
  347.         break;
  348.  
  349.     case XTYP_ADVDATA:
  350.         /*
  351.          * data from an active advise loop (from a server)
  352.          */
  353.         Delay(wDelay);
  354.         hwnd = FindAdviseChild((HWND)ci.hUser, hsz2, wFmt);
  355.         if (!IsWindow(hwnd)) {
  356.             PSTR pszItem, pszFmt;
  357.             /*
  358.              * AdviseStart window is gone, make a new one.
  359.              */
  360.             pxact = (XACT *)MyAlloc(sizeof(XACT));
  361.             pxact->wType = wType;
  362.             pxact->hConv = hConv;
  363.             pxact->wFmt = wFmt;
  364.             pxact->hszItem = hsz2;
  365.             DdeKeepStringHandle(idInst, hsz2);
  366.  
  367.             pszItem = GetHSZName(hsz2);
  368.             pszFmt = GetFormatName(wFmt);
  369.  
  370.             hwnd = CreateInfoCtrl(NULL,
  371.                     (int)SendMessage((HWND)ci.hUser, UM_GETNEXTCHILDX, 0, 0L),
  372.                     (int)SendMessage((HWND)ci.hUser, UM_GETNEXTCHILDY, 0, 0L),
  373.                     200, 100,
  374.                     (HWND)ci.hUser, hInst,
  375.                     Type2String(wType, 0), (LPSTR)pszItem, NULL,
  376.                     NULL, (LPSTR)pszFmt, NULL,
  377.                     ICSTY_SHOWFOCUS, 0, (DWORD)(LPSTR)pxact);
  378.  
  379.             MyFree(pszFmt);
  380.             MyFree(pszItem);
  381.  
  382.             if (!IsWindow(hwnd))
  383.                 return(DDE_FNOTPROCESSED);
  384.         }
  385.         if (!hData) {
  386.             /*
  387.              * XTYPF_NODATA case - request the info. (we do this synchronously
  388.              * for simplicity)
  389.              */
  390.             hData = DdeClientTransaction(NULL, 0L, hConv, hsz2, wFmt,
  391.                     XTYP_REQUEST, DefTimeout, NULL);
  392.         }
  393.         if (hData) {
  394.             PSTR pData;
  395.             /*
  396.              * Show incomming data on corresponding transaction window.
  397.              */
  398.             pData = GetTextData(hData);
  399.             SendMessage(hwnd, ICM_SETSTRING, ICSID_CENTER, (DWORD)(LPSTR)pData);
  400.             MyFree(pData);
  401.             DdeFreeDataHandle(hData);
  402.         }
  403.         SendMessage(hwnd, ICM_SETSTRING, ICSID_LL, (DWORD)(LPSTR)"Advised");
  404.         return(DDE_FACK);
  405.         break;
  406.  
  407.     case XTYP_XACT_COMPLETE:
  408.         /*
  409.          * An asynchronous transaction has completed.  Show the results.
  410.          *
  411.          * ...unless the XOPT_BLOCKRESULT is chosen.
  412.          */
  413.  
  414.         ci.cb = sizeof(CONVINFO);
  415.         if (DdeQueryConvInfo(hConv, lData1, &ci) &&
  416.                 IsWindow((HWND)ci.hUser) &&
  417.                 (pxact = (XACT *)GetWindowWord((HWND)ci.hUser, GWW_WUSER))) {
  418.  
  419.             if (pxact->fsOptions & XOPT_BLOCKRESULT) {
  420.                 pxact->fsOptions &= ~XOPT_BLOCKRESULT;
  421.                 return(CBR_BLOCK);
  422.             }
  423.  
  424.             pxact->Result = lData2;
  425.             pxact->ret = hData;
  426.             CompleteTransaction((HWND)ci.hUser, pxact);
  427.         }
  428.         break;
  429.     }
  430. }
  431.  
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438. /****************************************************************************
  439.  *                                                                          *
  440.  *  FUNCTION   : FindAdviseChild()                                          *
  441.  *                                                                          *
  442.  *  PURPOSE    : Search through the child windows of hwndMDI for an info    *
  443.  *               ctrl that has the same Item and format and is an           *
  444.  *               ADVSTART ADVSTOP or ADVDATA transaction window.            *
  445.  *                                                                          *
  446.  *               We use these to show the associated advise data.           *
  447.  *                                                                          *
  448.  *  RETURNS    : The transaction window handle or 0 on failure.             *
  449.  *                                                                          *
  450.  ****************************************************************************/
  451. HWND FindAdviseChild(
  452. HWND hwndMDI,
  453. HSZ hszItem,
  454. WORD wFmt)
  455. {
  456.     HWND hwnd, hwndStart;
  457.     XACT *pxact;
  458.  
  459.     if (!IsWindow(hwndMDI))
  460.         return 0;
  461.  
  462.     hwnd = hwndStart = GetWindow(hwndMDI, GW_CHILD);
  463.     while (hwnd && IsChild(hwndMDI, hwnd)) {
  464.         pxact = (XACT *)GetWindowWord(hwnd, GWW_WUSER);
  465.         if (pxact &&
  466.                 (pxact)->wFmt == wFmt &&
  467.                 (pxact)->hszItem == hszItem &&
  468.                 (
  469.                     ((pxact->wType & XTYP_ADVSTART) == XTYP_ADVSTART) ||
  470.                     (pxact->wType == XTYP_ADVSTOP) ||
  471.                     (pxact->wType == XTYP_ADVDATA)
  472.                 )
  473.            ) {
  474.             return(hwnd);
  475.         }
  476.         hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  477.         if (hwnd == hwndStart)
  478.             return 0;
  479.     }
  480.     return 0;
  481. }
  482.  
  483.  
  484.  
  485. /****************************************************************************
  486.  *                                                                          *
  487.  *  FUNCTION   : FindListWindow()                                           *
  488.  *                                                                          *
  489.  *  PURPOSE    : Locates the list window associated with this conversation  *
  490.  *               list.                                                      *
  491.  *                                                                          *
  492.  *  RETURNS    : The window handle of the list window or 0 on failure.      *
  493.  *                                                                          *
  494.  ****************************************************************************/
  495. HWND FindListWindow(
  496. HCONVLIST hConvList)
  497. {
  498.     HWND hwnd;
  499.     MYCONVINFO *pmci;
  500.  
  501.     hwnd = GetWindow(hwndMDIClient, GW_CHILD);
  502.     while (hwnd) {
  503.         pmci = (MYCONVINFO *)GetWindowWord(hwnd, 0);
  504.         if (pmci->fList && pmci->hConv == hConvList)
  505.             return(hwnd);
  506.         hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  507.     }
  508.     return 0;
  509. }
  510.  
  511.  
  512.  
  513.  
  514. /****************************************************************************
  515.  *                                                                          *
  516.  *  FUNCTION   : GetTextData()                                              *
  517.  *                                                                          *
  518.  *  PURPOSE    : Allocates and returns a pointer to the data contained in   *
  519.  *               hData.  This assumes that hData points to text data and    *
  520.  *               will properly handle huge text data by leaving out the     *
  521.  *               middle of the string and placing the size of the string    *
  522.  *               into this string portion.                                  *
  523.  *                                                                          *
  524.  *  RETURNS    : A pointer to the allocated string.                         *
  525.  *                                                                          *
  526.  ****************************************************************************/
  527. PSTR GetTextData(
  528. HDDEDATA hData)
  529. {
  530.     PSTR psz;
  531.     DWORD cb;
  532.  
  533. #define CBBUF  1024
  534.  
  535.     if (hData == NULL) {
  536.         return(NULL);
  537.     }
  538.  
  539.     cb = DdeGetData(hData, NULL, 0, 0);
  540.     if (!hData || !cb)
  541.         return NULL;
  542.  
  543.     if (cb > CBBUF) {                // possibly HUGE object!
  544.         psz = MyAlloc(CBBUF);
  545.         DdeGetData(hData, psz, CBBUF - 46, 0L);
  546.         wsprintf(&psz[CBBUF - 46], "<---Size=%ld", cb);
  547.     } else {
  548.         psz = MyAlloc((WORD)cb);
  549.         DdeGetData(hData, (LPBYTE)psz, cb, 0L);
  550.     }
  551.     return psz;
  552. #undef CBBUF
  553. }
  554.  
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561. /****************************************************************************
  562.  *                                                                          *
  563.  *  FUNCTION   : MyGetClipboardFormatName()                                 *
  564.  *                                                                          *
  565.  *  PURPOSE    : Properly retrieves the string associated with a clipboard  *
  566.  *               format.  If the format does not have a string associated   *
  567.  *               with it, the string #dddd is returned.                     *
  568.  *                                                                          *
  569.  *  RETURNS    : The number of characters copied into lpstr or 0 on error.  *
  570.  *                                                                          *
  571.  ****************************************************************************/
  572. int MyGetClipboardFormatName(
  573. WORD fmt,
  574. LPSTR lpstr,
  575. int cbMax)
  576. {
  577.     if (fmt < 0xc000) {
  578.         // predefined or integer format - just get the atom string
  579.         // wierdly enough, GetClipboardFormatName() doesn't support this.
  580.         return(GlobalGetAtomName(fmt, lpstr, cbMax));
  581.     } else {
  582.         return(GetClipboardFormatName(fmt, lpstr, cbMax));
  583.     }
  584. }
  585.  
  586.  
  587.  
  588.  
  589.  
  590. /****************************************************************************
  591.  *                                                                          *
  592.  *  FUNCTION   : GetFormatName()                                            *
  593.  *                                                                          *
  594.  *  PURPOSE    : allocates and returns a pointer to a string representing   *
  595.  *               a format.  Use MyFree() to free this string.               *
  596.  *                                                                          *
  597.  *  RETURNS    : The number of characters copied into lpstr or 0 on error.  *
  598.  *                                                                          *
  599.  ****************************************************************************/
  600. PSTR GetFormatName(
  601. WORD wFmt)
  602. {
  603.     PSTR psz;
  604.     WORD cb;
  605.  
  606.     if (wFmt == 1) {
  607.         psz = MyAlloc(8);
  608.         strcpy(psz, "CF_TEXT");
  609.         return psz;
  610.     }
  611.     psz = MyAlloc(255);
  612.     *psz = '\0';
  613.     cb = GetClipboardFormatName(wFmt, psz, 255) + 1;
  614.     return((PSTR)LocalReAlloc((HANDLE)psz, cb, LMEM_MOVEABLE));
  615. }
  616.  
  617.  
  618.  
  619.  
  620. /****************************************************************************
  621.  *                                                                          *
  622.  *  FUNCTION   : MyDisconnect()                                             *
  623.  *                                                                          *
  624.  *  PURPOSE    : Disconnects the given conversation after updating the      *
  625.  *               associated conversation window.                            *
  626.  *                                                                          *
  627.  *  RETURNS    : TRUE on success, FALSE on failuer.                         *
  628.  *                                                                          *
  629.  ****************************************************************************/
  630. BOOL MyDisconnect(
  631. HCONV hConv)
  632. {
  633.     CONVINFO ci;
  634.     HWND hwnd;
  635.     // before we disconnect, invalidate the associated list window - if
  636.     // applicable.
  637.  
  638.     ci.cb = sizeof(CONVINFO);
  639.  
  640.     if (DdeQueryConvInfo(hConv, (DWORD)QID_SYNC, &ci) && ci.hConvList &&
  641.             (hwnd = FindListWindow(ci.hConvList)))
  642.         InvalidateRect(hwnd, NULL, TRUE);
  643.     return(DdeDisconnect(hConv));
  644. }
  645.