internal TclCmdDeleteProcWrapper(TclInterp interp, TclCmdDeleteProc proc, Object deletee) { _interp = interp; _proc = proc; unsafe { _callback = new Tcl_CmdDeleteProc(this.CallbackProc); } _deletee = deletee; _interp.AddKeepAlive(this); _interp.AddKeepAlive(_deletee); }
internal TclCmdDeleteProcWrapper(TclInterp interp, TclCmdDeleteProc proc, Object deletee) { _interp = interp; _proc = proc; unsafe { _callback = new Tcl_CmdDeleteProc(this.CallbackProc); } _deletee = deletee; _interp.AddKeepAlive(this); _interp.AddKeepAlive(_deletee); }
internal static extern IntPtr Tcl_CreateCommand(Tcl_Interp* interp, [MarshalAs(UnmanagedType.LPStr)]string cmdName, Tcl_CmdProc proc, IntPtr clientData, Tcl_CmdDeleteProc deleteProc);
internal static extern IntPtr Tcl_CreateCommand(Tcl_Interp *interp, [MarshalAs(UnmanagedType.LPStr)] string cmdName, Tcl_CmdProc proc, IntPtr clientData, Tcl_CmdDeleteProc deleteProc);
/////////////////////////////////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose( bool disposing ) /* throw */ { TraceOps.DebugTrace(String.Format( "Dispose: called, disposing = {0}, disposed = {1}", disposing, disposed), typeof(TclBridge).Name, TracePriority.CleanupDebug); if (!disposed) { if (!this.disposing) { // // NOTE: We are now disposing this object (prevent re-entrancy). // this.disposing = true; // // NOTE: This method should not normally throw; however, if it does // we do not want our disposing flag to be stuck set to true. // try { //if (disposing) //{ // //////////////////////////////////// // // dispose managed resources here... // //////////////////////////////////// //} ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// // // NOTE: If necessary (and possible), delete the Tcl command via the // token we saved earlier (when the Tcl command was created). // ReturnCode deleteCode = ReturnCode.Ok; Result deleteError = null; // // NOTE: If we have a valid command token then we are still hooked to // Tcl via our inbound native delegates and we must unhook // successfully or throw to prevent our internal object state // from being made inconsistent. // if (token != IntPtr.Zero) { if (interpreter != null) { ITclApi tclApi = TclApi.GetTclApi(interpreter); // // BUGFIX: We want to force deletion of this bridged command // if the force flag was specified upon creation OR // if the command is not actively being used. // deleteCode = TclWrapper.DeleteCommandFromToken( tclApi, interp, forceDelete || (objCmdProcLevels == 0), ref token, ref deleteError); } else { deleteError = "invalid interpreter"; deleteCode = ReturnCode.Error; } } // // NOTE: Did we succeed in deleting the command from Tcl, if it // was necessary? // if (!noComplain && (deleteCode != ReturnCode.Ok)) { // // NOTE: If the command deletion was necessary and it failed // for any reason, complain very loudly. // DebugOps.Complain(interpreter, deleteCode, deleteError); // // BUGFIX: Also, we must throw an exception here to prevent // the delegates from being disposed while Tcl still // refers to them (tclLoad-1.2 GC race). // throw new ScriptException(deleteCode, deleteError); } // // NOTE: If necessary, release the GCHandle that is keeping this // object alive. // if (handle.IsAllocated) { handle.Free(); } // // NOTE: We do not own these objects; therefore, we just null out // the references to them (in case we are the only thing // keeping them alive). // interpreter = null; execute = null; clientData = null; // // NOTE: Zero out our Tcl interpreter. We do not delete it because // we do not own it. // interp = IntPtr.Zero; // // NOTE: Zero out our created Tcl command token. We should not need // to call Tcl to delete the actual command because by this time // it should already have been deleted. // token = IntPtr.Zero; // // NOTE: Finally, we should be able to safely remove our references // to the Tcl callback delegates at this point because we already // deleted the Tcl command related to them. // objCmdProc = null; cmdDeleteProc = null; // // NOTE: Zero out our command nesting level. // objCmdProcLevels = 0; // // NOTE: This object is now disposed. // disposed = true; } finally { // // NOTE: We are no longer disposing this object. // this.disposing = false; } } } }
/////////////////////////////////////////////////////////////////////////////////////////////// #region Private Constructors private TclBridge( Interpreter interpreter, IExecute execute, IClientData clientData, IntPtr interp, Tcl_ObjCmdProc objCmdProc, Tcl_CmdDeleteProc cmdDeleteProc, IntPtr token, bool fromThread, bool forceDelete, bool noComplain ) { // // NOTE: Lock this object in memory until we are disposed. // handle = GCHandle.Alloc(this, GCHandleType.Normal); /* throw */ // // NOTE: This will be used to keep track of the nesting levels for // the number of calls active to our Tcl_ObjCmdProc callback. // objCmdProcLevels = 0; // // NOTE: Setup the information we need to make callbacks. // this.interpreter = interpreter; this.execute = execute; this.clientData = clientData; // // NOTE: We need the Tcl interpreter later on (during cleanup) as well. // this.interp = interp; // // NOTE: Hold on to these delegates to prevent exceptions from // being thrown when they magically "go away". // this.objCmdProc = objCmdProc; this.cmdDeleteProc = cmdDeleteProc; // // NOTE: Keep track of the created Tcl command token. // this.token = token; // // NOTE: Does this Tcl command belong to an isolated Tcl thread? // this.fromThread = fromThread; // // NOTE: Do they want to forcibly delete the Tcl command during dispose? // this.forceDelete = forceDelete; // // NOTE: Do they want to ignore errors from deleting the Tcl command // during dispose? // this.noComplain = noComplain; }