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