| What | ffidl |
| Where | http://elf.org/ffidl/![]() |
| Where | http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/doc (dead) |
| version | 0.6 |
| Updated | ??/2006 |
| Contact | mailto:rec@elf.org (Roger E. Critchlow Jr.) |
Description edit
ffidl, "Foreign Function Interface with Dynamic Loading", by Roger E Critchlow, is an extension which allows pure Tcl extensions to invoke functions in shared libraries without having to create any glue code. Ffidl supports calls in both directions between C/C++ and Tcl, and operates on a variety of platforms.The Tcl command specifies a function name, a library, a list of argument types, and a return type, and ffidl takes care of the details of setting up the arguments and invoking the C function. Using ffidl, a pure Tcl wrapper to a shared library can be created.Avaliable for Linux, Windows, and Mac OS X.modifictions resulting in version 0.6 were contributed by DAS.Obtaining edit
For MacOS users, an unofficial update version 0.6.1 can be found at- source code

- MacOS Universal Binary
(tested on Leopard)
does not work on Tiger/Leopard (? only for PowerPC ?)A binary package for Win/Linux/Mac, unoficially versioned 0.6.1.1, is available at the irrational-numbers project
Note that a little BUG has been fixed ("Ffidlrt.tcl does not work if installed in a path name with whitespaces").version 0.6 from DAS edit
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)
- updated docs

- source tarball

- full source tarball

- includes libffi and ffcall sources
or the HEAD of libffi from the gcc CVS
with a small patch
to the buildsystem to make it build standalone (i.e. without relying on the gcc sourcetree structure)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 versionMac OS X ffidl binaries (with libffi) are available as tarball installer package or tarball
.A Windows ffidl binary (with libffi) built with MinGW on WIndowsXP in VirtualPC is now also available
.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
:hosts passing the test suite with both libffi and ffcall:- amd64-linux1
- alpha-linux1
- x86-linux1
- x86-linux2
- x86-solaris1
- x86-freebsd1
- x86-netbsd1
- sparc-solaris1
- x86-openbsd1
- ppc-osx1
- ppc-osx2
Code Using Ffidl edit
- always on top
- wrapper code
for AutoIt - web2desktop
- ZLM: includes an example of using ffidl to set the Windows desktop background.
- Custom Toplevel Frame
- SeS (26-10-2010): uses Ffidl to access DLL's and create customized toplevel frames in the Windows OS
Examples edit
- Getting Windows "special folders" with Ffidl
- 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.
- calling Fortran routines in a DLL
from Rolf Schroedter on c.l.t--- 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

#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 .scrfrom [Rob Hegt] 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
Example: Using Pointers edit
by daappC 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]
}Example: Accessing Carbon API's on Mac OS X edit
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 <das@users.sourceforge.net>
# 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 ADAS - 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; #HideDAS: another example
DAS] for Mac OS X in response to a question on c.l.t. from Steven MyersHow to set the current application's dock tile from a png file (c.f. API docs [1]):#!/bin/sh
#
# Set the Dock Tile from a png file with Ffidl
#
# Copyright (c) 2005, Daniel A. Steffen <das@users.sourceforge.net>
# 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.pngDAS - 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 <das@users.sourceforge.net>
# 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]Example: Microsof's net send edit
A wrapper for the Microsoft API-Call,
NetMessageBufferSend%|%: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...Example: expand a path containing Microsoft Windows environment variables edit
contributed by FMexpand strings like `%ProgramFiles%: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}result:C:\Program Files\windows media player\wmplayer.exe
Misc edit
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.[Lectus]: This is a must have functionality in Tcl. Any chance of moving it to core?See Also edit
- TWAPI
- an alternative to ffidl for accessing the Win32API
- critcl
- provides an alternative approach for "calling functions in arbitrary dynamic libraries"
- Yet another dll caller
- provides advanced data type handling over FFidl in a Windows-only version.
