Version 38 of winutils

Updated 2003-12-15 15:57:14

What is winutils?

winutils is a collection of useful Tcl commands that access some part of the Win32 API. This enables the user to use Windows specific services.

Who wrote it? David Gravereaux

Example :

David Gravereaux mailto:[email protected] recently wrote in news:comp.lang.tcl

 Message-ID: <[email protected]>

 You can simplify that down a whole lot with my winutils extension and it's
 winutils::shell command.  It's like `eval exec [auto_execok start] $file &`, but
 without the momentary command prompt flashing up on the screen.

 package require winutils
 proc LaunchEditor {myfile} {
    # try the "edit" command first.
    if {[catch {winutils::shell -v edit $myfile}]} {
        winutils::shell -v open $myfile
 proc Launch {args} {
    eval winutils::shell -v open $args

This makes use of associations and even does full URLs in the exact manner and behavior of the "Run..." dialog off the Start menu.

 % Launch mailto:[email protected]?subject=hi%20there&body=was%20up?
 % Launch
 % Launch somefile.txt
 % Launch netstat -a 2

I forget if I added the "edit" command to my .tcl association or whether the ActiveTcl installer did it for me..

 % LaunchEditor c:/progra~1/tcl/lib/tcl8.4/init.tcl

Pat Thoyts has suggested the GetLocaleInfo() API [L1 ] join winutils.

What other pieces of win32 make a nice fit here?

  • The special folders stuff - SHGetFolderPath() for retrieving the official path to application data user profile etc. This can be important for NT/XP systems where we may have reduced rights to write files elsewhere.

As the "directory watching" ability of winutils appears to undocumented, here is some info straight from David:

  set w [winutils::dirwatch new c:/somedir { puts gotcha! }]

close the watch with:

  winutils::dirwatch delete $w

or if I left the incr Tcl class for it, it would be:

  set w [winutils::dirwatch #auto c:/somedir { puts gotcha! }]
  $w delete

When a file is modified or created in the directory, the script fires. It saves the script as a persistent Tcl_Obj*, so the bytecode rep is maintained. It runs essentially as a proc in that any temp variables are cleared. Use global to link to any globals. I run it also from the scope of the ::winutils namespace, which might not be useful.

I can fire multiple times for the same event. I think because of the flags used with FindFirstChangeNotification(). Some experiments with changing the flags could improve it.

Scott Nichols I recommend adding this C++ code to the WinUtils library for creating unique global identifiers (GUIDs). I had a customer that required a GUID be generated with each SOAP transaction so I wrote the Tcl method call in C++ (below) for this, and thought it might be a good candidate for the WinUtils library. The C++ code below is using two dependent libs: tclstub83.lib and rpcrt4.lib. Rpcrt4.lib is part of the Windows OS, and should come with Visual Studio or .net. The source code is so short that I went ahead and posted both C++ source files below:


 * TclGUID.h v1.0 12-11-2003 Scott Nichols

 /* TCL Function prototype declarations */
 #ifndef TclGUID_H
 #define TclGUID_H

 #define USE_NON_CONST
 #define TCL_USE_STUBS

 /* Not sure which includes are really needed. I guessed. */
 #include <string.h>
 #include <tcl.h>
 #include <iostream>
 #include <afx.h>
 #include "StdAfx.h"

 extern "C"
    __declspec(dllexport) int Tclguid_Init(Tcl_Interp* interp);


 static int        GetGUID_ObjCmd _ANSI_ARGS_((ClientData clientData,
                    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));



 * TclGUID.cpp, v1.0 12/11/2003, Scott J. Nichols
 * [email protected]
 * The GetGUID Tcl method returns a 128 bit unique identifier
 * to the Tcl interpreter. Microsoft calls it a globally unique identifier (GUID).
 * The application I am using for is the transaction ID for SOAP messages. This was a customer
 * requirment of mine.

 #include "TclGUID.h"

 static int        GetGUID_ObjCmd _ANSI_ARGS_((ClientData clientData,
                    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]))

        GUID guid;

        // create random GUID
        guid = GUID_NULL;

        if (guid == GUID_NULL)
                Tcl_Obj                *obj_result = Tcl_NewStringObj((const char *)"Unable to create GUID.",
                                strlen((const char *) "Unable to create GUID."));

                return TCL_ERROR;

                BYTE * str;
                UuidToString((UUID*)&guid, &str);

                CString unique((LPTSTR)str);



                // Return the GUID to the Tcl Interpreter
                Tcl_Obj        *obj_result = Tcl_NewStringObj((const char *)unique,
                                                       strlen((const char *)unique));
                return TCL_OK;


 /* Main Routine in the TCL Extension DLL */
 int Tclguid_Init(Tcl_Interp *interp)

        #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1
                // Does the TCL interpreter support version 8.3 of TCL?
                if (Tcl_InitStubs(interp,"8.3",0) == NULL)
                        return TCL_ERROR;

        Tcl_CreateObjCommand(interp, "GetGUID", GetGUID_ObjCmd,
                      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

        return (Tcl_PkgProvide(interp,"TclGUID","1.0") == TCL_ERROR ? TCL_ERROR : TCL_OK);

Each time the GetGUID method is called from Tcl a different 128 bit number is returned that looks similar to this: DE4ED408-5200-46E5-8AD1-EEF7351A7C07

[ Category Package ]