COSH.EXEA COM Shell for TCL running on Microsoft NTbyDavid Shepherd at ICR GmbH, Ueberlingen, GermanyCOSH is a shell programm for TCL like WISH - in fact it *is* WISH with the addition of a COM interface called ITclScript. Perhaps you could call COSH the "COM version" of WISH. For anyone involved in distributed processing on Microsoft NT systems,
COM is the first word in the vocabulary. It turns out that COM and TCL can
work together quite happily and can actually compliment each other in a
couple of important respects. Some points to note about COSH
are:
AvailabilityBinaries and sources are available for download atCOSH InstallationCOSH needs TCL 8.0 to run and VC++ 5.0 to compileThe following files need to be installed on any machine which will run COSH. On the server side they are:
In addition there is on the client side, as a demonstration - COSHCLIENT.DLL A TCL extension DLL
demonstrating a COSH client
Why bother with COM?Microsoft has gone all out over COM and has successively built larger portions of COM and OLE into the operating system itself. For anyone working on programming distributed systems on NT, COM is *the* method of choice to get servers and clients connected and components managed, because you are using native OS services to do it.But while COM of itself, being a relatively elegant solution for component interoperability, is a Good Thing, Microsoft has built a services superstructure on top of COM (OLE, ActiveX et al) of enormous complexity. If, as a software developer, you subscribe solely to the Microsoft Way, you already have your work cut out for you - its reputation for being hard to learn and use is thoroughly deserved. Fortunately, COM/OLE is not an all or nothing proposition. You can also
say "as much COM as necessary and as little as possible". A distributed scenarioCOSH comes out of a project for a system of distributed server processes which has some similarity to workflow systems. A requirement of the system is that it can be controlled from a single point-of-administration. This central administration program can create and monitor a number of server processes on various machines, can perform load balancing among them etc etc. The servers read from and write to message queues and can be arranged into "processing networks".This product runs on Windows-NT and has a modular component architecture, so COM is the first thing which springs to mind. However, we also have a significant investment in TCL scripting technology and, seeing the amazing flexibilty which it affords us, we want to retain TCL as far as possible as the glueing technology of our products. While we do not track each and every Microsoft technology as and when they are released, we definitely want to use such services as DCOM, Transaction Server, Message Queue, Active Directory and so on. On the other hand, we like to package our components as TCL extension DLLs and scripts - a nice, easy, effective and lo-tech way of doing things. What to do? In the end, the idea of a COM-capable TCL shell distilled out of what
we wanted to do. We would install on each machine in the network (1)
TCL/TK plus a number of support packages and (2) a generic TCL shell which
can be remotely activated and can accept scripts for evaluation from its
client. The "client" in this case is a central administration and monitor
programm, displayed as a "Game Board". This program creates COM shells as
needed according to its load balancing rules and bootstraps them as server
processes by pumping scripts for evaluation into them at load time via the
ITclScript interface. This is nice because all the server scripts can be
kept in one place. What is the current status of COSH?On a scale where 0.1 represents "almost useless" and 1.0 is "production quality" COSH is about at 0.70. Plenty still to do, but on the other hand you can do at the moment about 90% of all things you need to do with it.The ITclScript interfaceDisregarding for a moment the standard COM functions such as QueryInterface, AddRef and the like, this interface contains exactly one function, called "eval". It is prototyped in IDL like this: HRESULT eval ([in] BSTR script, [out] BSTR*
result,
It is pretty obvious what this function does. It takes an arbitrary
script and evaluates it in its host interpreter, returning the result, the
status code (TCL_OK, TCL_ERROR etc) and maybe the errorInfo stack
trace. The CoshClient Extension DLLA TCL extension DLL called CoshClient.dll is part of the distribution. It demonstrates how to remote start, terminate and feed scripts to a COSH program. It makes use of the vtable interface in COSH.EXE and uses COSHPS.DLL to marshall the call parameters across process boundaries. It instantiates an object of type COMSHELL. It implements the command:comshell ?machine-id? When called without the machine-id argument, the command attempts
If successful, the command returns a token (cosh000, cosh001 etc)
which cosh000 eval script Synchronously evaluate $script in the COM
shell. If an error cosh000 release ?-lockserver? Release our reference on the interface. This
will normally cause cosh000 getptr Get the actual pointer value of the Interface.
It is returned ITclScriptDisp Dual InterfaceCOSHD.EXE implements the same function "eval" as described above, but builds on the standard IDispatch interface instead of using a custom proxy/stub DLL to do the call marshalling. If you wish to connect to a COM shell via this interface, you must instantiate an object of type COMSHELLD.COSH Activation optionsAt present COSH is hardcoded as a "Single Use" server. That means each time an interface is instantiated with a call to CoCreateInstance, a new and independent COSH is started. This precludes connecting to an already running server.In the source code there is a define (REGISTER_ACTIVE) which will activate code which enters the current instance of the COSH server in the Running Object Table (ROT). Any client which from then on requests an instance of ITclScript on that machine will receive a pointer to that running instance. One nice thing here is that COM will serialize multiple client calls so that no problems arise with the non-multithreadedness of TCL. If you need to mix those models - ie sometimes connect to a running
instance and sometimes create a new one on a single machine, then it looks
at first glance as if you are out of luck. This is because, when you
connect via COM, you are connecting to an *interface*, and that interface
has only one definition on any one machine (which you can find in the
registry under HKEY_CLASSES_ROOT\\Interface\\<Interface_ID>). I
suspect, however, that it will not be too difficult to find some kind of
workaround for that. The call to "eval" in ITclScript is synchronousOf its nature, COM only deals in synchronous calls. When you start to need asynchronous calls, callback notification, exception propagation and what have you, things start to get very hairy very quickly when you have to do things the Microsoft Way. You need to start creating interfaces which, although defined in the server, are actually implemented in the client. (Or was it theother way around?) Your client has more or less no choice than to run multithreaded, which creates a whole new dimension of hassles and snags for what are, for the most part, otherwise relatively simple server and client programms. Thus there is no provision for callback mechanisms in the ITclScript
interface - all calls are synchronous and that's all there is to it.
However, TCL would not be TCL if there were no way around that. I would like to mention in this connection a mechanism we haved used to
achieve the same end. It involves using not COM as the RPC mechanism but
the TCL-DP package. Our problem was, from a single point-of-administration
to start and monitor various servers in a distributed system, preferably
without resorting to the use of launch daemons. We use an ITclScript
client to remotely The client code looks like this: set csh [comshell 156.98.0.61];# start remote shell - return token #====== read the server bootstrap script from local file set f [open server_script.tcl r]
#======= pump server script into comshell using token from above $csh eval $script Next we tell the comshell to create a DP server socket on any available
TCP port and to return the port and ip-address needed to connect to it, to
which the client then connects. #====== start dp server in the remote COM
shell $csh eval { ;# eval this script on the remote machine
package require
dp
;# needs dp from Zeno
}
#========= retrieve server address and connect set ip [lindex $svr_addr 0]
At this point we have two different connections open to the COM shell - a DP socket and the COM connection. The two connections compliment each other in the sense that, while in DP you can only call already existing procs in the server (either synchronously or asynchronously), the ITclScript interface allows you to evaluate abitrary scripts in the server, (albeit only ynchronously). In practice, you can create the procs via ITclScript, which you then call asynchronously via DP. If you don't need to evaluate any more scripts in the server but only calls its procs, then you can release your reference on the ITclScript interface. Normally this would have the effect of terminating the server, but by calling "AddRef" on the interface you can lock it into memory. The code below does this (see the CoshClient code). #====== lock server and release our COM interface $csh release -lockserver ;# release our
reference with addref You can then do asynchonous RPCs using the dp_RDO command together with
the -callback option. DCOM is Not EasyThe thing I like most about COSH is that it keeps my COM involvement to an absolute minimum while still opening a lot of doors on to Microsoft technologies which are, after all, not *that* bad.. In doing so, I don't need to slavishly follow the Microsoft Way and learn the arcane details of the IConnectionPointContainer or IParseDisplayNames interfaces. One interface with one function is about as basic as you can get in COM - and there are good reasons to keep everything very straightforward when working with DCOM.DCOM is definitely wierd. For one thing, when you remote start a process on another machine, it has no window. It only shows up in the Task List. Programms with windows, such as TK, still function OK though. I don't know why this is or if you can (or should want to) do anything about t. If you start it on the local machine though, it has a window, but only if you specify the UNC name of the computer or no name at all - if it is started locally via its ip address, it again has no window. I have experienced other wierdnesses too. For instance, I have two machines networked. I install COSH server and client on both. Client A with Server B works fine, but not Server A with Client B. (I suspect "Service Pack" discrepancies). Or, a Dispatch interface which works perfectly well on the local machine will give strange error messages when called remotely ("Stub received corrupt data" and the like). And they told me DCOM was just COM "with a long wire" - don't you believe it! Sometimes COM seems to hang for about 5-10 seconds or so before completing a remote function call - again, don't know why. As a COM server, COSH participates in the NT Security schemes. This may
cause unexpected hiccups during development - it certainly did for me.
Beware of security concerns, permissions and the like The code in COSH has been hacked from different sources - maybe there are bugs, although everything seems straightforward enough. Maybe there are just kinks in my network setup - it is not very homogeneous. Maybe everything will be fine in NT 5.0. Who knows? DCOM can be a real pain. If you experience DCOM problems - well I am as much in the dark as you. First test locally, then remotely. You can connect to a running
instance of COSH with a debugger. I recommend "Professional DCOM programming" by Richard Grimes (Wrox
Press) as a DCOM-manual and a good introduction to COM in general.
What's to do?
DLLs into services like Microsoft Transaction Server. Still have to figure out a good way of getting the bootstrap scripts evaluated in the DLL at load time.
enable both IDispatch and vtable bindings. However there always seemed to be a gotcha where that wouldn't work and in the end I gave up and made two separate versions. For instance, the WIN32 API "LoadRegTypeLib" insists on overwriting registry entries which effectively disables vtable binding. This causes at minimum a performance hit and at worst hard-to-debug crashes.
behavoir - Safe interp or not. Enter the instance in the Running Object Table etc. Also specify security attributes.
to file at the remote end. This could be the basis of a mechanism which can remotely install DLLs and scripts on the remote host if they're not there already.
are security conscious. Still, as a COM server COSH is covered by the "NT security blanket" - whatever that means - and TCL itself offers some handles for implementing security policies through safe interpreters. Also TCL-DP from Zeno, which we use extensively in connection with COSH, offers further ways of limiting the scope for mischief. If anybody could offer advice about which mix of security mechanisms offer the best coverage, I would be pleased to hear about it.
Good Luck!--o David Shepherd o Independent Software Author o T/F (+49) 7557-91015 o mailto:dshepherd@t-online.de o http://home.t-online.de/home/dshepherd |