home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / ipc / ddeml / client / dde.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  26KB  |  671 lines

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