Posted to tcl by rar at Tue Jul 10 20:11:46 GMT 2007view pretty

        /// <summary>
        /// Dispatch is the callback registered with Tcl API for every added command.
        /// when we get a callback, we look at argv[0] to figure out what command is
        /// desired, and find the actual method in the commandHash and call that with
        /// appropriate arguments.
        /// note this matches delegate Tcl_ObjCmdProc
        /// </summary>
        /// <param name="clientData"></param>
        /// <param name="pinterp"></param>
        /// <param name="argc"></param>
        /// <param name="argv"></param>
        /// <returns></returns>
        public unsafe int Dispatch(IntPtr clientData, 
                                   IntPtr interpPtr,
                                   Int32 objc, 
                                   IntPtr objv)
        {
            // get the context
            GCHandle contextHandle = (GCHandle)clientData;
            object context = (object)contextHandle.Target;

            // get a/the wrapped interpreter
            TclInterpreter interp = TclInterpreter.GetWrappedInterp(interpPtr);

            // parse objc+objv -> args
            // note that we are marshalling from unmanaged to managed manually here,
            // since C# doesn't know how big the objv array is -- but we do, since it's
            // provided by objc.  see also comments for Tcl_ObjCmdProc delegate in TclAPI.cs
            // this should be a reasonably safe bit of unsafe code, as long as we can
            // trust objc.  this MAY blow up on 64bit, depending on how smart my use 
            // of IntPtr (vs., say, Int32) is.  
            // also note that entries in the objv arrays are pointers to TclObjs -- we 
            // simply use these pointers (i.e. the address of the TclObj) as opaque 
            // handles on which we call Tcl_GetStringFromObj.
            string[] args = new string[objc];

            for (int i = 0; i < objc; i++)
            {
                // we assume here that only strings (or string representations) 
                // will be passed; this is a valid assumption, though possibly
                // not the most efficient.  
                // question: do we need to make a private copy of this string?
                // i.e. is args[i] going to point to memory managed and mutable
                // by Tcl?
                // answer: apparently not, because the marshalling code does
                // this automatically, at least in our case of converting 8 bit
                // ansi/ascii chars to 16 bit unicode chars.  

                // at some point I was getting Invalid Access errors, i.e. there
                // would be a bad pointer getting referenced inside of Tcl_GetString.
                // after rebooting the system, I no longer see this.  I suspect this 
                // issue is related to how Tcl handles empty strings (i.e. ""), as it
                // was only empty string arguments that were causing the problem.
                // just hit it again, with: Stub setSMA ""
                // it appears to be the case that this happens after some amount of 
                // uptime, probably prior to that the bad pointers are to zeroed-out
                // memory so things work ok.  
                // this is bug 1038, which may or may not get fixed depending on whether
                // it ever is seen in real use
                IntPtr tclObjAddr = Marshal.ReadIntPtr(objv, i * sizeof(IntPtr));
                args[i] = TclAPI.Tcl_GetString(tclObjAddr);
            }

            // XXX bug 1038 XXX
            // this is a super ugly hack.  to get around the problem of crashing when
            // passing in an empty string argument, Stub is aliased to a command that
            // will substitute "" with _EMPTY_STRING_ARG_.  so here we need to look
            // through our args, and replace _EMPTY_STRING_ARG_ with ""
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i] == "_EMPTY_STRING_ARG_")
                {
                    args[i] = "";
                }
            }

            // get the actual command from the commandHash based on command
            // name from tcl
            // XXX bug 1038 hack XXX
            // if we've wrapped the command, we have to remove the .wrapped bit
            // or we won't find a matching command.  uf, hadn't thought of this
            // before...
            // need a more generalized mechanism if we're going to wrap other
            // commands.
            if (args[0] == "Stub.wrapped")
            {
                args[0] = "Stub";
            }

            TclCommand command = commandHash[args[0]] as TclCommand;

            // execute that command, cast the Tcl_Result to an int
            return ((int)command.Execute(args, context, interp));

        }