home *** CD-ROM | disk | FTP | other *** search
- /*
- * This module serves to demonstrate one way a sophisticated DDE server
- * that uses enumerable topics and items might be implemented. It takes
- * full advantage of appowned data handles (when fAppowned is set) to
- * minimize the need for repeated rendering of data when shared with
- * multiple clients.
- *
- * The server supports full system topic information plus help and non
- * system topic item enumeration for the benefit of browsing clients
- * that are wondering what's around.
- *
- * This server can be made secure by altering the conversation context
- * filter.
- *
- * This server can appear to support alternate codepages and languages
- * by altering the conversation context filter. On Windows this is
- * pretty much moot since there is not yet a clearly defined way of
- * doing international communication and because the atom manager restricts
- * what topic and item strings can be used on the system.
- */
-
- #include "ddemlsv.h"
- #include "huge.h"
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <memory.h>
-
- /*
- * This function verifies that the incomming conversation context fits the
- * server's context filter's requirements.
- */
- BOOL ValidateContext(
- PCONVCONTEXT pCC)
- {
- // make sure our CCFilter allows it...mock security, language support
- // old DDE app client case...pCC == NULL
- if (pCC == NULL &&
- CCFilter.dwSecurity == 0 && // were nonsecure
- CCFilter.iCodePage == CP_WINANSI // were normal cp
- ) {
- return(TRUE);
- }
-
- if (pCC &&
- pCC->wFlags == CCFilter.wFlags && // no special flags needed
- pCC->iCodePage == CCFilter.iCodePage && // codepages match
- pCC->dwSecurity == CCFilter.dwSecurity) { // security passes
- // dont care about language and country.
- return(TRUE);
- }
- return(FALSE); // disallow no match
- }
-
-
- /***************************** Public Function ****************************\
- *
- * This function is called by the DDE manager DLL and passes control onto
- * the apropriate function pointed to by the global topic and item arrays.
- * It handles all DDE interaction generated by external events.
- *
- \***************************************************************************/
- HDDEDATA EXPENTRY __export DdeCallback(
- WORD wType,
- WORD wFmt,
- HCONV hConv,
- HSZ hszTopic,
- HSZ hszItem,
- HDDEDATA hData,
- DWORD lData1,
- DWORD lData2)
- {
- WORD i, j;
- register ITEMLIST *pItemList;
- WORD cItems, iFmt;
- HDDEDATA hDataRet;
-
- /*
- * Block this callback if its blockable and we are supposed to.
- */
- if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) {
- fBlockNextCB = FALSE;
- fAllEnabled = FALSE;
- return(CBR_BLOCK);
- }
-
- /*
- * Block this callback if its associated with a conversation and we
- * are supposed to.
- */
- if (fTermNextCB && hConv) {
- fTermNextCB = FALSE;
- DdeDisconnect(hConv);
- wType = XTYP_DISCONNECT;
- }
-
- /*
- * Keep a count of connections
- */
- if (wType == XTYP_CONNECT_CONFIRM) {
- cServers++;
- InvalidateRect(hwndServer, &rcConnCount, TRUE);
- return(0);
- }
- if (wType == XTYP_DISCONNECT) {
- cServers--;
- InvalidateRect(hwndServer, &rcConnCount, TRUE);
- return(0);
- }
-
-
- /*
- * only allow transactions on the formats we support if they have a format.
- */
- if (wFmt) {
- for (iFmt = 0; iFmt < CFORMATS; iFmt++) {
- if (wFmt == aFormats[iFmt].atom)
- break;
- }
- if (iFmt == CFORMATS)
- return(0); // illegal format - ignore now.
- }
-
- /*
- * Executes are allowed only on the system topic. This is a general
- * convention, not a requirement.
- *
- * Any executes received result in the execute text being shown in
- * the server client area. No real action is taken.
- */
- if (wType == XTYP_EXECUTE) {
- if (hszTopic == topicList[0].hszTopic) { // must be on system topic
- // Format is assumed to be CF_TEXT.
- DdeGetData(hData, (LPBYTE)szExec, MAX_EXEC, 0);
- szExec[MAX_EXEC - 1] = '\0';
- InvalidateRect(hwndServer, &rcExec, TRUE);
- hDataRet = TRUE;
- goto ReturnSpot;
- }
- pszComment = "Execute received on non-system topic - ignored";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- return(0);
- }
-
- /*
- * Process wild initiates here
- */
- if (wType == XTYP_WILDCONNECT) {
- HSZ ahsz[(CTOPICS + 1) * 2];
- /*
- * He wants a hsz list of all our available app/topic pairs
- * that conform to hszTopic and hszItem(App).
- */
-
- if (!ValidateContext((PCONVCONTEXT)lData1)) {
- return(FALSE);
- }
-
- if (hszItem != hszAppName && hszItem != NULL) {
- // we only support the hszAppName service
- return(0);
- }
-
- // scan the topic table and create hsz pairs
- j = 0;
- for (i = 0; i < CTOPICS; i++) {
- if (hszTopic == NULL || hszTopic == topicList[i].hszTopic) {
- ahsz[j++] = hszAppName;
- ahsz[j++] = topicList[i].hszTopic;
- }
- }
-
- // cap off the list with 0s
- ahsz[j++] = ahsz[j++] = 0L;
-
- // send it back
- return(DdeCreateDataHandle(idInst, (LPBYTE)&ahsz[0], sizeof(HSZ) * j, 0L, 0, wFmt, 0));
- }
-
- /*
- * Check our hsz tables and send to the apropriate proc. to process.
- * We use DdeCmpStringHandles() which is the portable case-insensitive
- * method of comparing string handles. (this is a macro on windows so
- * there is no real speed hit.) On WINDOWS, HSZs are case-insensitive
- * anyway, but this may not be the case on other platforms.
- */
- for (i = 0; i < CTOPICS; i++) {
- if (DdeCmpStringHandles(topicList[i].hszTopic, hszTopic) == 0) {
-
- /*
- * connections must be on a topic we support.
- */
- if (wType == XTYP_CONNECT) {
- return(ValidateContext((PCONVCONTEXT)lData1));
- }
-
- pItemList = topicList[i].pItemList;
- cItems = topicList[i].cItems;
- for (j = 0; j < cItems; j++) {
- if (DdeCmpStringHandles(pItemList[j].hszItem, hszItem) == 0) {
-
- /*
- * Make call to worker function here...
- */
- hDataRet = (*pItemList[j].npfnCallback)
- ((PXFERINFO)&lData2, iFmt);
-
-
- ReturnSpot:
- /*
- * The table functions return a boolean or data.
- * It gets translated here.
- */
- switch (wType & XCLASS_MASK) {
- case XCLASS_DATA:
- return(hDataRet);
- break;
- case XCLASS_FLAGS:
- return(hDataRet ? DDE_FACK : DDE_FNOTPROCESSED);
- break;
- case XCLASS_BOOL:
- return(TRUE);
- default: // XCLASS_NOTIFICATION
- return(0);
- break;
- }
- break;
- }
- }
- break;
- }
- }
-
- /*
- * anything else fails - DDEML is designed so that a 0 return is ALWAYS ok.
- */
- return(0);
- }
-
-
-
-
-
- /***************************** Private Function ****************************\
- * This passes out a standard tab-delimited list of topic names for this
- * application.
- *
- * This support is required for other apps to be able to
- * find out about us. This kind of support should be in every DDE
- * application.
- *
- \***************************************************************************/
- HDDEDATA TopicListXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- WORD cbAlloc, i;
- LPSTR pszTopicList;
- HDDEDATA hData;
-
- if (pXferInfo->wType == XTYP_ADVSTART)
- return(TRUE);
-
- if (pXferInfo->wType != XTYP_REQUEST &&
- pXferInfo->wType != XTYP_ADVREQ)
- return(0);
- /*
- * construct the list of topics we have
- */
- cbAlloc = 0;
- for (i = 0; i < CTOPICS; i++)
- cbAlloc += lstrlen(topicList[i].pszTopic) + 1; // 1 for tab
-
- // allocate a data handle big enough for the list.
- hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
- pXferInfo->wFmt, 0);
- pszTopicList = (LPSTR)DdeAccessData(hData, NULL);
- if (pszTopicList) {
- for (i = 0; i < CTOPICS; i++) {
- _fstrcpy(pszTopicList, topicList[i].pszTopic);
- pszTopicList += strlen(topicList[i].pszTopic);
- *pszTopicList++ = '\t';
- }
- *--pszTopicList = '\0';
- DdeUnaccessData(hData);
- return(hData);
- }
- return(0);
- }
-
-
-
-
- /***************************** Private Function ****************************\
- * This passes out a standard tab-delimited list of item names for the
- * specified topic.
- *
- * This support is required for other apps to be able to
- * find out about us. This kind of support should be in every DDE
- * application.
- *
- \***************************************************************************/
- HDDEDATA ItemListXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- WORD cbAlloc, i, iItem, cItems;
- ITEMLIST *pItemList = 0;
- LPSTR pszItemList;
- HDDEDATA hData;
-
- if (pXferInfo->wType == XTYP_ADVSTART)
- return(TRUE);
-
- if (pXferInfo->wType != XTYP_REQUEST &&
- pXferInfo->wType != XTYP_ADVREQ)
- return(0);
- /*
- * construct the list of items we support for this topic - this supports
- * more than the minimum standard which would support SysItems only on
- * the system topic.
- */
-
- // locate the requested topic item table
- for (i = 0; i < CTOPICS; i++) {
- if (pXferInfo->hszTopic == topicList[i].hszTopic) {
- pItemList = topicList[i].pItemList;
- cItems = topicList[i].cItems;
- break;
- }
- }
-
- if (!pItemList)
- return(0); // item not found
-
- cbAlloc = 0;
- for (iItem = 0; iItem < cItems; iItem++)
- cbAlloc += lstrlen(pItemList[iItem].pszItem) + 1; // 1 for tab
-
- // allocate a data handle big enough for the list.
- hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
- pXferInfo->wFmt, 0);
- pszItemList = (LPSTR)DdeAccessData(hData, NULL);
- if (pszItemList) {
- for (i = 0; i < cItems; i++) {
- _fstrcpy(pszItemList, pItemList[i].pszItem);
- pszItemList += strlen(pItemList[i].pszItem);
- *pszItemList++ = '\t';
- }
- *--pszItemList = '\0';
- DdeUnaccessData(hData);
- return(hData);
- }
- return(0);
- }
-
-
-
-
-
- /***************************** Private Function ****************************\
- * Gives out a 0 terminated array of dde format numbers supported by this app.
- *
- * This support is required for other apps to be able to
- * find out about us. This kind of support should be in every DDE
- * application.
- *
- \***************************************************************************/
- HDDEDATA sysFormatsXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- int i, cb;
- LPSTR psz, pszT;
- HDDEDATA hData;
-
- if (pXferInfo->wType == XTYP_ADVSTART)
- return(TRUE);
-
- if (pXferInfo->wType != XTYP_REQUEST &&
- pXferInfo->wType != XTYP_ADVREQ)
- return(0);
-
- for (i = 0, cb = 0; i < CFORMATS; i++)
- cb += strlen(aFormats[i].sz) + 1;
-
- hData = DdeCreateDataHandle(idInst, NULL, (DWORD)cb,
- 0L, pXferInfo->hszItem, pXferInfo->wFmt, 0);
- psz = pszT = DdeAccessData(hData, NULL);
- for (i = 0; i < CFORMATS; i++) {
- _fstrcpy(pszT, aFormats[i].sz);
- pszT += _fstrlen(pszT);
- *pszT++ = '\t';
- }
- *(--pszT) = '\0';
- DdeUnaccessData(hData);
- return(hData);
- }
-
-
-
- /*
- * This is a runaway item. Each time it is requested, it changes.
- * pokes just make it change again.
- */
- HDDEDATA TestRandomXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- char szT[10]; // SS==DS!
- LPSTR pszData;
- HDDEDATA hData;
- WORD i;
-
- switch (pXferInfo->wType) {
- case XTYP_POKE:
- // we expect an ascii number to replace the current seed.
- pszComment = "Rand poke received.";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- InvalidateRect(hwndServer, &rcRand, TRUE);
- if (DdeGetData(pXferInfo->hData, szT, 10, 0)) {
- szT[9] = '\0'; // just incase we overran.
- sscanf(szT, "%d", &seed);
- for (i = 0; i < CFORMATS; i++) {
- if (hDataRand[i])
- DdeFreeDataHandle(hDataRand[i]);
- hDataRand[i] = 0;
- }
- DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
- return(1);
- }
- break;
-
- case XTYP_REQUEST:
- pszComment = "Rand data requested.";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- case XTYP_ADVREQ:
- Delay(RenderDelay, FALSE);
- if (!hDataRand[iFmt]) {
- hDataRand[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
- pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
- if (pszData = DdeAccessData(hDataRand[iFmt], NULL)) {
- wsprintf(pszData, "%d", seed);
- DdeUnaccessData(hDataRand[iFmt]);
- }
- }
- hData = hDataRand[iFmt];
- if (!fAppowned)
- hDataRand[iFmt] = 0;
- return(hData);
- break;
-
- case XTYP_ADVSTART:
- return(1);
- }
- return(0);
- }
-
- /*
- * This is a runaway item. Each time it is requested, it changes.
- * pokes just make it change again.
- */
- HDDEDATA TestCountXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- char szT[16]; // SS==DS!
- LPSTR pszData;
- HDDEDATA hData;
- WORD i;
-
- switch (pXferInfo->wType) {
- case XTYP_POKE:
- // we expect an ascii number to replace the current count.
- pszComment = "Count poke received";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- InvalidateRect(hwndServer, &rcCount, TRUE);
- if (DdeGetData(pXferInfo->hData, szT, 10, 0)) {
- szT[9] = '\0'; // just incase we overran.
- sscanf(szT, "%ld", &count);
- for (i = 0; i < CFORMATS; i++) {
- if (hDataCount[i])
- DdeFreeDataHandle(hDataCount[i]);
- hDataCount[i] = 0;
- }
- DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
- return(1);
- }
- break;
-
- case XTYP_REQUEST:
- pszComment = "Count data requested.";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- case XTYP_ADVREQ:
- Delay(RenderDelay, FALSE);
- if (!hDataCount[iFmt]) {
- hDataCount[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
- pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
- if (pszData = DdeAccessData(hDataCount[iFmt], NULL)) {
- wsprintf(pszData, "%ld", count);
- DdeUnaccessData(hDataCount[iFmt]);
- }
- }
- hData = hDataCount[iFmt];
- if (!fAppowned)
- hDataCount[iFmt] = 0;
- return(hData);
- break;
-
- case XTYP_ADVSTART:
- return(1);
- }
- return(0);
- }
-
-
- /*
- * This is not a runaway item. Only Pokes make it change.
- */
- HDDEDATA TestHugeXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- BOOL fSuccess;
- DWORD ulcb;
- LPBYTE lpData;
- WORD i;
- HDDEDATA hData;
-
- switch (pXferInfo->wType) {
- case XTYP_POKE:
- ulcb = DdeGetData(pXferInfo->hData, NULL, 0, 0);
- fSuccess = CheckHugeData(pXferInfo->hData);
- if (fSuccess) {
- pszComment = "Huge poke data successfully received.";
- } else {
- wsprintf(szComment, "%ld bytes of invalid Huge data received.", ulcb);
- pszComment = szComment;
- }
- InvalidateRect(hwndServer, &rcComment, TRUE);
- InvalidateRect(hwndServer, &rcHugeSize, TRUE);
- if (fSuccess) {
- for (i = 0; i < CFORMATS; i++) {
- if (hDataHuge[i]) {
- DdeFreeDataHandle(hDataHuge[i]);
- hDataHuge[i] = 0;
- }
- }
- /*
- * Since callback data handles are only good for the duration of
- * the callback, we must copy the data to our own data handle.
- */
- lpData = DdeAccessData(pXferInfo->hData, &cbHuge);
- hDataHuge[iFmt] = DdeCreateDataHandle(idInst, lpData, cbHuge, 0,
- pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
- DdeUnaccessData(pXferInfo->hData);
- DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
- }
- return(fSuccess);
- break;
-
- case XTYP_REQUEST:
- pszComment = "Huge data requested.";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- case XTYP_ADVREQ:
- Delay(RenderDelay, FALSE);
- if (!hDataHuge[iFmt]) {
- cbHuge = (DWORD)rand() * 64L + 0x10000L;
- wsprintf(szComment, "Generating huge data - length=%ld...", cbHuge);
- pszComment = szComment;
- InvalidateRect(hwndServer, &rcComment, TRUE);
- UpdateWindow(hwndServer);
- hDataHuge[iFmt] = CreateHugeDataHandle(cbHuge, 4325, 345, 5,
- pXferInfo->hszItem,
- pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
- pszComment = "";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- InvalidateRect(hwndServer, &rcHugeSize, TRUE);
- }
- hData = hDataHuge[iFmt];
- if (!fAppowned)
- hDataHuge[iFmt] = 0;
- return(hData);
- break;
-
- case XTYP_ADVSTART:
- return(1);
- }
- return(0);
- }
-
-
- HDDEDATA HelpXfer(
- PXFERINFO pXferInfo,
- WORD iFmt)
- {
- HDDEDATA hData;
-
- switch (pXferInfo->wType) {
- case XTYP_REQUEST:
- pszComment = "Help text requested.";
- InvalidateRect(hwndServer, &rcComment, TRUE);
- case XTYP_ADVREQ:
- if (!hDataHelp[iFmt]) {
- hDataHelp[iFmt] = DdeCreateDataHandle(idInst, szDdeHelp, strlen(szDdeHelp) + 1,
- 0, pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
- }
- hData = hDataHelp[iFmt];
- if (!fAppowned)
- hDataHelp[iFmt] = 0;
- return(hData);
- break;
-
- case XTYP_ADVSTART:
- return(1);
- }
- return(0);
- }
-
-
- /***************************** Private Function ****************************\
- * This creates often used global hszs from standard global strings.
- * It also fills the hsz fields of the topic and item tables.
- *
- \***************************************************************************/
- void Hszize()
- {
- register ITEMLIST *pItemList;
- WORD iTopic, iItem;
-
- hszAppName = DdeCreateStringHandle(idInst, szServer, NULL);
-
- for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
- topicList[iTopic].hszTopic =
- DdeCreateStringHandle(idInst, topicList[iTopic].pszTopic, NULL);
- pItemList = topicList[iTopic].pItemList;
- for (iItem = 0; iItem < topicList[iTopic].cItems; iItem++) {
- pItemList[iItem].hszItem =
- DdeCreateStringHandle(idInst, pItemList[iItem].pszItem, NULL);
- }
- }
- }
-
-
-
-
-
- /***************************** Private Function ****************************\
- * This destroys often used global hszs from standard global strings.
- *
- \***************************************************************************/
- void UnHszize()
- {
- register ITEMLIST *pItemList;
- WORD iTopic, iItem;
-
- DdeFreeStringHandle(idInst, hszAppName);
-
- for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
- DdeFreeStringHandle(idInst, topicList[iTopic].hszTopic);
- pItemList = topicList[iTopic].pItemList;
- for (iItem = 0; iItem < topicList[iTopic].cItems; iItem++) {
- DdeFreeStringHandle(idInst, pItemList[iItem].hszItem);
- }
- }
- }
-