DDE.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:23k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /******************************************************************************
  2. *       This is a part of the Microsoft Source Code Samples.
  3. *       Copyright (C) 1993-1997 Microsoft Corporation.
  4. *       All rights reserved.
  5. *       This source code is only intended as a supplement to
  6. *       Microsoft Development Tools and/or WinHelp documentation.
  7. *       See these sources for detailed information regarding the
  8. *       Microsoft samples programs.
  9. ******************************************************************************/
  10. /*
  11.  * This module serves to demonstrate one way a sophisticated DDE server
  12.  * that uses enumerable topics and items might be implemented.  It takes
  13.  * full advantage of appowned data handles (when fAppowned is set) to
  14.  * minimize the need for repeated rendering of data when shared with
  15.  * multiple clients.
  16.  *
  17.  * The server supports full system topic information plus help and non
  18.  * system topic item enumeration for the benefit of browsing clients
  19.  * that are wondering what's around.
  20.  *
  21.  * This server can be made secure by altering the conversation context
  22.  * filter.
  23.  *
  24.  * This server can appear to support alternate codepages and languages
  25.  * by altering the conversation context filter.   On Windows this is
  26.  * pretty much moot since there is not yet a clearly defined way of
  27.  * doing international communication and because the atom manager restricts
  28.  * what topic and item strings can be used on the system.
  29.  */
  30. #include <string.h>
  31. #include <stdlib.h>
  32. #include <stdio.h>
  33. #include <memory.h>
  34. #include "server.h"
  35. #include "huge.h"
  36. /*
  37.  * This function verifies that the incomming conversation context fits the
  38.  * server's context filter's requirements.
  39.  */
  40. BOOL ValidateContext(
  41. PCONVCONTEXT pCC)
  42. {
  43.     // make sure our CCFilter allows it...mock security, language support
  44.     // old DDE app client case...pCC == NULL
  45.     if (pCC == NULL &&
  46.             CCFilter.dwSecurity == 0 &&      // were nonsecure
  47.             CCFilter.iCodePage == CP_WINANSI && // were normal cp
  48.             CCFilter.qos.ImpersonationLevel == SecurityImpersonation
  49.             ) {
  50.         return(TRUE);
  51.     }
  52.     if (pCC &&
  53.             pCC->wFlags == CCFilter.wFlags && // no special flags needed
  54.             pCC->iCodePage == CCFilter.iCodePage && // codepages match
  55.             pCC->dwSecurity == CCFilter.dwSecurity && // security passes
  56.             pCC->qos.ImpersonationLevel >= CCFilter.qos.ImpersonationLevel) {
  57.         // dont care about language and country.
  58.         return(TRUE);
  59.     }
  60.     return(FALSE);  // disallow no match
  61. }
  62. /***************************** Public  Function ****************************
  63. *
  64. * This function is called by the DDE manager DLL and passes control onto
  65. * the apropriate function pointed to by the global topic and item arrays.
  66. * It handles all DDE interaction generated by external events.
  67. *
  68. ***************************************************************************/
  69. HDDEDATA CALLBACK DdeCallback(
  70. UINT wType,
  71. UINT wFmt,
  72. HCONV hConv,
  73. HSZ hszTopic,
  74. HSZ hszItem,
  75. HDDEDATA hData,
  76. DWORD lData1,
  77. DWORD lData2)
  78. {
  79.     WORD i, j;
  80.     register ITEMLIST *pItemList;
  81.     WORD cItems, iFmt;
  82.     HDDEDATA hDataRet;
  83.     /*
  84.      * Block this callback if its blockable and we are supposed to.
  85.      */
  86.     if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) {
  87.         fBlockNextCB = FALSE;
  88.         fAllEnabled = FALSE;
  89.         return(CBR_BLOCK);
  90.     }
  91.     /*
  92.      * Block this callback if its associated with a conversation and we
  93.      * are supposed to.
  94.      */
  95.     if (fTermNextCB && hConv) {
  96.         fTermNextCB = FALSE;
  97.         DdeDisconnect(hConv);
  98.         wType = XTYP_DISCONNECT;
  99.     }
  100.     /*
  101.      * Keep a count of connections
  102.      */
  103.     if (wType == XTYP_CONNECT_CONFIRM) {
  104.         cServers++;
  105.         InvalidateRect(hwndServer, &rcConnCount, TRUE);
  106.         return(0);
  107.     }
  108.     if (wType == XTYP_DISCONNECT) {
  109.         cServers--;
  110.         InvalidateRect(hwndServer, &rcConnCount, TRUE);
  111.         return(0);
  112.     }
  113.     /*
  114.      * only allow transactions on the formats we support if they have a format.
  115.      */
  116.     if (wFmt) {
  117.         for (iFmt = 0; iFmt < CFORMATS; iFmt++) {
  118.             if ((ATOM)wFmt == aFormats[iFmt].atom)
  119.                 break;
  120.         }
  121.         if (iFmt == CFORMATS)
  122.             return(0);          // illegal format - ignore now.
  123.     }
  124.     /*
  125.      * Executes are allowed only on the system topic.  This is a general
  126.      * convention, not a requirement.
  127.      *
  128.      * Any executes received result in the execute text being shown in
  129.      * the server client area.  No real action is taken.
  130.      */
  131.     if (wType == XTYP_EXECUTE) {
  132.         if (hszTopic == topicList[0].hszTopic) {    // must be on system topic
  133.             // Format is assumed to be CF_TEXT.
  134.             DdeGetData(hData, (LPBYTE)szExec, MAX_EXEC, 0);
  135.             szExec[MAX_EXEC - 1] = TEXT('');
  136.             InvalidateRect(hwndServer, &rcExec, TRUE);
  137.             hDataRet = (HDDEDATA)TRUE;
  138.             goto ReturnSpot;
  139.         }
  140.         pszComment = TEXT("Execute received on non-system topic - ignored");
  141.         InvalidateRect(hwndServer, &rcComment, TRUE);
  142.         return(0);
  143.     }
  144.     /*
  145.      * Process wild initiates here
  146.      */
  147.     if (wType == XTYP_WILDCONNECT) {
  148.         HSZ ahsz[(CTOPICS + 1) * 2];
  149.         /*
  150.          * He wants a hsz list of all our available app/topic pairs
  151.          * that conform to hszTopic and hszItem(App).
  152.          */
  153.         if (!ValidateContext((PCONVCONTEXT)lData1)) {
  154.             return(FALSE);
  155.         }
  156.         if (hszItem != hszAppName && hszItem != 0) {
  157.             // we only support the hszAppName service
  158.             return(0);
  159.         }
  160.         // scan the topic table and create hsz pairs
  161.         j = 0;
  162.         for (i = 0; i < CTOPICS; i++) {
  163.             if (hszTopic == 0 || hszTopic == topicList[i].hszTopic) {
  164.                 ahsz[j++] = hszAppName;
  165.                 ahsz[j++] = topicList[i].hszTopic;
  166.             }
  167.         }
  168.         // cap off the list with 0s
  169.         ahsz[j++] = ahsz[j++] = 0L;
  170.         // send it back
  171.         return(DdeCreateDataHandle(idInst, (LPBYTE)&ahsz[0], sizeof(HSZ) * j, 0L, 0, wFmt, 0));
  172.     }
  173.     /*
  174.      * Check our hsz tables and send to the apropriate proc. to process.
  175.      * We use DdeCmpStringHandles() which is the portable case-insensitive
  176.      * method of comparing string handles.  (this is a macro on windows so
  177.      * there is no real speed hit.)  On WINDOWS, HSZs are case-insensitive
  178.      * anyway, but this may not be the case on other platforms.
  179.      */
  180.     for (i = 0; i < CTOPICS; i++) {
  181.         if (DdeCmpStringHandles(topicList[i].hszTopic, hszTopic) == 0) {
  182.             /*
  183.              * connections must be on a topic we support.
  184.              */
  185.             if (wType == XTYP_CONNECT) {
  186.                 return((HDDEDATA)ValidateContext((PCONVCONTEXT)lData1));
  187.             }
  188.             pItemList = topicList[i].pItemList;
  189.             cItems = topicList[i].cItems;
  190.             for (j = 0; j < cItems; j++) {
  191.                 if (DdeCmpStringHandles(pItemList[j].hszItem, hszItem) == 0) {
  192.                     XFERINFO xi;
  193.                     /*
  194.                      * If Win32s - security isn't supported
  195.                      */
  196.                     if(GetVersion() & 0x80000000) {
  197.                         /*
  198.                          * Make call to worker function here...
  199.                          */
  200.                         xi.wType = wType;
  201.                         xi.wFmt = wFmt;
  202.                         xi.hConv = hConv;
  203.                         xi.hszTopic = hszTopic;
  204.                         xi.hszItem = hszItem;
  205.                         xi.hData = hData;
  206.                         xi.lData1 = lData1;
  207.                         xi.lData2 = lData2;
  208.                         hDataRet = (*pItemList[j].npfnCallback)(&xi, iFmt);
  209.                      } else {
  210.                         /*
  211.                          * Windows NT - impersonate client here
  212.                          */
  213.                         if (DdeImpersonateClient(hConv)) {
  214.                             /*
  215.                              * Make call to worker function here...
  216.                              */
  217.                             xi.wType = wType;
  218.                             xi.wFmt = wFmt;
  219.                             xi.hConv = hConv;
  220.                             xi.hszTopic = hszTopic;
  221.                             xi.hszItem = hszItem;
  222.                             xi.hData = hData;
  223.                             xi.lData1 = lData1;
  224.                             xi.lData2 = lData2;
  225.                             hDataRet = (*pItemList[j].npfnCallback)(&xi, iFmt);
  226.                             RevertToSelf();
  227.                         } else {
  228.                             pszComment = TEXT("Impersonation failed.");
  229.                             InvalidateRect(hwndServer, &rcComment, TRUE);
  230.                             hDataRet = 0;
  231.                         }
  232.                      }
  233. ReturnSpot:
  234.                     /*
  235.                      * The table functions return a boolean or data.
  236.                      * It gets translated here.
  237.                      */
  238.                     switch (wType & XCLASS_MASK) {
  239.                     case XCLASS_DATA:
  240.                         return(hDataRet);
  241.                         break;
  242.                     case XCLASS_FLAGS:
  243.                         return(HDDEDATA)(hDataRet ? DDE_FACK : DDE_FNOTPROCESSED);
  244.                         break;
  245.                     case XCLASS_BOOL:
  246.                         return((HDDEDATA)TRUE);
  247.                     default: // XCLASS_NOTIFICATION
  248.                         return(0);
  249.                         break;
  250.                     }
  251.                     break;
  252.                 }
  253.             }
  254.             break;
  255.         }
  256.     }
  257.     /*
  258.      * anything else fails - DDEML is designed so that a 0 return is ALWAYS ok.
  259.      */
  260.     return(0);
  261. }
  262. /***************************** Private Function ****************************
  263. * This passes out a standard tab-delimited list of topic names for this
  264. * application.
  265. *
  266. * This support is required for other apps to be able to
  267. * find out about us.  This kind of support should be in every DDE
  268. * application.
  269. *
  270. ***************************************************************************/
  271. HDDEDATA TopicListXfer(
  272. PXFERINFO pXferInfo,
  273. WORD iFmt)
  274. {
  275.     WORD cbAlloc, i;
  276.     LPTSTR pszTopicList;
  277.     HDDEDATA hData;
  278.     if (pXferInfo->wType == XTYP_ADVSTART)
  279.         return((HDDEDATA)TRUE);
  280.     if (pXferInfo->wType != XTYP_REQUEST &&
  281.             pXferInfo->wType != XTYP_ADVREQ)
  282.         return(0);
  283.     /*
  284.      * construct the list of topics we have
  285.      */
  286.     cbAlloc = 0;
  287.     for (i = 0; i < CTOPICS; i++)
  288.         cbAlloc += (_tcslen(topicList[i].pszTopic) + 1) * sizeof(TCHAR);  // 1 for tab
  289.     // allocate a data handle big enough for the list.
  290.     hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
  291.             pXferInfo->wFmt, 0);
  292.     pszTopicList = (LPTSTR)DdeAccessData(hData, NULL);
  293.     if (pszTopicList) {
  294.         for (i = 0; i < CTOPICS; i++) {
  295.             _tcscpy(pszTopicList, topicList[i].pszTopic);
  296.             pszTopicList += _tcslen(topicList[i].pszTopic);
  297.             *pszTopicList++ = TEXT('t');
  298.         }
  299.         *--pszTopicList = TEXT('');
  300.         DdeUnaccessData(hData);
  301.         return(hData);
  302.     }
  303.     return(0);
  304. }
  305. /***************************** Private Function ****************************
  306. * This passes out a standard tab-delimited list of item names for the
  307. * specified topic.
  308. *
  309. * This support is required for other apps to be able to
  310. * find out about us.  This kind of support should be in every DDE
  311. * application.
  312. *
  313. ***************************************************************************/
  314. HDDEDATA ItemListXfer(
  315. PXFERINFO pXferInfo,
  316. WORD iFmt)
  317. {
  318.     WORD cbAlloc, i, iItem, cItems;
  319.     ITEMLIST *pItemList = 0;
  320.     LPTSTR pszItemList;
  321.     HDDEDATA hData;
  322.     if (pXferInfo->wType == XTYP_ADVSTART)
  323.         return((HDDEDATA)TRUE);
  324.     if (pXferInfo->wType != XTYP_REQUEST &&
  325.                 pXferInfo->wType != XTYP_ADVREQ)
  326.         return(0);
  327.     /*
  328.      * construct the list of items we support for this topic - this supports
  329.      * more than the minimum standard which would support SysItems only on
  330.      * the system topic.
  331.      */
  332.     // locate the requested topic item table
  333.     for (i = 0; i < CTOPICS; i++) {
  334.         if (pXferInfo->hszTopic == topicList[i].hszTopic) {
  335.             pItemList = topicList[i].pItemList;
  336.             cItems = topicList[i].cItems;
  337.             break;
  338.         }
  339.     }
  340.     if (!pItemList)
  341.         return(0);  // item not found
  342.     cbAlloc = 0;
  343.     for (iItem = 0; iItem < cItems; iItem++)
  344.         cbAlloc += (_tcslen(pItemList[iItem].pszItem) + 1) * sizeof(TCHAR); // 1 for tab
  345.     // allocate a data handle big enough for the list.
  346.     hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
  347.             pXferInfo->wFmt, 0);
  348.     pszItemList = (LPTSTR)DdeAccessData(hData, NULL);
  349.     if (pszItemList) {
  350.         for (i = 0; i < cItems; i++) {
  351.             _tcscpy(pszItemList, pItemList[i].pszItem);
  352.             pszItemList += _tcslen(pItemList[i].pszItem);
  353.             *pszItemList++ = TEXT('t');
  354.         }
  355.         *--pszItemList = TEXT('');
  356.         DdeUnaccessData(hData);
  357.         return(hData);
  358.     }
  359.     return(0);
  360. }
  361. /***************************** Private Function ****************************
  362. * Gives out a 0 terminated array of dde format numbers supported by this app.
  363. *
  364. * This support is required for other apps to be able to
  365. * find out about us.  This kind of support should be in every DDE
  366. * application.
  367. *
  368. ***************************************************************************/
  369. HDDEDATA sysFormatsXfer(
  370. PXFERINFO pXferInfo,
  371. WORD iFmt)
  372. {
  373.     INT i, cb;
  374.     LPTSTR psz, pszT;
  375.     HDDEDATA hData;
  376.     if (pXferInfo->wType == XTYP_ADVSTART)
  377.         return((HDDEDATA)TRUE);
  378.     if (pXferInfo->wType != XTYP_REQUEST &&
  379.             pXferInfo->wType != XTYP_ADVREQ)
  380.         return(0);
  381.     for (i = 0, cb = 0; i < CFORMATS; i++)
  382.         cb += (_tcslen(aFormats[i].sz) + 1) * sizeof(TCHAR);
  383.     hData = DdeCreateDataHandle(idInst, NULL, (DWORD)cb,
  384.             0L, pXferInfo->hszItem, pXferInfo->wFmt, 0);
  385.     psz = pszT = (LPTSTR)DdeAccessData(hData, NULL);
  386.     for (i = 0; i < CFORMATS; i++) {
  387.         _tcscpy(pszT, aFormats[i].sz);
  388.         pszT += _tcslen(pszT);
  389.         *pszT++ = TEXT('t');
  390.     }
  391.     *(--pszT) = TEXT('');
  392.     DdeUnaccessData(hData);
  393.     return(hData);
  394. }
  395. /*
  396.  * This is a runaway item.  Each time it is requested, it changes.
  397.  * pokes just make it change again.
  398.  */
  399. HDDEDATA TestRandomXfer(
  400. PXFERINFO pXferInfo,
  401. WORD iFmt)
  402. {
  403.     TCHAR szT[10];   // SS==DS!
  404.     LPTSTR pszData;
  405.     HDDEDATA hData;
  406.     WORD i;
  407.     switch (pXferInfo->wType) {
  408.     case XTYP_POKE:
  409.         // we expect an ascii number to replace the current seed.
  410.         pszComment = TEXT("Rand poke received.");
  411.         InvalidateRect(hwndServer, &rcComment, TRUE);
  412.         InvalidateRect(hwndServer, &rcRand, TRUE);
  413.         if (DdeGetData(pXferInfo->hData, (PBYTE)szT, 10, 0)) {
  414.             szT[9] = TEXT('');  // just incase we overran.
  415.             _stscanf(szT, TEXT("%d"), &seed);
  416.             for (i = 0; i < CFORMATS; i++) {
  417.                 if (hDataRand[i])
  418.                     DdeFreeDataHandle(hDataRand[i]);
  419.                 hDataRand[i] = 0;
  420.             }
  421.             DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
  422.             return((HDDEDATA)1);
  423.         }
  424.         break;
  425.     case XTYP_REQUEST:
  426.         pszComment = TEXT("Rand data requested.");
  427.         InvalidateRect(hwndServer, &rcComment, TRUE);
  428.     case XTYP_ADVREQ:
  429.         Delay(RenderDelay, FALSE);
  430.         if (!hDataRand[iFmt]) {
  431.             hDataRand[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10,
  432.                     pXferInfo->hszItem, (UINT)pXferInfo->wFmt,
  433.                     fAppowned ? HDATA_APPOWNED : 0);
  434.             if (pszData = (LPTSTR)DdeAccessData(hDataRand[iFmt], NULL)) {
  435.                 wsprintf(pszData, TEXT("%d"), seed);
  436.                 DdeUnaccessData(hDataRand[iFmt]);
  437.             }
  438.         }
  439.         hData = hDataRand[iFmt];
  440.         if (!fAppowned)
  441.             hDataRand[iFmt] = 0;
  442.         return(hData);
  443.         break;
  444.     case XTYP_ADVSTART:
  445.         return((HDDEDATA)1);
  446.     }
  447.     return(0);
  448. }
  449. /*
  450.  * This is a runaway item.  Each time it is requested, it changes.
  451.  * pokes just make it change again.
  452.  */
  453. HDDEDATA TestCountXfer(
  454. PXFERINFO pXferInfo,
  455. WORD iFmt)
  456. {
  457.     TCHAR szT[16];   // SS==DS!
  458.     LPTSTR pszData;
  459.     HDDEDATA hData;
  460.     WORD i;
  461.     switch (pXferInfo->wType) {
  462.     case XTYP_POKE:
  463.         // we expect an ascii number to replace the current count.
  464.         pszComment = TEXT("Count poke received");
  465.         InvalidateRect(hwndServer, &rcComment, TRUE);
  466.         InvalidateRect(hwndServer, &rcCount, TRUE);
  467.         if (DdeGetData(pXferInfo->hData, (PBYTE)szT, 10, 0)) {
  468.             szT[9] = TEXT('');  // just incase we overran.
  469.             _stscanf(szT, TEXT("%ld"), &count);
  470.             for (i = 0; i < CFORMATS; i++) {
  471.                 if (hDataCount[i])
  472.                     DdeFreeDataHandle(hDataCount[i]);
  473.                 hDataCount[i] = 0;
  474.             }
  475.             DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
  476.             return((HDDEDATA)1);
  477.         }
  478.         break;
  479.     case XTYP_REQUEST:
  480.         pszComment = TEXT("Count data requested.");
  481.         InvalidateRect(hwndServer, &rcComment, TRUE);
  482.     case XTYP_ADVREQ:
  483.         Delay(RenderDelay, FALSE);
  484.         if (!hDataCount[iFmt]) {
  485.             hDataCount[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
  486.                     pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
  487.             if (pszData = (LPTSTR)DdeAccessData(hDataCount[iFmt], NULL)) {
  488.                 wsprintf(pszData, TEXT("%ld"), count);
  489.                 DdeUnaccessData(hDataCount[iFmt]);
  490.             }
  491.         }
  492.         hData = hDataCount[iFmt];
  493.         if (!fAppowned)
  494.             hDataCount[iFmt] = 0;
  495.         return(hData);
  496.         break;
  497.     case XTYP_ADVSTART:
  498.         return((HDDEDATA)1);
  499.     }
  500.     return(0);
  501. }
  502. /*
  503.  * This is not a runaway item.  Only Pokes make it change.
  504.  */
  505. HDDEDATA TestHugeXfer(
  506. PXFERINFO pXferInfo,
  507. WORD iFmt)
  508. {
  509.     BOOL fSuccess;
  510.     DWORD ulcb;
  511.     LPBYTE lpData;
  512.     WORD i;
  513.     HDDEDATA hData;
  514.     switch (pXferInfo->wType) {
  515.     case XTYP_POKE:
  516.         ulcb = DdeGetData(pXferInfo->hData, NULL, 0, 0);
  517.         fSuccess = CheckHugeData(pXferInfo->hData);
  518.         if (fSuccess) {
  519.             pszComment = TEXT("Huge poke data successfully received.");
  520.         } else {
  521.             wsprintf(szComment, TEXT("%ld bytes of invalid Huge data received."), ulcb);
  522.             pszComment = szComment;
  523.         }
  524.         InvalidateRect(hwndServer, &rcComment, TRUE);
  525.         InvalidateRect(hwndServer, &rcHugeSize, TRUE);
  526.         if (fSuccess) {
  527.             for (i = 0; i < CFORMATS; i++) {
  528.                 if (hDataHuge[i]) {
  529.                     DdeFreeDataHandle(hDataHuge[i]);
  530.                     hDataHuge[i] = 0;
  531.                 }
  532.             }
  533.             /*
  534.              * Since callback data handles are only good for the duration of
  535.              * the callback, we must copy the data to our own data handle.
  536.              */
  537.             lpData = DdeAccessData(pXferInfo->hData, &cbHuge);
  538.             hDataHuge[iFmt] = DdeCreateDataHandle(idInst, lpData, cbHuge, 0,
  539.                     pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
  540.             DdeUnaccessData(pXferInfo->hData);
  541.             DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
  542.         }
  543.         return((HDDEDATA)fSuccess);
  544.         break;
  545.     case XTYP_REQUEST:
  546.         pszComment = TEXT("Huge data requested.");
  547.         InvalidateRect(hwndServer, &rcComment, TRUE);
  548.     case XTYP_ADVREQ:
  549.         Delay(RenderDelay, FALSE);
  550.         if (!hDataHuge[iFmt]) {
  551.             cbHuge = (DWORD)rand() * 64L + 0x10000L;
  552.             wsprintf(szComment, TEXT("Generating huge data - length=%ld..."), cbHuge);
  553.             pszComment = szComment;
  554.             InvalidateRect(hwndServer, &rcComment, TRUE);
  555.             UpdateWindow(hwndServer);
  556.             hDataHuge[iFmt] = CreateHugeDataHandle((LONG)cbHuge, 4325, 345, 5,
  557.                     pXferInfo->hszItem,
  558.                     pXferInfo->wFmt, (WORD)(fAppowned ? HDATA_APPOWNED : 0));
  559.             pszComment = TEXT("");
  560.             InvalidateRect(hwndServer, &rcComment, TRUE);
  561.             InvalidateRect(hwndServer, &rcHugeSize, TRUE);
  562.         }
  563.         hData = hDataHuge[iFmt];
  564.         if (!fAppowned)
  565.             hDataHuge[iFmt] = 0;
  566.         return(hData);
  567.         break;
  568.     case XTYP_ADVSTART:
  569.         return((HDDEDATA)1);
  570.     }
  571.     return(0);
  572. }
  573. HDDEDATA HelpXfer(
  574. PXFERINFO pXferInfo,
  575. WORD iFmt)
  576. {
  577.     HDDEDATA hData;
  578.     switch (pXferInfo->wType) {
  579.     case XTYP_REQUEST:
  580.         pszComment = TEXT("Help text requested.");
  581.         InvalidateRect(hwndServer, &rcComment, TRUE);
  582.     case XTYP_ADVREQ:
  583.         if (!hDataHelp[iFmt]) {
  584.            hDataHelp[iFmt] = DdeCreateDataHandle(idInst,
  585.                     (PBYTE)szDdeHelp,
  586.                     (_tcslen(szDdeHelp) + 1) * sizeof(TCHAR),
  587.                     0, pXferInfo->hszItem, pXferInfo->wFmt,
  588.                     fAppowned ? HDATA_APPOWNED : 0);
  589.         }
  590.         hData = hDataHelp[iFmt];
  591.         if (!fAppowned)
  592.             hDataHelp[iFmt] = 0;
  593.         return(hData);
  594.         break;
  595.     case XTYP_ADVSTART:
  596.         return((HDDEDATA)1);
  597.     }
  598.     return(0);
  599. }
  600. /***************************** Private Function ****************************
  601. *  This creates often used global hszs from standard global strings.
  602. *  It also fills the hsz fields of the topic and item tables.
  603. *
  604. ***************************************************************************/
  605. VOID Hszize()
  606. {
  607.     register ITEMLIST *pItemList;
  608.     int iTopic, iItem;
  609.     hszAppName = DdeCreateStringHandle(idInst, szServer, 0);
  610.     for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
  611.         topicList[iTopic].hszTopic =
  612.                 DdeCreateStringHandle(idInst, topicList[iTopic].pszTopic, 0);
  613.         pItemList = topicList[iTopic].pItemList;
  614.         for (iItem = 0; iItem < (int)topicList[iTopic].cItems; iItem++) {
  615.             pItemList[iItem].hszItem =
  616.                     DdeCreateStringHandle(idInst, pItemList[iItem].pszItem, 0);
  617.         }
  618.     }
  619. }
  620. /***************************** Private Function ****************************
  621. *  This destroys often used global hszs from standard global strings.
  622. *
  623. ***************************************************************************/
  624. VOID UnHszize()
  625. {
  626.     register ITEMLIST *pItemList;
  627.     int iTopic, iItem;
  628.     DdeFreeStringHandle(idInst, hszAppName);
  629.     for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
  630.         DdeFreeStringHandle(idInst, topicList[iTopic].hszTopic);
  631.         pItemList = topicList[iTopic].pItemList;
  632.         for (iItem = 0; iItem < (int)topicList[iTopic].cItems; iItem++) {
  633.             DdeFreeStringHandle(idInst, pItemList[iItem].hszItem);
  634.         }
  635.     }
  636. }