| 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 |  | 
|---|
| 35 | typedef 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 |  | 
|---|
| 48 | typedef 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 |  | 
|---|
| 56 | typedef struct DdeEnumServices { | 
|---|
| 57 |     Tcl_Interp *interp; | 
|---|
| 58 |     int result; | 
|---|
| 59 |     ATOM service; | 
|---|
| 60 |     ATOM topic; | 
|---|
| 61 |     HWND hwnd; | 
|---|
| 62 | } DdeEnumServices; | 
|---|
| 63 |  | 
|---|
| 64 | typedef 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; | 
|---|
| 72 | static 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 |  | 
|---|
| 79 | static HSZ ddeServiceGlobal = 0; | 
|---|
| 80 | static DWORD ddeInstance;       /* The application instance handle given to us | 
|---|
| 81 |                                  * by DdeInitialize. */ | 
|---|
| 82 | static 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 |  | 
|---|
| 89 | TCL_DECLARE_MUTEX(ddeMutex) | 
|---|
| 90 |  | 
|---|
| 91 | /* | 
|---|
| 92 |  * Forward declarations for functions defined later in this file. | 
|---|
| 93 |  */ | 
|---|
| 94 |  | 
|---|
| 95 | static LRESULT CALLBACK DdeClientWindowProc(HWND hwnd, UINT uMsg, | 
|---|
| 96 |                             WPARAM wParam, LPARAM lParam); | 
|---|
| 97 | static int              DdeCreateClient(struct DdeEnumServices *es); | 
|---|
| 98 | static BOOL CALLBACK    DdeEnumWindowsCallback(HWND hwndTarget, LPARAM lParam); | 
|---|
| 99 | static void             DdeExitProc(ClientData clientData); | 
|---|
| 100 | static int              DdeGetServicesList(Tcl_Interp *interp, | 
|---|
| 101 |                             char *serviceName, char *topicName); | 
|---|
| 102 | static HDDEDATA CALLBACK DdeServerProc(UINT uType, UINT uFmt, HCONV hConv, | 
|---|
| 103 |                             HSZ ddeTopic, HSZ ddeItem, HDDEDATA hData, | 
|---|
| 104 |                             DWORD dwData1, DWORD dwData2); | 
|---|
| 105 | static LRESULT          DdeServicesOnAck(HWND hwnd, WPARAM wParam, | 
|---|
| 106 |                             LPARAM lParam); | 
|---|
| 107 | static void             DeleteProc(ClientData clientData); | 
|---|
| 108 | static Tcl_Obj *        ExecuteRemoteObject(RegisteredInterp *riPtr, | 
|---|
| 109 |                             Tcl_Obj *ddeObjectPtr); | 
|---|
| 110 | static int              MakeDdeConnection(Tcl_Interp *interp, char *name, | 
|---|
| 111 |                             HCONV *ddeConvPtr); | 
|---|
| 112 | static void             SetDdeError(Tcl_Interp *interp); | 
|---|
| 113 |  | 
|---|
| 114 | int                     Tcl_DdeObjCmd(ClientData clientData, | 
|---|
| 115 |                             Tcl_Interp *interp, int objc, | 
|---|
| 116 |                             Tcl_Obj *CONST objv[]); | 
|---|
| 117 |  | 
|---|
| 118 | EXTERN int              Dde_Init(Tcl_Interp *interp); | 
|---|
| 119 | EXTERN 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 |  | 
|---|
| 137 | int | 
|---|
| 138 | Dde_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 |  | 
|---|
| 169 | int | 
|---|
| 170 | Dde_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 |  | 
|---|
| 196 | static void | 
|---|
| 197 | Initialize(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 |  | 
|---|
| 268 | static char * | 
|---|
| 269 | DdeSetServerName( | 
|---|
| 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 |  | 
|---|
| 435 | static RegisteredInterp * | 
|---|
| 436 | DdeGetRegistrationPtr( | 
|---|
| 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 |  | 
|---|
| 467 | static void | 
|---|
| 468 | DeleteProc( | 
|---|
| 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 |  | 
|---|
| 521 | static Tcl_Obj * | 
|---|
| 522 | ExecuteRemoteObject( | 
|---|
| 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 |  | 
|---|
| 594 | static HDDEDATA CALLBACK | 
|---|
| 595 | DdeServerProc( | 
|---|
| 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 |  | 
|---|
| 853 | static void | 
|---|
| 854 | DdeExitProc( | 
|---|
| 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 |  | 
|---|
| 879 | static int | 
|---|
| 880 | MakeDdeConnection( | 
|---|
| 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 |  | 
|---|
| 927 | static int | 
|---|
| 928 | DdeCreateClient( | 
|---|
| 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 |  | 
|---|
| 951 | static LRESULT CALLBACK | 
|---|
| 952 | DdeClientWindowProc( | 
|---|
| 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 |  | 
|---|
| 980 | static LRESULT | 
|---|
| 981 | DdeServicesOnAck( | 
|---|
| 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 |  | 
|---|
| 1036 | static BOOL CALLBACK | 
|---|
| 1037 | DdeEnumWindowsCallback( | 
|---|
| 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 |  | 
|---|
| 1050 | static int | 
|---|
| 1051 | DdeGetServicesList( | 
|---|
| 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 |  | 
|---|
| 1097 | static void | 
|---|
| 1098 | SetDdeError( | 
|---|
| 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 |  | 
|---|
| 1139 | int | 
|---|
| 1140 | Tcl_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 |  */ | 
|---|