using System; using System.Text; using System.Runtime.InteropServices; using System.Collections.Generic; // Works with TCL86, [Bill Moore (WPM) 3/16/2017] // Requires: tcl86.dll and zlib1.dll from ActiveState Tcl placed in EXE directory namespace TCL { public delegate int Callback(List<string> args, out string result); public interface IShell { void AddFunction (string cmd, Callback F); int Eval (string script, out string result); } public class Shell : IShell { private IntPtr interp; private Dictionary<string, Callback> DictCmds; private TclDll.Tcl_ObjCmdProc DispatcherDelegate; public Shell() { DictCmds = new Dictionary<string, Callback>(); // Need Dispatcher Delegate for lifetime of object to avoid GC // (Apparently C# automatically handles pinning of the pointers for delegates) unsafe { DispatcherDelegate = new TclDll.Tcl_ObjCmdProc(Dispatcher); } try { interp = TclDll.Tcl_CreateInterp(); } catch(Exception x) { System.Windows.MessageBox.Show("TclDll.Tcl_CreateInterp failed 1\r\n" + x.ToString()); return; } if (interp == IntPtr.Zero) { System.Windows.MessageBox.Show("TclDll.Tcl_CreateInterp failed 2"); return; } AddFunction("hello", Cmd_MyHello); AddFunction("listfunt", Cmd_ListFunt); return; } public int Eval(string script, out string ResultString) { int ResultInt; // Fix to allow multi-line scripts using either windows or linux formatted string.. // Remove Windows '\r' from Windows NewLine of '\r\n' leaving only '\n' Linux Newline script = script.Replace("\r", ""); ResultInt = TclDll.Tcl_Eval(interp, script); ResultString = TclDll.Helper_GetStringResult(interp); return ResultInt; } public void AddFunction(string cmd, Callback F) { if (interp == null) { throw new SystemException ("ERROR: tcl_backend: interp is null\n"); } DictCmds.Add(cmd, F); TclDll.Helper_RegisterProc(interp, cmd, DispatcherDelegate); } private unsafe int Dispatcher( IntPtr clientData, IntPtr interp, int objc, TclDll.Tcl_Obj** objv) { List<string> args = TclDll.Helper_ObjvToList(objc, objv); if (args.Count == 0) { System.Windows.MessageBox.Show("EMPTY cmd dispatch"); return 0; } string cmd = args[0]; if (DictCmds.ContainsKey(cmd)) { string result_string; int result_code; var F = DictCmds[cmd]; result_code = F(args, out result_string); TclDll.Tcl_SetObjResult(interp, TclDll.Tcl_NewStringObj(result_string, -1)); return result_code; } return -1; //TclDll.TCL_OK; } public int Cmd_MyHello(List<string> args, out string result) { //System.Windows.MessageBox.Show("MyHello!\n"); result="DONE WITH RESULT"; return 0; } public int Cmd_ListFunt(List<string> args, out string result) { List<string> keys = new List<string>(DictCmds.Keys); result = ""; foreach (string key in keys) { result += key + "\r\n"; } return 0; } } // Class Shell public class TclDll { public const int TCL_OK = 0; [StructLayout(LayoutKind.Sequential)] public struct Tcl_Obj { int refCount; IntPtr bytes; int length; IntPtr typePtr; Int64 value; }; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public unsafe delegate int Tcl_ObjCmdProc( IntPtr clientData, IntPtr interp, int objc, TclDll.Tcl_Obj** argv); [DllImport(@"tcl86.dll", CallingConvention= CallingConvention.Cdecl)] public static extern IntPtr Tcl_CreateObjCommand( IntPtr interp, [In, MarshalAs (UnmanagedType.LPStr)] string cmdName, IntPtr proc, IntPtr clientData, IntPtr deleteProc ); [DllImport(@"tcl86.dll")] public unsafe static extern IntPtr Tcl_GetString(Tcl_Obj* obj); [DllImport(@"tcl86.dll")] public static extern IntPtr Tcl_CreateInterp(); [DllImport(@"tcl86.dll")] public static extern int Tcl_Eval( IntPtr interp, [In, MarshalAs (UnmanagedType.LPStr)] string script); [DllImport(@"tcl86.dll")] public static extern IntPtr Tcl_GetObjResult(IntPtr interp); [DllImport(@"tcl86.dll")] public static extern int Tcl_SetObjResult(IntPtr interp, IntPtr objPtr); [DllImport(@"tcl86.dll")] public static extern IntPtr Tcl_NewStringObj( [In, MarshalAs (UnmanagedType.LPStr)] string ObjName, int length ); public unsafe static string Helper_GetString(Tcl_Obj* obj) { IntPtr b = TclDll.Tcl_GetString(obj); string s = Marshal.PtrToStringAnsi(b); return s; } public static string Helper_GetString(IntPtr obj) { unsafe { IntPtr b = TclDll.Tcl_GetString((Tcl_Obj*)obj); string s = Marshal.PtrToStringAnsi(b); return s; } } public static string Helper_GetStringResult(IntPtr interp) { IntPtr obj = Tcl_GetObjResult(interp); if (obj == IntPtr.Zero) { return ""; } else { return Helper_GetString(obj); } } public static unsafe List<string> Helper_ObjvToList( int objc, Tcl_Obj** objv) { var args = new List<string>(); for (int i=0; i < objc; i++) { string s = Helper_GetString(objv[i]); args.Add(s); } return args; } public static unsafe void Helper_RegisterProc(IntPtr interp, string name, TclDll.Tcl_ObjCmdProc proc) { Tcl_CreateObjCommand( interp : interp, cmdName : name, proc : Marshal.GetFunctionPointerForDelegate(proc), clientData : IntPtr.Zero, deleteProc : IntPtr.Zero ); } } } // Namespace