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)); }