Use Windows default mail client

Windows XP has a clear notion of a default email client: it's shown at the top of the Start menu for example. You can use this to compose emails for the user to send, without knowing any of the details of the user's SMTP server configuration. The idea is to create a mailto: URL, and to hand the URL to the system to open. The user will see his or her familiar composition window appear. The rest is out of your hands: it is up to the user if he or she wishes to edit the message further and to send it.

  #-- Generate email
  set address "[email protected]"
  set subject "Use Windows default mail client"
  set body "Hello,\n\n"
  append body "Please think kindly of me"
  append body " for this small tip.\n\n"
  append body "Good luck!"

  #-- Create using default client
  eval exec [auto_execok start] mailto:$address?subject=[urlEncode $subject]^&body=[urlEncode $body]

There are a few points to note:

1. The procedure to encode the URL is almost lifted from the ncgi package in Tcllib but is modified so as to replace spaces with "%20", not with "+". KPV You can simplify this with string map {+ %20} ::ncgi::encode $str.

 proc urlEncode {string} {
  # 1 leave alphanumerics characters alone
  # 2 Convert every other character to an array lookup
  # 3 Escape constructs that are "special" to the tcl parser
  # 4 "subst" the result, doing all the array substitutions

  for {set i 1} {$i <= 256} {incr i} {
    set c [format %c $i]
    if {![string match \[a-zA-Z0-9\] $c]} {
      set map($c) %[format %.2X $i]

  # These are handled specially
  array set map {
    " " %20   \n %0D%0A

  regsub -all -- \[^a-zA-Z0-9\] $string {$map(&)} string
  # This quotes cases like $map([) or $map($) => $map(\[) ...
  regsub -all -- {[][{})\\]\)} $string {\\&} string
  return [subst -nocommand $string]

2. You can add "cc" and (usually) "bcc" recipients to the URL, in the part after the "?", separated by "&" from one another.

LV Are you saying this is what it should look like?

set cc "[email protected]"
set bcc ""

eval exec [auto_execok start] mailto:$address?$cc&$bcc&subject=[urlEncode $subject]^&body=[urlEncode $body]

Or do I need to somehow specify that the values in cc and bcc are the Cc and the Bcc?

Also, can address, cc, and bcc contain more than one address? If so, what would the delimiter be?

Finally, using the original code, I am having a problem. I changed the setting of body to open a file for reading, then doing a read:

set fd [open [file join $::env(HOME) Desktop "exceptionsRpt.txt"] "r"]
set body [read $fd]

When I then do the eval exec, I first see an error dialog that has, as its title, the mailto string. The contents of the error dialog says

Windows cannot access the specified device, path, or file. You may not have the appropriate permissions to access the item.

and then a stack trace that says:

Access is denied.
    while executing
"exec C:/WINDOWS/system32/cmd.exe /c start mailto:[email protected]?subject=exceptionRpt^&body=Total%20lines%20in%20eq%2Ecsv%20is%3A%202118%0D%0AErrors%0..."
    ("eval" body line 1)
    invoked from within
"eval exec [auto_execok start] mailto:$address?subject=[urlEncode $subject]^&body=[urlEncode $body]"
    (file "../bin/sendmsg.tcl" line 35)

The weird thing is that if I start tclsh.exe up as an interactive shell, then type in

eval exec [auto_execok start] mailto:[email protected]?subject=Ha^&body=Nothing

and hit return, Outlook opens a new message box with that subject and body. So I have permission to invoke the mailto. The body text is quoted. I'm just uncertain what is going wrong here.


3. On the subject of the "&", note that the Windows XP command language requires it be escaped using the "^" character. KPV I use 4nt instead of cmd and, alas, it has a different escape mechanism.

4. Also in Tcllib is the uri package. This contains tools nearly to construct the URL, but for the mailto: type, it is only familiar with the host and user parts, and not cc, bcc, subject and body.

Alastair Davies - 17 Jan 2006

KPV 2008-06-20 : I needed to send email but with an attachment. Looking at the rfc for the mailto schema [L1 ] there's no way to do it with some mailto trick. If outlook is your default mail client you can use outlook's command line switches to do it (for more info, search outlook's help for command-line switches):

package require registry
set cmd [registry get HKEY_CLASSES_ROOT\\mailto\\shell\\open\\command {}]
set n [regexp {^"(.*?)"} $cmd => cmd] ;# Extract command w/o switches
if {! $n} {
    regsub {\s[-/].*$} $cmd {} cmd
exec $cmd /a $attach_filename &