0
votes

Im trying to embedded Tcl interpreter to C# GUI application, and everything works fine, even AttachingNewFunction to TclCommand. But one thing is to hard for me, I want to redirect stdout, stdin, stderr to some TextBox'es. Im working now with C++, becouse its easier to debug and compile. so i using code

Tcl_Channel StdOut = Tcl_GetStdChannel(TCL_STDOUT);
Tcl_UnregisterChannel(interp,StdOut);
Tcl_Channel myStdOut = Tcl_CreateChannel(typePtr, "stdout",
    NULL, TCL_READABLE | TCL_WRITABLE);


Tcl_RegisterChannel(interp, myStdOut);
Tcl_SetStdChannel(myStdOut, TCL_STDOUT);

to register new stdout, typePtr look like

typePtr->typeName = "stdout";
typePtr->version = TCL_CHANNEL_VERSION_2;
typePtr->getHandleProc = Tcl_MyDriverGetHandleProc;
typePtr->inputProc = Tcl_MyDriverInputProc;
typePtr->outputProc = Tcl_MyDriverOutputProc;
typePtr->flushProc = Tcl_MyDriverFlushProc;
typePtr->watchProc = Tcl_MyDriverWatchProc;
typePtr->closeProc = Tcl_MyDriverCloseProc;
typePtr->blockModeProc = Tcl_MyDriverBlockModeProc;
typePtr->seekProc = NULL;
typePtr->close2Proc = NULL;
typePtr->handlerProc = NULL;
typePtr->wideSeekProc = NULL;
typePtr->truncateProc = NULL;
typePtr->setOptionProc = NULL;
typePtr->getOptionProc = NULL;
typePtr->threadActionProc = NULL;

and every function which i connect return TCL_OK or EINVAL (i know it from API) and puts some text to file, example

int Tcl_MyDriverCloseProc(ClientData instanceData,
    Tcl_Interp *interp) {
    std::cout << "\n Tcl_MyDriverCloseProc\n";
    file << "\n Tcl_MyDriverCloseProc\n";
    file.flush();
    return EINVAL;
}

i also use std::cout to debugging, but i dont believe him. When i compile&run nothing happen, stdout doesnt work, the result is for example

result:stderr file8adcd0 stdout stdin:
result::

the code what i compiled is

Tcl_GetChannelNames(interp);
std::cout << "result:" << Tcl_GetStringResult(interp) << ":\n";

Tcl_Eval(interp, "puts SomeOneHelp");
std::cout << "result:" << Tcl_GetStringResult(interp) << ":\n";

I also cannot create custom channel and used it like

"puts myChannel pleHdeeNI"

when i done with C++ im going to make function in C# which will be writing 3 TCL standart channels into TextBox'es, but its easy.

1
I suggest implementing the channels in Tcl first. There is even example code out there. (And if you don't mind using Tk's text widget, try tcl::chan::textwindow) - Johannes Kuhn
You probably want to make that channel type declaration be a static struct; Tcl assumes that it is. (It's just like a vtable, except for a C API.) - Donal Fellows
static next to typePtr doesnt help. im creating typePtr like so *static Tcl_ChannelType typePtr = new Tcl_ChannelType(); - Jan3Sobieski

1 Answers

1
votes

The documentation of the low level of Tcl channels isn't the easiest, so it is probably instructive to look at example code. generic/tkConsole.c in Tk's implementation shows how the real stdout and stderr redirections are done. In particular, the fields that need non-NULL values are the name, version, closeProc (or close2Proc), inputProc, outputProc, watchProc and getHandleProc, and many of those can actually be dummies for the channels you create to handle stdout and stderr.

However, the Tk console widget doesn't support actually providing a real stdin (instead, it uses Tcl_Eval to run commands in the main interpreter) and the one it provides just claims to always be at end-of-file. It's a bit of a cop-out. Also, none of the channels are at all able to be passed to subprocesses as they don't have any representation at the level of the OS. Fixing that would require enormously more work (perhaps with anonymous pipes and worker threads and tricks to deal with the inevitable buffering issues; using something like the Expect package would do a much more complete job, though at a cost of even more complexity).

You probably want to return non-error results from things. For example, always returning 0 from your outputProc will cause great problems with the generic parts of Tcl's channel code; it assumes that this means that things are blocked and just buffers things up until it gets told that they have become unblocked. For a real swallow-everything first try, return the number of bytes written as the same as the number of bytes you were asked to write. Similarly, it is also important to make the closeProc work right; if you've no instance data to dispose of or underlying OS resources to get rid of, you can just return 0 there to indicate that everything is OK.