Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/win/tclWinDde.c @ 47

Last change on this file since 47 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 44.3 KB
Line 
1/*
2 * tclWinDde.c --
3 *
4 *      This file provides functions that implement the "send" command,
5 *      allowing commands to be passed from interpreter to interpreter.
6 *
7 * Copyright (c) 1997 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclWinDde.c,v 1.31 2006/09/26 00:05:03 patthoyts Exp $
13 */
14
15#include "tclInt.h"
16#include <dde.h>
17#include <ddeml.h>
18#include <tchar.h>
19
20/*
21 * TCL_STORAGE_CLASS is set unconditionally to DLLEXPORT because the Dde_Init
22 * declaration is in the source file itself, which is only accessed when we
23 * are building a library. DO NOT MOVE BEFORE ANY #include LINES. ONLY USE
24 * EXTERN TO INDICATE EXPORTED FUNCTIONS FROM NOW ON.
25 */
26
27#undef TCL_STORAGE_CLASS
28#define TCL_STORAGE_CLASS DLLEXPORT
29
30/*
31 * The following structure is used to keep track of the interpreters
32 * registered by this process.
33 */
34
35typedef struct RegisteredInterp {
36    struct RegisteredInterp *nextPtr;
37                                /* The next interp this application knows
38                                 * about. */
39    char *name;                 /* Interpreter's name (malloc-ed). */
40    Tcl_Obj *handlerPtr;        /* The server handler command */
41    Tcl_Interp *interp;         /* The interpreter attached to this name. */
42} RegisteredInterp;
43
44/*
45 * Used to keep track of conversations.
46 */
47
48typedef struct Conversation {
49    struct Conversation *nextPtr;
50                                /* The next conversation in the list. */
51    RegisteredInterp *riPtr;    /* The info we know about the conversation. */
52    HCONV hConv;                /* The DDE handle for this conversation. */
53    Tcl_Obj *returnPackagePtr;  /* The result package for this conversation. */
54} Conversation;
55
56typedef struct DdeEnumServices {
57    Tcl_Interp *interp;
58    int result;
59    ATOM service;
60    ATOM topic;
61    HWND hwnd;
62} DdeEnumServices;
63
64typedef struct ThreadSpecificData {
65    Conversation *currentConversations;
66                                /* A list of conversations currently being
67                                 * processed. */
68    RegisteredInterp *interpListPtr;
69                                /* List of all interpreters registered in the
70                                 * current process. */
71} ThreadSpecificData;
72static Tcl_ThreadDataKey dataKey;
73
74/*
75 * The following variables cannot be placed in thread-local storage. The Mutex
76 * ddeMutex guards access to the ddeInstance.
77 */
78
79static HSZ ddeServiceGlobal = 0;
80static DWORD ddeInstance;       /* The application instance handle given to us
81                                 * by DdeInitialize. */
82static int ddeIsServer = 0;
83
84#define TCL_DDE_VERSION         "1.3.2"
85#define TCL_DDE_PACKAGE_NAME    "dde"
86#define TCL_DDE_SERVICE_NAME    "TclEval"
87#define TCL_DDE_EXECUTE_RESULT  "$TCLEVAL$EXECUTE$RESULT"
88
89TCL_DECLARE_MUTEX(ddeMutex)
90
91/*
92 * Forward declarations for functions defined later in this file.
93 */
94
95static LRESULT CALLBACK DdeClientWindowProc(HWND hwnd, UINT uMsg,
96                            WPARAM wParam, LPARAM lParam);
97static int              DdeCreateClient(struct DdeEnumServices *es);
98static BOOL CALLBACK    DdeEnumWindowsCallback(HWND hwndTarget, LPARAM lParam);
99static void             DdeExitProc(ClientData clientData);
100static int              DdeGetServicesList(Tcl_Interp *interp,
101                            char *serviceName, char *topicName);
102static HDDEDATA CALLBACK DdeServerProc(UINT uType, UINT uFmt, HCONV hConv,
103                            HSZ ddeTopic, HSZ ddeItem, HDDEDATA hData,
104                            DWORD dwData1, DWORD dwData2);
105static LRESULT          DdeServicesOnAck(HWND hwnd, WPARAM wParam,
106                            LPARAM lParam);
107static void             DeleteProc(ClientData clientData);
108static Tcl_Obj *        ExecuteRemoteObject(RegisteredInterp *riPtr,
109                            Tcl_Obj *ddeObjectPtr);
110static int              MakeDdeConnection(Tcl_Interp *interp, char *name,
111                            HCONV *ddeConvPtr);
112static void             SetDdeError(Tcl_Interp *interp);
113
114int                     Tcl_DdeObjCmd(ClientData clientData,
115                            Tcl_Interp *interp, int objc,
116                            Tcl_Obj *CONST objv[]);
117
118EXTERN int              Dde_Init(Tcl_Interp *interp);
119EXTERN int              Dde_SafeInit(Tcl_Interp *interp);
120
121/*
122 *----------------------------------------------------------------------
123 *
124 * Dde_Init --
125 *
126 *      This function initializes the dde command.
127 *
128 * Results:
129 *      A standard Tcl result.
130 *
131 * Side effects:
132 *      None.
133 *
134 *----------------------------------------------------------------------
135 */
136
137int
138Dde_Init(
139    Tcl_Interp *interp)
140{
141    ThreadSpecificData *tsdPtr;
142
143    if (!Tcl_InitStubs(interp, "8.0", 0)) {
144        return TCL_ERROR;
145    }
146
147    Tcl_CreateObjCommand(interp, "dde", Tcl_DdeObjCmd, NULL, NULL);
148    tsdPtr = TCL_TSD_INIT(&dataKey);
149    Tcl_CreateExitHandler(DdeExitProc, NULL);
150    return Tcl_PkgProvide(interp, TCL_DDE_PACKAGE_NAME, TCL_DDE_VERSION);
151}
152
153/*
154 *----------------------------------------------------------------------
155 *
156 * Dde_SafeInit --
157 *
158 *      This function initializes the dde command within a safe interp
159 *
160 * Results:
161 *      A standard Tcl result.
162 *
163 * Side effects:
164 *      None.
165 *
166 *----------------------------------------------------------------------
167 */
168
169int
170Dde_SafeInit(
171    Tcl_Interp *interp)
172{
173    int result = Dde_Init(interp);
174    if (result == TCL_OK) {
175        Tcl_HideCommand(interp, "dde", "dde");
176    }
177    return result;
178}
179
180/*
181 *----------------------------------------------------------------------
182 *
183 * Initialize --
184 *
185 *      Initialize the global DDE instance.
186 *
187 * Results:
188 *      None.
189 *
190 * Side effects:
191 *      Registers the DDE server proc.
192 *
193 *----------------------------------------------------------------------
194 */
195
196static void
197Initialize(void)
198{
199    int nameFound = 0;
200    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
201
202    /*
203     * See if the application is already registered; if so, remove its current
204     * name from the registry. The deletion of the command will take care of
205     * disposing of this entry.
206     */
207
208    if (tsdPtr->interpListPtr != NULL) {
209        nameFound = 1;
210    }
211
212    /*
213     * Make sure that the DDE server is there. This is done only once, add an
214     * exit handler tear it down.
215     */
216
217    if (ddeInstance == 0) {
218        Tcl_MutexLock(&ddeMutex);
219        if (ddeInstance == 0) {
220            if (DdeInitialize(&ddeInstance, DdeServerProc,
221                    CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS
222                    | CBF_FAIL_POKES, 0) != DMLERR_NO_ERROR) {
223                ddeInstance = 0;
224            }
225        }
226        Tcl_MutexUnlock(&ddeMutex);
227    }
228    if ((ddeServiceGlobal == 0) && (nameFound != 0)) {
229        Tcl_MutexLock(&ddeMutex);
230        if ((ddeServiceGlobal == 0) && (nameFound != 0)) {
231            ddeIsServer = 1;
232            Tcl_CreateExitHandler(DdeExitProc, NULL);
233            ddeServiceGlobal = DdeCreateStringHandle(ddeInstance,
234                    TCL_DDE_SERVICE_NAME, 0);
235            DdeNameService(ddeInstance, ddeServiceGlobal, 0L, DNS_REGISTER);
236        } else {
237            ddeIsServer = 0;
238        }
239        Tcl_MutexUnlock(&ddeMutex);
240    }
241}
242
243/*
244 *----------------------------------------------------------------------
245 *
246 * DdeSetServerName --
247 *
248 *      This function is called to associate an ASCII name with a Dde server.
249 *      If the interpreter has already been named, the name replaces the old
250 *      one.
251 *
252 * Results:
253 *      The return value is the name actually given to the interp. This will
254 *      normally be the same as name, but if name was already in use for a Dde
255 *      Server then a name of the form "name #2" will be chosen, with a high
256 *      enough number to make the name unique.
257 *
258 * Side effects:
259 *      Registration info is saved, thereby allowing the "send" command to be
260 *      used later to invoke commands in the application. In addition, the
261 *      "send" command is created in the application's interpreter. The
262 *      registration will be removed automatically if the interpreter is
263 *      deleted or the "send" command is removed.
264 *
265 *----------------------------------------------------------------------
266 */
267
268static char *
269DdeSetServerName(
270    Tcl_Interp *interp,
271    char *name,                 /* The name that will be used to refer to the
272                                 * interpreter in later "send" commands. Must
273                                 * be globally unique. */
274    int exactName,              /* Should we make a unique name? 0 = unique */
275    Tcl_Obj *handlerPtr)        /* Name of the optional proc/command to handle
276                                 * incoming Dde eval's */
277{
278    int suffix, offset;
279    RegisteredInterp *riPtr, *prevPtr;
280    Tcl_DString dString;
281    char *actualName;
282    Tcl_Obj *srvListPtr = NULL, **srvPtrPtr = NULL;
283    int n, srvCount = 0, lastSuffix, r = TCL_OK;
284    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
285
286    /*
287     * See if the application is already registered; if so, remove its current
288     * name from the registry. The deletion of the command will take care of
289     * disposing of this entry.
290     */
291
292    for (riPtr = tsdPtr->interpListPtr, prevPtr = NULL; riPtr != NULL;
293            prevPtr = riPtr, riPtr = riPtr->nextPtr) {
294        if (riPtr->interp == interp) {
295            if (name != NULL) {
296                if (prevPtr == NULL) {
297                    tsdPtr->interpListPtr = tsdPtr->interpListPtr->nextPtr;
298                } else {
299                    prevPtr->nextPtr = riPtr->nextPtr;
300                }
301                break;
302            } else {
303                /*
304                 * The name was NULL, so the caller is asking for the name of
305                 * the current interp.
306                 */
307
308                return riPtr->name;
309            }
310        }
311    }
312
313    if (name == NULL) {
314        /*
315         * The name was NULL, so the caller is asking for the name of the
316         * current interp, but it doesn't have a name.
317         */
318
319        return "";
320    }
321
322    /*
323     * Get the list of currently registered Tcl interpreters by calling the
324     * internal implementation of the 'dde services' command.
325     */
326
327    Tcl_DStringInit(&dString);
328    actualName = name;
329
330    if (!exactName) {
331        r = DdeGetServicesList(interp, TCL_DDE_SERVICE_NAME, NULL);
332        if (r == TCL_OK) {
333            srvListPtr = Tcl_GetObjResult(interp);
334        }
335        if (r == TCL_OK) {
336            r = Tcl_ListObjGetElements(interp, srvListPtr, &srvCount,
337                    &srvPtrPtr);
338        }
339        if (r != TCL_OK) {
340            OutputDebugString(Tcl_GetStringResult(interp));
341            return NULL;
342        }
343
344        /*
345         * Pick a name to use for the application. Use "name" if it's not
346         * already in use. Otherwise add a suffix such as " #2", trying larger
347         * and larger numbers until we eventually find one that is unique.
348         */
349
350        offset = lastSuffix = 0;
351        suffix = 1;
352
353        while (suffix != lastSuffix) {
354            lastSuffix = suffix;
355            if (suffix > 1) {
356                if (suffix == 2) {
357                    Tcl_DStringAppend(&dString, name, -1);
358                    Tcl_DStringAppend(&dString, " #", 2);
359                    offset = Tcl_DStringLength(&dString);
360                    Tcl_DStringSetLength(&dString, offset + TCL_INTEGER_SPACE);
361                    actualName = Tcl_DStringValue(&dString);
362                }
363                sprintf(Tcl_DStringValue(&dString) + offset, "%d", suffix);
364            }
365
366            /*
367             * See if the name is already in use, if so increment suffix.
368             */
369
370            for (n = 0; n < srvCount; ++n) {
371                Tcl_Obj* namePtr;
372
373                Tcl_ListObjIndex(interp, srvPtrPtr[n], 1, &namePtr);
374                if (strcmp(actualName, Tcl_GetString(namePtr)) == 0) {
375                    suffix++;
376                    break;
377                }
378            }
379        }
380        Tcl_DStringSetLength(&dString,
381                offset + (int)strlen(Tcl_DStringValue(&dString)+offset));
382    }
383
384    /*
385     * We have found a unique name. Now add it to the registry.
386     */
387
388    riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
389    riPtr->interp = interp;
390    riPtr->name = ckalloc((unsigned int) strlen(actualName) + 1);
391    riPtr->nextPtr = tsdPtr->interpListPtr;
392    riPtr->handlerPtr = handlerPtr;
393    if (riPtr->handlerPtr != NULL) {
394        Tcl_IncrRefCount(riPtr->handlerPtr);
395    }
396    tsdPtr->interpListPtr = riPtr;
397    strcpy(riPtr->name, actualName);
398
399    if (Tcl_IsSafe(interp)) {
400        Tcl_ExposeCommand(interp, "dde", "dde");
401    }
402
403    Tcl_CreateObjCommand(interp, "dde", Tcl_DdeObjCmd,
404            (ClientData) riPtr, DeleteProc);
405    if (Tcl_IsSafe(interp)) {
406        Tcl_HideCommand(interp, "dde", "dde");
407    }
408    Tcl_DStringFree(&dString);
409
410    /*
411     * Re-initialize with the new name.
412     */
413
414    Initialize();
415
416    return riPtr->name;
417}
418
419/*
420 *----------------------------------------------------------------------
421 *
422 * DdeGetRegistrationPtr
423 *
424 *      Retrieve the registration info for an interpreter.
425 *
426 * Results:
427 *      Returns a pointer to the registration structure or NULL
428 *
429 * Side effects:
430 *      None
431 *
432 *----------------------------------------------------------------------
433 */
434
435static RegisteredInterp *
436DdeGetRegistrationPtr(
437    Tcl_Interp *interp)
438{
439    RegisteredInterp *riPtr;
440    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
441
442    for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
443            riPtr = riPtr->nextPtr) {
444        if (riPtr->interp == interp) {
445            break;
446        }
447    }
448    return riPtr;
449}
450
451/*
452 *----------------------------------------------------------------------
453 *
454 * DeleteProc
455 *
456 *      This function is called when the command "dde" is destroyed.
457 *
458 * Results:
459 *      none
460 *
461 * Side effects:
462 *      The interpreter given by riPtr is unregistered.
463 *
464 *----------------------------------------------------------------------
465 */
466
467static void
468DeleteProc(
469    ClientData clientData)      /* The interp we are deleting passed as
470                                 * ClientData. */
471{
472    RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
473    RegisteredInterp *searchPtr, *prevPtr;
474    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
475
476    for (searchPtr = tsdPtr->interpListPtr, prevPtr = NULL;
477            searchPtr != NULL && searchPtr != riPtr;
478            prevPtr = searchPtr, searchPtr = searchPtr->nextPtr) {
479        /*
480         * Empty loop body.
481         */
482    }
483
484    if (searchPtr != NULL) {
485        if (prevPtr == NULL) {
486            tsdPtr->interpListPtr = tsdPtr->interpListPtr->nextPtr;
487        } else {
488            prevPtr->nextPtr = searchPtr->nextPtr;
489        }
490    }
491    ckfree(riPtr->name);
492    if (riPtr->handlerPtr) {
493        Tcl_DecrRefCount(riPtr->handlerPtr);
494    }
495    Tcl_EventuallyFree(clientData, TCL_DYNAMIC);
496}
497
498/*
499 *----------------------------------------------------------------------
500 *
501 * ExecuteRemoteObject --
502 *
503 *      Takes the package delivered by DDE and executes it in the server's
504 *      interpreter.
505 *
506 * Results:
507 *      A list Tcl_Obj * that describes what happened. The first element is
508 *      the numerical return code (TCL_ERROR, etc.). The second element is the
509 *      result of the script. If the return result was TCL_ERROR, then the
510 *      third element will be the value of the global "errorCode", and the
511 *      fourth will be the value of the global "errorInfo". The return result
512 *      will have a refCount of 0.
513 *
514 * Side effects:
515 *      A Tcl script is run, which can cause all kinds of other things to
516 *      happen.
517 *
518 *----------------------------------------------------------------------
519 */
520
521static Tcl_Obj *
522ExecuteRemoteObject(
523    RegisteredInterp *riPtr,        /* Info about this server. */
524    Tcl_Obj *ddeObjectPtr)          /* The object to execute. */
525{
526    Tcl_Obj *returnPackagePtr;
527    int result = TCL_OK;
528
529    if (riPtr->handlerPtr == NULL && Tcl_IsSafe(riPtr->interp)) {
530        Tcl_SetObjResult(riPtr->interp, Tcl_NewStringObj("permission denied: "
531                "a handler procedure must be defined for use in a safe "
532                "interp", -1));
533        result = TCL_ERROR;
534    }
535
536    if (riPtr->handlerPtr != NULL) {
537        /*
538         * Add the dde request data to the handler proc list.
539         */
540
541        Tcl_Obj *cmdPtr = Tcl_DuplicateObj(riPtr->handlerPtr);
542
543        result = Tcl_ListObjAppendElement(riPtr->interp, cmdPtr, ddeObjectPtr);
544        if (result == TCL_OK) {
545            ddeObjectPtr = cmdPtr;
546        }
547    }
548
549    if (result == TCL_OK) {
550        result = Tcl_EvalObjEx(riPtr->interp, ddeObjectPtr, TCL_EVAL_GLOBAL);
551    }
552
553    returnPackagePtr = Tcl_NewListObj(0, NULL);
554
555    Tcl_ListObjAppendElement(NULL, returnPackagePtr, Tcl_NewIntObj(result));
556    Tcl_ListObjAppendElement(NULL, returnPackagePtr,
557            Tcl_GetObjResult(riPtr->interp));
558
559    if (result == TCL_ERROR) {
560        Tcl_Obj *errorObjPtr = Tcl_GetVar2Ex(riPtr->interp, "errorCode", NULL,
561                TCL_GLOBAL_ONLY);
562        if (errorObjPtr) {
563            Tcl_ListObjAppendElement(NULL, returnPackagePtr, errorObjPtr);
564        }
565        errorObjPtr = Tcl_GetVar2Ex(riPtr->interp, "errorInfo", NULL,
566                TCL_GLOBAL_ONLY);
567        if (errorObjPtr) {
568            Tcl_ListObjAppendElement(NULL, returnPackagePtr, errorObjPtr);
569        }
570    }
571
572    return returnPackagePtr;
573}
574
575/*
576 *----------------------------------------------------------------------
577 *
578 * DdeServerProc --
579 *
580 *      Handles all transactions for this server. Can handle execute, request,
581 *      and connect protocols. Dde will call this routine when a client
582 *      attempts to run a dde command using this server.
583 *
584 * Results:
585 *      A DDE Handle with the result of the dde command.
586 *
587 * Side effects:
588 *      Depending on which command is executed, arbitrary Tcl scripts can be
589 *      run.
590 *
591 *----------------------------------------------------------------------
592 */
593
594static HDDEDATA CALLBACK
595DdeServerProc(
596    UINT uType,                 /* The type of DDE transaction we are
597                                 * performing. */
598    UINT uFmt,                  /* The format that data is sent or received. */
599    HCONV hConv,                /* The conversation associated with the
600                                 * current transaction. */
601    HSZ ddeTopic, HSZ ddeItem,  /* String handles. Transaction-type
602                                 * dependent. */
603    HDDEDATA hData,             /* DDE data. Transaction-type dependent. */
604    DWORD dwData1, DWORD dwData2)
605                                /* Transaction-dependent data. */
606{
607    Tcl_DString dString;
608    int len;
609    DWORD dlen;
610    char *utilString;
611    Tcl_Obj *ddeObjectPtr;
612    HDDEDATA ddeReturn = NULL;
613    RegisteredInterp *riPtr;
614    Conversation *convPtr, *prevConvPtr;
615    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
616
617    switch(uType) {
618    case XTYP_CONNECT:
619        /*
620         * Dde is trying to initialize a conversation with us. Check and make
621         * sure we have a valid topic.
622         */
623
624        len = DdeQueryString(ddeInstance, ddeTopic, NULL, 0, 0);
625        Tcl_DStringInit(&dString);
626        Tcl_DStringSetLength(&dString, len);
627        utilString = Tcl_DStringValue(&dString);
628        DdeQueryString(ddeInstance, ddeTopic, utilString, (DWORD) len + 1,
629                CP_WINANSI);
630
631        for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
632                riPtr = riPtr->nextPtr) {
633            if (stricmp(utilString, riPtr->name) == 0) {
634                Tcl_DStringFree(&dString);
635                return (HDDEDATA) TRUE;
636            }
637        }
638
639        Tcl_DStringFree(&dString);
640        return (HDDEDATA) FALSE;
641
642    case XTYP_CONNECT_CONFIRM:
643        /*
644         * Dde has decided that we can connect, so it gives us a conversation
645         * handle. We need to keep track of it so we know which execution
646         * result to return in an XTYP_REQUEST.
647         */
648
649        len = DdeQueryString(ddeInstance, ddeTopic, NULL, 0, 0);
650        Tcl_DStringInit(&dString);
651        Tcl_DStringSetLength(&dString, len);
652        utilString = Tcl_DStringValue(&dString);
653        DdeQueryString(ddeInstance, ddeTopic, utilString, (DWORD) len + 1,
654                CP_WINANSI);
655        for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
656                riPtr = riPtr->nextPtr) {
657            if (stricmp(riPtr->name, utilString) == 0) {
658                convPtr = (Conversation *) ckalloc(sizeof(Conversation));
659                convPtr->nextPtr = tsdPtr->currentConversations;
660                convPtr->returnPackagePtr = NULL;
661                convPtr->hConv = hConv;
662                convPtr->riPtr = riPtr;
663                tsdPtr->currentConversations = convPtr;
664                break;
665            }
666        }
667        Tcl_DStringFree(&dString);
668        return (HDDEDATA) TRUE;
669
670    case XTYP_DISCONNECT:
671        /*
672         * The client has disconnected from our server. Forget this
673         * conversation.
674         */
675
676        for (convPtr = tsdPtr->currentConversations, prevConvPtr = NULL;
677                convPtr != NULL;
678                prevConvPtr = convPtr, convPtr = convPtr->nextPtr) {
679            if (hConv == convPtr->hConv) {
680                if (prevConvPtr == NULL) {
681                    tsdPtr->currentConversations = convPtr->nextPtr;
682                } else {
683                    prevConvPtr->nextPtr = convPtr->nextPtr;
684                }
685                if (convPtr->returnPackagePtr != NULL) {
686                    Tcl_DecrRefCount(convPtr->returnPackagePtr);
687                }
688                ckfree((char *) convPtr);
689                break;
690            }
691        }
692        return (HDDEDATA) TRUE;
693
694    case XTYP_REQUEST:
695        /*
696         * This could be either a request for a value of a Tcl variable, or it
697         * could be the send command requesting the results of the last
698         * execute.
699         */
700
701        if (uFmt != CF_TEXT) {
702            return (HDDEDATA) FALSE;
703        }
704
705        ddeReturn = (HDDEDATA) FALSE;
706        for (convPtr = tsdPtr->currentConversations; (convPtr != NULL)
707                && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
708            /*
709             * Empty loop body.
710             */
711        }
712
713        if (convPtr != NULL) {
714            char *returnString;
715
716            len = DdeQueryString(ddeInstance, ddeItem, NULL, 0, CP_WINANSI);
717            Tcl_DStringInit(&dString);
718            Tcl_DStringSetLength(&dString, len);
719            utilString = Tcl_DStringValue(&dString);
720            DdeQueryString(ddeInstance, ddeItem, utilString, (DWORD) len + 1,
721                    CP_WINANSI);
722            if (stricmp(utilString, TCL_DDE_EXECUTE_RESULT) == 0) {
723                returnString =
724                        Tcl_GetStringFromObj(convPtr->returnPackagePtr, &len);
725                ddeReturn = DdeCreateDataHandle(ddeInstance, returnString,
726                        (DWORD) len+1, 0, ddeItem, CF_TEXT, 0);
727            } else {
728                if (Tcl_IsSafe(convPtr->riPtr->interp)) {
729                    ddeReturn = NULL;
730                } else {
731                    Tcl_Obj *variableObjPtr = Tcl_GetVar2Ex(
732                            convPtr->riPtr->interp, utilString, NULL,
733                            TCL_GLOBAL_ONLY);
734                    if (variableObjPtr != NULL) {
735                        returnString = Tcl_GetStringFromObj(variableObjPtr,
736                                &len);
737                        ddeReturn = DdeCreateDataHandle(ddeInstance,
738                                returnString, (DWORD) len+1, 0, ddeItem,
739                                CF_TEXT, 0);
740                    } else {
741                        ddeReturn = NULL;
742                    }
743                }
744            }
745            Tcl_DStringFree(&dString);
746        }
747        return ddeReturn;
748
749    case XTYP_EXECUTE: {
750        /*
751         * Execute this script. The results will be saved into a list object
752         * which will be retreived later. See ExecuteRemoteObject.
753         */
754
755        Tcl_Obj *returnPackagePtr;
756
757        for (convPtr = tsdPtr->currentConversations; (convPtr != NULL)
758                && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
759            /*
760             * Empty loop body.
761             */
762        }
763
764        if (convPtr == NULL) {
765            return (HDDEDATA) DDE_FNOTPROCESSED;
766        }
767
768        utilString = (char *) DdeAccessData(hData, &dlen);
769        len = dlen;
770        ddeObjectPtr = Tcl_NewStringObj(utilString, -1);
771        Tcl_IncrRefCount(ddeObjectPtr);
772        DdeUnaccessData(hData);
773        if (convPtr->returnPackagePtr != NULL) {
774            Tcl_DecrRefCount(convPtr->returnPackagePtr);
775        }
776        convPtr->returnPackagePtr = NULL;
777        returnPackagePtr = ExecuteRemoteObject(convPtr->riPtr, ddeObjectPtr);
778        Tcl_IncrRefCount(returnPackagePtr);
779        for (convPtr = tsdPtr->currentConversations; (convPtr != NULL)
780                && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
781            /*
782             * Empty loop body.
783             */
784        }
785        if (convPtr != NULL) {
786            convPtr->returnPackagePtr = returnPackagePtr;
787        } else {
788            Tcl_DecrRefCount(returnPackagePtr);
789        }
790        Tcl_DecrRefCount(ddeObjectPtr);
791        if (returnPackagePtr == NULL) {
792            return (HDDEDATA) DDE_FNOTPROCESSED;
793        } else {
794            return (HDDEDATA) DDE_FACK;
795        }
796    }
797
798    case XTYP_WILDCONNECT: {
799        /*
800         * Dde wants a list of services and topics that we support.
801         */
802
803        HSZPAIR *returnPtr;
804        int i;
805        int numItems;
806
807        for (i = 0, riPtr = tsdPtr->interpListPtr; riPtr != NULL;
808                i++, riPtr = riPtr->nextPtr) {
809            /*
810             * Empty loop body.
811             */
812        }
813
814        numItems = i;
815        ddeReturn = DdeCreateDataHandle(ddeInstance, NULL,
816                (numItems + 1) * sizeof(HSZPAIR), 0, 0, 0, 0);
817        returnPtr = (HSZPAIR *) DdeAccessData(ddeReturn, &dlen);
818        len = dlen;
819        for (i = 0, riPtr = tsdPtr->interpListPtr; i < numItems;
820                i++, riPtr = riPtr->nextPtr) {
821            returnPtr[i].hszSvc = DdeCreateStringHandle(ddeInstance,
822                    TCL_DDE_SERVICE_NAME, CP_WINANSI);
823            returnPtr[i].hszTopic = DdeCreateStringHandle(ddeInstance,
824                    riPtr->name, CP_WINANSI);
825        }
826        returnPtr[i].hszSvc = NULL;
827        returnPtr[i].hszTopic = NULL;
828        DdeUnaccessData(ddeReturn);
829        return ddeReturn;
830    }
831
832    default:
833        return NULL;
834    }
835}
836
837/*
838 *----------------------------------------------------------------------
839 *
840 * DdeExitProc --
841 *
842 *      Gets rid of our DDE server when we go away.
843 *
844 * Results:
845 *      None.
846 *
847 * Side effects:
848 *      The DDE server is deleted.
849 *
850 *----------------------------------------------------------------------
851 */
852
853static void
854DdeExitProc(
855    ClientData clientData)          /* Not used in this handler. */
856{
857    DdeNameService(ddeInstance, NULL, 0, DNS_UNREGISTER);
858    DdeUninitialize(ddeInstance);
859    ddeInstance = 0;
860}
861
862/*
863 *----------------------------------------------------------------------
864 *
865 * MakeDdeConnection --
866 *
867 *      This function is a utility used to connect to a DDE server when given
868 *      a server name and a topic name.
869 *
870 * Results:
871 *      A standard Tcl result.
872 *
873 * Side effects:
874 *      Passes back a conversation through ddeConvPtr
875 *
876 *----------------------------------------------------------------------
877 */
878
879static int
880MakeDdeConnection(
881    Tcl_Interp *interp,         /* Used to report errors. */
882    char *name,                 /* The connection to use. */
883    HCONV *ddeConvPtr)
884{
885    HSZ ddeTopic, ddeService;
886    HCONV ddeConv;
887
888    ddeService = DdeCreateStringHandle(ddeInstance, TCL_DDE_SERVICE_NAME, 0);
889    ddeTopic = DdeCreateStringHandle(ddeInstance, name, 0);
890
891    ddeConv = DdeConnect(ddeInstance, ddeService, ddeTopic, NULL);
892    DdeFreeStringHandle(ddeInstance, ddeService);
893    DdeFreeStringHandle(ddeInstance, ddeTopic);
894
895    if (ddeConv == (HCONV) NULL) {
896        if (interp != NULL) {
897            Tcl_AppendResult(interp, "no registered server named \"",
898                    name, "\"", NULL);
899        }
900        return TCL_ERROR;
901    }
902
903    *ddeConvPtr = ddeConv;
904    return TCL_OK;
905}
906
907/*
908 *----------------------------------------------------------------------
909 *
910 * DdeGetServicesList --
911 *
912 *      This function obtains the list of DDE services.
913 *
914 *      The functions between here and this function are all involved with
915 *      handling the DDE callbacks for this. They are: DdeCreateClient,
916 *      DdeClientWindowProc, DdeServicesOnAck, and DdeEnumWindowsCallback
917 *
918 * Results:
919 *      A standard Tcl result.
920 *
921 * Side effects:
922 *      Sets the services list into the interp result.
923 *
924 *----------------------------------------------------------------------
925 */
926
927static int
928DdeCreateClient(
929    struct DdeEnumServices *es)
930{
931    WNDCLASSEX wc;
932    static const char *szDdeClientClassName = "TclEval client class";
933    static const char *szDdeClientWindowName = "TclEval client window";
934
935    memset(&wc, 0, sizeof(wc));
936    wc.cbSize = sizeof(wc);
937    wc.lpfnWndProc = DdeClientWindowProc;
938    wc.lpszClassName = szDdeClientClassName;
939    wc.cbWndExtra = sizeof(struct DdeEnumServices *);
940
941    /*
942     * Register and create the callback window.
943     */
944
945    RegisterClassEx(&wc);
946    es->hwnd = CreateWindowEx(0, szDdeClientClassName, szDdeClientWindowName,
947            WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, (LPVOID)es);
948    return TCL_OK;
949}
950
951static LRESULT CALLBACK
952DdeClientWindowProc(
953    HWND hwnd,                  /* What window is the message for */
954    UINT uMsg,                  /* The type of message received */
955    WPARAM wParam,
956    LPARAM lParam)              /* (Potentially) our local handle */
957{
958
959    switch (uMsg) {
960    case WM_CREATE: {
961        LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
962        struct DdeEnumServices *es =
963                (struct DdeEnumServices *) lpcs->lpCreateParams;
964
965#ifdef _WIN64
966        SetWindowLongPtr(hwnd, GWLP_USERDATA, (long)es);
967#else
968        SetWindowLong(hwnd, GWL_USERDATA, (long)es);
969#endif
970        return (LRESULT) 0L;
971    }
972    case WM_DDE_ACK:
973        return DdeServicesOnAck(hwnd, wParam, lParam);
974        break;
975    default:
976        return DefWindowProc(hwnd, uMsg, wParam, lParam);
977    }
978}
979
980static LRESULT
981DdeServicesOnAck(
982    HWND hwnd,
983    WPARAM wParam,
984    LPARAM lParam)
985{
986    HWND hwndRemote = (HWND)wParam;
987    ATOM service = (ATOM)LOWORD(lParam);
988    ATOM topic = (ATOM)HIWORD(lParam);
989    struct DdeEnumServices *es;
990    TCHAR sz[255];
991
992#ifdef _WIN64
993    es = (struct DdeEnumServices *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
994#else
995    es = (struct DdeEnumServices *) GetWindowLong(hwnd, GWL_USERDATA);
996#endif
997
998    if ((es->service == (ATOM)NULL || es->service == service)
999            && (es->topic == (ATOM)NULL || es->topic == topic)) {
1000        Tcl_Obj *matchPtr = Tcl_NewListObj(0, NULL);
1001        Tcl_Obj *resultPtr = Tcl_GetObjResult(es->interp);
1002
1003        GlobalGetAtomName(service, sz, 255);
1004        Tcl_ListObjAppendElement(NULL, matchPtr, Tcl_NewStringObj(sz, -1));
1005        GlobalGetAtomName(topic, sz, 255);
1006        Tcl_ListObjAppendElement(NULL, matchPtr, Tcl_NewStringObj(sz, -1));
1007
1008        /*
1009         * Adding the hwnd as a third list element provides a unique
1010         * identifier in the case of multiple servers with the name
1011         * application and topic names.
1012         */
1013        /*
1014         * Needs a TIP though:
1015         * Tcl_ListObjAppendElement(NULL, matchPtr,
1016         *      Tcl_NewLongObj((long)hwndRemote));
1017         */
1018
1019        if (Tcl_IsShared(resultPtr)) {
1020            resultPtr = Tcl_DuplicateObj(resultPtr);
1021        }
1022        if (Tcl_ListObjAppendElement(es->interp, resultPtr,
1023                matchPtr) == TCL_OK) {
1024            Tcl_SetObjResult(es->interp, resultPtr);
1025        }
1026    }
1027
1028    /*
1029     * Tell the server we are no longer interested.
1030     */
1031
1032    PostMessage(hwndRemote, WM_DDE_TERMINATE, (WPARAM)hwnd, 0L);
1033    return 0L;
1034}
1035
1036static BOOL CALLBACK
1037DdeEnumWindowsCallback(
1038    HWND hwndTarget,
1039    LPARAM lParam)
1040{
1041    LRESULT dwResult = 0;
1042    struct DdeEnumServices *es = (struct DdeEnumServices *) lParam;
1043
1044    SendMessageTimeout(hwndTarget, WM_DDE_INITIATE, (WPARAM)es->hwnd,
1045            MAKELONG(es->service, es->topic), SMTO_ABORTIFHUNG, 1000,
1046            &dwResult);
1047    return TRUE;
1048}
1049
1050static int
1051DdeGetServicesList(
1052    Tcl_Interp *interp,
1053    char *serviceName,
1054    char *topicName)
1055{
1056    struct DdeEnumServices es;
1057
1058    es.interp = interp;
1059    es.result = TCL_OK;
1060    es.service = (serviceName == NULL)
1061            ? (ATOM)NULL : GlobalAddAtom(serviceName);
1062    es.topic = (topicName == NULL) ? (ATOM)NULL : GlobalAddAtom(topicName);
1063
1064    Tcl_ResetResult(interp); /* our list is to be appended to result. */
1065    DdeCreateClient(&es);
1066    EnumWindows(DdeEnumWindowsCallback, (LPARAM)&es);
1067
1068    if (IsWindow(es.hwnd)) {
1069        DestroyWindow(es.hwnd);
1070    }
1071    if (es.service != (ATOM)NULL) {
1072        GlobalDeleteAtom(es.service);
1073    }
1074    if (es.topic != (ATOM)NULL) {
1075        GlobalDeleteAtom(es.topic);
1076    }
1077    return es.result;
1078}
1079
1080/*
1081 *----------------------------------------------------------------------
1082 *
1083 * SetDdeError --
1084 *
1085 *      Sets the interp result to a cogent error message describing the last
1086 *      DDE error.
1087 *
1088 * Results:
1089 *      None.
1090 *
1091 * Side effects:
1092 *      The interp's result object is changed.
1093 *
1094 *----------------------------------------------------------------------
1095 */
1096
1097static void
1098SetDdeError(
1099    Tcl_Interp *interp)     /* The interp to put the message in. */
1100{
1101    char *errorMessage;
1102
1103    switch (DdeGetLastError(ddeInstance)) {
1104    case DMLERR_DATAACKTIMEOUT:
1105    case DMLERR_EXECACKTIMEOUT:
1106    case DMLERR_POKEACKTIMEOUT:
1107        errorMessage = "remote interpreter did not respond";
1108        break;
1109    case DMLERR_BUSY:
1110        errorMessage = "remote server is busy";
1111        break;
1112    case DMLERR_NOTPROCESSED:
1113        errorMessage = "remote server cannot handle this command";
1114        break;
1115    default:
1116        errorMessage = "dde command failed";
1117    }
1118
1119    Tcl_SetObjResult(interp, Tcl_NewStringObj(errorMessage, -1));
1120}
1121
1122/*
1123 *----------------------------------------------------------------------
1124 *
1125 * Tcl_DdeObjCmd --
1126 *
1127 *      This function is invoked to process the "dde" Tcl command. See the
1128 *      user documentation for details on what it does.
1129 *
1130 * Results:
1131 *      A standard Tcl result.
1132 *
1133 * Side effects:
1134 *      See the user documentation.
1135 *
1136 *----------------------------------------------------------------------
1137 */
1138
1139int
1140Tcl_DdeObjCmd(
1141    ClientData clientData,      /* Used only for deletion */
1142    Tcl_Interp *interp,         /* The interp we are sending from */
1143    int objc,                   /* Number of arguments */
1144    Tcl_Obj *CONST * objv)      /* The arguments */
1145{
1146    static CONST char *ddeCommands[] = {
1147        "servername", "execute", "poke", "request", "services", "eval",
1148        (char *) NULL
1149    };
1150    enum DdeSubcommands {
1151        DDE_SERVERNAME, DDE_EXECUTE, DDE_POKE, DDE_REQUEST, DDE_SERVICES,
1152        DDE_EVAL
1153    };
1154    static CONST char *ddeSrvOptions[] = {
1155        "-force", "-handler", "--", NULL
1156    };
1157    enum DdeSrvOptions {
1158        DDE_SERVERNAME_EXACT, DDE_SERVERNAME_HANDLER, DDE_SERVERNAME_LAST,
1159    };
1160    static CONST char *ddeExecOptions[] = {
1161        "-async", NULL
1162    };
1163    static CONST char *ddeReqOptions[] = {
1164        "-binary", NULL
1165    };
1166
1167    int index, i, length;
1168    int async = 0, binary = 0, exact = 0;
1169    int result = TCL_OK, firstArg = 0;
1170    HSZ ddeService = NULL, ddeTopic = NULL, ddeItem = NULL, ddeCookie = NULL;
1171    HDDEDATA ddeData = NULL, ddeItemData = NULL, ddeReturn;
1172    HCONV hConv = NULL;
1173    char *serviceName = NULL, *topicName = NULL, *string;
1174    DWORD ddeResult;
1175    Tcl_Obj *objPtr, *handlerPtr = NULL;
1176
1177    /*
1178     * Initialize DDE server/client
1179     */
1180
1181    if (objc < 2) {
1182        Tcl_WrongNumArgs(interp, 1, objv, "command ?arg ...?");
1183        return TCL_ERROR;
1184    }
1185
1186    if (Tcl_GetIndexFromObj(interp, objv[1], ddeCommands, "command", 0,
1187            &index) != TCL_OK) {
1188        return TCL_ERROR;
1189    }
1190
1191    switch ((enum DdeSubcommands) index) {
1192    case DDE_SERVERNAME:
1193        for (i = 2; i < objc; i++) {
1194            int argIndex;
1195            if (Tcl_GetIndexFromObj(interp, objv[i], ddeSrvOptions,
1196                    "option", 0, &argIndex) != TCL_OK) {
1197                /*
1198                 * If it is the last argument, it might be a server name
1199                 * instead of a bad argument.
1200                 */
1201
1202                if (i != objc-1) {
1203                    return TCL_ERROR;
1204                }
1205                Tcl_ResetResult(interp);
1206                break;
1207            }
1208            if (argIndex == DDE_SERVERNAME_EXACT) {
1209                exact = 1;
1210            } else if (argIndex == DDE_SERVERNAME_HANDLER) {
1211                if ((objc - i) == 1) {  /* return current handler */
1212                    RegisteredInterp *riPtr = DdeGetRegistrationPtr(interp);
1213
1214                    if (riPtr && riPtr->handlerPtr) {
1215                        Tcl_SetObjResult(interp, riPtr->handlerPtr);
1216                    } else {
1217                        Tcl_ResetResult(interp);
1218                    }
1219                    return TCL_OK;
1220                }
1221                handlerPtr = objv[++i];
1222            } else if (argIndex == DDE_SERVERNAME_LAST) {
1223                i++;
1224                break;
1225            }
1226        }
1227
1228        if ((objc - i) > 1) {
1229            Tcl_ResetResult(interp);
1230            Tcl_WrongNumArgs(interp, 2, objv,
1231                    "?-force? ?-handler proc? ?--? ?serverName?");
1232            return TCL_ERROR;
1233        }
1234
1235        firstArg = (objc == i) ? 1 : i;
1236        break;
1237    case DDE_EXECUTE:
1238        if (objc == 5) {
1239            firstArg = 2;
1240            break;
1241        } else if (objc == 6) {
1242            int dummy;
1243            if (Tcl_GetIndexFromObj(NULL, objv[2], ddeExecOptions, "option", 0,
1244                    &dummy) == TCL_OK) {
1245                async = 1;
1246                firstArg = 3;
1247                break;
1248            }
1249        }
1250        /* otherwise... */
1251        Tcl_WrongNumArgs(interp, 2, objv,
1252                "?-async? serviceName topicName value");
1253        return TCL_ERROR;
1254    case DDE_POKE:
1255        if (objc != 6) {
1256            Tcl_WrongNumArgs(interp, 2, objv,
1257                    "serviceName topicName item value");
1258            return TCL_ERROR;
1259        }
1260        firstArg = 2;
1261        break;
1262    case DDE_REQUEST:
1263        if (objc == 5) {
1264            firstArg = 2;
1265            break;
1266        } else if (objc == 6) {
1267            int dummy;
1268            if (Tcl_GetIndexFromObj(NULL, objv[2], ddeReqOptions, "option", 0,
1269                    &dummy) == TCL_OK) {
1270                binary = 1;
1271                firstArg = 3;
1272                break;
1273            }
1274        }
1275
1276        /*
1277         * Otherwise ...
1278         */
1279
1280        Tcl_WrongNumArgs(interp, 2, objv,
1281                "?-binary? serviceName topicName value");
1282        return TCL_ERROR;
1283    case DDE_SERVICES:
1284        if (objc != 4) {
1285            Tcl_WrongNumArgs(interp, 2, objv, "serviceName topicName");
1286            return TCL_ERROR;
1287        }
1288        firstArg = 2;
1289        break;
1290    case DDE_EVAL:
1291        if (objc < 4) {
1292        wrongDdeEvalArgs:
1293            Tcl_WrongNumArgs(interp, 2, objv, "?-async? serviceName args");
1294            return TCL_ERROR;
1295        } else {
1296            int dummy;
1297
1298            firstArg = 2;
1299            if (Tcl_GetIndexFromObj(NULL, objv[2], ddeExecOptions, "option", 0,
1300                    &dummy) == TCL_OK) {
1301                if (objc < 5) {
1302                    goto wrongDdeEvalArgs;
1303                }
1304                async = 1;
1305                firstArg++;
1306            }
1307            break;
1308        }
1309    }
1310
1311    Initialize();
1312
1313    if (firstArg != 1) {
1314        serviceName = Tcl_GetStringFromObj(objv[firstArg], &length);
1315    } else {
1316        length = 0;
1317    }
1318
1319    if (length == 0) {
1320        serviceName = NULL;
1321    } else if ((index != DDE_SERVERNAME) && (index != DDE_EVAL)) {
1322        ddeService = DdeCreateStringHandle(ddeInstance, serviceName,
1323                CP_WINANSI);
1324    }
1325
1326    if ((index != DDE_SERVERNAME) && (index != DDE_EVAL)) {
1327        topicName = Tcl_GetStringFromObj(objv[firstArg + 1], &length);
1328        if (length == 0) {
1329            topicName = NULL;
1330        } else {
1331            ddeTopic = DdeCreateStringHandle(ddeInstance, topicName,
1332                    CP_WINANSI);
1333        }
1334    }
1335
1336    switch ((enum DdeSubcommands) index) {
1337    case DDE_SERVERNAME:
1338        serviceName = DdeSetServerName(interp, serviceName, exact, handlerPtr);
1339        if (serviceName != NULL) {
1340            Tcl_SetObjResult(interp, Tcl_NewStringObj(serviceName, -1));
1341        } else {
1342            Tcl_ResetResult(interp);
1343        }
1344        break;
1345
1346    case DDE_EXECUTE: {
1347        int dataLength;
1348        char *dataString = Tcl_GetStringFromObj(objv[firstArg + 2],
1349                &dataLength);
1350
1351        if (dataLength == 0) {
1352            Tcl_SetObjResult(interp,
1353                    Tcl_NewStringObj("cannot execute null data", -1));
1354            result = TCL_ERROR;
1355            break;
1356        }
1357        hConv = DdeConnect(ddeInstance, ddeService, ddeTopic, NULL);
1358        DdeFreeStringHandle(ddeInstance, ddeService);
1359        DdeFreeStringHandle(ddeInstance, ddeTopic);
1360
1361        if (hConv == NULL) {
1362            SetDdeError(interp);
1363            result = TCL_ERROR;
1364            break;
1365        }
1366
1367        ddeData = DdeCreateDataHandle(ddeInstance, dataString,
1368                (DWORD) dataLength+1, 0, 0, CF_TEXT, 0);
1369        if (ddeData != NULL) {
1370            if (async) {
1371                DdeClientTransaction((LPBYTE) ddeData, 0xFFFFFFFF, hConv, 0,
1372                        CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &ddeResult);
1373                DdeAbandonTransaction(ddeInstance, hConv, ddeResult);
1374            } else {
1375                ddeReturn = DdeClientTransaction((LPBYTE) ddeData, 0xFFFFFFFF,
1376                        hConv, 0, CF_TEXT, XTYP_EXECUTE, 30000, NULL);
1377                if (ddeReturn == 0) {
1378                    SetDdeError(interp);
1379                    result = TCL_ERROR;
1380                }
1381            }
1382            DdeFreeDataHandle(ddeData);
1383        } else {
1384            SetDdeError(interp);
1385            result = TCL_ERROR;
1386        }
1387        break;
1388    }
1389    case DDE_REQUEST: {
1390        char *itemString = Tcl_GetStringFromObj(objv[firstArg + 2], &length);
1391
1392        if (length == 0) {
1393            Tcl_SetObjResult(interp,
1394                    Tcl_NewStringObj("cannot request value of null data", -1));
1395            result = TCL_ERROR;
1396            goto cleanup;
1397        }
1398        hConv = DdeConnect(ddeInstance, ddeService, ddeTopic, NULL);
1399        DdeFreeStringHandle(ddeInstance, ddeService);
1400        DdeFreeStringHandle(ddeInstance, ddeTopic);
1401
1402        if (hConv == NULL) {
1403            SetDdeError(interp);
1404            result = TCL_ERROR;
1405        } else {
1406            Tcl_Obj *returnObjPtr;
1407            ddeItem = DdeCreateStringHandle(ddeInstance, itemString,
1408                    CP_WINANSI);
1409            if (ddeItem != NULL) {
1410                ddeData = DdeClientTransaction(NULL, 0, hConv, ddeItem,
1411                        CF_TEXT, XTYP_REQUEST, 5000, NULL);
1412                if (ddeData == NULL) {
1413                    SetDdeError(interp);
1414                    result = TCL_ERROR;
1415                } else {
1416                    DWORD tmp;
1417                    char *dataString = DdeAccessData(ddeData, &tmp);
1418
1419                    if (binary) {
1420                        returnObjPtr = Tcl_NewByteArrayObj(dataString,
1421                                (int) tmp);
1422                    } else {
1423                        returnObjPtr = Tcl_NewStringObj(dataString, -1);
1424                    }
1425                    DdeUnaccessData(ddeData);
1426                    DdeFreeDataHandle(ddeData);
1427                    Tcl_SetObjResult(interp, returnObjPtr);
1428                }
1429            } else {
1430                SetDdeError(interp);
1431                result = TCL_ERROR;
1432            }
1433        }
1434
1435        break;
1436    }
1437    case DDE_POKE: {
1438        char *itemString = Tcl_GetStringFromObj(objv[firstArg + 2], &length);
1439        char *dataString;
1440
1441        if (length == 0) {
1442            Tcl_SetObjResult(interp,
1443                    Tcl_NewStringObj("cannot have a null item", -1));
1444            result = TCL_ERROR;
1445            goto cleanup;
1446        }
1447        dataString = Tcl_GetStringFromObj(objv[firstArg + 3], &length);
1448
1449        hConv = DdeConnect(ddeInstance, ddeService, ddeTopic, NULL);
1450        DdeFreeStringHandle(ddeInstance, ddeService);
1451        DdeFreeStringHandle(ddeInstance, ddeTopic);
1452
1453        if (hConv == NULL) {
1454            SetDdeError(interp);
1455            result = TCL_ERROR;
1456        } else {
1457            ddeItem = DdeCreateStringHandle(ddeInstance, itemString,
1458                    CP_WINANSI);
1459            if (ddeItem != NULL) {
1460                ddeData = DdeClientTransaction(dataString, (DWORD) length+1,
1461                        hConv, ddeItem, CF_TEXT, XTYP_POKE, 5000, NULL);
1462                if (ddeData == NULL) {
1463                    SetDdeError(interp);
1464                    result = TCL_ERROR;
1465                }
1466            } else {
1467                SetDdeError(interp);
1468                result = TCL_ERROR;
1469            }
1470        }
1471        break;
1472    }
1473
1474    case DDE_SERVICES:
1475        result = DdeGetServicesList(interp, serviceName, topicName);
1476        break;
1477
1478    case DDE_EVAL: {
1479        RegisteredInterp *riPtr;
1480        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1481
1482        if (serviceName == NULL) {
1483            Tcl_SetObjResult(interp,
1484                    Tcl_NewStringObj("invalid service name \"\"", -1));
1485            result = TCL_ERROR;
1486            goto cleanup;
1487        }
1488
1489        objc -= (async + 3);
1490        objv += (async + 3);
1491
1492        /*
1493         * See if the target interpreter is local. If so, execute the command
1494         * directly without going through the DDE server. Don't exchange
1495         * objects between interps. The target interp could compile an object,
1496         * producing a bytecode structure that refers to other objects owned
1497         * by the target interp. If the target interp is then deleted, the
1498         * bytecode structure would be referring to deallocated objects.
1499         */
1500
1501        for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
1502                riPtr = riPtr->nextPtr) {
1503            if (stricmp(serviceName, riPtr->name) == 0) {
1504                break;
1505            }
1506        }
1507
1508        if (riPtr != NULL) {
1509            Tcl_Interp *sendInterp;
1510
1511            /*
1512             * This command is to a local interp. No need to go through the
1513             * server.
1514             */
1515
1516            Tcl_Preserve((ClientData) riPtr);
1517            sendInterp = riPtr->interp;
1518            Tcl_Preserve((ClientData) sendInterp);
1519
1520            /*
1521             * Don't exchange objects between interps. The target interp would
1522             * compile an object, producing a bytecode structure that refers
1523             * to other objects owned by the target interp. If the target
1524             * interp is then deleted, the bytecode structure would be
1525             * referring to deallocated objects.
1526             */
1527
1528            if (Tcl_IsSafe(riPtr->interp) && riPtr->handlerPtr == NULL) {
1529                Tcl_SetResult(riPtr->interp, "permission denied: "
1530                        "a handler procedure must be defined for use in "
1531                        "a safe interp", TCL_STATIC);
1532                result = TCL_ERROR;
1533            }
1534
1535            if (result == TCL_OK) {
1536                if (objc == 1)
1537                    objPtr = objv[0];
1538                else {
1539                    objPtr = Tcl_ConcatObj(objc, objv);
1540                }
1541                if (riPtr->handlerPtr != NULL) {
1542                    /* add the dde request data to the handler proc list */
1543                    /*
1544                     *result = Tcl_ListObjReplace(sendInterp, objPtr, 0, 0, 1,
1545                     *      &(riPtr->handlerPtr));
1546                     */
1547                    Tcl_Obj *cmdPtr = Tcl_DuplicateObj(riPtr->handlerPtr);
1548                    result = Tcl_ListObjAppendElement(sendInterp, cmdPtr,
1549                            objPtr);
1550                    if (result == TCL_OK) {
1551                        objPtr = cmdPtr;
1552                    }
1553                }
1554            }
1555            if (result == TCL_OK) {
1556                Tcl_IncrRefCount(objPtr);
1557                result = Tcl_EvalObjEx(sendInterp, objPtr, TCL_EVAL_GLOBAL);
1558                Tcl_DecrRefCount(objPtr);
1559            }
1560            if (interp != sendInterp) {
1561                if (result == TCL_ERROR) {
1562                    /*
1563                     * An error occurred, so transfer error information from
1564                     * the destination interpreter back to our interpreter.
1565                     */
1566
1567                    Tcl_ResetResult(interp);
1568                    objPtr = Tcl_GetVar2Ex(sendInterp, "errorInfo", NULL,
1569                            TCL_GLOBAL_ONLY);
1570                    if (objPtr) {
1571                        string = Tcl_GetStringFromObj(objPtr, &length);
1572                        Tcl_AddObjErrorInfo(interp, string, length);
1573                    }
1574
1575                    objPtr = Tcl_GetVar2Ex(sendInterp, "errorCode", NULL,
1576                            TCL_GLOBAL_ONLY);
1577                    if (objPtr) {
1578                        Tcl_SetObjErrorCode(interp, objPtr);
1579                    }
1580                }
1581                Tcl_SetObjResult(interp, Tcl_GetObjResult(sendInterp));
1582            }
1583            Tcl_Release((ClientData) riPtr);
1584            Tcl_Release((ClientData) sendInterp);
1585        } else {
1586            /*
1587             * This is a non-local request. Send the script to the server and
1588             * poll it for a result.
1589             */
1590
1591            if (MakeDdeConnection(interp, serviceName, &hConv) != TCL_OK) {
1592            invalidServerResponse:
1593                Tcl_SetObjResult(interp,
1594                        Tcl_NewStringObj("invalid data returned from server",
1595                        -1));
1596                result = TCL_ERROR;
1597                goto cleanup;
1598            }
1599
1600            objPtr = Tcl_ConcatObj(objc, objv);
1601            string = Tcl_GetStringFromObj(objPtr, &length);
1602            ddeItemData = DdeCreateDataHandle(ddeInstance, string,
1603                    (DWORD) length+1, 0, 0, CF_TEXT, 0);
1604
1605            if (async) {
1606                ddeData = DdeClientTransaction((LPBYTE) ddeItemData,
1607                        0xFFFFFFFF, hConv, 0,
1608                        CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &ddeResult);
1609                DdeAbandonTransaction(ddeInstance, hConv, ddeResult);
1610            } else {
1611                ddeData = DdeClientTransaction((LPBYTE) ddeItemData,
1612                        0xFFFFFFFF, hConv, 0,
1613                        CF_TEXT, XTYP_EXECUTE, 30000, NULL);
1614                if (ddeData != 0) {
1615                    ddeCookie = DdeCreateStringHandle(ddeInstance,
1616                            TCL_DDE_EXECUTE_RESULT, CP_WINANSI);
1617                    ddeData = DdeClientTransaction(NULL, 0, hConv, ddeCookie,
1618                            CF_TEXT, XTYP_REQUEST, 30000, NULL);
1619                }
1620            }
1621
1622            Tcl_DecrRefCount(objPtr);
1623
1624            if (ddeData == 0) {
1625                SetDdeError(interp);
1626                result = TCL_ERROR;
1627            }
1628
1629            if (async == 0) {
1630                Tcl_Obj *resultPtr;
1631
1632                /*
1633                 * The return handle has a two or four element list in it. The
1634                 * first element is the return code (TCL_OK, TCL_ERROR, etc.).
1635                 * The second is the result of the script. If the return code
1636                 * is TCL_ERROR, then the third element is the value of the
1637                 * variable "errorCode", and the fourth is the value of the
1638                 * variable "errorInfo".
1639                 */
1640
1641                resultPtr = Tcl_NewObj();
1642                length = DdeGetData(ddeData, NULL, 0, 0);
1643                Tcl_SetObjLength(resultPtr, length);
1644                string = Tcl_GetString(resultPtr);
1645                DdeGetData(ddeData, string, (DWORD) length, 0);
1646                Tcl_SetObjLength(resultPtr, (int) strlen(string));
1647
1648                if (Tcl_ListObjIndex(NULL, resultPtr, 0, &objPtr) != TCL_OK) {
1649                    Tcl_DecrRefCount(resultPtr);
1650                    goto invalidServerResponse;
1651                }
1652                if (Tcl_GetIntFromObj(NULL, objPtr, &result) != TCL_OK) {
1653                    Tcl_DecrRefCount(resultPtr);
1654                    goto invalidServerResponse;
1655                }
1656                if (result == TCL_ERROR) {
1657                    Tcl_ResetResult(interp);
1658
1659                    if (Tcl_ListObjIndex(NULL, resultPtr, 3,
1660                            &objPtr) != TCL_OK) {
1661                        Tcl_DecrRefCount(resultPtr);
1662                        goto invalidServerResponse;
1663                    }
1664                    length = -1;
1665                    string = Tcl_GetStringFromObj(objPtr, &length);
1666                    Tcl_AddObjErrorInfo(interp, string, length);
1667
1668                    Tcl_ListObjIndex(NULL, resultPtr, 2, &objPtr);
1669                    Tcl_SetObjErrorCode(interp, objPtr);
1670                }
1671                if (Tcl_ListObjIndex(NULL, resultPtr, 1, &objPtr) != TCL_OK) {
1672                    Tcl_DecrRefCount(resultPtr);
1673                    goto invalidServerResponse;
1674                }
1675                Tcl_SetObjResult(interp, objPtr);
1676                Tcl_DecrRefCount(resultPtr);
1677            }
1678        }
1679    }
1680    }
1681
1682  cleanup:
1683    if (ddeCookie != NULL) {
1684        DdeFreeStringHandle(ddeInstance, ddeCookie);
1685    }
1686    if (ddeItem != NULL) {
1687        DdeFreeStringHandle(ddeInstance, ddeItem);
1688    }
1689    if (ddeItemData != NULL) {
1690        DdeFreeDataHandle(ddeItemData);
1691    }
1692    if (ddeData != NULL) {
1693        DdeFreeDataHandle(ddeData);
1694    }
1695    if (hConv != NULL) {
1696        DdeDisconnect(hConv);
1697    }
1698    return result;
1699}
1700
1701/*
1702 * Local variables:
1703 * mode: c
1704 * indent-tabs-mode: t
1705 * tab-width: 8
1706 * c-basic-offset: 4
1707 * fill-column: 78
1708 * End:
1709 */
Note: See TracBrowser for help on using the repository browser.