%|||% &|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''. [Effe] 2018-06-20: I ran a bunch of tests on Win32 with ffidl 0.7 and libffi 3.1, 3.2.1 and 3.3-rc0, which is the latest version at the moment. I found that all problems stem from ffidl's use of the raw API. Raw calls in libffi 3.2.1 don't work, raw calls in libffi 3.3-rc0 work, but raw callbacks don't. All versions of libffi pass the basic and callback tests of ffidl if the raw API is ignored. Please consider that the raw API of libffi is neither documented nor covered by any test in its own test suite. I also looked at some libffi wrappers of other languages and found none that uses the raw API. Since raw calls are a runtime decision of ffidl depending on parameter types, I strongly suggest to make it an option that can be enabled at runtime (for those who need the speed). Additionally, Tcl_LoadFile works on Windows (the Makefile doesn't advertise it). The advantage with Tcl_LoadFile is that Tcl itself takes care of making a copy of a DLL into a temporary folder if the DLL is located in a starkit. Note for users of ActiveTcl: It seems that ActiveTcl installs a hook on LoadLibrary for added comfort. This means that you can use a good old ffidl 0.6 (which uses LoadLibrary) and load a DLL from a starkit without any hassle while all other Tcl interpreters require you to make a copy of the DLL. 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 [https://sourceforge.net/projects/irrational-numbers/files/ffidl-0.6.1.1.zip/download%|%Sourceforge%|%] 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