Version 49 of Ffidl

Updated 2009-04-02 17:25:31 by ABU

What: ffidl

 Where: http://elf.org/ffidl/
 Description: Extension which allows pure Tcl extensions to invoke shared
        library functions without glue.
        Available for Linux, Windows and Mac OS X.
        Currently at version 0.6 .
 Updated: ??/2006
 Contact: mailto:[email protected] (Roger E. Critchlow Jr.)

RM you can find a version 0.6 at [L1 ] (see below for details).

ABU 2-apr-2009 - For MacOS users, an unofficial update version 0.6.1 can be found at

 [http://www.categorifiedcoder.info/tcltk/ffidl/ffidl-0.6.1-darwin-9-univ.tar.gz]  SOURCE
 [http://www.tcl.tk/starkits/ffidl0.6.1_ub.tgz]  MacOS Universal Binary  (tested on Leopard)

Note that the binary package listed at http://elf.org/ffidl/ does not work on Tiger/Leopard (? only for PowerPC ?)

LV Someone should contact the author to discuss version 0.6 with him.

DAS I have done so at the time I made 0.6 available, Roger responded that he would look at and integrate the changes eventually.

kostix 21-Jan-2007: I've found 0.6 available on its official site. Probably DAS should further edit this page since his changes are now in the trunk.


Roger E Critchlow [L2 ] has made an experimental release of ffidl, an experimental package that allows you to call C functions using pure Tcl wrappers. You specify a function name, a library, a list of argument types, and a return type, and Ffidl takes care of the nasty details of converting a Tcl command call into a C function call for you. So, if you have a shared library and a specification of the entries in the library, you can wrap the library into a Tcl extension with Ffidl and pure Tcl.

(The quotation is from the ffidl manual at [L3 ].)

Ffidl supports calls in both directions between C/C++ and Tcl, and operates on a variety of platforms.

The name, by the way, appears to stand for "Foreign Function Interface with Dynamic Loading."

DLR Including ffidl in the core would be a huge boost to Tcl, and specially, Tcllib, as many modules could be written in pure Tcl. Part of the success of Mono/.NET is its P/Invoke feature which allows it to effortlessly wrap native libraries. The Mono implementation uses (or at least used to do) ffidl at its core.


Rolf Schroedter gave a example on c.l.t on the use of ffidl

 To give you an impression about the use of ffidl, 
 look at the following C and TCL-declarations:
 --- file foo.h: ---
 int foo_init( int adr, int log );
 int foo_done( void );
 int foo_info( FOO_INFO *infoPtr ); /* FOO_INFO is a structure */
 int foo_open( const char *port );
 --- file foo.tcl: ---
 load ffidl05.dll
 set DLL foo.dll
 ffidl::callout foo_init {int int} int  [ffidl::symbol $DLL foo_init]
 ffidl::callout foo_done {} int   [ffidl::symbol $DLL foo_done]
 ffidl::callout foo_info {pointer-var} int [ffidl::symbol $DLL foo_info]
 ffidl::callout foo_open {pointer-utf8} int [ffidl::symbol $DLL foo_open]

[Explain Rolf Schroedter's screensaver example in http://groups.google.com/groups?th=ec295f4a4849b362 .]

 #Rolf Schroedter
 #German Aerospace Center
 #Institute of Space Sensor Technology and Planetary Exploration

 load ffidl05.dll

 ffidl::callout dll_FindWindow       {pointer-utf8 pointer-utf8} int     [ffidl::symbol user32.dll FindWindowA]
 ffidl::callout dll_FindWindowTitle  {int pointer-utf8} int              [ffidl::symbol user32.dll FindWindowA]
 ffidl::callout dll_FindWindowClass  {pointer-utf8 int} int              [ffidl::symbol user32.dll FindWindowA]
 ffidl::callout dll_SetWindowPos     {int int int int int int int} int   [ffidl::symbol user32.dll SetWindowPos]
 ffidl::callout dll_SystemParametersInfo {int int pointer int} int       [ffidl::symbol user32.dll SystemParametersInfoA]

 proc FindWindow { class title } {
    if { [string length $class] == 0 } {
        dll_FindWindowTitle 0 $title
    } elseif { [string length $title] == 0 } {
        dll_FindWindowClass $class 0
    } else {
        dll_FindWindow $class $title
    }
 }
 proc SetWindowPos { hwnd after x y cx cy {flags 0} } {
    array set VAL {TOP 0 BOTTOM 1 TOPMOST -1 NOTOPMOST -2}
    set iAfter $VAL([string toupper $after])
    dll_SetWindowPos $hwnd $iAfter $x $y $cx $cy $flags
 }
 proc SetupScreenSaver { bool } {
    dll_SystemParametersInfo 97 $bool 0 0   ;# SPI_SCREENSAVERRUNNING=97
 }
 proc exit? {} {
    set answer [tk_messageBox -message "Really quit?" -type yesno -icon question]
    switch -- $answer {
        yes {
            SetupScreenSaver 0
            exit
        }
        no {}
    }
 }
 proc ScreenSaver {win} {
    set size(X) [winfo screenwidth .]
    set size(Y) [winfo screenheight .]

    toplevel $win
    wm title $win "TclScreenSaver"     ;# to find the window

    wm overrideredirect $win true
    $win configure -relief flat -bd 0
    $win configure -cursor hand2    ;# Ohne cursor ???

    update idletasks                ;# virtually display $win, allows window to be found
    set hwnd [FindWindow "" "TclScreenSaver"]
    set res1 [SetWindowPos $hwnd TOPMOST 0 0 $size(X) $size(Y)]   ;# ever makes full screen
    set res2 [SetupScreenSaver 1]

    canvas $win.c -background yellow -width $size(X) -height $size(Y) -relief flat -bd 0
    pack $win.c -expand yes -fill both

    focus -force $win

    bind $win <Key> exit?
    bind $win <Motion> {}
 }

 wm withdraw .
 ScreenSaver .scr

Per: Rob Hegt post on c.l.t Subject: solution for regaining focus from OpTcl hosted ActiveX control

 load lib/ffidl05.dll
 ffidl::callout dll_SetFocus {int} int [ffidl::symbol user32.dll SetFocus]
 proc GrabFocus {args} {dll_SetFocus [winfo id .]}

Then just bind GrabFocus to some event. In the post he uses <button> .

 bind . <Button> +GrabFocus

Michael Jacobson ~ Also see always on top for a another example.


Michael Jacobson ~ AutoIt wrapper code using Ffidl is here [L4 ].


ZLM ~ web2desktop includes an example of using ffidl to set the Windows desktop background.


Another small example from Matthias Hoffmann: A wrapper around Microsoft's API-Call NetMessageBufferSend (available via commandline through the net send subcommand; see [L5 ]):

 package require Ffidl 0.5
 ffidl::callout dll_netSend {pointer-utf16 pointer-utf16 pointer-utf16 pointer-utf16 long} long \
                            [ffidl::symbol netapi32.dll NetMessageBufferSend]
 proc netSend {dest mesg {srv {}}} {
      set from $::tcl_platform(user)
      # or:
      # set from [info host]
      #  (only these two alternatives seems to work...)
      return [dll_netSend $srv $dest $from $mesg [expr [string length $mesg]*2]]
 }

This is to send small messages to computers or users or work groups, which will immediately pop-up on the screen (using NT/2000/XP, if the messenger service is started, or with DOS/Win3x/9x, if winpopup/netpop.exe is running) - a task often needed by administrators! Note: The data-type-definitions are somewhat tricky.... With the srv Argument it is theoretically possible to specify the system which will perform the sending task - (example: \\machine1), but this involves some complex security aspects...


kostix offers a solution for getting "special folders" on Windows platforms. While TWAPI can do this out-of-the-box, it doesn't work on Win9x and is big. Ffidl doesn't have these limitations. See: Getting Windows "special folders" with Ffidl.


(FM) Another example for the windows registry. How to expand a path containing Microsoft Windows environment variables (e.g., %ProgramFiles%) ?

Let's do this :

ffidl::callout dll_ExpandEnvironmentStringsForUser \

    {int pointer-utf16  pointer-utf16 long} int \
    [ffidl::symbol Userenv.dll ExpandEnvironmentStringsForUserW]

proc {ExpandEnvironmentStringsForUser} {WPath} {

    set TclPath [string repeat \u0000 300]
    if [dll_ExpandEnvironmentStringsForUser 0 $WPath $TclPath 300] {
        set ix [string first \u0000 $TclPath]
        if {$ix > 0} {
            return [string range $TclPath 0 [expr {$ix - 1}]]
        } else {
            return {}
        }
    } else {
        return {}
    }

}

let's try it : ExpandEnvironmentStringsForUser {%ProgramFiles%\windows media player\wmplayer.exe} give us : C:\Program Files\windows media player\wmplayer.exe


An energetic person could use SWIG to wrap all the standard Win32api to make it accessible in a more-or-less standard way for Tcl. No one yet seems motivated to do this. 10/28/03 - TWAPI seems to have taken this concept and turned it into code.


critcl provides an alternative way approach for "calling functions in arbitrary dynamic libraries" [L6 ].


AM Here is an example of the use of [ffdil] for calling Fortran routines in a DLL

Yet another dll caller provides advanced data type handling over FFidl in a Windows-only version. The examples are quite impressive. RT


DAS - I have updated Ffidl to support Darwin/Mac OS X, as well as modernized it in other ways:

  • updates for 2005 versions of libffi & ffcall
  • TEA 3.2 buildsystem, testsuite
  • support for Tcl 8.4, TclpDlopen, Tcl_WideInt
  • fixes for 64bit LP64
  • callouts & callbacks are created/used relative to current namespace (for unqualified names)
  • addition of [ffidl::stubsymbol] for Tcl/Tk symbol resolution via stubs tables
  • callbacks can be called anytime, not just from inside call-outs (using Tcl_BackgroundError to report errors)

the testsuite is just the existing tests wrapped into .test files.

updated docs at [L7 ], source tarball at [L8 ] and full source tarball (also including libffi and ffcall sources) at [L9 ].

Ffidl either uses unmodified ffcall 1.10 [L10 ] or the HEAD of libffi from the gcc CVS [L11 ] with a small patch to the buildsystem to make it build standalone (i.e. without relying on the gcc sourcetree structure) [L12 ]. Note that libffi is under BSD license but ffcall is GPLd. APN In response to a question on c.l.t DAS replied: no, ffidl uses either libffi or ffcall, but never both. If you use my 0.6 update to ffidl, the choice of libffi vs ffcall is a compile time option (with libffi being the default for GPL avoidance reasons). A ffidl binary built with the default configure options (or with -enable-libffi) will contain only BSD licensed code, whereas a ffidl built with --enable-ffcall will indeed become GPLd as a whole by virtue of static linking with ffcall.

The diff of ffidl.c against the 0.5 version is available for info [L13 ].

Mac OS X ffidl binaries (with libffi) are available as installer package or tarball [L14 ].

A Windows ffidl binary (with libffi) built with MinGW on WIndowsXP in VirtualPC is now also available [L15 ].

I have tested & exercised this quite extensively on Mac OS X 10.3 (with both libffi and ffcall), and have verified that it builds and passes the testsuite on Windows XP with MinGW (in Virtual PC on my Mac...).

I have also built ffidl and run the testsuite on all the machines in the sourceforge compilefarm [L16 ]:

hosts passing the test suite with both libffi and ffcall:

  • amd64-linux1
  • alpha-linux1
  • x86-linux1
  • x86-linux2
  • x86-solaris1
  • x86-freebsd1
  • x86-netbsd1

host core dumping when running the test suite (with both libffi and ffcall):

  • sparc-solaris1

hosts passing the test suite with ffcall, but where building the libffi library fails:

  • x86-openbsd1
  • ppc-osx1
  • ppc-osx2

the last two may be fixable by reverting to an earlier version of libffi


DAS - The script below is a brief demo of Ffidl's usefulness for accessing Carbon APIs on Mac OS X, in particular it shows a carbon event handler implemented in tcl. It also shows how to access Tk APIs via the new [::ffidl::stubsymbol].

The demo installs the the system wide hotkey Cmd-Shift-A, pressing it makes the blue labelframe flash red. Note how the hotkey works even with Wish not in front...

 #!/bin/sh
 #
 # Let's ffidl with Carbon HotKeys!
 #
 # Copyright (c) 2005, Daniel A. Steffen <[email protected]>
 # BSD License: c.f. <http://www.opensource.org/licenses/bsd-license>
 # 
 #\
 exec wish $0 "$@"

 package require Tk
 package require Ffidl

 namespace eval carbon {

     ::ffidl::typedef EventHotKeyID {unsigned long} uint32
     ::ffidl::typedef EventTypeSpec uint32 uint32
     ::ffidl::typedef EventTargetRef pointer
     ::ffidl::typedef OSStatus sint32

     ::ffidl::callout RegisterEventHotKey {uint32 uint32 EventHotKeyID EventTargetRef \
                                           uint32 pointer-var} OSStatus \
             [::ffidl::symbol Carbon.framework/Carbon RegisterEventHotKey]

     ::ffidl::callout GetApplicationEventTarget {} EventTargetRef \
             [::ffidl::symbol Carbon.framework/Carbon GetApplicationEventTarget]

     ::ffidl::callout InstallEventHandler {EventTargetRef pointer-proc uint32 pointer-byte \
                                           pointer pointer-var} OSStatus \
             [::ffidl::symbol Carbon.framework/Carbon InstallEventHandler]

     ::ffidl::callout XKeysymToKeycode {pointer {unsigned long}} {unsigned long} \
             [::ffidl::stubsymbol tk intXLibStubs 35]; #XKeysymToKeycode

     ::ffidl::callout TkStringToKeysym {pointer-utf8} {unsigned long} \
             [::ffidl::stubsymbol tk intStubs 86]; #TkStringToKeysym
 }

 proc hotkeyHandler {handlerCallRef event userData} {
     .l configure -bg red
     after 200 .l configure -bg blue
     return 0
 }

 proc installHotKey {key} {
     labelframe .l -width 100 -height 100 -bg blue
     pack .l

     ::ffidl::callback hotkeyHandler {pointer pointer pointer} OSStatus

     set EventHandlerRef [binary format I 0]
     set res [carbon::InstallEventHandler [carbon::GetApplicationEventTarget] hotkeyHandler 1 \
         [binary format a4I keyb 5] 0 EventHandlerRef]
     if {$res} {puts stderr "InstallEventHandler failed: $res"; exit -1}

     set keycode [expr {[carbon::XKeysymToKeycode 0 [carbon::TkStringToKeysym $key]]>>16}]
     set modifiers [expr {1 << 8 | 1 << 9}]; #Cmd-Shift
     #set modifiers [expr {1 << 8}]; #Cmd

     set EventHotKeyRef [binary format I 0]
     set res [carbon::RegisterEventHotKey  $keycode $modifiers [binary format a4I wish 1] \
         [carbon::GetApplicationEventTarget] 0 EventHotKeyRef]
     if {$res} {puts stderr "RegisterEventHotKey failed: $res"; exit -1}
 }

 installHotKey A

DAS - How to set the application menu name at runtime on Mac OS X using undocumented Apple SPI:

 package require Tk
 package require Ffidl 0.6

 ::ffidl::callout CPSSetProcessName {pointer-byte pointer-utf8} sint32 \
         [::ffidl::symbol /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics CPSSetProcessName]
 CPSSetProcessName [binary format I2 {0 2}] "MyCoolApp"

and how to show or hide the current application: (also see tclCarbonProcesses)

 ::ffidl::callout ShowHideProcess {pointer-byte int} sint32 [::ffidl::symbol Carbon.framework/Carbon ShowHideProcess]
 ShowHideProcess [binary format I2 {0 2}] 1; #Show
 ShowHideProcess [binary format I2 {0 2}] 0; #Hide

DAS - Another Mac OS X example in response to a question on c.l.t. from Steven Myers [L17 ]

How to set the current application's dock tile from a png file (c.f. API docs [L18 ]):

 #!/bin/sh
 #
 # Set the Dock Tile from a png file with Ffidl
 #
 # Copyright (c) 2005, Daniel A. Steffen <[email protected]>
 # BSD License: c.f. <http://www.opensource.org/licenses/bsd-license>
 # 
 #\
 exec wish $0 "$@"

 package require Tk
 package require Ffidl

 namespace eval carbon {
     proc api {name argl ret lib} {::ffidl::callout $name $argl $ret \
         [::ffidl::symbol $lib.framework/$lib $name]}
     proc type {name type} {::ffidl::typedef $name $type}
     proc const {name args} {variable {}; eval set [list ($name)] $args}

     type OSStatus sint32
     type bool int
     type CFURLRef pointer
     type CGDataProviderRef pointer
     type CGImageRef pointer
     type CGColorRenderingIntent int
     const kCGRenderingIntentDefault 0

     api CFURLCreateFromFileSystemRepresentation {pointer pointer-utf8 \
             int bool} CFURLRef CoreFoundation
     api CFRelease {pointer} void CoreFoundation
     api CGDataProviderCreateWithURL {CFURLRef} CGDataProviderRef \
             ApplicationServices
     api CGImageCreateWithPNGDataProvider {CGDataProviderRef pointer \
             bool CGColorRenderingIntent} CGImageRef ApplicationServices
     api SetApplicationDockTileImage {CGImageRef} OSStatus Carbon

     proc setDockTileToPNG {pngFile} {
         if {[file exists $pngFile]} {
             set url [CFURLCreateFromFileSystemRepresentation 0 $pngFile \
                     [string bytelength $pngFile] 0]
             if {$url} {
                 set dp [CGDataProviderCreateWithURL $url]
                 if {$dp} {
                     set img [CGImageCreateWithPNGDataProvider $dp 0 1 \
                             [const kCGRenderingIntentDefault]]
                     if {$img} {
                         SetApplicationDockTileImage $img
                         CFRelease $img
                     }
                     CFRelease $dp
                 }
                 CFRelease $url
             }
         }
     }
 }

 carbon::setDockTileToPNG test.png

DAS - Yet another Mac OS X example on how to find the user's preferred locale (as set in system preferences 'International') via the CFLocale API:

Note that kroc has since found a way to get this info without resorting to Ffidl: [exec defaults read NSGlobalDomain AppleLocale]

 #!/bin/sh
 #
 # Ffidling CFLocale
 #
 # Copyright (c) 2005, Daniel A. Steffen <[email protected]>
 # BSD License: c.f. <http://www.opensource.org/licenses/bsd-license>
 #
 #\
 exec tclsh $0 "$@"

 package require Ffidl 0.6

 namespace eval corefoundation {
     proc api {name argl ret} {::ffidl::callout $name $argl $ret \
         [::ffidl::symbol CoreFoundation.framework/CoreFoundation $name]}
     api CFLocaleCopyCurrent {} pointer
     api CFLocaleGetIdentifier pointer pointer
     api CFStringGetLength pointer sint32
     ::ffidl::typedef CFRange sint32 sint32
     api CFStringGetCharacters {pointer CFRange pointer-var} void
     api CFRelease pointer void

     proc getLocaleIdentifier {} {
         set cfloc [CFLocaleCopyCurrent]
         set cfstr [CFLocaleGetIdentifier $cfloc]
         set len [CFStringGetLength $cfstr]
         set buf [binary format x[expr {2*$len}]]
         set range [binary format [::ffidl::info format CFRange] 0 $len]
         CFStringGetCharacters $cfstr $range buf
         CFRelease $cfloc
         encoding convertfrom unicode $buf
     }
 }

 puts [corefoundation::getLocaleIdentifier]

daapp Example using pointers:

C declarations:

  typedef short           I16;
  typedef unsigned short  U16;


  I16 _7443_initial(I16 *existCards);
  I16 _7443_close(void);
  I16 _7443_version_info(I16 CardNo, U16 *HardwareInfo, U16 *SoftwareInfo, U16 *DriverInfo);
  I16 _7443_d_output(I16 CardNo, I16 Ch_No, I16 value);

Tcl code:

  namespace eval 7443 {
      variable dll_name PPCI7443.dll

      ffidl::callout _initial {pointer-var} sint16 \
          [ffidl::symbol $dll_name _7443_initial]

      ffidl::callout close {} void [ffidl::symbol $dll_name _7443_close]

      ffidl::callout _version_info {sint16 pointer-var pointer-var pointer-var} \
          sint16 [ffidl::symbol $dll_name _7443_version_info]

      ffidl::callout d_output {sint16 sint16 sint16} sint16 \
            [ffidl::symbol $dll_name _7443_d_output]

  }

  # varName should containt quantity of available cards
  proc 7443::initial {varName} {
      upvar $varName existsCards
      set cards [binary format s 0]
      set result [_initial cards]
      binary scan $cards s existsCards
      return $result
  }

  # return: {errorCode hardwareInfo softwareInfo driverInfo}
  proc 7443::version_info {cardNumber} {
      set hardwareInfo [binary format s 0]
      set softwareInfo [binary format s 0]
      set driverInfo   [binary format s 0]

      set result [_version_info $cardNumber hardwareInfo softwareInfo driverInfo]

      binary scan $hardwareInfo s hi
      binary scan $softwareInfo s si
      binary scan $driverInfo   s di

      return [list $result $hi $si $di]
  }