0

I am trying to build a WPF GUI to control IXIA interface which is written in TCL (IXIA is application which control hardware), TCL version is 86.
For this purpose, I have created the following:

  1. C# project which consists of
    1. TCL_API.cs class - contains the tcl DLLs.
    2. IXIA.cs class - Contains all methods to access IXIA additianlly the evalScript method that invoke the TCL_API.cs class which is handle with the TCL commands.
  2. WPF in visual Studio 2013. With calling methods to IXIA.cs.

The problem is that one of TCL commands get an error "can not find channel named "stdout". As I found out, its probably happens because this TCL command contains puts method inside. I think puts override can solve this issue but don"t know how to do is as I don"t have entire TCL code but only the DLLs.

See the code:

  1. TclAPI.cs

    class TclAPI
    {
        [DllImport("tcl86.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Tcl_Init(IntPtr interp);
        [DllImport("tcl86.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Tcl_CreateInterp();
        [DllImport("tcl86.Dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Tcl_Eval(IntPtr interp, string skript);
        [DllImport("tcl86.Dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Tcl_GetObjResult(IntPtr interp);
        [DllImport("tcl86.Dll", CallingConvention = CallingConvention.Cdecl)]
        unsafe public static extern char* Tcl_GetStringFromObj(IntPtr tclObj, IntPtr length);
    }
    
  2. IXIA.cs constructor:

    public IXIA(string ip, string username="admin", string password = "admin") 
    {
        interp = TclAPI.Tcl_CreateInterp();
        TclAPI.Tcl_Init(interp);
        if (interp == IntPtr.Zero)
        {
            throw new SystemException("can not ini tialize Tcl interpreter");
        }
        _ip = ip;
        _userName = username;
        _password = password;
    }
    
  3. evalScript method in IXIA.cs class:

    public int evalScript(string script)
        {
            int Evalres = TclAPI.Tcl_Eval(interp, script);
            return Evalres;
        }
    
Donal Fellows
  • 120,022
  • 18
  • 134
  • 199
Julya Levin
  • 477
  • 1
  • 4
  • 7

1 Answers1

1

Since you're interfacing to a real Tcl interpreter, the problem is that the standard channels (of which stdout is a member) are not being registered. I'm not exactly sure what is wrong, but the problem is either that Tcl's incompletely initialised, or that you've built your overall application in a mode that doesn't support standard channels.

If it is the former, adding a registration for Tcl_FindExecutable (takes a single char*, which can be NULL, and returns void) and calling it once, before any other Tcl API functions will fix it. (That function is a bit mis-named; it really should be called something like Tcl_InitLibrary or something like that.)

[DllImport("tcl86.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern void Tcl_FindExecutable(string argv0);
TclAPI.Tcl_FindExecutable(null);

(See How can I run a static initializer method in C# before the Main() method? for a good way to make that call happen exactly once; this is exactly the sort of code that belongs in a static constructor.)

If the latter, you need to either build your application in a mode that ensures that there's a console available, or use Tcl_SetStdChannel to install a replacement. That's a public API, but not one designed to be easy to call from any languages other than C (or C++ in a pinch) since it involves creating a general Tcl channel instance first via Tcl_CreateChannel and that's a non-trivial API; I think you probably ought to regard it as off-limits as it requires quite a lot of knowledge of Tcl's channel model to use well.

Because of the API complexity involved, I'll not guide you through doing a new channel.


You could try replacing the puts command prior to running the problem script… but as that is used for all file and socket writing, not just writing to the console, you need to be careful. Try evaluating the script below before running the problem script:

rename puts puts_original
proc puts {args} {
    if {[llength $args] == 1} {
        set args [linsert $args 0 stdout]
    } elseif {[llength $args] == 2 && [lindex $args 0] eq "-nonewline"} {
        set args [linsert $args 1 stdout]
    }
    if {[lindex $args end-1] eq "stdout"} {
        # Doing nothing here, but might want to log somehow?
    } else {
        puts_original {*}$args
    }
}

This isn't code that I'd recommend normally — it's much better to actually fix the standard channels so that they work, and this is all rather flaky — but sometimes it is the best that can be done. Unless the code also creates sub-interpreters or threads, in which case you're really stuck.

Community
  • 1
  • 1
Donal Fellows
  • 120,022
  • 18
  • 134
  • 199
  • Thank for you answer, I have applied your former solution. It worked but only when I run it in debugging mode (click run from inside of VisualStudio), but while double clicking on application itself (the .exe file) it return the same error. Why is it so? Can it be fixed? – Julya Levin Jan 04 '17 at 10:25