Register file types under Windows

Purpose: Demonstrate how to register file types under Windows


This don't work in Windows XP: unable to open key: Invalid access to memory location.

This will be because you need local administrator privileges to edit the HKCR section of the registry. The same thing will be true for all the NT derived versions of Windows (NT, Win2K, XP) - PT


Why register a file type?

Many Tcl-based systems deal with particular file types. Often, these files are Tcl scripts or other text that can be readily decoded from Tcl. Other times, they are more specialized formats.

The Tk dialogs, tk_getOpenFile and tk_getSaveFile give a convenient way for applications to open and save files. Windows users, however, expect more: they generally expect to be able to open a file by double-clicking on it in the Explorer, to paste it as an enclosure to e-mail, or even to create a new file of a given type by using 'File->New->...' in the Explorer menu. They also expect to see appropriate icons for the files in the Explorer.

All of these features are controlled by entries in the Windows system registry, so a Tcl program using the 'registry' package can set them up easily.

Let's define a Tcl procedure, registerFileType, that does the necessary work.

What do we need to know?

  1. We need the file extension (for example, Tcl scripts use .tcl) for the proposed new file type.
  2. We need a machine-readable 'class name' for the file extension.
  3. We need a human-readable description of the file type (for display when the user selects 'View->Details' in the Explorer.
  4. We need to know how to open the file. For this discussion, let's assume that we open a file by launching a particular Tcl script and passing the file name as its sole parameter:
    exec wish83.exe someScript.tcl someFile.xyz

Example: Let's define a file type, .acd, that's an 'address card file.' We'll use a class name of AddrCard and use CardFile.tcl to open it:

    registerFileType .acd AddrCard "Address Card File" 
        [file join $scriptDir CardFile.tcl]

What else may we want to specify?

The above information is the minimal set needed to make the double-click work. In addition to this basic information, the programmer may want to provide:

  1. An icon, so that the file displays nicely in the Explorer.
  2. A MIME type, so that the file can be enclosed in e-mail.
  3. An indication that new instances of this file type can be created from the 'File->New' menu in the Explorer.
  4. An indication that the file is plain text 'under the hood.' If the file is plain text, registerFileType can add information that provides Edit and Print menus when the user right-clicks the mouse from the Explorer; the Edit menu will launch Notepad (or whatever editor the user likes for text files), and the Print menu will print the text file through 'notepad /P' or whatever other method the user uses.

We specify these by using options after the four mandatory parameters to registerFileType:

  • -icon pathName,n - Use the icon at ordinal position n within the file identified by pathName, which may be an executable, DLL, ICO, or icon library.
  • -mimetype type - Use type (e.g., application/x-addr-card-file) as the MIME type for the files.
  • -new boolean - Control whether or not the file should appear in the Explorer's 'File->New' menu.
  • -text boolean - Indicate whether or not the file contains plain text.

Example: Expanding the definition of registering an address card file, we provide an icon file, a MIME type, and turn on the indication that the file is text and that it should appear in the Explorer's 'New' menu:

    RegisterFileType::RegisterFileType 
            .acd 
            "AddrCardFile"              "Address Card File" 
            [file join $scriptDir CardFile.tcl] 
            -mimetype application/x-cardfile 
            -icon [file join $scriptDir cardfile.ico],0 
            -new 1 
            -text 1

Without further ado, here's the source for RegisterFileType:


 #----------------------------------------------------------------------
 #
 # RegisterFileType::RegisterFileType --
 #
 #       Register a file type on Windows
 #
 # Author:
 #       Kevin Kenny <[email protected]>.
 #       Last revised: 27 Nov 2000, 22:35 UTC
 #
 # Parameters:
 #       extension -- Extension (e.g., .tcl) of the new type
 #                    being registered.
 #       className -- Class name (e.g., "tclfile") of the new type
 #       textName  -- Textual name (e.g. "Tcl Script") of the
 #                    new type.
 #       script    -- Name of the file containing a Tcl script
 #                    to run when a file of the given type is
 #                    opened.  The script will receive the name
 #                    of the file in [lindex $argv 0].
 #
 # Options:
 #       -icon FILENAME,NUMBER
 #               Set the icon for files of the new type
 #               to be the NUMBER'th icon in the given file.
 #               The file must be a full path name.
 #       -mimetype TYPE
 #               Set the MIME type corresponding to the new
 #               file type to the specified string.
 #       -new BOOLEAN
 #               If BOOLEAN is true, set things up so that
 #               the new file type appears in the "New" menu
 #               in the Explorer and the system tray.
 #       -text BOOLEAN
 #               If BOOLEAN is true, the new file type contains
 #               plain ASCII text of some sort.  Set the
 #               Edit and Print actions to open and print
 #               ASCII files.
 #
 # Results:
 #       None.
 #
 # Side effects:
 #       Adds the following keys to the system registry:
 #
 #       HKEY_CLASSES_ROOT
 #         (Extension)           (Default value)         ClassName
 #                               "Content Type"          MimeType        [1]
 #           ShellNew            "NullFile"              ""              [2]
 #         (ClassName)           (Default value)         TextName
 #           DefaultIcon         (Default value)         IconName,#      [3]
 #           Shell
 #             Open
 #               command         (Default value)         -SEE BELOW-
 #             Edit
 #               command         (Default value)         -SEE BELOW-     [4]
 #             Print
 #               command         (Default value)         -SEE BELOW-     [4]
 #         MIME
 #           Database
 #             Content Type
 #               (MimeType)      (Default value)         Extension       [1]
 #
 #       [1] These values are added only if the -mimetype option is used.
 #       [2] This value is added only if the -new option is true.
 #       [3] This value is added only if the -icon option is used.
 #       [4] These values are added only if the -text option is true.
 #
 #       The command to open the file consists of three arguments.
 #       The first is the name of the current Tcl executable.  The
 #       second is the script name, and the third is "%1", which causes
 #       the target file to be passed as a command-line argument.
 #       The edit command is the command that opens text files, and the
 #       print command is the command that prints text files.
 #
 #----------------------------------------------------------------------

 proc RegisterFileType::RegisterFileType {
     extension className textName script args
 } {

     package require registry

     # extPath is the class path for the file's extension

     set extPath HKEY_CLASSES_ROOT\$extension
     registry set $extPath {} $className sz

     # classPath is the class path for the file's class

     set classPath HKEY_CLASSES_ROOT\$className
     registry set $classPath {} $textName sz

     # shellPath is the shell key within classPath

     set shellPath $classPath\Shell

     # Set up the 'Open' action

     set openCommand {}
     append openCommand " 
             [file nativename [info nameofexecutable]] 
             " { } " [file nativename $script] " { } "%1"
     registry set $shellPath\open\command {} $openCommand sz

     # Process optional args

     foreach {key val} $args {
         switch -exact -- $key {

             -mimetype {

                 # Set up the handler for the MIME content type,
                 # and add the content type item to the database

                 registry set $extPath "Content Type" $val sz
                 set mimeDbPath 
                         "HKEY_CLASSES_ROOT\MIME\Database"
                 append mimeDbPath "\Content Type\" $val
                 registry set $mimeDbPath Extension 
                         $extension sz
             }

             -icon {

                 # Add the file icon to the shell database

                 if {![regexp {^(.*),([^,]*)} $val 
                         junk file icon]} {
                     error "-icon option requires
                             fileName,iconNumber"
                 }
                 registry set $classPath\DefaultIcon {} 
                         [file nativename $file],$icon sz
             }

             -text {
                 if {$val} {

                     # Copy the Print action for text files
                     # into the Print action for the new type

                     set textPath 
                             HKEY_CLASSES_ROOT\txtfile\Shell
                     if {![catch {
                         registry get 
                                 $textPath\print\command {}
                     } pCmd]} {
                         registry set 
                                 $shellPath\print\command 
                                 {} $pCmd sz
                         registry set 
                                 $shellPath\print {} 
                                 &Print sz

                     }

                     # Copy the Open action for text files
                     # into the Edit action for the new type.

                     if {![catch {
                         registry get 
                                 $textPath\open\command {}
                     } eCmd]} {
                         registry set 
                                 $shellPath\edit\command 
                                 {} $eCmd sz
                         registry set 
                                 $shellPath\edit {} 
                                 &Edit sz
                     }
                 }
             }

             -new {
                 if {$val} {

                     # Add the 'NullFile' action to the
                     # shell's New menu

                     registry set $extPath\ShellNew NullFile 
                             {} sz
                 }
             }

             default {
                 error "unknown option $key, must be -icon,
                         -mimetype, -new or -text"
             }
         }
     }
 }

Nat Pryce provides an alternative perspective on many of the same issues in his explanation of "Integrating Tcl with the NT Shell" [1 ]. Also, one benefit of the ActiveTcl releases is to manage some associations (.tcl).

BR - Actually for me personally this is a downside of the ActiveTcl distros. As a developer I want the default association for Tcl files to be my editor, not wish. I have quite a lot more Tcl command-line scripts than Tk programs. For the few GUI programs I can always create the appropriate shortcuts individually.


It is also useful to add right mouse button context menus as well as supporting dde. Dde can be used to call an active Tcl application when you launch a file rather than continue to bring up new instances of the TCL application. An example of how to register a context menu handler and dde is shown in regContextHandler. TFW


It is also worth remembering that at least for Windows 2000 and XP there are two commandline programs ASSOC and FTYPE that deal with file types. One might do

    ASSOC .tcl=TclScript
    FTYPE TclScript=wish.exe %1 %*

will make tcl script invokable from the Windows commandline by myscript.tcl arguments

glennj: you probably want some "quotes" in there to protect pathnames with spaces:

    FTYPE TclScript="%ProgramFiles%\Tcl\bin\wish.exe" "%1" %*

Googie - 26 Feb 2012 - I fixed it a little (replaced single "\" with "\\", etc) to get it working and also changed "script" to any "application" that we want to associate (my version of sources are below).

It creates registry entries as it should, but those entries don't seem to make any difference on my XP :( I think this solution is outdated, since they introduced new file association management method in Vista/7. I guess they backported it to XP with some patch. Can anyone confirm that?

The question now is - do we have any pure-Tcl method to manage Windows file associations? The method with "FTYPE" and "ASSOC" (mentioned above) doesn't work either.

beware It should be trivial to do it in pure-tcl by writing a .reg file and exec'ing it with regedit (and some cmd line switches). As I recall, .reg files need to be binary encoded.

My version of registerFileType:

#----------------------------------------------------------------------
#
# registerFileType --
#
#       Register a file type on Windows
#
# Author:
#       Kevin Kenny <[email protected]>.
#       Last revised: 27 Nov 2000, 22:35 UTC
#                Mofified by: Pawel Salawa at 26 Feb 2012
#
# Parameters:
#       extension -- Extension (e.g., .tcl) of the new type
#                    being registered.
#       className -- Class name (e.g., "tclfile") of the new type
#       textName  -- Textual name (e.g. "Tcl Script") of the
#                    new type.
#       appl      -- Name of the executable to run when a file
#                    of the given type is opened.  The executable will
#                    receive the name of the file executed.
#
# Options:
#       -icon FILENAME,NUMBER
#               Set the icon for files of the new type
#               to be the NUMBER'th icon in the given file.
#               The file must be a full path name.
#       -mimetype TYPE
#               Set the MIME type corresponding to the new
#               file type to the specified string.
#       -new BOOLEAN
#               If BOOLEAN is true, set things up so that
#               the new file type appears in the "New" menu
#               in the Explorer and the system tray.
#       -text BOOLEAN
#               If BOOLEAN is true, the new file type contains
#               plain ASCII text of some sort.  Set the
#               Edit and Print actions to open and print
#               ASCII files.
#
# Results:
#       None.
#
# Side effects:
#       Adds the following keys to the system registry:
#
#       HKEY_CLASSES_ROOT
#         (Extension)           (Default value)         ClassName
#                               "Content Type"          MimeType        [1]
#           ShellNew            "NullFile"              ""              [2]
#         (ClassName)           (Default value)         TextName
#           DefaultIcon         (Default value)         IconName,#      [3]
#           Shell
#             Open
#               command         (Default value)         -SEE BELOW-
#             Edit
#               command         (Default value)         -SEE BELOW-     [4]
#             Print
#               command         (Default value)         -SEE BELOW-     [4]
#         MIME
#           Database
#             Content Type
#               (MimeType)      (Default value)         Extension       [1]
#
#       [1] These values are added only if the -mimetype option is used.
#       [2] This value is added only if the -new option is true.
#       [3] This value is added only if the -icon option is used.
#       [4] These values are added only if the -text option is true.
#
#       The command to open the file consists of three arguments.
#       The first is the name of the current Tcl executable.  The
#       second is the script name, and the third is "%1", which causes
#       the target file to be passed as a command-line argument.
#       The edit command is the command that opens text files, and the
#       print command is the command that prints text files.
#
#----------------------------------------------------------------------

proc registerFileType {extension className textName appl args} {

    package require registry

    # extPath is the class path for the file's extension

    set extPath HKEY_CLASSES_ROOT\\$extension
    registry set $extPath {} $className sz

    # classPath is the class path for the file's class

    set classPath HKEY_CLASSES_ROOT\\$className
    registry set $classPath {} $textName sz

    # shellPath is the shell key within classPath

    set shellPath $classPath\\Shell

    # Set up the 'Open' action

    set openCommand "[file nativename $appl] %1"
    registry set $shellPath\\open\\command {} $openCommand sz

    # Process optional args

    foreach {key val} $args {
                switch -exact -- $key {
        
                     -mimetype {
        
                                 # Set up the handler for the MIME content type,
                                 # and add the content type item to the database
                
                                 registry set $extPath "Content Type" $val sz
                                 set mimeDbPath "HKEY_CLASSES_ROOT\\MIME\\Database"
                                 append mimeDbPath "\\Content Type\\" $val
                                 registry set $mimeDbPath Extension $extension sz
                     }
        
                     -icon {
        
                                 # Add the file icon to the shell database
                
                                 if {![regexp {^(.*),([^,]*)} $val junk file icon]} {
                                     error "-icon option requires fileName,iconNumber"
                                 }
                                 registry set $classPath\\DefaultIcon {} [file nativename $file],$icon sz
                     }
        
                     -text {
                                 if {$val} {
                
                                     # Copy the Print action for text files
                                     # into the Print action for the new type
                
                                     set textPath HKEY_CLASSES_ROOT\\txtfile\\Shell
                                     if {![catch {
                                                 registry get $textPath\\print\\command {}
                                     } pCmd]} {
                                                 registry set $shellPath\\print\\command {} $pCmd sz
                                                 registry set $shellPath\\print {} &Print sz
                                     }
                
                                     # Copy the Open action for text files
                                     # into the Edit action for the new type.
                
                                     if {![catch {
                                                 registry get $textPath\\open\\command {}
                                     } eCmd]} {
                                                 registry set $shellPath\\edit\\command {} $eCmd sz
                                                 registry set $shellPath\\edit {} &Edit sz
                                     }
                                 }
                     }
        
                     -new {
                                 if {$val} {
                
                                     # Add the 'NullFile' action to the
                                     # shell's New menu
                
                                     registry set $extPath\\ShellNew NullFile {} sz
                                 }
                     }
        
                     default {
                         error "unknown option $key, must be -icon, -mimetype, -new or -text"
                     }
                }
    }
}