%|||% &|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 for 32-bit Windows as well as for 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