📄 tclwindde.c
字号:
/* * tclWinDde.c -- * * This file provides procedures that implement the "send" * command, allowing commands to be passed from interpreter * to interpreter. * * Copyright (c) 1997 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclWinDde.c,v 1.8 2002/01/18 14:07:40 dgp Exp $ */#include "tclPort.h"#include <ddeml.h>/* * TCL_STORAGE_CLASS is set unconditionally to DLLEXPORT because the * Registry_Init declaration is in the source file itself, which is only * accessed when we are building a library. */#undef TCL_STORAGE_CLASS#define TCL_STORAGE_CLASS DLLEXPORT/* * The following structure is used to keep track of the interpreters * registered by this process. */typedef struct RegisteredInterp { struct RegisteredInterp *nextPtr; /* The next interp this application knows * about. */ char *name; /* Interpreter's name (malloc-ed). */ Tcl_Interp *interp; /* The interpreter attached to this name. */} RegisteredInterp;/* * Used to keep track of conversations. */typedef struct Conversation { struct Conversation *nextPtr; /* The next conversation in the list. */ RegisteredInterp *riPtr; /* The info we know about the conversation. */ HCONV hConv; /* The DDE handle for this conversation. */ Tcl_Obj *returnPackagePtr; /* The result package for this conversation. */} Conversation;typedef struct ThreadSpecificData { Conversation *currentConversations; /* A list of conversations currently * being processed. */ RegisteredInterp *interpListPtr; /* List of all interpreters registered * in the current process. */} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;/* * The following variables cannot be placed in thread-local storage. * The Mutex ddeMutex guards access to the ddeInstance. */static HSZ ddeServiceGlobal = 0;static DWORD ddeInstance; /* The application instance handle given * to us by DdeInitialize. */static int ddeIsServer = 0;#define TCL_DDE_VERSION "1.2"#define TCL_DDE_PACKAGE_NAME "dde"#define TCL_DDE_SERVICE_NAME "TclEval"TCL_DECLARE_MUTEX(ddeMutex)/* * Forward declarations for procedures defined later in this file. */static void DdeExitProc _ANSI_ARGS_((ClientData clientData));static void DeleteProc _ANSI_ARGS_((ClientData clientData));static Tcl_Obj * ExecuteRemoteObject _ANSI_ARGS_(( RegisteredInterp *riPtr, Tcl_Obj *ddeObjectPtr));static int MakeDdeConnection _ANSI_ARGS_((Tcl_Interp *interp, char *name, HCONV *ddeConvPtr));static HDDEDATA CALLBACK DdeServerProc _ANSI_ARGS_((UINT uType, UINT uFmt, HCONV hConv, HSZ ddeTopic, HSZ ddeItem, HDDEDATA hData, DWORD dwData1, DWORD dwData2));static void SetDdeError _ANSI_ARGS_((Tcl_Interp *interp));int Tcl_DdeObjCmd(ClientData clientData, /* Used only for deletion */ Tcl_Interp *interp, /* The interp we are sending from */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[]); /* The arguments */EXTERN int Dde_Init(Tcl_Interp *interp);/* *---------------------------------------------------------------------- * * Dde_Init -- * * This procedure initializes the dde command. * * Results: * A standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */intDde_Init( Tcl_Interp *interp){ ThreadSpecificData *tsdPtr; if (!Tcl_InitStubs(interp, "8.0", 0)) { return TCL_ERROR; } Tcl_CreateObjCommand(interp, "dde", Tcl_DdeObjCmd, NULL, NULL); tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData((Tcl_ThreadDataKey *) &dataKey, sizeof(ThreadSpecificData)); if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->currentConversations = NULL; tsdPtr->interpListPtr = NULL; } Tcl_CreateExitHandler(DdeExitProc, NULL); return Tcl_PkgProvide(interp, TCL_DDE_PACKAGE_NAME, TCL_DDE_VERSION);}/* *---------------------------------------------------------------------- * * Initialize -- * * Initialize the global DDE instance. * * Results: * None. * * Side effects: * Registers the DDE server proc. * *---------------------------------------------------------------------- */static voidInitialize(void){ int nameFound = 0; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * See if the application is already registered; if so, remove its * current name from the registry. The deletion of the command * will take care of disposing of this entry. */ if (tsdPtr->interpListPtr != NULL) { nameFound = 1; } /* * Make sure that the DDE server is there. This is done only once, * add an exit handler tear it down. */ if (ddeInstance == 0) { Tcl_MutexLock(&ddeMutex); if (ddeInstance == 0) { if (DdeInitialize(&ddeInstance, DdeServerProc, CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS | CBF_FAIL_POKES, 0) != DMLERR_NO_ERROR) { ddeInstance = 0; } } Tcl_MutexUnlock(&ddeMutex); } if ((ddeServiceGlobal == 0) && (nameFound != 0)) { Tcl_MutexLock(&ddeMutex); if ((ddeServiceGlobal == 0) && (nameFound != 0)) { ddeIsServer = 1; Tcl_CreateExitHandler(DdeExitProc, NULL); ddeServiceGlobal = DdeCreateStringHandle(ddeInstance, \ TCL_DDE_SERVICE_NAME, 0); DdeNameService(ddeInstance, ddeServiceGlobal, 0L, DNS_REGISTER); } else { ddeIsServer = 0; } Tcl_MutexUnlock(&ddeMutex); }} /* *-------------------------------------------------------------- * * DdeSetServerName -- * * This procedure is called to associate an ASCII name with a Dde * server. If the interpreter has already been named, the * name replaces the old one. * * Results: * The return value is the name actually given to the interp. * This will normally be the same as name, but if name was already * in use for a Dde Server then a name of the form "name #2" will * be chosen, with a high enough number to make the name unique. * * Side effects: * Registration info is saved, thereby allowing the "send" command * to be used later to invoke commands in the application. In * addition, the "send" command is created in the application's * interpreter. The registration will be removed automatically * if the interpreter is deleted or the "send" command is removed. * *-------------------------------------------------------------- */static char *DdeSetServerName( Tcl_Interp *interp, char *name /* The name that will be used to * refer to the interpreter in later * "send" commands. Must be globally * unique. */ ){ int suffix, offset; RegisteredInterp *riPtr, *prevPtr; Tcl_DString dString; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * See if the application is already registered; if so, remove its * current name from the registry. The deletion of the command * will take care of disposing of this entry. */ for (riPtr = tsdPtr->interpListPtr, prevPtr = NULL; riPtr != NULL; prevPtr = riPtr, riPtr = riPtr->nextPtr) { if (riPtr->interp == interp) { if (name != NULL) { if (prevPtr == NULL) { tsdPtr->interpListPtr = tsdPtr->interpListPtr->nextPtr; } else { prevPtr->nextPtr = riPtr->nextPtr; } break; } else { /* * the name was NULL, so the caller is asking for * the name of the current interp. */ return riPtr->name; } } } if (name == NULL) { /* * the name was NULL, so the caller is asking for * the name of the current interp, but it doesn't * have a name. */ return ""; } /* * Pick a name to use for the application. Use "name" if it's not * already in use. Otherwise add a suffix such as " #2", trying * larger and larger numbers until we eventually find one that is * unique. */ suffix = 1; offset = 0; Tcl_DStringInit(&dString); /* * We have found a unique name. Now add it to the registry. */ riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp)); riPtr->interp = interp; riPtr->name = ckalloc(strlen(name) + 1); riPtr->nextPtr = tsdPtr->interpListPtr; tsdPtr->interpListPtr = riPtr; strcpy(riPtr->name, name); Tcl_CreateObjCommand(interp, "dde", Tcl_DdeObjCmd, (ClientData) riPtr, DeleteProc); if (Tcl_IsSafe(interp)) { Tcl_HideCommand(interp, "dde", "dde"); } Tcl_DStringFree(&dString); /* * re-initialize with the new name */ Initialize(); return riPtr->name;}/* *-------------------------------------------------------------- * * DeleteProc * * This procedure is called when the command "dde" is destroyed. * * Results: * none * * Side effects: * The interpreter given by riPtr is unregistered. * *-------------------------------------------------------------- */static voidDeleteProc(clientData) ClientData clientData; /* The interp we are deleting passed * as ClientData. */{ RegisteredInterp *riPtr = (RegisteredInterp *) clientData; RegisteredInterp *searchPtr, *prevPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); for (searchPtr = tsdPtr->interpListPtr, prevPtr = NULL; (searchPtr != NULL) && (searchPtr != riPtr); prevPtr = searchPtr, searchPtr = searchPtr->nextPtr) { /* * Empty loop body. */ } if (searchPtr != NULL) { if (prevPtr == NULL) { tsdPtr->interpListPtr = tsdPtr->interpListPtr->nextPtr; } else { prevPtr->nextPtr = searchPtr->nextPtr; } } ckfree(riPtr->name); Tcl_EventuallyFree(clientData, TCL_DYNAMIC);}/* *-------------------------------------------------------------- * * ExecuteRemoteObject -- * * Takes the package delivered by DDE and executes it in * the server's interpreter. * * Results: * A list Tcl_Obj * that describes what happened. The first * element is the numerical return code (TCL_ERROR, etc.). * The second element is the result of the script. If the * return result was TCL_ERROR, then the third element * will be the value of the global "errorCode", and the * fourth will be the value of the global "errorInfo". * The return result will have a refCount of 0. * * Side effects: * A Tcl script is run, which can cause all kinds of other * things to happen. * *-------------------------------------------------------------- */static Tcl_Obj *ExecuteRemoteObject( RegisteredInterp *riPtr, /* Info about this server. */ Tcl_Obj *ddeObjectPtr) /* The object to execute. */{ Tcl_Obj *errorObjPtr; Tcl_Obj *returnPackagePtr; int result; result = Tcl_EvalObjEx(riPtr->interp, ddeObjectPtr, TCL_EVAL_GLOBAL); returnPackagePtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); Tcl_ListObjAppendElement(NULL, returnPackagePtr, Tcl_NewIntObj(result)); Tcl_ListObjAppendElement(NULL, returnPackagePtr, Tcl_GetObjResult(riPtr->interp)); if (result == TCL_ERROR) { errorObjPtr = Tcl_GetVar2Ex(riPtr->interp, "errorCode", NULL, TCL_GLOBAL_ONLY); Tcl_ListObjAppendElement(NULL, returnPackagePtr, errorObjPtr); errorObjPtr = Tcl_GetVar2Ex(riPtr->interp, "errorInfo", NULL, TCL_GLOBAL_ONLY); Tcl_ListObjAppendElement(NULL, returnPackagePtr, errorObjPtr); } return returnPackagePtr;}/* *-------------------------------------------------------------- * * DdeServerProc -- * * Handles all transactions for this server. Can handle * execute, request, and connect protocols. Dde will * call this routine when a client attempts to run a dde * command using this server. * * Results: * A DDE Handle with the result of the dde command. * * Side effects: * Depending on which command is executed, arbitrary * Tcl scripts can be run. * *-------------------------------------------------------------- */static HDDEDATA CALLBACKDdeServerProc ( UINT uType, /* The type of DDE transaction we * are performing. */ UINT uFmt, /* The format that data is sent or * received. */ HCONV hConv, /* The conversation associated with the * current transaction. */ HSZ ddeTopic, /* A string handle. Transaction-type * dependent. */ HSZ ddeItem, /* A string handle. Transaction-type * dependent. */ HDDEDATA hData, /* DDE data. Transaction-type dependent. */ DWORD dwData1, /* Transaction-dependent data. */ DWORD dwData2) /* Transaction-dependent data. */{ Tcl_DString dString; int len; char *utilString; Tcl_Obj *ddeObjectPtr; HDDEDATA ddeReturn = NULL; RegisteredInterp *riPtr; Conversation *convPtr, *prevConvPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -