Call a function in a shared library
Show how to call a function in a shared library (without dynamically linking to it at compile-time). In particular, show how to call the shared library function if the library is available, otherwise use an internal equivalent function.
You are encouraged to solve this task according to the task description, using any language you may know.
This is a special case of calling a foreign language function where the focus is close to the ABI level and not at the normal API level.
Related tasks
- OpenGL -- OpenGL is usually maintained as a shared library.
Examples
Ada
Windows
The following solution calls MessageBox from Windows' dynamic library user32.dll. It does not use Win32 bindings, which would be meaningless, because MessageBox is already there. Instead of that it links statically to kernel32.dll, which required to load anything under Windows. From there it uses LoadLibrary to load user32.dll and then GetProcAddress to get the MessageBox entry point there. Note how Windows mangles names of functions in the import libraries. So "LoadLibrary" becomes "_LoadLibraryA@4", which is its real name. "A" means ASCII. Once address of MessageBox is obtained it is converted to a pointer to a function that has an interface corresponding to it. Note Windows' call convention, which is stdcall. <lang Ada>with Ada.Text_IO; use Ada.Text_IO; with Interfaces; use Interfaces; with Interfaces.C; use Interfaces.C; with System; use System;
with Ada.Unchecked_Conversion;
procedure Shared_Library_Call is
-- -- Interface to kernel32.dll which is resposible for loading DLLs under Windows. -- There are ready to use Win32 binding. We don't want to use them here. -- type HANDLE is new Unsigned_32; function LoadLibrary (lpFileName : char_array) return HANDLE; pragma Import (stdcall, LoadLibrary, "LoadLibrary", "_LoadLibraryA@4");
function GetProcAddress (hModule : HANDLE; lpProcName : char_array) return Address; pragma Import (stdcall, GetProcAddress, "GetProcAddress", "_GetProcAddress@8"); -- -- The interface of the function we want to call. It is a pointer (access type) -- because we will link it dynamically. The function is from User32.dll -- type MessageBox is access function ( hWnd : Address := Null_Address; lpText : char_array; lpCaption : char_array := To_C ("Greeting"); uType : Unsigned_16 := 0 ) return Integer_16; pragma Convention (Stdcall, MessageBox); function To_MessageBox is new Ada.Unchecked_Conversion (Address, MessageBox);
Library : HANDLE := LoadLibrary (To_C ("user32.dll")); Pointer : Address := GetProcAddress (Library, To_C ("MessageBoxA"));
begin
if Pointer /= Null_Address then declare Result : Integer_16; begin Result := To_MessageBox (Pointer) (lpText => To_C ("Hello!")); end; else Put_Line ("Unable to load the library " & HANDLE'Image (Library)); end if;
end Shared_Library_Call;</lang>
Linux
Here we are using the dl library statically (-ldl switch upon linking) and Xlib dynamically (libX11.so). The function dlopen loads a library. The function dlsym looks up for an entry point there. From libX11.so, first XOpenDisplay is called to open an X11 display, which name is in the DISPLAY environment variable. Then XDisplayWidth of the display is obtained an printed into the standard output. <lang Ada>with Ada.Environment_Variables; use Ada.Environment_Variables; with Ada.Text_IO; use Ada.Text_IO; with Interfaces; use Interfaces; with Interfaces.C; use Interfaces.C; with System; use System;
with Ada.Unchecked_Conversion;
procedure Shared_Library_Call is
-- -- Interface to libdl to load dynamically linked libraries -- function dlopen (FileName : char_array; Flag : int) return Address; pragma Import (C, dlopen);
function dlsym (Handle : address; Symbol : char_array) return Address; pragma Import (C, dlsym); -- -- The interfaces of the functions we want to call. These are pointers -- (access type) because we will link it dynamically. The functions -- come from libX11.so. -- type XOpenDisplay is access function (Display_Name : char_array) return Address; pragma Convention (C, XOpenDisplay); function To_Ptr is new Ada.Unchecked_Conversion (Address, XOpenDisplay);
type XDisplayWidth is access function (Display : Address; Screen : int) return int; pragma Convention (C, XDisplayWidth); function To_Ptr is new Ada.Unchecked_Conversion (Address, XDisplayWidth);
Library : Address := dlopen (To_C ("libX11.so"), 1); OpenDisplay : XOpenDisplay := To_Ptr (dlsym (Library, To_C ("XOpenDisplay"))); DisplayWidth : XDisplayWidth := To_Ptr (dlsym (Library, To_C ("XDisplayWidth")));
begin
if OpenDisplay /= null and then DisplayWidth /= null then declare Display : Address; begin Display := OpenDisplay (To_C (Value ("DISPLAY"))); if Display = Null_Address then Put_Line ("Unable to open display " & Value ("DISPLAY")); else Put_Line (Value ("DISPLAY") & " width is" & int'image (DisplayWidth (Display, 0))); end if; end; else Put_Line ("Unable to load the library"); end if;
end Shared_Library_Call;</lang>
AutoHotkey
dllhost.ahk <lang AutoHotkey>ahkdll := DllCall("LoadLibrary", "str", "AutoHotkey.dll") clientHandle := DllCall("AutoHotkey\ahkdll", "str", "dllclient.ahk", "str" , "", "str", "parameter1 parameter2", "Cdecl Int")</lang> dllclient.ahk <lang AutoHotkey>Msgbox, hello from client</lang>
C
Tested with gcc on a GNU/Linux system (on GNU/Linux dl*
functions are available linking to libdl, i.e. with -ldl option)
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <dlfcn.h>
int myopenimage(const char *in) {
static int handle=0; fprintf(stderr, "internal openimage opens %s...\n", in); return handle++;
}
int main() {
void *imglib; int (*extopenimage)(const char *); int imghandle;
imglib = dlopen("./fakeimglib.so", RTLD_LAZY); if ( imglib != NULL ) { /* extopenimage = (int (*)(const char *))dlsym(imglib,...) "man dlopen" says that C99 standard leaves casting from "void *" to a function pointer undefined. The following is the POSIX.1-2003 workaround found in man */ *(void **)(&extopenimage) = dlsym(imglib, "openimage"); /* the following works with gcc, gives no warning even with -Wall -std=c99 -pedantic options... :D */ /* extopenimage = dlsym(imglib, "openimage"); */ imghandle = extopenimage("fake.img"); } else { imghandle = myopenimage("fake.img"); } printf("opened with handle %d\n", imghandle); /* ... */ if (imglib != NULL ) dlclose(imglib); return EXIT_SUCCESS;
}</lang>
The fake fakeimglib.so code is
<lang c>#include <stdio.h> /* gcc -shared -nostartfiles fakeimglib.c -o fakeimglib.so */ int openimage(const char *s) {
static int handle = 100; fprintf(stderr, "opening %s\n", s); return handle++;
}</lang>
When the library fakeimglib.so exists in the current directory (this choice is senseful only for testing purposes), the output is:
opening fake.img opened with handle 100
otherwise the output is:
internal openimage opens fake.img... opened with handle 0
C#
In Windows.
<lang csharp>using System.Runtime.InteropServices;
class Program {
[DllImport("fakelib.dll")] public static extern int fakefunction(int args);
static void Main(string[] args) { int r = fakefunction(10); }
}</lang>
Common Lisp
<lang lisp>CL-USER> (cffi:load-foreign-library "libX11.so")
- <CFFI::FOREIGN-LIBRARY {1004F4ECC1}>
CL-USER> (cffi:foreign-funcall "XOpenDisplay"
:string #+sbcl (sb-posix:getenv "DISPLAY") #-sbcl ":0.0" :pointer)
- .(SB-SYS:INT-SAP #X00650FD0)</lang>
J
Most of this was borrowed from Call a foreign-language function#J
<lang J>require 'dll' strdup=: 'msvcrt.dll _strdup >x *' cd < free=: 'msvcrt.dll free n x' cd < getstr=: free ] memr@,&0 _1
DupStr=:verb define
try. getstr@strdup y catch. y end.
)</lang>
You get a domain error when the required library is not present at run time. A try/catch will let you handle this (as would the ::
adverse operator).
Example use: <lang J> DupStr 'hello' hello
getstr@strdup ::] 'hello'
hello</lang>
Java
The native keyword is the key here. For this method, the library must be written to the Java Native Interface; this is not a general FFI. <lang java>public class LoadLib{
private static native void functionInSharedLib(); //change return type or parameters as necessary
public static void main(String[] args){ System.loadLibrary("Path/to/library/here/lib.dll"); functionInSharedLib(); }
}</lang>
<lang java>import com.sun.jna.Library; import com.sun.jna.Native;
public class LoadLibJNA{
private interface YourSharedLibraryName extends Library{ //put shared library functions here with no definition public void sharedLibraryfunction(); }
public static void main(String[] args){ YourSharedLibraryName lib = (YourSharedLibraryName)Native.loadLibrary("sharedLibrary",//as in "sharedLibrary.dll" YourSharedLibraryName.class); lib.sharedLibraryFunction(); }
}</lang>
OCaml
As long as I know there are not any solution in OCaml to load dynamically a functions from a C library. So I have quickly implemented a module named Dlffi that you can find in this sub-page. It is basically a wrapper around the GNU/Linux dl* functions and the libffi.
Here is an example of use of this Dlffi module: <lang ocaml>open Dlffi
let get_int = function Int v -> v | _ -> failwith "get_int" let get_ptr = function Ptr v -> v | _ -> failwith "get_ptr" let get_float = function Float v -> v | _ -> failwith "get_float" let get_double = function Double v -> v | _ -> failwith "get_double" let get_string = function String v -> v | _ -> failwith "get_string"
let () =
(* load the library *) let xlib = dlopen "/usr/lib/libX11.so" [RTLD_LAZY] in (* load the functions *) let _open_display = dlsym xlib "XOpenDisplay" and _default_screen = dlsym xlib "XDefaultScreen" and _display_width = dlsym xlib "XDisplayWidth" and _display_height = dlsym xlib "XDisplayHeight" in (* wrap functions to provide a higher level interface *) let open_display ~name = get_ptr(fficall _open_display [| String name |] Return_ptr) and default_screen ~dpy = get_int(fficall _default_screen [| (Ptr dpy) |] Return_int) and display_width ~dpy ~scr = get_int(fficall _display_width [| (Ptr dpy); (Int scr) |] Return_int) and display_height ~dpy ~scr = get_int(fficall _display_height [| (Ptr dpy); (Int scr) |] Return_int) in (* use our functions *) let dpy = open_display ~name:":0" in let screen_number = default_screen ~dpy in let width = display_width ~dpy ~scr:screen_number and height = display_height ~dpy ~scr:screen_number in Printf.printf "# Screen dimensions are: %d x %d pixels\n" width height; dlclose xlib;
- </lang>
PowerBASIC
In this example, if the library can't be found (user32), or the desired function in the library (MessageBoxA), the equivalent built-in function (MSGBOX) is at the "epicFail" label... but really, if you can't find user32.dll, you've got bigger things to worry about. <lang powerbasic>#INCLUDE "Win32API.inc"
FUNCTION PBMAIN () AS LONG
DIM hWnd AS LONG DIM msg AS ASCIIZ * 14, titl AS ASCIIZ * 8
hWnd = LoadLibrary ("user32") msg = "Hello, world!" titl = "Example" IF ISTRUE (hWnd) THEN funcAddr& = GetProcAddress (hWnd, "MessageBoxA") IF ISTRUE (funcAddr&) THEN ASM push 0& tAdr& = VARPTR(titl) ASM push tAdr& mAdr& = VARPTR(msg) ASM push mAdr& ASM push 0& CALL DWORD funcAddr& ELSE GOTO epicFail END IF ELSE GOTO epicFail END IF
GOTO getMeOuttaHere
epicFail:
MSGBOX msg, , titl
getMeOuttaHere:
IF ISTRUE(hWnd) THEN tmp& = FreeLibrary (hWnd) IF ISFALSE(tmp&) THEN MSGBOX "Error freeing library... [shrug]" END IF
END FUNCTION</lang>
PicoLisp
This differs between the 32-bit and 64-bit versions. While the 64-bit version can interface directly to C functions (in external libraries or not), requires the 32-bit function some glue code.
32-bit version
For the 32-bit version, we need some glue code: <lang PicoLisp>(load "@lib/gcc.l")
(gcc "x11" '("-lX11") 'xOpenDisplay 'xCloseDisplay)
- include <X11/Xlib.h>
any xOpenDisplay(any ex) {
any x = evSym(cdr(ex)); // Get display name char display[bufSize(x)]; // Create a buffer for the name
bufString(x, display); // Upack the name return boxCnt((long)XOpenDisplay(display));
}
any xCloseDisplay(any ex) {
return boxCnt(XCloseDisplay((Display*)evCnt(ex, cdr(ex))));
} /**/
- With that we can open and close the display:
- (setq Display (xOpenDisplay ":0.7")) # Wrong
-> 0
- (setq Display (xOpenDisplay ":0.0")) # Correct
-> 158094320
- (xCloseDisplay Display)
-> 0</lang>
64-bit version
In the 64-bit version, we can call the library directly: <lang PicoLisp>: (setq Display (native "/usr/lib/libX11.so.6" "XOpenDisplay" 'N ":0.0")) -> 6502688
- (native "/usr/lib/libX11.so.6" "XCloseDisplay" 'I Display)
-> 0</lang>
PureBasic
Older PureBasic versions normally relied on CallFunction() and CallFunctionFast() <lang Purebasic>if OpenLibrary(0, "USER32.DLL")
*MessageBox = GetFunction(0, "MessageBoxA") CallFunctionFast(*MessageBox, 0, "Body", "Title", 0) CloseLibrary(0)
endif</lang> Since versions 4 the recommended way is via the usage of Prototypes even if the old system still is supported. <lang PureBasic>Prototype.l ProtoMessageBoxW(Window.l, Body.p-unicode, Title.p-unicode, Flags.l = 0)
If OpenLibrary(0, "User32.dll")
MsgBox.ProtoMessageBoxW = GetFunction(0, "MessageBoxW") MsgBox(0, "Hello", "World") CloseLibrary(0)
EndIf</lang>
Python
The import statement can be done in a try/except block <lang python>try:
import psyco psyco.full() print "# With psyco JIT compiler"
except:
print "# Without psyco JIT compiler"
- Rest of program</lang>
Ruby
Using the standard
library:
<lang ruby>require 'dl/import'
FakeImgLib = DL.dlopen("/path/to/fakeimg.so")
module FakeImage
def self.openimage filename FakeImgLib["openimage", "IS"].call(filename)[0] end
end
handle = FakeImage.openimage("path/to/image")</lang>
Using the nicer
package ffi, following C example
<lang ruby>require 'ffi' module FakeImgLib
extend FFI::Library ffi_lib "path/to/fakeimglib.so" attach_function :openimage, [:string], :int
end
handle = FakeImgLib.openimage("path/to/image")</lang>
Smalltalk
The code tries to load the fakeimglib (cfr C example); if it succeed, the symbol openimage will exist, and will be called; otherwise, it is executed an "internal" code for openimage. In this example return code of the function of the library is ignored (ValueHolder null)
<lang smalltalk>DLD addLibrary: 'fakeimglib'.
Object subclass: ExtLib [
ExtLib class >> openimage: aString [ (CFunctionDescriptor isFunction: 'openimage') ifTrue: [ (CFunctionDescriptor for: 'openimage' returning: #int withArgs: #( #string ) ) callInto: (ValueHolder null). ] ifFalse: [ ('internal open image %1' % { aString }) displayNl ] ]
].
ExtLib openimage: 'test.png'.</lang>
Tcl
<lang Tcl>package require Ffidl
if {[catch {
ffidl::callout OpenImage {pointer-utf8} int [ffidl::symbol fakeimglib.so openimage]
}]} then {
# Create the OpenImage command by other means here...
}
set handle [OpenImage "/the/file/name"]</lang>
Note that if the library is appropriately set up with the correct entry function, it can be accessed directly with load
which will cause it to register a Tcl command for the functionality it exports. SWIG can be used to automatically generate the interface code. Alternatively, critcl can be used to allow writing glue C code directly embedded within a Tcl script.
With this many ways to perform the call, the best approach often depends on the size and complexity of the API being mapped. SWIG excels at large APIs, Ffidl is better when you just want to call a particular simple function, and critcl handles complex cases (callbacks, etc.) better than the other two.
Ursala
When abs(x) is evaluated, a run time check is performed for the availability of the system library's absolute value function (fabs), and if found, it is used. If not, the user defined replacement function is invoked. <lang Ursala>#import std
- import flo
my_replacement = fleq/0.?/~& negative
abs = math.|fabs my_replacement</lang>