|
|
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: * * 14: * MODULE : dde.c * 15: * * 16: * PURPOSE : Contains routines for handling of DDE interaction with * 17: * DDEML. * 18: * * 19: ***************************************************************************/ 20: #include "client.h" 21: #include <string.h> 22: #include <memory.h> 23: #include "infoctrl.h" 1.1.1.2 root 24: #include "huge.h" 1.1 root 25: 26: CHAR szT[100]; 27: 28: /**************************************************************************** 29: * * 30: * FUNCTION : CreateXactionWindow() * 31: * * 32: * PURPOSE : Creates a transaction window for the given transaction * 33: * under the given conversation window. * 34: * * 35: * RETURNS : TRUE - If successful. * 36: * FALSE - otherwise. * 37: * * 38: ****************************************************************************/ 39: HWND CreateXactionWindow( 40: HWND hwndMDI, 41: XACT *pxact) 42: { 43: PSTR pszFmt, pszItem; 44: PSTR pData; 45: HWND hwnd; 46: 47: pszItem = GetHSZName(pxact->hszItem); 48: pszFmt = GetFormatName(pxact->wFmt); 49: pData = GetTextData(pxact->hDdeData); 50: 51: /* 52: * �type/opts������� ITEM ���������������retĿ GWL_USER=pxact 53: * � � 54: * � � 55: * � � 56: * � � 57: * � DATA � 58: * � � 59: * � � 60: * � � 61: * � � 62: * �state/error����� FORMAT ����������Result�� 63: */ 64: hwnd = CreateInfoCtrl((LPSTR)pData, 65: (INT)SendMessage(hwndMDI, UM_GETNEXTCHILDX, 0, 0L), 66: (INT)SendMessage(hwndMDI, UM_GETNEXTCHILDY, 0, 0L), 67: 200, 100, hwndMDI, hInst, 68: Type2String(pxact->wType, pxact->fsOptions), pszItem, NULL, 69: "Starting", (LPSTR)pszFmt, NULL, 70: ICSTY_SHOWFOCUS, 0, (DWORD)(LPSTR)pxact); 71: MyFree(pszItem); 72: MyFree(pszFmt); 73: MyFree(pData); 74: return(hwnd); 75: } 76: 77: 78: 79: 80: /**************************************************************************** 81: * * 82: * FUNCTION : ProcessTransaction() * 83: * * 84: * PURPOSE : Processes synchronous transactions entirely and starts * 85: * async transactions. Transaction attempts result in a * 86: * transaction window being created which displays the state * 87: * or results of the transaction. (the callback function * 88: * updates these windows as it gets calls) Transaction * 89: * windows stay around until abandoned by the user or until * 90: * the conversation is disconnected. Advise Data and Advise * 91: * Stop transactions are special. We don't create a new * 92: * window if the associated advise start transaction window * 93: * can be found. * 94: * * 95: * RETURNS : TRUE - If successful. * 96: * FALSE - otherwise. * 97: * * 98: ****************************************************************************/ 99: BOOL ProcessTransaction( 100: XACT *pxact) 101: { 102: CONVINFO ci; 103: HWND hwndInfoCtrl = 0; 104: 105: /* create transaction window to show we tried (except in ADVSTOP case) */ 106: 107: pxact = (XACT *)memcpy(MyAlloc(sizeof(XACT)), (PSTR)pxact, sizeof(XACT)); 108: ci.cb = sizeof(CONVINFO); 109: DdeQueryConvInfo(pxact->hConv, QID_SYNC, &ci); // ci.hUser==hConv 110: if (pxact->wType == XTYP_ADVSTOP) { 111: hwndInfoCtrl = FindAdviseChild((HWND)ci.hUser, pxact->hszItem, 112: pxact->wFmt); 113: if (hwndInfoCtrl) { 114: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UL, 115: (DWORD)(LPSTR)Type2String(pxact->wType, pxact->fsOptions)); 116: DdeFreeStringHandle(idInst, pxact->hszItem); 117: } 118: } 119: /* 120: * If we still need to create a transaction window, do so here. 121: */ 122: if (!hwndInfoCtrl) { 123: hwndInfoCtrl = CreateXactionWindow((HWND)ci.hUser, pxact); 124: if (!hwndInfoCtrl) { 125: MyFree(pxact); 126: return 0; 127: } 128: SetFocus(hwndInfoCtrl); 129: } 130: /* 131: * Disable callbacks for this conversation now if the XOPT_DISABLEFIRST 132: * option is set. This tests disabling asynchronous transactions 133: * before they are completed. 134: */ 135: if (pxact->fsOptions & XOPT_DISABLEFIRST) 136: DdeEnableCallback(idInst, pxact->hConv, EC_DISABLE); 137: /* 138: * Adjust the timeout for asynchronous transactions. 139: */ 140: if (pxact->fsOptions & XOPT_ASYNC) 141: pxact->ulTimeout = TIMEOUT_ASYNC; 142: 143: /* 144: * start transaction with DDEML here 145: */ 146: pxact->ret = DdeClientTransaction((LPBYTE)pxact->hDdeData, 0xFFFFFFFF, 147: pxact->hConv, pxact->hszItem, pxact->wFmt, 148: pxact->wType, 149: pxact->ulTimeout, (LPDWORD)&pxact->Result); 150: 151: /* 152: * show return value in transaction window 153: */ 154: wsprintf(szT, "ret=%lx", pxact->ret); 155: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UR, (DWORD)(LPSTR)szT); 156: 157: /* 158: * show result or ID value in transaction window 159: */ 160: wsprintf(szT, pxact->fsOptions & XOPT_ASYNC ? "ID=%ld" : 161: "result=0x%lx", pxact->Result); 162: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LR, (DWORD)(LPSTR)szT); 163: 164: if ((pxact->fsOptions & XOPT_ASYNC) && pxact->ret) { 165: /* 166: * asynchronous successful start - link transaction to window. 167: */ 168: DdeSetUserHandle(pxact->hConv, pxact->Result, (DWORD)hwndInfoCtrl); 169: 170: /* 171: * Abandon started async transaction after initiated if 172: * XOPT_ABANDONAFTERSTART is chosen. This tests the mid-transaction 173: * abandoning code. 174: */ 175: if (pxact->fsOptions & XOPT_ABANDONAFTERSTART) 176: DdeAbandonTransaction(idInst, pxact->hConv, pxact->Result); 177: /* 178: * show actual status 179: */ 180: ci.cb = sizeof(CONVINFO); 181: DdeQueryConvInfo(pxact->hConv, pxact->Result, &ci); 182: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL, 183: (DWORD)(LPSTR)State2String(ci.wConvst)); 184: } else { 185: /* 186: * Synchronous transactions are completed already so pass on to 187: * CompleteTransaction right away. 188: */ 189: CompleteTransaction(hwndInfoCtrl, pxact); 190: } 191: return TRUE; 192: } 193: 194: 195: 196: 197: 198: /**************************************************************************** 199: * * 200: * FUNCTION : CompleteTransaction() * 201: * * 202: * PURPOSE : This handles completed synchronous and asynchronous * 203: * transactions as well as failed attempted transactions. * 204: * * 205: * RETURNS : TRUE - If successful. * 206: * FALSE - otherwise. * 207: * * 208: ****************************************************************************/ 209: VOID CompleteTransaction( 210: HWND hwndInfoCtrl, 211: XACT *pxact) 212: { 213: PSTR psz; 214: 215: if (pxact->ret) { 216: /* 217: * Successful transaction case 218: */ 219: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL, 220: (DWORD)(LPSTR)"Completed"); 221: 222: if (pxact->wType == XTYP_REQUEST) { 223: /* 224: * Show resulting data 225: */ 226: psz = GetTextData((HDDEDATA)pxact->ret); 1.1.1.2 root 227: if (!DdeCmpStringHandles(pxact->hszItem, hszHuge) && 228: !CheckHugeData((HDDEDATA)pxact->ret)) { 229: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_CENTER, 230: (DWORD)(LPSTR)"Invalid Huge Data"); 231: } else { 232: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_CENTER, 233: (DWORD)(LPSTR)psz); 234: } 1.1 root 235: MyFree(psz); 236: /* 237: * free returned data since it is displayed. 238: */ 239: DdeFreeDataHandle(pxact->ret); 240: pxact->ret = 0L; 1.1.1.3 ! root 241: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UR, 0); 1.1 root 242: } 243: } else { 244: /* 245: * failed - show error result. 246: */ 247: SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL, 248: (DWORD)(LPSTR)Error2String(DdeGetLastError(idInst))); 249: } 250: pxact->fsOptions |= XOPT_COMPLETED; 251: } 252: 253: 254: 255: 256: /**************************************************************************** 257: * * 258: * FUNCTION : DdeCallback() * 259: * * 260: * PURPOSE : This handles all callbacks from the DDEML. This handles * 261: * updating of the associated conversation and any special * 262: * testing cases such as blocking callbacks etc. * 263: * * 264: * For the most part, clients only handle advise data and * 265: * asynchronous transaction completion here. * 266: * * 267: * RETURNS : Results vary depending on transaction type. * 268: * * 269: ****************************************************************************/ 270: HDDEDATA CALLBACK DdeCallback( 271: UINT wType, 272: UINT wFmt, 273: HCONV hConv, 274: HSZ hsz1, 275: HSZ hsz2, 276: HDDEDATA hData, 277: DWORD lData1, 278: DWORD lData2) 279: { 280: HWND hwnd; 281: CONVINFO ci; 282: XACT *pxact; 283: 284: if (hConv) { 285: /* 286: * update conversation status if it changed. 287: */ 288: MYCONVINFO *pmci; 289: 290: ci.cb = sizeof(CONVINFO); 291: if (!DdeQueryConvInfo(hConv, QID_SYNC, &ci) || (!IsWindow((HWND)ci.hUser))) { 292: /* 293: * This conversation does not yet have a corresponding MDI window 294: * or is disconnected. 295: */ 296: return 0; 297: } 298: if (pmci = (MYCONVINFO *)GetWindowLong((HWND)ci.hUser, 0)) { 299: if (pmci->ci.wStatus != ci.wStatus || 300: pmci->ci.wConvst != ci.wConvst || 301: pmci->ci.wLastError != ci.wLastError) { 302: /* 303: * Things have changed, updated the conversation window. 304: */ 305: InvalidateRect((HWND)ci.hUser, NULL, TRUE); 306: } 307: if (ci.wConvst & ST_INLIST) { 308: /* 309: * update the associated list window (if any) as well. 310: */ 311: if (hwnd = FindListWindow(ci.hConvList)) 312: InvalidateRect(hwnd, NULL, TRUE); 313: } 314: } 315: } 316: 317: /* 318: * handle special block on next callback option here. This demonstrates 319: * the CBR_BLOCK feature. 320: */ 321: if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) { 322: fBlockNextCB = FALSE; 323: return(CBR_BLOCK); 324: } 325: 326: /* 327: * handle special termination here. This demonstrates that at any time 328: * a client can drop a conversation. 329: */ 330: if (fTermNextCB && hConv && wType != XTYP_DISCONNECT) { 331: fTermNextCB = FALSE; 332: MyDisconnect(hConv); 333: return(0); 334: } 335: 336: /* 337: * Now we begin sort out what to do. 338: */ 339: switch (wType) { 340: case XTYP_REGISTER: 341: case XTYP_UNREGISTER: 342: /* 343: * This is where the client would insert code to keep track of 344: * what servers are available. This could cause the initiation 345: * of some conversations. 346: */ 347: break; 348: 349: case XTYP_DISCONNECT: 350: if (fAutoReconnect) { 351: /* 352: * attempt a reconnection 353: */ 354: if (hConv = DdeReconnect(hConv)) { 355: AddConv(ci.hszServiceReq, ci.hszTopic, hConv, FALSE); 356: return 0; 357: } 358: } 359: 360: /* 361: * update conv window to show its new state. 362: */ 363: SendMessage((HWND)ci.hUser, UM_DISCONNECTED, 0, 0); 364: return 0; 365: break; 366: 367: case XTYP_ADVDATA: 368: /* 369: * data from an active advise loop (from a server) 370: */ 371: Delay(wDelay); 372: hwnd = FindAdviseChild((HWND)ci.hUser, hsz2, wFmt); 373: if (!IsWindow(hwnd)) { 374: PSTR pszItem, pszFmt; 375: /* 376: * AdviseStart window is gone, make a new one. 377: */ 378: pxact = (XACT *)MyAlloc(sizeof(XACT)); 379: pxact->wType = wType; 380: pxact->hConv = hConv; 381: pxact->wFmt = wFmt; 382: pxact->hszItem = hsz2; 383: DdeKeepStringHandle(idInst, hsz2); 384: 385: pszItem = GetHSZName(hsz2); 386: pszFmt = GetFormatName(wFmt); 387: 388: hwnd = CreateInfoCtrl(NULL, 389: (INT)SendMessage((HWND)ci.hUser, UM_GETNEXTCHILDX, 0, 0L), 390: (INT)SendMessage((HWND)ci.hUser, UM_GETNEXTCHILDY, 0, 0L), 391: 200, 100, 392: (HWND)ci.hUser, hInst, 393: Type2String(wType, 0), (LPSTR)pszItem, NULL, 394: NULL, (LPSTR)pszFmt, NULL, 395: ICSTY_SHOWFOCUS, 0, (DWORD)(LPSTR)pxact); 396: 397: MyFree(pszFmt); 398: MyFree(pszItem); 399: 400: if (!IsWindow(hwnd)) 401: return(DDE_FNOTPROCESSED); 402: } 403: if (!hData) { 404: /* 405: * XTYPF_NODATA case - request the info. (we do this synchronously 406: * for simplicity) 407: */ 408: hData = DdeClientTransaction(NULL, 0L, hConv, hsz2, wFmt, 409: XTYP_REQUEST, DefTimeout, NULL); 410: } 411: if (hData) { 412: PSTR pData; 413: /* 414: * Show incomming data on corresponding transaction window. 415: */ 416: pData = GetTextData(hData); 417: SendMessage(hwnd, ICM_SETSTRING, ICSID_CENTER, (DWORD)(LPSTR)pData); 418: MyFree(pData); 419: DdeFreeDataHandle(hData); 420: } 421: SendMessage(hwnd, ICM_SETSTRING, ICSID_LL, (DWORD)(LPSTR)"Advised"); 422: return(DDE_FACK); 423: break; 424: 425: case XTYP_XACT_COMPLETE: 426: /* 427: * An asynchronous transaction has completed. Show the results. 428: * 429: * ...unless the XOPT_BLOCKRESULT is chosen. 430: */ 431: 432: ci.cb = sizeof(CONVINFO); 433: if (DdeQueryConvInfo(hConv, lData1, &ci) && 434: IsWindow((HWND)ci.hUser) && 435: (pxact = (XACT *)GetWindowLong((HWND)ci.hUser, GWL_USER))) { 436: 437: if (pxact->fsOptions & XOPT_BLOCKRESULT) { 438: pxact->fsOptions &= ~XOPT_BLOCKRESULT; 439: return(CBR_BLOCK); 440: } 441: 442: pxact->Result = lData2; 443: pxact->ret = hData; 444: CompleteTransaction((HWND)ci.hUser, pxact); 445: } 446: break; 447: } 448: } 449: 450: 451: 452: 453: 454: 455: 456: /**************************************************************************** 457: * * 458: * FUNCTION : FindAdviseChild() * 459: * * 460: * PURPOSE : Search through the child windows of hwndMDI for an info * 461: * ctrl that has the same Item and format and is an * 462: * ADVSTART ADVSTOP or ADVDATA transaction window. * 463: * * 464: * We use these to show the associated advise data. * 465: * * 466: * RETURNS : The transaction window handle or 0 on failure. * 467: * * 468: ****************************************************************************/ 469: HWND FindAdviseChild( 470: HWND hwndMDI, 471: HSZ hszItem, 472: DWORD wFmt) 473: { 474: HWND hwnd, hwndStart; 475: XACT *pxact; 476: 477: if (!IsWindow(hwndMDI)) 478: return 0; 479: 480: hwnd = hwndStart = GetWindow(hwndMDI, GW_CHILD); 481: while (hwnd && IsChild(hwndMDI, hwnd)) { 482: pxact = (XACT *)GetWindowLong(hwnd, GWL_USER); 483: if (pxact && 484: (pxact)->wFmt == wFmt && 485: (pxact)->hszItem == hszItem && 486: ( 487: ((pxact->wType & XTYP_ADVSTART) == XTYP_ADVSTART) || 488: (pxact->wType == XTYP_ADVSTOP) || 489: (pxact->wType == XTYP_ADVDATA) 490: ) 491: ) { 492: return(hwnd); 493: } 494: hwnd = GetWindow(hwnd, GW_HWNDNEXT); 495: if (hwnd == hwndStart) 496: return 0; 497: } 498: return 0; 499: } 500: 501: 502: 503: /**************************************************************************** 504: * * 505: * FUNCTION : FindListWindow() * 506: * * 507: * PURPOSE : Locates the list window associated with this conversation * 508: * list. * 509: * * 510: * RETURNS : The window handle of the list window or 0 on failure. * 511: * * 512: ****************************************************************************/ 513: HWND FindListWindow( 514: HCONVLIST hConvList) 515: { 516: HWND hwnd; 517: MYCONVINFO *pmci; 518: 519: hwnd = GetWindow(hwndMDIClient, GW_CHILD); 520: while (hwnd) { 521: if (GetWindowLong(hwnd, GWL_WNDPROC) == (LONG)MDIChildWndProc) { 522: pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 523: if (pmci != NULL && pmci->fList && pmci->hConv == hConvList) 524: return(hwnd); 525: } 526: hwnd = GetWindow(hwnd, GW_HWNDNEXT); 527: } 528: return 0; 529: } 530: 531: 532: 533: 534: /**************************************************************************** 535: * * 536: * FUNCTION : GetTextData() * 537: * * 538: * PURPOSE : Allocates and returns a pointer to the data contained in * 539: * hData. This assumes that hData points to text data and * 540: * will properly handle huge text data by leaving out the * 541: * middle of the string and placing the size of the string * 542: * into this string portion. * 543: * * 544: * RETURNS : A pointer to the allocated string. * 545: * * 546: ****************************************************************************/ 547: PSTR GetTextData( 548: HDDEDATA hData) 549: { 550: PSTR psz; 551: DWORD cb; 552: 553: #define CBBUF 1024 554: 1.1.1.3 ! root 555: if (hData == 0) { 1.1 root 556: return(NULL); 557: } 558: 559: cb = DdeGetData(hData, NULL, 0, 0); 560: if (!hData || !cb) 561: return NULL; 562: 563: if (cb > CBBUF) { // possibly HUGE object! 564: psz = MyAlloc(CBBUF); 565: DdeGetData(hData, psz, CBBUF - 46, 0L); 566: wsprintf(&psz[CBBUF - 46], "<---Size=%ld", cb); 567: } else { 568: psz = MyAlloc((DWORD)cb); 569: DdeGetData(hData, (LPBYTE)psz, cb, 0L); 570: } 571: return psz; 572: #undef CBBUF 573: } 574: 575: 576: 577: 578: 579: 580: 581: /**************************************************************************** 582: * * 583: * FUNCTION : MyGetClipboardFormatName() * 584: * * 585: * PURPOSE : Properly retrieves the string associated with a clipboard * 586: * format. If the format does not have a string associated * 587: * with it, the string #dddd is returned. * 588: * * 589: * RETURNS : The number of characters copied into lpstr or 0 on error. * 590: * * 591: ****************************************************************************/ 592: INT MyGetClipboardFormatName( 593: DWORD fmt, 594: LPSTR lpstr, 595: INT cbMax) 596: { 597: if (fmt < 0xc000) { 598: // predefined or integer format - just get the atom string 599: // wierdly enough, GetClipboardFormatName() doesn't support this. 1.1.1.2 root 600: return(GlobalGetAtomName((ATOM)fmt, lpstr, cbMax)); 1.1 root 601: } else { 602: return(GetClipboardFormatName(fmt, lpstr, cbMax)); 603: } 604: } 605: 606: 607: 608: 609: 610: /**************************************************************************** 611: * * 612: * FUNCTION : GetFormatName() * 613: * * 614: * PURPOSE : allocates and returns a pointer to a string representing * 615: * a format. Use MyFree() to free this string. * 616: * * 617: * RETURNS : The number of characters copied into lpstr or 0 on error. * 618: * * 619: ****************************************************************************/ 620: PSTR GetFormatName( 621: DWORD wFmt) 622: { 623: PSTR psz; 624: DWORD cb; 625: 626: if (wFmt == 1) { 627: psz = MyAlloc(8); 628: strcpy(psz, "CF_TEXT"); 629: return psz; 630: } 631: psz = MyAlloc(255); 632: *psz = '\0'; 633: cb = GetClipboardFormatName(wFmt, psz, 255) + 1; 634: return((PSTR)LocalReAlloc((HANDLE)psz, cb, LMEM_MOVEABLE)); 635: } 636: 637: 638: 639: 640: /**************************************************************************** 641: * * 642: * FUNCTION : MyDisconnect() * 643: * * 644: * PURPOSE : Disconnects the given conversation after updating the * 645: * associated conversation window. * 646: * * 647: * RETURNS : TRUE on success, FALSE on failuer. * 648: * * 649: ****************************************************************************/ 650: BOOL MyDisconnect( 651: HCONV hConv) 652: { 653: CONVINFO ci; 654: HWND hwnd; 655: // before we disconnect, invalidate the associated list window - if 656: // applicable. 657: 658: ci.cb = sizeof(CONVINFO); 659: 660: if (DdeQueryConvInfo(hConv, QID_SYNC, &ci) && ci.hConvList && 661: (hwnd = FindListWindow(ci.hConvList))) 662: InvalidateRect(hwnd, NULL, TRUE); 663: return(DdeDisconnect(hConv)); 664: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.