tclWinDde.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:40k
源码类别:

通讯编程

开发平台:

Visual C++

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