Updated 2008-11-02 12:32:16 by hae

A/AK: Deployment for Microsoft Windows is a tricky thing. There are many solutions enabling me to make a "standalone .exe file" from my Tcl application: starpacks, freewrap, prowrap, etc. But there are also the cases when any single-file solution is unacceptable.

Consider the case when an application consists of the following components:

1) [NT service] doing its job in the background

2) Administrative GUI to set up and control this service

3) Several console utilities that interact with this service.

There is a number of problems arising here if you decide to use freewrap or starpack for such an application. The first problem is with implementing NT service in a wrapped application; some wrapping techniques address this problem, but it used to be serious enough to make me write Winserv. After installing 25 Kbytes of winserv.exe somewhere in the target system, we may run any freewrapped/starpacked/starkitted application as NT service.

The second problem is different subsystems required for the product: console and GUI. Well, we may use tclkitsh.exe for console utilities and tclkit.exe for GUI - but it almost doubles the distribution size and creates a temptation to throw "wrapping" away at all: with normally installed script library and Tcl/Tk DLLs, you may create as many custom executables as you want, with different subsystems and other properties, and all these executables will be relatively small. There are some reasons, however, why it's still better for Windows application to be wrapped.

It is possible to get rid of the problems described above, as well as of some other unmentioned problems, using the "starter wrappers". I use this name to denote MS Windows executable files that do the following:

1) find a 'real' application's executable file

2) start this file with CreateProcess, passing all necessary information to the 'real' application.

3) (optionally) wait for the 'real' application to exit, and return its exit code as their own one.

Initially, I decided to use these executables to solve a particular problem with console/GUI subsystems. But it turned out that there are many good things achieved this way:

  • These executables may carry additional information for the 'real' application: there may be a starkit or a zip archive appended to the executable. The 'real' application, in this case, will behave like tclkit.exe.
  • These executables may all be generated from 2-3 templates without need to recompile or relink them; it is still possible to alter their icons (see ico for details) and their vendor/name/version information in STRINGINFO resources (see sdx for an example of doing this).
  • The end-user sees different functions of your application as different executable files, accompanied with one large 'runtime library' (which is a 'real' application indeed, but who cares). All TCLish machinery is deep inside. Do you remember that cmd.exe will run .kit files from the command line when their type is registered properly, but command.com won't? Now it doesn't matter. The starter may be started exactly the same way across all MS Windows versions.

Basically, "starters" for MS Windows do the same thing as #! magic on UNIX; as usually, what takes 2 bytes on UNIX, takes about 10K on MS Windows, but it's not too much anyway.

Here is an example of a starter (.c source): [1]. If you decide to reuse it, look into the source code to find necessary #defineS for adapting it for your own purpose.

As of console/GUI subsystem problem, to resolve it with starters the 'real application' has to be built in a special way - the standard Freewrap.exe / Tclkit.exe won't suffice. It must be a CONSOLE executable, but with Tk compiled in (or available for dynamic loading, that is possible for tclkitsh.exe). Why are the things so strange?

Someone may think that it's because of that GUI process cannot have stdin, stdout and stderr. But this is not true: though wish.exe doesn't expect to find useful standard handles (and it's very bad, but that's another story), sometimes they are there. Run "notepad.exe >logfile.txt", and... notepad.exe will have stdout (of course it doesn't output anything there). GUI-subsystem executables may have standard handles, they may inherit them from console executables, they may even inherit the console handles. They may even call SetConsoleMode() for these handles successfully; ironically, the latter is the very thing that creates a problem.

When TCL runtime detects presence of stdin/out/err handles, it tries to determine a channel type for these handles automatically; when SetConsoleMode succeeds on a handle, TCL concludes that it's a console (right) and creates a channel of the special type to do console I/O. The bad news is that TCL does console I/O with ReadConsole and WriteConsole, and that is the thing that a GUI application cannot do.

So if the GUI executable inherits console handles from a console "starter", these handles would be useful for any C program that doesn't do anything console-specific, but not for a TCL one: TCL won't use the handles as a standard file channels (SetConsoleMode() succeeds), but they also cannot be used with ReadConsole/WriteConsole.

Conclusion: if both console and gui starters will be created for an executable, the executable itself must be a console one. GUI starter should pass DETACHED_PROCESS flag to CreateProcess, so the console won't be allocated (actually, console executables started with DETACHED_PROCESS behave like GUI executables; there is no 'hidden console', the console doesn't exist at all). Console starter doesn't need to do anything special: the console is inherited automatically, without a single line of C code taking care on this. ---