From 74081d2c1d6b25a47c0877842f2c26866b5837d7 Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Tue, 2 Apr 2002 23:39:07 +0000 Subject: [PATCH] Added support for multiple interpreters. Added the appname subcommand to report and set the registered name. Added in some debugging support functions. Wrote some more documentation as a .man format file (see tcllib) Added a second VB script demo. Incremented the version to 0.4 --- .cvsignore | 5 + Makefile | 13 ++- Readme | 5 +- WinSendCom.c | 14 ++- WinSendCom.h | 2 +- debug.c | 17 +++ debug.h | 30 +++++ pkgIndex.tcl | 4 +- test.vbs | 11 +- test2.vbs | 21 ++++ winsend.c | 323 ++++++++++++++++++++++++++++++++++++++++----------- winsend.dsp | 16 ++- winsend.html | 4 +- winsend.man | 56 +++++++++ 14 files changed, 425 insertions(+), 96 deletions(-) create mode 100644 .cvsignore create mode 100644 debug.c create mode 100644 debug.h create mode 100644 test2.vbs create mode 100644 winsend.man diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..392bc9c --- /dev/null +++ b/.cvsignore @@ -0,0 +1,5 @@ +.cvsignore +Debug +Release +*.ilk +*.dll \ No newline at end of file diff --git a/Makefile b/Makefile index c9cace8..5d525a2 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,10 @@ # # @(#)$Id$ -VER =03 +#TCLROOT ="c:/Program Files/Tcl" +TCLROOT =/opt/tcl + +VER =04 DBGX =d DBGFLAGS=-D_DEBUG @@ -16,8 +19,8 @@ CC =gcc -g DLLWRAP =dllwrap DLLTOOL =dlltool RM =rm -f -CFLAGS =-Wall -I/opt/tcl/include -DUSE_TCL_STUBS $(DBGFLAGS) -LDFLAGS =-L/opt/tcl/lib +CFLAGS =-Wall -I$(TCLROOT)/include -DUSE_TCL_STUBS $(DBGFLAGS) +LDFLAGS =-L$(TCLROOT)/lib LIBS =-ltclstub83${DBGX} -lole32 -loleaut32 -ladvapi32 -luuid DLL =winsend${VER}${DBGX}.dll @@ -25,7 +28,7 @@ DEFFILE =winsend.def WRAPFLAGS =--driver-name $(CC) --def $(DEFFILE) -CSRCS =winsend.c WinSendCom.c +CSRCS =winsend.c WinSendCom.c debug.c OBJS =$(CSRCS:.c=.o) $(DLL): $(OBJS) @@ -37,7 +40,9 @@ clean: %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ +winsend.o: WinSendCom.h debug.h WinSendCom.o: WinSendCom.c WinSendCom.h +debug.o: debug.h .PHONY: clean diff --git a/Readme b/Readme index c1465a6..26828fe 100644 --- a/Readme +++ b/Readme @@ -47,11 +47,10 @@ WScript.Echo interp.Send("info tcl") You should see the message printed in your tkcon window and see your Tcl version printed in your DOS box. -The code needs tidying up a bit before I publish the source but this -shouldn't take long. In the meantime see +See http://tclsoap.sourceforge.net/winsend.html and - http://tclsoap.sourceforge.net/winsend03.zip + http://tclsoap.sourceforge.net/winsend.zip for the docs (this document) and code respectively. Pat Thoyts. diff --git a/WinSendCom.c b/WinSendCom.c index e0167e1..53f0bae 100644 --- a/WinSendCom.c +++ b/WinSendCom.c @@ -13,9 +13,10 @@ static const char rcsid[] = #include "WinSendCom.h" -/* ---------------------------------------------------------------------- */ -/* Non-public prototypes. -/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- + * Non-public prototypes. + * ---------------------------------------------------------------------- + */ static STDMETHODIMP WinSendCom_QueryInterface(IDispatch *This, REFIID riid, void **ppvObject); @@ -37,9 +38,10 @@ static STDMETHODIMP WinSendCom_Invoke(IDispatch *This, DISPID dispidMember, UINT *puArgErr); static HRESULT Send(WinSendCom* obj, VARIANT vCmd, VARIANT* pvResult); -/* ---------------------------------------------------------------------- */ -/* COM Class Helpers -/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- + * COM Class Helpers + * ---------------------------------------------------------------------- + */ /* Description: * Create and initialises a new instance of the WinSend COM class and diff --git a/WinSendCom.h b/WinSendCom.h index efefb8e..d8a4efb 100644 --- a/WinSendCom.h +++ b/WinSendCom.h @@ -18,7 +18,7 @@ typedef struct WinSendCom_t { } WinSendCom; /* - * WinSendCom public methods + * WinSendCom public functions */ HRESULT WinSendCom_CreateInstance(Tcl_Interp *interp, REFIID riid, void **ppv); void WinSendCom_Destroy(LPDISPATCH pdisp); diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..fb64b78 --- /dev/null +++ b/debug.c @@ -0,0 +1,17 @@ +#include "debug.h" +#include + +void +LocalTrace(LPCTSTR format, ...) +{ + int n; + const int max = sizeof(TCHAR) * 512; + TCHAR buffer[512]; + va_list args; + va_start (args, format); + + n = _vsntprintf(buffer, max, format, args); + _ASSERTE(n < max); + OutputDebugString(buffer); + va_end(args); +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..0d868ad --- /dev/null +++ b/debug.h @@ -0,0 +1,30 @@ +/* debug.h - Copyright (C) 2002 Pat Thoyts + * + * $Id$ + */ + +#ifndef _Debug_h_INCLUDE +#define _Debug_h_INCLUDE + +#include +#include +#include +#include + +/* Debug related stuff */ +#ifndef _DEBUG + +#define _ASSERTE(x) ((void)0) +#define LTRACE 1 ? ((void)0) : LocalTrace + +#else /* _DEBUG */ + +#define _ASSERTE(x) if (!(x)) _assert(#x, __FILE__, __LINE__) +#define LTRACE LocalTrace + +#endif /* _DEBUG */ + +/* Debug functions. */ +void LocalTrace(LPCTSTR szFormat, ...); + +#endif /* _Debug_h_INCLUDE */ \ No newline at end of file diff --git a/pkgIndex.tcl b/pkgIndex.tcl index 9efa827..ef069c1 100644 --- a/pkgIndex.tcl +++ b/pkgIndex.tcl @@ -9,8 +9,8 @@ # full path name of this file's directory. if {[info exists ::tcl_platform(debug)] && $::tcl_platform(debug)} { - package ifneeded winsend 0.3 [list load [file join $dir winsend03d.dll]] + package ifneeded winsend 0.4 [list load [file join $dir winsend04d.dll]] } else { - package ifneeded winsend 0.3 [list load [file join $dir winsend03.dll]] + package ifneeded winsend 0.4 [list load [file join $dir winsend04.dll]] } diff --git a/test.vbs b/test.vbs index 51ac78d..f602394 100644 --- a/test.vbs +++ b/test.vbs @@ -1,10 +1,3 @@ Dim o -Set o = GetObject("\\.\TclInterp\tkcon.tcl") -WScript.Echo o.Send("puts {Hello, from VB} ; tkcon master wm title .") - -Dim p -Set p = GetObject("\\.\TclInterp\tkcon.tcl #2") -WScript.Echo p.Send("puts {Hello, 2}; tkcon master wm title .") - - - +Set o = GetObject("\\.\TclInterp\tkchat") +WScript.Echo o.Send("puts {Hello, from VB} ; info tcl") diff --git a/test2.vbs b/test2.vbs new file mode 100644 index 0000000..614b8f3 --- /dev/null +++ b/test2.vbs @@ -0,0 +1,21 @@ +' test2.vbs - Copyright (C) 2002 Pat Thoyts +' +' Demo accessing Tcl using the winsend package from the Visual Basic +' Scripting engine via the Windown Scripting Host (WSH). +' +' Run this using 'cscript test2.vbs' or 'wscript test2.vbs' when you +' have a wish session using the winsend appname wish. +' +' $Id$ + +Dim o +Set o = GetObject("\\.\TclInterp\wish") + +cmd = "package require Tk" +cmd = cmd & vbNewline & "toplevel .t" +cmd = cmd & vbNewline & "button .t.b -text OK -command {destroy .t}" +cmd = cmd & vbNewline & "pack .t.b -side top -fill both" +cmd = cmd & vbNewline & "tkwait window .t" +cmd = cmd & vbNewline & "info tcl" + +WScript.Echo o.Send(cmd) diff --git a/winsend.c b/winsend.c index ba58467..8c6aa24 100644 --- a/winsend.c +++ b/winsend.c @@ -19,6 +19,7 @@ static const char rcsid[] = #include #include "WinSendCom.h" +#include "debug.h" #ifndef DECLSPEC_EXPORT #define DECLSPEC_EXPORT __declspec(dllexport) @@ -26,23 +27,36 @@ static const char rcsid[] = /* Should be defined in WTypes.h but mingw 1.0 is missing them */ #ifndef _ROTFLAGS_DEFINED +#define _ROTFLAGS_DEFINED #define ROTFLAGS_REGISTRATIONKEEPSALIVE 0x01 #define ROTFLAGS_ALLOWANYCLIENT 0x02 #endif /* ! _ROTFLAGS_DEFINED */ -#define WINSEND_PACKAGE_VERSION "0.3" +#define WINSEND_PACKAGE_VERSION "0.4" #define WINSEND_PACKAGE_NAME "winsend" #define WINSEND_CLASS_NAME "TclEval" -DWORD g_dwROTCookie; - -static void Winsend_PkgDeleteProc _ANSI_ARGS_((ClientData clientData)); +/* Package information structure. + * This is used to keep interpreter specific details for use when releasing + * the package resources upon interpreter deletion or package removal. + */ +typedef struct WinsendPkg_t { + Tcl_Obj *appname; /* the registered application name */ + DWORD ROT_cookie; /* ROT cookie returned on registration */ + LPUNKNOWN obj; /* Interface for the registration object */ + Tcl_Command token; /* Winsend command token */ +} WinsendPkg; + +static void Winsend_InterpDeleteProc (ClientData clientData, Tcl_Interp *interp); +static void Winsend_PkgDeleteProc (ClientData clientData); static int Winsend_CmdProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static int Winsend_CmdInterps(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static int Winsend_CmdSend(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +static int Winsend_CmdAppname(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); static int Winsend_CmdTest(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static int Winsend_ObjSendCmd(LPDISPATCH pdispInterp, Tcl_Interp *interp, @@ -50,9 +64,10 @@ static int Winsend_ObjSendCmd(LPDISPATCH pdispInterp, Tcl_Interp *interp, static HRESULT BuildMoniker(LPCOLESTR name, LPMONIKER *pmk); static Tcl_Obj* Winsend_Win32ErrorObj(HRESULT hrError); -// ------------------------------------------------------------------------- -// DllMain -// ------------------------------------------------------------------------- +/* ------------------------------------------------------------------------- + * DllMain + * ------------------------------------------------------------------------- + */ EXTERN_C BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) @@ -68,30 +83,38 @@ DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) return TRUE; } -// ------------------------------------------------------------------------- -// Winsend_Init -// ------------------------------------------------------------------------- +/* ------------------------------------------------------------------------- + * Winsend_Init + * ------------------------------------------------------------------------- + */ EXTERN_C int DECLSPEC_EXPORT Winsend_Init(Tcl_Interp* interp) { HRESULT hr = S_OK; int r = TCL_OK; - IUnknown *pUnk = NULL; + WinsendPkg *pkg = NULL; #ifdef USE_TCL_STUBS Tcl_InitStubs(interp, "8.3", 0); #endif - // Initialize COM + pkg = (WinsendPkg*)Tcl_Alloc(sizeof(WinsendPkg)); + if (pkg == NULL) { + Tcl_SetResult(interp, "out of memory", TCL_STATIC); + return TCL_ERROR; + } + memset(pkg, 0, sizeof(WinsendPkg)); + + /* Initialize COM */ hr = CoInitialize(0); if (FAILED(hr)) { Tcl_SetResult(interp, "failed to initialize the " WINSEND_PACKAGE_NAME " package", TCL_STATIC); return TCL_ERROR; } - // Create our registration object. - hr = WinSendCom_CreateInstance(interp, &IID_IUnknown, (void**)&pUnk); + /* Create our registration object. */ + hr = WinSendCom_CreateInstance(interp, &IID_IUnknown, (void**)&pkg->obj); if (SUCCEEDED(hr)) { @@ -123,9 +146,14 @@ Winsend_Init(Tcl_Interp* interp) if (SUCCEEDED(hr)) { hr = pROT->lpVtbl->Register(pROT, - ROTFLAGS_REGISTRATIONKEEPSALIVE - | ROTFLAGS_ALLOWANYCLIENT, - pUnk, pmk, &g_dwROTCookie); + ROTFLAGS_REGISTRATIONKEEPSALIVE, + pkg->obj, + pmk, + &pkg->ROT_cookie); + if (SUCCEEDED(hr)) { + pkg->appname = Tcl_NewUnicodeObj(oleName, -1); + Tcl_IncrRefCount(pkg->appname); + } pmk->lpVtbl->Release(pmk); } @@ -133,32 +161,33 @@ Winsend_Init(Tcl_Interp* interp) * try again. */ if (hr == MK_S_MONIKERALREADYREGISTERED) - pROT->lpVtbl->Revoke(pROT, g_dwROTCookie); + pROT->lpVtbl->Revoke(pROT, pkg->ROT_cookie); } while (hr == MK_S_MONIKERALREADYREGISTERED); pROT->lpVtbl->Release(pROT); } - pUnk->lpVtbl->Release(pUnk); - - // Create our winsend command + /* Create our winsend command */ if (SUCCEEDED(hr)) { - Tcl_CreateObjCommand(interp, "winsend", Winsend_CmdProc, (ClientData)0, (Tcl_CmdDeleteProc*)0); + pkg->token = Tcl_CreateObjCommand(interp, + "winsend", + Winsend_CmdProc, + (ClientData)pkg, + (Tcl_CmdDeleteProc*)NULL); } /* Create an exit procedure to handle unregistering when the * Tcl interpreter terminates. */ - Tcl_CreateExitHandler(Winsend_PkgDeleteProc, NULL); + Tcl_CallWhenDeleted(interp, Winsend_InterpDeleteProc, (ClientData)pkg); + Tcl_CreateExitHandler(Winsend_PkgDeleteProc, (ClientData)pkg); r = Tcl_PkgProvide(interp, WINSEND_PACKAGE_NAME, WINSEND_PACKAGE_VERSION); } if (FAILED(hr)) { - // TODO: better error handling - rip code from w32_exception.h - Tcl_Obj *err = Winsend_Win32ErrorObj(hr); - Tcl_SetObjResult(interp, err); + Tcl_SetObjResult(interp, Winsend_Win32ErrorObj(hr)); r = TCL_ERROR; } else { Tcl_SetResult(interp, "", TCL_STATIC); @@ -167,9 +196,9 @@ Winsend_Init(Tcl_Interp* interp) return r; } -// ------------------------------------------------------------------------- -// Winsend_SafeInit -// ------------------------------------------------------------------------- +/* ------------------------------------------------------------------------- + * Winsend_SafeInit + * ------------------------------------------------------------------------- */ EXTERN_C int DECLSPEC_EXPORT Winsend_SafeInit(Tcl_Interp* interp) @@ -178,33 +207,100 @@ Winsend_SafeInit(Tcl_Interp* interp) return TCL_ERROR; } -// ------------------------------------------------------------------------- -// WinsendExitProc -// ------------------------------------------------------------------------- +/* ------------------------------------------------------------------------- + * Winsend_InterpDeleteProc + * ------------------------------------------------------------------------- + * Description: + * Called when the interpreter is deleted, this procedure clean up the COM + * registration for us. We need to revoke our registered object, and release + * our own object reference. The WinSendCom object should delete itself now. + */ -static void -Winsend_PkgDeleteProc(ClientData clientData) +static void +Winsend_InterpDeleteProc (ClientData clientData, Tcl_Interp *interp) { + WinsendPkg *pkg = (WinsendPkg*)clientData; LPRUNNINGOBJECTTABLE pROT = NULL; - HRESULT hr = GetRunningObjectTable(0, &pROT); - if (SUCCEEDED(hr)) - { - hr = pROT->lpVtbl->Revoke(pROT, g_dwROTCookie); - pROT->lpVtbl->Release(pROT); + HRESULT hr = S_OK; + + LTRACE(_T("Winsend_InterpDeleteProc( {ROT_cookie: 0x%08X, obj: 0x%08X})\n"), + pkg->ROT_cookie, pkg->obj); + + /* Lock the package structure in memory */ + Tcl_Preserve((ClientData)pkg); + + if (pkg->ROT_cookie != 0) { + hr = GetRunningObjectTable(0, &pROT); + if (SUCCEEDED(hr)) + { + hr = pROT->lpVtbl->Revoke(pROT, pkg->ROT_cookie); + pROT->lpVtbl->Release(pROT); + pkg->ROT_cookie = 0; + } + _ASSERTE(SUCCEEDED(hr)); } - //ASSERT + + /* Remove the winsend command and release the interp pointer */ + /* I get assertions from this from within tcl 8.3 - it's not + * really needed anyway + */ + /* if (interp != NULL) { + Tcl_DeleteCommandFromToken(interp, pkg->token); + pkg->token = 0; + }*/ + + /* release the appname object */ + if (pkg->appname != NULL) { + Tcl_DecrRefCount(pkg->appname); + pkg->appname = NULL; + } + + /* Release the registration object */ + pkg->obj->lpVtbl->Release(pkg->obj); + pkg->obj = NULL; + + /* unlock the package data structure. */ + Tcl_Release((ClientData)pkg); } -// ------------------------------------------------------------------------- -// Winsend_CmdProc -// ------------------------------------------------------------------------- +/* ------------------------------------------------------------------------- + * Winsend_PkgDeleteProc + * ------------------------------------------------------------------------- + * Description: + * Called when the package is removed, we clean up any outstanding memory + * and release any handles. + */ +static void +Winsend_PkgDeleteProc(ClientData clientData) +{ + WinsendPkg *pkg = (WinsendPkg*)clientData; + LTRACE(_T("Winsend_PkgDeleteProc( {ROT_cookie: 0x%08X, obj: 0x%08X})\n"), + pkg->ROT_cookie, pkg->obj); + + if (pkg->obj != NULL) { + Winsend_InterpDeleteProc(clientData, NULL); + } + + _ASSERTE(pkg->obj == NULL); + Tcl_Free((char*)pkg); + + CoUninitialize(); +} + +/* ------------------------------------------------------------------------- + * Winsend_CmdProc + * ------------------------------------------------------------------------- + * Description: + * The Tcl winsend proc is first processed here and then passed on depending + * upong the command argument. + */ static int Winsend_CmdProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - enum {WINSEND_INTERPS, WINSEND_SEND, WINSEND_TEST}; - static char* cmds[] = { "interps", "send", "test", NULL }; + enum {WINSEND_INTERPS, WINSEND_SEND, WINSEND_APPNAME, WINSEND_TEST}; + static char* cmds[] = { "interps", "send", "appname", "test", NULL }; int index = 0, r = TCL_OK; if (objc < 2) { @@ -223,6 +319,9 @@ Winsend_CmdProc(ClientData clientData, Tcl_Interp *interp, case WINSEND_SEND: r = Winsend_CmdSend(clientData, interp, objc, objv); break; + case WINSEND_APPNAME: + r = Winsend_CmdAppname(clientData, interp, objc, objv); + break; case WINSEND_TEST: r = Winsend_CmdTest(clientData, interp, objc, objv); break; @@ -231,10 +330,14 @@ Winsend_CmdProc(ClientData clientData, Tcl_Interp *interp, return r; } -// ------------------------------------------------------------------------- -// Winsend_CmdInterps -// ------------------------------------------------------------------------- - +/* ------------------------------------------------------------------------- + * Winsend_CmdInterps + * ------------------------------------------------------------------------- + * Description: + * Iterate over the running object table and identify all the Tcl registered + * objects. We build a list using the tail part of the file moniker and return + * this as the list of registered interpreters. + */ static int Winsend_CmdInterps(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) @@ -274,8 +377,9 @@ Winsend_CmdInterps(ClientData clientData, Tcl_Interp *interp, if (wcsncmp(olestr, oleszStub, wcslen(oleszStub)) == 0) { - LPOLESTR p = olestr + wcslen(oleszStub) + 1; - r = Tcl_ListObjAppendElement(interp, objList, Tcl_NewUnicodeObj(p, -1)); + LPOLESTR p = olestr + wcslen(oleszStub); + if (*p) + r = Tcl_ListObjAppendElement(interp, objList, Tcl_NewUnicodeObj(p + 1, -1)); } hr = CoGetMalloc(1, &pMalloc); @@ -310,10 +414,10 @@ Winsend_CmdInterps(ClientData clientData, Tcl_Interp *interp, return r; } -// ------------------------------------------------------------------------- -// Winsend_CmdSend -// ------------------------------------------------------------------------- - +/* ------------------------------------------------------------------------- + * Winsend_CmdSend + * ------------------------------------------------------------------------- + */ static int Winsend_CmdSend(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) @@ -345,7 +449,6 @@ Winsend_CmdSend(ClientData clientData, Tcl_Interp *interp, IDispatch* pdispInterp = NULL; hr = pROT->lpVtbl->IsRunning(pROT, pmk); hr = pmk->lpVtbl->BindToObject(pmk, pBindCtx, NULL, &IID_IUnknown, (void**)&punkInterp); - //hr = pROT->lpVtbl->GetObject(pROT, pmk, &punkInterp); if (SUCCEEDED(hr)) hr = punkInterp->lpVtbl->QueryInterface(punkInterp, &IID_IDispatch, (void**)&pdispInterp); if (SUCCEEDED(hr)) @@ -370,6 +473,80 @@ Winsend_CmdSend(ClientData clientData, Tcl_Interp *interp, return r; } +/* ------------------------------------------------------------------------- + * Winsend_CmdAppname + * ------------------------------------------------------------------------- + * Description: + * If called with no additional parameters returns the registered application + * name. If a new name is given then we attempt to register the new app name + * If the name is already registered then we cancel the new registration and + * continue using the old appname. + */ +static int +Winsend_CmdAppname(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]) +{ + int r = TCL_OK; + HRESULT hr = S_OK; + WinsendPkg* pkg = (WinsendPkg*)clientData; + + if (objc == 2) { + + Tcl_SetObjResult(interp, Tcl_DuplicateObj(pkg->appname)); + + } else if (objc > 3) { + + Tcl_WrongNumArgs(interp, 2, objv, "?appname?"); + r = TCL_ERROR; + + } else { + + LPRUNNINGOBJECTTABLE pROT = NULL; + hr = GetRunningObjectTable(0, &pROT); + if (SUCCEEDED(hr)) { + LPMONIKER pmk; + LPOLESTR szNewName; + + szNewName = Tcl_GetUnicode(objv[2]); + + /* construct a new moniker */ + hr = BuildMoniker(szNewName, &pmk); + if (SUCCEEDED(hr)) { + DWORD cookie = 0; + + /* register the new name */ + hr = pROT->lpVtbl->Register(pROT, ROTFLAGS_REGISTRATIONKEEPSALIVE, pkg->obj, pmk, &cookie); + if (SUCCEEDED(hr)) { + if (hr == MK_S_MONIKERALREADYREGISTERED) { + pROT->lpVtbl->Revoke(pROT, cookie); + Tcl_SetObjResult(interp, Winsend_Win32ErrorObj(hr)); + r = TCL_ERROR; + } else { + + /* revoke the old name */ + hr = pROT->lpVtbl->Revoke(pROT, pkg->ROT_cookie); + if (SUCCEEDED(hr)) { + + /* update the package structure */ + pkg->ROT_cookie = cookie; + Tcl_SetUnicodeObj(pkg->appname, szNewName, -1); + Tcl_SetObjResult(interp, pkg->appname); + } + } + } + pmk->lpVtbl->Release(pmk); + } + pROT->lpVtbl->Release(pROT); + } + if (FAILED(hr)) + { + Tcl_SetObjResult(interp, Winsend_Win32ErrorObj(hr)); + r = TCL_ERROR; + } + } + return r; +} + /* ------------------------------------------------------------------------- * Winsend_CmdTest * ------------------------------------------------------------------------- @@ -404,15 +581,19 @@ Winsend_CmdTest(ClientData clientData, Tcl_Interp *interp, return r; } -// ------------------------------------------------------------------------- -// Helpers. -// ------------------------------------------------------------------------- +/* ------------------------------------------------------------------------- + * Helpers. + * ------------------------------------------------------------------------- + */ -// ------------------------------------------------------------------------- -// Winsend_ObjSendCmd -// ------------------------------------------------------------------------- -// Description: -// +/* ------------------------------------------------------------------------- + * Winsend_ObjSendCmd + * ------------------------------------------------------------------------- + * Description: + * Perform an interface call to the server object. We convert the Tcl arguments + * into a BSTR using 'concat'. The result should be a BSTR that we can set as the + * interp's result string. + */ static int Winsend_ObjSendCmd(LPDISPATCH pdispInterp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) @@ -450,7 +631,13 @@ Winsend_ObjSendCmd(LPDISPATCH pdispInterp, Tcl_Interp *interp, return (SUCCEEDED(hr) ? TCL_OK : TCL_ERROR); } -// Construct a Tcl interpreter moniker from an interp name. +/* ------------------------------------------------------------------------- + * BuildMoniker + * ------------------------------------------------------------------------- + * Description: + * Construct a moniker from the given name. This ensures that all our + * monikers have the same prefix. + */ static HRESULT BuildMoniker(LPCOLESTR name, LPMONIKER *pmk) { @@ -475,6 +662,12 @@ BuildMoniker(LPCOLESTR name, LPMONIKER *pmk) return hr; } +/* ------------------------------------------------------------------------- + * Winsend_Win32ErrorObj + * ------------------------------------------------------------------------- + * Description: + * Convert COM or Win32 API errors into Tcl strings. + */ static Tcl_Obj* Winsend_Win32ErrorObj(HRESULT hrError) { diff --git a/winsend.dsp b/winsend.dsp index 36bb995..9d143a8 100644 --- a/winsend.dsp +++ b/winsend.dsp @@ -53,7 +53,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 tclstub83.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"winsend03.dll" +# ADD LINK32 tclstub83.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"winsend04.dll" !ELSEIF "$(CFG)" == "winsend - Win32 Debug" @@ -68,8 +68,8 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSEND_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSEND_EXPORTS" /D "USE_TCL_STUBS" /YX /FD /GZ /c +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSEND_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSEND_EXPORTS" /D "USE_TCL_STUBS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" @@ -79,7 +79,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 tclstub83d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"winsend03d.dll" /pdbtype:sept +# ADD LINK32 tclstub83d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"winsend04d.dll" /pdbtype:sept !ENDIF @@ -92,6 +92,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\debug.c +# End Source File +# Begin Source File + SOURCE=.\winsend.c # End Source File # Begin Source File @@ -108,6 +112,10 @@ SOURCE=.\WinSendCom.c # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\debug.h +# End Source File +# Begin Source File + SOURCE=.\WinSendCom.h # End Source File # End Group diff --git a/winsend.html b/winsend.html index e5376fe..0e229d9 100644 --- a/winsend.html +++ b/winsend.html @@ -58,8 +58,8 @@ WScript.Echo interp.Send("info tcl") Tcl version printed in your DOS box.

