Version 49 of COM on! - a tiny web browser

Updated 2007-06-11 14:29:02 by LV

Richard Suchenwirth 2002-09-18 - I've learned and preached that Tcl/Tk can be used to glue software components together, but how strong this is I experienced only last night. Steve Offutt hinted me in the Tcl chatroom at optcl, an extension that allows exploring and using Windows COM modules, and mailed me a little 2 KB script that implements a web Browser (by embedding part of Internet Explorer via optcl), animated GIFs as well as directory browsing supported, and even Acrobat Reader gets embedded in turn if you point it to a PDF document.

WikiDbImage comon.jpg

I played with it at home, adjusted and simplified it somehow, and noticed that the whole browser, capable of navigating the WWW as well as your local file system, can fit in less than 1 KB (if you exempt these explanations, and assuming you're on Windows >= 95 and accept the GPL), while allowing you to style it at your heart's delight, just as we know it from Tcl. (Only printing is not supported, it is now ~ MPJ, but we know that from Tcl too ;-) So here goes my "lean and mean" version: }

 wm title . "COM on!"
 package require optcl
 set home  c:/html/index.htm ;# local setting on my box at home
 option add *Button.pady        0
 option add *Button.borderWidth 1
 option add *Button.relief      flat
 bind Button <Enter> {+ %W config -relief raised}
 bind Button <Leave> {+ %W config -relief flat}

 set htm [optcl::new -window .htm Shell.Explorer.2]
 .htm config -width 600 -height 400

 frame  .fr
 button .fr.h -text ^   -command {catch {$htm navigate [set url $home]}}
 button .fr.o -text ... -command {$htm navigate [set url [tk_getOpenFile]]}
 button .fr.b -text <   -command {catch {$htm goBack}}
 button .fr.f -text >   -command {catch {$htm goForward}}
 button .fr.p -text P   -command {catch {$htm ExecWB "OLECMDID_PRINT" "OLECMDEXECOPT_PROMPTUSER"}}
 entry  .fr.e -textvar url -width 80
 bind   .fr.e <Return> {$htm navigate $url}
 set url $home
 eval pack [winfo children .fr] -side left -fill y

 pack .fr  -fill x
 pack .htm -fill both -expand 1
 catch {$htm navigate $url}
 bind . <Escape> {exec wish $argv0 &; exit} ;# useful in development

SO 9/19/02 - I appreciate Richard mentioning our conversation in the chatroom, and the little script I mailed him. I would be remiss, however, if I failed to point out that my little hack was based on a script provided by Michael Jacobson, on the optcl page. Also, I would love to see other examples of using optcl/tcom appear on the wiki. These are truly powerful tools for those of us who spend most of our programming time in the world of windows.

MPJ 9/19/02 - Note there is a little bug in Tk or optcl that does not allow the text box to grab the focus correctly [L1 ]. I notice that your little app has this problem too. I mention it on the optcl page with this solution (using my other favorite dll ... ffidl). UPDATE I like the way GPS solved this on his WippleWobble - A Mini Web Browser (for Windows) page using forceFocus proc.

 load ffidl05.dll
 ffidl::callout dll_SetFocus {int} int [ffidl::symbol user32.dll SetFocus]
 proc GrabFocus {args} {dll_SetFocus [winfo id .]}
 bind . <Button> +GrabFocus

