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