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