Added support for multiple interpreters. winsend-0-4
authorPat Thoyts <patthoyts@users.sourceforge.net>
Tue, 2 Apr 2002 23:39:07 +0000 (23:39 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Tue, 2 Apr 2002 23:39:07 +0000 (23:39 +0000)
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

14 files changed:
.cvsignore [new file with mode: 0644]
Makefile
Readme
WinSendCom.c
WinSendCom.h
debug.c [new file with mode: 0644]
debug.h [new file with mode: 0644]
pkgIndex.tcl
test.vbs
test2.vbs [new file with mode: 0644]
winsend.c
winsend.dsp
winsend.html
winsend.man [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..392bc9c
--- /dev/null
@@ -0,0 +1,5 @@
+.cvsignore
+Debug
+Release
+*.ilk
+*.dll
\ No newline at end of file
index c9cace8976bd554b518aadfb1f681ff2ca888519..5d525a2e1bc78c7e2530f2ee9f8c02637a70ff31 100644 (file)
--- 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 c1465a671320974a0770c1a120530e712cea6512..26828feffe3ef169241742d157c5cf2c87056e5b 100644 (file)
--- 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.
index e0167e180ff36704edabb0dd899aba7609dd7e43..53f0bae8b019ba216a5b842a4c28bf9e5bd94f0c 100644 (file)
@@ -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
index efefb8e41a7bf4205885e87654d54fb3ed12314f..d8a4efbabf91fe6b0310d4d1656b2d0692d9d5f3 100644 (file)
@@ -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 (file)
index 0000000..fb64b78
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,17 @@
+#include "debug.h"
+#include <stdio.h>
+
+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 (file)
index 0000000..0d868ad
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,30 @@
+/* debug.h - Copyright (C) 2002 Pat Thoyts <Pat.Thoyts@bigfoot.com>
+ *
+ * $Id$
+ */
+
+#ifndef _Debug_h_INCLUDE
+#define _Debug_h_INCLUDE
+
+#include <windows.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <tchar.h>
+
+/* 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
index 9efa827c5642e148ac3dab8bf8f04c9026c55b33..ef069c1fc39d884004241d50ac5b3501435f0f3e 100644 (file)
@@ -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]]
 }
    
index 51ac78dcb725ebfaf1bff2b6b30cb368e946307e..f602394e8f613d59bf9dd9fae5be53395c085dd6 100644 (file)
--- 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 (file)
index 0000000..614b8f3
--- /dev/null
+++ b/test2.vbs
@@ -0,0 +1,21 @@
+' test2.vbs - Copyright (C) 2002 Pat Thoyts <Pat.Thoyts@bigfoot.com>
+'
+' 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)
index ba58467811d355bd76ccd2ff630efd964eb1cc5b..8c6aa240f67ce6c8978d95c5ec775f192af11b6a 100644 (file)
--- a/winsend.c
+++ b/winsend.c
@@ -19,6 +19,7 @@ static const char rcsid[] =
 
 #include <initguid.h>
 #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)
 {
index 36bb995319b7e55f1acc1368cf38066862ac140f..9d143a84d8b00be3fbfc9d831cb8b3ed6a6acd60 100644 (file)
@@ -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
index e5376fef95e6fee599f26e9d9b5b774d120df9f4..0e229d974ecea8949f336f89b69862bbf2cc1689 100644 (file)
@@ -58,8 +58,8 @@ WScript.Echo interp.Send("info tcl")</pre>
 Tcl version printed in your DOS box.</p>
 
 <p>The package containing binaries and source is available from 
-<a href="http://tclsoap.sf.net/winsend03.zip">
-http://tclsoap.sf.net/winsend03.zip</a></p>
+<a href="http://tclsoap.sf.net/winsend.zip">
+http://tclsoap.sf.net/winsend.zip</a></p>
 
 <p>Pat Thoyts.</p>
 
diff --git a/winsend.man b/winsend.man
new file mode 100644 (file)
index 0000000..09b485e
--- /dev/null
@@ -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]