We can also add the use of event bindings from the ActiveX componet to this interface. Here is a little code from NewzPoint to display the MouseOver urls in a status bar at the bottom.

 proc linkchanged {id page} {
   global link htm
   if {$page == "" || [string equal -nocase "done" $page]} {
      set link [$htm : LocationURL]
   } else {
      set link $page
 optcl::bind $htm StatusTextChange linkchanged
 label .sta -relief sunken -borderwidth 2  -anchor w -textvariable link
 pack .sta -fill x -expand 1

Another item that is a must-have for a browser is security level (icon). Here is how to implement it with optcl.

 optcl::bind $html SetSecureLockIcon security
 proc security {id secureid} {
        global securitylevel
        set securitylevel [lindex "Unsecure Mixed Unknown 40Bits 56Bits Fortezza 128Bits" $secureid]
 # now put securitylevel in a status bar

Finally, a moving icon is usually implemeted to show that the browser is navigating to a link and it stops when the link is displayed. This can be accomplished by binding to the BeforeNavigate2 and NavigateComplete2 events. Also you can always check on the status of a page by calling the Busy method (boolean return value).

  set pagestatus [$htm : Busy] ;# 1 is busy, 0 is displayed

Martin Lemburg - 20.09.2002:

I "clean" up the code, collected the suggestions and tips - here is the "new" code:

 package require optcl;

 proc forceFocus {win} {
        catch {focus -force $win} ;# MPJ change

 proc linkchanged {id page} {
     global link htm;

     if {($page == "") || [string equal -nocase "done" $page]} {
         set link [$htm : LocationURL];
     } else {
         set link $page;


 proc security {id secureid} {
     global securitylevel;

     set securitylevel [lindex {
         Unsecure Mixed
         Unknown 40Bits
         56Bits Fortezza
     } $secureid];


 proc startLoading {args} {
     global stopLoading loadingTime;

     set loadingTime 0.00;
     set stopLoading 0;

     after 250 incrLoadingTime;


 proc incrLoadingTime {} {
     global stopLoading loadingTime;

     set loadingTime [format {%.2lf} [expr {$loadingTime + 0.25}]];

     if {!$stopLoading} {
         after 250 incrLoadingTime;


 proc stopLoading {args} {
     global stopLoading;

     set stopLoading 1;


 set home;

 wm title . "COM on!";

 option add *Button.pady        0;
 option add *Button.borderWidth 1;
 option add *Button.relief      flat;

 bind Button <Enter> {+ %W config -relief raised};
 bind Button <Leave> {+ %W config -relief flat};

 frame  .fr;
     button .fr.h -text ^   -command {catch {$htm navigate [set url $home]}};
     button .fr.o -text ... -command {$htm navigate [set url [tk_getOpenFile]]};
     button .fr.b -text <   -command {catch {$htm goBack}};
     button .fr.f -text >   -command {catch {$htm goForward}};
     button .fr.p -text P   -command {catch {$htm ExecWB "OLECMDID_PRINT" "OLECMDEXECOPT_PROMPTUSER"}};
     entry  .fr.e -textvar url -width 80;
     label  .fr.l -textvar loadingTime -width 7 -relief sunken -borderwidth 2;
 eval pack [winfo children .fr] -side left -fill none -expand 0;
 pack .fr.e -side left  -fill x    -expand 1;
 pack .fr.l -side right -fill none -expand 0;

 set htm [optcl::new -window .htm Shell.Explorer.2];
 .htm config -width 600 -height 400;

 frame .sta
     label -relief sunken -borderwidth 2  -anchor w -textvariable link;
     label .sta.secu -relief sunken -borderwidth 2  -anchor w -textvariable securitylevel -width 10;
 pack -side left  -fill x    -expand 1;
 pack .sta.secu -side right -fill none -expand 0;

 set url    $home;

 pack .fr  -side top    -fill x    -expand 0;
 pack .htm -side top    -fill both -expand 1;
 pack .sta -side bottom -fill x    -expand 0;

 bind .fr.e <Return> {$htm navigate $url};
 bind . <Escape> {exec wish $argv0 &; exit} ;# useful in development
 bind all <Enter> {forceFocus %W} ;# MPJ change
 bind all <ButtonPress-1> {forceFocus %W} ;# MPJ change

 optcl::bind $htm StatusTextChange  linkchanged;
 catch {optcl::bind $htm SetSecureLockIcon security}
 optcl::bind $htm BeforeNavigate2   startLoading;
 optcl::bind $htm NavigateComplete2 stopLoading;

 catch {$htm navigate $url};

RS: You like semicolons, don't you?-) I use them only where necessary, which is very rare - inlined comments, or just too boring lines... But if you omit a semicolon once (in the line frame .sta above), I start to wonder what information that should convey (to the human; Tcl doesn't care)...

Martin Lemburg: Yes - I like them very much! I love them! ;^) No - it's just that I use the same coding standards in C(++), Java and tcl. I hate errors, because of just missing semicolons, because coding "too much" tcl without any semicolons.

Michael Jacobson: I updated the above so it does not need ffidl anymore. Give credit to GPS for this trick. Also if someone wants to try this code out in a Starkit format then just download this file!.kit (92kb) and run with with TclKit. (Note: that it includes the optcl extension so all you need are the Tclkit executable and the COMon! Starkit)

VL 11 jun 03: Trying to run the starkit on my w2k machine I get an error. I'll try to hunt it down when I have some time but if anyone know the problem help is appreciated?

 event not found: SetSecureLockIcon

MPJ: There are different versions of the IE COM object depending on which version of windows you are using. Some do not have the SetSecureLockIcon event. Therefore I just put a catch around this line are repacked the starkit. Please download and try it again. VL: works like a charm!

VL 11 jun 2003: Now if I could only add the local html-file I want to view as a commandline argument this would be perfect before I get my table-viewer stuff finished?

MPJ: Per you request I updated my starkit to take the 1st argument as the new home page. So you can do this...

 tclkit COMon!.kit

And yahoo will be the new home page and start to load. Please download and try it again. VL Thanks!

Here is a Starkit version where I changed the homepage to be served locally using the code from the Comic Server page ...!ics.kit (96kb). If you want to see the code just unwrap it "tclkit sdx.kit unwrap COMon!ics.kit" .

Michael Jacobson: 12/13/02 ~ I just added a print button to the examples after noticing RS' remarks that it was not supported (it is but you need to use the OLE interface to the WebBrowser component). Below is how you can also do a CUT, COPY and PASTE button too.

 button -text "PA" -command {catch {$htm ExecWB "OLECMDID_PASTE" "OLECMDEXECOPT_DODEFAULT"}}
 button -text "CO" -command {catch {$htm ExecWB "OLECMDID_COPY" "OLECMDEXECOPT_DODEFAULT"}}
 button -text "CU" -command {catch {$htm ExecWB "OLECMDID_CUT" "OLECMDEXECOPT_DODEFAULT"}}

In fact, the cut and paste are pretty interesting to have available all the time (MSIE allows it only for text fields).

Category Internet