The package containing binaries and source is available from - -http://tclsoap.sf.net/winsend03.zip

+ +http://tclsoap.sf.net/winsend.zip

Pat Thoyts.

diff --git a/winsend.man b/winsend.man new file mode 100644 index 0000000..09b485e --- /dev/null +++ b/winsend.man @@ -0,0 +1,56 @@ +[manpage_begin winsend n 0.4] +[moddesc {winsend}] +[titledesc {Windows send Command}] +[require Tcl 8.3] +[require winsend [opt 0.4]] +[description] +[para] + +The [cmd winsend] package is an attempt to provide the complete +functionality of the Tk [cmd send] command using the Microsoft Windows +platform. The two key points are inter-process communications between +one interpreter and another, and automatic registration of +interpreters with a central source. With the Tk send command this is +done via the X Windows server. The [cmd winfo interps] command returns +a list of registered interpreters and the [cmd send] command allows +you to execute Tcl commands in the named interpreter. + +[section COMMANDS] + +[list_begin definitions] + +[call [cmd winsend] [arg command] [opt [arg "arguments ..."]]] + +[list_begin definitions] +[lst_item "[cmd interps]"] + Get the list of registered interpreter names for use with send. +[lst_item "[cmd appname] [opt [arg name]]"] + When called with no arguments this returns the registered name of +this interpreter. If a single argument is given, this is used to +re-register the application using the new name. If the registration +fails for some reason, such as the name is already in use, then an +error is returned and the old appname remains in use. +[lst_item "[cmd send]"] + send +[list_end] + +[list_end] + +[section EXAMPLES] + +[para] +[example { +% package require winsend +0.4 +% winsend appname MyApp +MyApp +% winsend send MyApp info tcl +8.3 +}] + +[see_also send(n)] +[section AUTHORS] +Pat Thoyts + +[keywords send appname] +[manpage_end] -- 2.23.0