/////////////////////////////////////////////////////////////////////////////////////////////// // // -- CmdDeleteProc -- // // DeleteProc will be invoked when (if) name is deleted. This can occur through a // call to Tcl_DeleteCommand, Tcl_DeleteCommandFromToken, or Tcl_DeleteInterp, or // by replacing name in another call to Tcl_CreateObjCommand. DeleteProc is // invoked before the command is deleted, and gives the application an // opportunity to release any structures associated with the command. // private static void CmdDeleteProc( IntPtr clientData ) { // // NOTE: We need to kill the associated TclBridge object // instance and remove any references to the bridge // from the containing interpreter. // try { // // NOTE: Rehydrate the handle from the clientData that Tcl just // passed us. // GCHandle handle = GCHandle.FromIntPtr(clientData); /* throw */ // // NOTE: Make sure the handle has a valid target. // if (handle.IsAllocated && (handle.Target != null)) { // // NOTE: Attempt to cast the handle to a TclBridge object; if this // fails, we cannot continue to handle this call. // TclBridge tclBridge = handle.Target as TclBridge; if (tclBridge != null) { // // NOTE: Skip messing with the TclBridge or interpreter object if it is // already being disposed (i.e. we are NOT being called directly // due to the command being removed from the Tcl interpreter or // the Tcl interpreter being deleted). The caller of the dispose // method will handle removing the TclBridge object from the // collection in the interpreter. // if (!tclBridge.disposing) { // // NOTE: Grab the associated interpreter from the TclBridge // object. // Interpreter interpreter = tclBridge.interpreter; // // NOTE: Remove all instances of the TclBridge object from its // interpreter. // if (interpreter != null) { /* IGNORED */ interpreter.RemoveTclBridges(tclBridge); } // // NOTE: Prevent the Dispose method from trying to delete the Tcl // command itself (since it is already being deleted). // tclBridge.token = IntPtr.Zero; // // NOTE: Cleanup all the resources used by this TclBridge object. // In theory, this object disposal can throw; however, in // practice we know that it does not attempt to do anything // that can actually "fail" unless it has a valid command // token, which we have already cleared (Tcl has notified us, // by calling this delegate, that it has already deleted the // command in question). // tclBridge.Dispose(); /* throw */ } } else { TraceOps.DebugTrace( "invalid Tcl bridge object", typeof(Tcl_CmdDeleteProc).Name, TracePriority.MarshalError); } } else { TraceOps.DebugTrace( "invalid GC handle", typeof(Tcl_CmdDeleteProc).Name, TracePriority.MarshalError); } } catch (Exception e) { // // NOTE: Nothing we can do here except log the failure. // TraceOps.DebugTrace( e, typeof(Tcl_CmdDeleteProc).Name, TracePriority.NativeError); } }
/////////////////////////////////////////////////////////////////////////////////////////////// #region Static "Factory" Members public static TclBridge Create( Interpreter interpreter, IExecute execute, IClientData clientData, IntPtr interp, string name, bool fromThread, bool forceDelete, bool noComplain, ref Result error ) { // // NOTE: Create and return a TclBridge object that creates and // associates a named Tcl command with the specified Eagle // command. // // The marshalling of the command arguments and the result // will be handled by this class (via the ObjCmdProc wrapper). // // Tcl command lifetime management will also be handled by // this class (via the CmdDeleteProc). // // Eagle command lifetime management will also be handled by // this class. The Tcl command will be deleted if the Eagle // command is deleted. // if (interpreter != null) { ITclApi tclApi = TclApi.GetTclApi(interpreter); if (TclApi.CheckModule(tclApi, ref error)) { if (tclApi.CheckInterp(interp, ref error)) { if (execute != null) { // // NOTE: *WARNING* Empty Tcl command/procedure names are allowed, // please do not change this to "!String.IsNullOrEmpty". // if (name != null) { // // NOTE: Create a TclBridge object to handle the command // callbacks from Tcl. // ReturnCode code = ReturnCode.Ok; TclBridge result = null; try { result = new TclBridge( interpreter, execute, clientData, interp, new Tcl_ObjCmdProc(ObjCmdProc), new Tcl_CmdDeleteProc(CmdDeleteProc), IntPtr.Zero, fromThread, forceDelete, noComplain); // // NOTE: Create the Tcl command that calls into the ObjCmdProc // callback TclBridge dispatcher methods and save the // created Tcl command token for later deletion. // code = TclWrapper.CreateCommand( tclApi, interp, name, result.objCmdProc, GCHandle.ToIntPtr(result.handle), result.cmdDeleteProc, ref result.token, ref error); if (code == ReturnCode.Ok) { return(result); } } catch (Exception e) { error = e; code = ReturnCode.Error; } finally { if ((code != ReturnCode.Ok) && (result != null)) { // // NOTE: Dispose and clear the partially created TclBridge // object because the Tcl command creation failed. // This can throw an exception if the command token // is valid and we cannot manage to delete it; however, // since Tcl command creation is the very last step // above, this corner case should be rare. // result.Dispose(); /* throw */ result = null; } } } else { error = "invalid command name"; } } else { error = "invalid command target"; } } } } else { error = "invalid interpreter"; } return(null); }