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 | */ |
---|