|
|
1.1.1.3 ! root 1: ! 2: /******************************************************************************\ ! 3: * This is a part of the Microsoft Source Code Samples. ! 4: * Copyright (C) 1993 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: 1.1 root 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 1.1.1.3 ! root 51: CCFilter.iCodePage == CP_WINANSI && // were normal cp ! 52: CCFilter.qos.ImpersonationLevel == SecurityImpersonation 1.1 root 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 1.1.1.3 ! root 60: pCC->dwSecurity == CCFilter.dwSecurity && // security passes ! 61: pCC->qos.ImpersonationLevel >= CCFilter.qos.ImpersonationLevel) { 1.1 root 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( 1.1.1.2 root 77: UINT wType, 78: UINT wFmt, 1.1 root 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++) { 1.1.1.3 ! root 130: if ((ATOM)wFmt == aFormats[iFmt].atom) 1.1 root 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] = '\0'; 149: InvalidateRect(hwndServer, &rcExec, TRUE); 150: hDataRet = TRUE; 151: goto ReturnSpot; 152: } 153: pszComment = "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(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; 1.1.1.3 ! root 215: 1.1 root 216: /* 1.1.1.3 ! root 217: * If Win32s - security isn't supported 1.1 root 218: */ 1.1.1.3 ! root 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 = "Impersonation failed."; ! 252: InvalidateRect(hwndServer, &rcComment, TRUE); ! 253: hDataRet = 0; ! 254: } ! 255: } 1.1 root 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(hDataRet ? DDE_FACK : DDE_FNOTPROCESSED); 268: break; 269: case XCLASS_BOOL: 270: return(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: LPSTR pszTopicList; 307: HDDEDATA hData; 308: 309: if (pXferInfo->wType == XTYP_ADVSTART) 310: return(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 += lstrlen(topicList[i].pszTopic) + 1; // 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 = (LPSTR)DdeAccessData(hData, NULL); 326: if (pszTopicList) { 327: for (i = 0; i < CTOPICS; i++) { 1.1.1.2 root 328: strcpy(pszTopicList, topicList[i].pszTopic); 1.1 root 329: pszTopicList += strlen(topicList[i].pszTopic); 330: *pszTopicList++ = '\t'; 331: } 332: *--pszTopicList = '\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: LPSTR pszItemList; 358: HDDEDATA hData; 359: 360: if (pXferInfo->wType == XTYP_ADVSTART) 361: return(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 += lstrlen(pItemList[iItem].pszItem) + 1; // 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 = (LPSTR)DdeAccessData(hData, NULL); 392: if (pszItemList) { 393: for (i = 0; i < cItems; i++) { 1.1.1.2 root 394: strcpy(pszItemList, pItemList[i].pszItem); 1.1 root 395: pszItemList += strlen(pItemList[i].pszItem); 396: *pszItemList++ = '\t'; 397: } 398: *--pszItemList = '\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: LPSTR psz, pszT; 423: HDDEDATA hData; 424: 425: if (pXferInfo->wType == XTYP_ADVSTART) 426: return(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 += strlen(aFormats[i].sz) + 1; 434: 435: hData = DdeCreateDataHandle(idInst, NULL, (DWORD)cb, 436: 0L, pXferInfo->hszItem, pXferInfo->wFmt, 0); 437: psz = pszT = DdeAccessData(hData, NULL); 438: for (i = 0; i < CFORMATS; i++) { 1.1.1.2 root 439: strcpy(pszT, aFormats[i].sz); 440: pszT += strlen(pszT); 1.1 root 441: *pszT++ = '\t'; 442: } 443: *(--pszT) = '\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: CHAR szT[10]; // SS==DS! 459: LPSTR 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 = "Rand poke received."; 467: InvalidateRect(hwndServer, &rcComment, TRUE); 468: InvalidateRect(hwndServer, &rcRand, TRUE); 469: if (DdeGetData(pXferInfo->hData, szT, 10, 0)) { 470: szT[9] = '\0'; // just incase we overran. 471: sscanf(szT, "%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(1); 479: } 480: break; 481: 482: case XTYP_REQUEST: 483: pszComment = "Rand data requested."; 484: InvalidateRect(hwndServer, &rcComment, TRUE); 485: case XTYP_ADVREQ: 486: Delay(RenderDelay, FALSE); 487: if (!hDataRand[iFmt]) { 1.1.1.2 root 488: hDataRand[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, 489: pXferInfo->hszItem, (UINT)pXferInfo->wFmt, 490: fAppowned ? HDATA_APPOWNED : 0); 1.1 root 491: if (pszData = DdeAccessData(hDataRand[iFmt], NULL)) { 492: wsprintf(pszData, "%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(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: CHAR szT[16]; // SS==DS! 517: LPSTR 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 = "Count poke received"; 525: InvalidateRect(hwndServer, &rcComment, TRUE); 526: InvalidateRect(hwndServer, &rcCount, TRUE); 527: if (DdeGetData(pXferInfo->hData, szT, 10, 0)) { 528: szT[9] = '\0'; // just incase we overran. 529: sscanf(szT, "%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(1); 537: } 538: break; 539: 540: case XTYP_REQUEST: 541: pszComment = "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 = DdeAccessData(hDataCount[iFmt], NULL)) { 549: wsprintf(pszData, "%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(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 = "Huge poke data successfully received."; 585: } else { 586: wsprintf(szComment, "%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(fSuccess); 609: break; 610: 611: case XTYP_REQUEST: 612: pszComment = "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, "Generating huge data - length=%ld...", cbHuge); 619: pszComment = szComment; 620: InvalidateRect(hwndServer, &rcComment, TRUE); 621: UpdateWindow(hwndServer); 1.1.1.2 root 622: hDataHuge[iFmt] = CreateHugeDataHandle((LONG)cbHuge, 4325, 345, 5, 1.1 root 623: pXferInfo->hszItem, 1.1.1.2 root 624: pXferInfo->wFmt, (WORD)(fAppowned ? HDATA_APPOWNED : 0)); 1.1 root 625: pszComment = ""; 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(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 = "Help text requested."; 651: InvalidateRect(hwndServer, &rcComment, TRUE); 652: case XTYP_ADVREQ: 653: if (!hDataHelp[iFmt]) { 654: hDataHelp[iFmt] = DdeCreateDataHandle(idInst, szDdeHelp, strlen(szDdeHelp) + 1, 655: 0, pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0); 656: } 657: hData = hDataHelp[iFmt]; 658: if (!fAppowned) 659: hDataHelp[iFmt] = 0; 660: return(hData); 661: break; 662: 663: case XTYP_ADVSTART: 664: return(1); 665: } 666: return(0); 667: } 668: 669: 670: /***************************** Private Function ****************************\ 671: * This creates often used global hszs from standard global strings. 672: * It also fills the hsz fields of the topic and item tables. 673: * 674: \***************************************************************************/ 675: VOID Hszize() 676: { 677: register ITEMLIST *pItemList; 1.1.1.3 ! root 678: int iTopic, iItem; 1.1 root 679: 680: hszAppName = DdeCreateStringHandle(idInst, szServer, 0); 681: 682: for (iTopic = 0; iTopic < CTOPICS; iTopic++) { 683: topicList[iTopic].hszTopic = 684: DdeCreateStringHandle(idInst, topicList[iTopic].pszTopic, 0); 685: pItemList = topicList[iTopic].pItemList; 1.1.1.3 ! root 686: for (iItem = 0; iItem < (int)topicList[iTopic].cItems; iItem++) { 1.1 root 687: pItemList[iItem].hszItem = 688: DdeCreateStringHandle(idInst, pItemList[iItem].pszItem, 0); 689: } 690: } 691: } 692: 693: 694: 695: 696: 697: /***************************** Private Function ****************************\ 698: * This destroys often used global hszs from standard global strings. 699: * 700: \***************************************************************************/ 701: VOID UnHszize() 702: { 703: register ITEMLIST *pItemList; 1.1.1.3 ! root 704: int iTopic, iItem; 1.1 root 705: 706: DdeFreeStringHandle(idInst, hszAppName); 707: 708: for (iTopic = 0; iTopic < CTOPICS; iTopic++) { 709: DdeFreeStringHandle(idInst, topicList[iTopic].hszTopic); 710: pItemList = topicList[iTopic].pItemList; 1.1.1.3 ! root 711: for (iItem = 0; iItem < (int)topicList[iTopic].cItems; iItem++) { 1.1 root 712: DdeFreeStringHandle(idInst, pItemList[iItem].hszItem); 713: } 714: } 715: } 716: 717:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.