%|||%
&|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%|%Roger E. Critchlow Jr.])|&
** See Also **
[Ffix - Ffidl eXtented]: an experimental wrapper to make foreign function calling easier
** Description **
'''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].
modifications resulting in version 0.6 were contributed by [DAS].
** Development **
[https://github.com/prs-de/ffidl%|%git repository]: Released 0.7 version with Tcl 8.6 support (incorporates [PYK] changes below, compatibility with recent libffi and some cleanups). Check out the renewed [https://prs-de.github.io/ffidl/%|%documentation]. [APN] Nice to see a new release. From the docs though it is not clear if the new version has support for 64-bit Windows (the original version did not). This is due to the fact, that size of data type ''long ''is 4 bytes even on 64-bit Windows and therefore unsuitable to store and retrieve function pointers.
[https://chiselapp.com/user/pooryorick/repository/ffidl%|%unofficial fossil repository]: no releases yet, but some minor fixes have been made. Unlike 0.6, below, it builds with Tcl-8.6.
Changes to file ffidl.c version 0.7 however let build a properly working binary package on 32-bit Windows as well as on 64-bit Windows, which passes test suite. Verified using
* Tcl/Tk version 8.6.4.1 installation's header and library files
* ftp://sourceware.org/pub/libffi%|%libffi%|% library version 3.1.< >Note: Binary built using library version 3.2.1 did not pass test suite.
* https://sourceforge.net/projects/mingw-w64%|%MinGW-w64 for 32 and 64 bit Windows%|% compilers version 7.2.0.< >Note: Use ''-mlong-double-64'' compiler option as Microsoft's data type ''long double'' is 8 bytes and equal to data type ''double''.
The diff applied to file ffidl.c against version 0.7:
======none
@@ -540,0 +541 @@ EXTERN int Ffidl_Init _ANSI_ARGS_((Tcl_I
+#endif
@@ -553 +553,0 @@ EXTERN int Ffidl_Init _ANSI_ARGS_((Tcl_I
-#endif
@@ -1228 +1228 @@ static int cif_protocol(Tcl_Interp *inte
-#ifdef __WIN32__
+#if defined(__WIN32__) && ! defined(__WIN64__)
@@ -1546 +1546 @@ static void callback_callback(ffi_cif *f
- long ltmp;
+ Tcl_WideIntOrLong ltmp;
@@ -1616 +1616 @@ static void callback_callback(ffi_cif *f
- Tcl_ListObjAppendElement(interp, list, Tcl_NewLongObj((long)(*(void **)argp)));
+ Tcl_ListObjAppendElement(interp, list, Tcl_NewWideIntOrLong((Tcl_WideIntOrLong)(*(void **)argp)));
@@ -1652 +1652 @@ static void callback_callback(ffi_cif *f
- ltmp = (long)dtmp;
+ ltmp = (Tcl_WideIntOrLong)dtmp;
@@ -1654 +1654 @@ static void callback_callback(ffi_cif *f
- if (Tcl_GetLongFromObj(interp, obj, <mp) == TCL_ERROR) {
+ if (Tcl_GetWideIntOrLongFromObj(interp, obj, <mp) == TCL_ERROR) {
@@ -1658 +1658 @@ static void callback_callback(ffi_cif *f
- } else if (Tcl_GetLongFromObj(interp, obj, <mp) == TCL_ERROR) {
+ } else if (Tcl_GetWideIntOrLongFromObj(interp, obj, <mp) == TCL_ERROR) {
@@ -1682 +1682 @@ static void callback_callback(ffi_cif *f
- if (Tcl_GetLongFromObj(interp, obj, <mp) == TCL_ERROR) {
+ if (Tcl_GetWideIntOrLongFromObj(interp, obj, <mp) == TCL_ERROR) {
@@ -2257 +2257 @@ static int tcl_ffidl_info(ClientData cli
- Tcl_SetObjResult(interp, Tcl_NewLongObj((long)interp));
+ Tcl_SetObjResult(interp, Tcl_NewWideIntOrLong((Tcl_WideIntOrLong)interp));
@@ -2400 +2400 @@ static int tcl_ffidl_call(ClientData cli
- long ltmp;
+ Tcl_WideIntOrLong ltmp;
@@ -2421 +2421 @@ static int tcl_ffidl_call(ClientData cli
- ltmp = (long)dtmp;
+ ltmp = (Tcl_WideIntOrLong)dtmp;
@@ -2423 +2423 @@ static int tcl_ffidl_call(ClientData cli
- if (Tcl_GetLongFromObj(interp, obj, <mp) == TCL_ERROR)
+ if (Tcl_GetWideIntOrLongFromObj(interp, obj, <mp) == TCL_ERROR)
@@ -2425 +2425 @@ static int tcl_ffidl_call(ClientData cli
- } else if (Tcl_GetLongFromObj(interp, obj, <mp) == TCL_ERROR)
+ } else if (Tcl_GetWideIntOrLongFromObj(interp, obj, <mp) == TCL_ERROR)
@@ -2441 +2441 @@ static int tcl_ffidl_call(ClientData cli
- if (Tcl_GetLongFromObj(interp, obj, <mp) == TCL_ERROR)
+ if (Tcl_GetWideIntOrLongFromObj(interp, obj, <mp) == TCL_ERROR)
@@ -2624 +2624 @@ static int tcl_ffidl_call(ClientData cli
- case FFIDL_PTR: Tcl_SetObjResult(interp, Tcl_NewLongObj((long)cif->rvalue.v_pointer)); break;
+ case FFIDL_PTR: Tcl_SetObjResult(interp, Tcl_NewWideIntOrLong((Tcl_WideIntOrLong)cif->rvalue.v_pointer)); break;
@@ -2647 +2647 @@ static int tcl_ffidl_callout(ClientData
- long tmp;
+ Tcl_WideIntOrLong tmp;
@@ -2675 +2675 @@ static int tcl_ffidl_callout(ClientData
- if (Tcl_GetLongFromObj(interp, objv[4], &tmp) == TCL_ERROR) return TCL_ERROR;
+ if (Tcl_GetWideIntOrLongFromObj(interp, objv[4], (Tcl_WideIntOrLong*)&tmp) == TCL_ERROR) return TCL_ERROR;
@@ -2867 +2867 @@ static int tcl_ffidl_symbol(ClientData c
- Tcl_SetObjResult(interp, Tcl_NewLongObj((long)address));
+ Tcl_SetObjResult(interp, Tcl_NewWideIntOrLong((Tcl_WideIntOrLong)address));
@@ -2938 +2938 @@ static int tcl_ffidl_stubsymbol(ClientDa
- Tcl_SetObjResult(interp, Tcl_NewLongObj((long)address));
+ Tcl_SetObjResult(interp, Tcl_NewWideIntOrLong((Tcl_WideIntOrLong)address));
======
** Obtaining **
[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.
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/doc/%|%updated docs%|%]:
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/ffidl.tar.gz%|%source tarball%|%]:
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/ffidl-full.tar.gz%|%full source tarball%|%]: includes libffi and ffcall sources
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 code%|%]
* [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 ?)
A binary package for Win/Linux/Mac, unoficially versioned 0.6.1.1, is available
at the
[http://irrational-numbers.googlecode.com/files/ffidl-0.6.1.1.zip%|%irrational-numbers project%|%]
Note that a little BUG has been fixed ("Ffidlrt.tcl does not work if installed in a path name with whitespaces").
Ffidl either uses unmodified
[http://www.haible.de/bruno/gnu/ffcall-1.10.tar.gz%|%ffcall 1.10%|%] or the
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/libffi.tar.bz2%|%HEAD of
libffi from the gcc CVS%|%] with a
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/libffi.diff%|%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.
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/ffidl.c.diff%|%The
diff%|%] of ffidl.c against the 0.5 version
[Mac OS X] ffidl binaries (with libffi) are available as tarball
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/binaries/darwin/%|%installer package or tarball%|%].
A [Windows] ffidl binary (with libffi) built with [MinGW] on WIndowsXP in
VirtualPC is now also
[http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/binaries/windows/Ffidl0.6.zip%|%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
[http://sourceforge.net/docman/display_doc.php?group_id=1&docid=762#hosts%|%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
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
** Code Using Ffidl **
[always on top]:
[http://wiki.tcl.tk/4016%|%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
[Collation]: [bll] 2016-5-20: Uses Ffidl to call setlocale() and wcscoll() to provide a collated sort.
** Example: The Tcl Library **
[PYK] 2014-09-19: In the following example, [Tcl C API] functions are called.
One thing to note is how space for a pointer is passed into `Tcl_GetCwd`.
======
#! /bin/env tclsh
package require Ffidl
namespace eval ::ffidl {
namespace export *
namespace ensemble create
}
if {[namespace current] ne {::}} {
namespace import ::ffidl
}
set tclso libtcl8.6.so
#set tclso libtcl8.5.so
#set tclso [ffidl::find-lib tcl8.6]
set Tcl_CreateInterp_sym [ffidl symbol $tclso Tcl_CreateInterp]
set Tcl_GetCwd_sym [ffidl symbol $tclso Tcl_GetCwd]
set Tcl_InterpDeleted_sym [ffidl symbol $tclso Tcl_InterpDeleted]
set Tcl_GetString_sym [ffidl symbol $tclso Tcl_GetString]
set Tcl_EvalObjEx_sym [ffidl symbol $tclso Tcl_EvalObjEx]
ffidl callout Tcl_CreateInterp {} pointer $Tcl_CreateInterp_sym
ffidl callout Tcl_GetCwd {pointer pointer-var} pointer-utf8 $Tcl_GetCwd_sym
ffidl callout Tcl_InterpDeleted pointer int $Tcl_InterpDeleted_sym
ffidl callout Tcl_GetString pointer-obj pointer-utf8 $Tcl_GetString_sym
ffidl callout TclEvalObjEx {pointer pointer-obj int} int $Tcl_EvalObjEx_sym
set interp [Tcl_CreateInterp]
set script {puts [pwd]}
puts [Tcl_GetString $script]
TclEvalObjEx $interp $script 0
set bufferPtr [binary format [ffidl info format pointer] 0]
set pwd [Tcl_GetCwd $interp bufferPtr]
puts $pwd
======
** Examples **
[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]:
[AutoIt]: ffidl wrapper brought to you by [Michael Jacobsen]
[Windows Desktop modifications with Ffidl]: playing around with [Microsoft Windows%|%Windows] desktop properties.
----
from [Rolf Schroedter] on c.l.t
--- file foo.h: ---
======c
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] [http://groups.google.com/groups?th=ec295f4a4849b362%|%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 exit?
bind $win {}
}
wm withdraw .
ScreenSaver .scr
======
----
from [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