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

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