Call a function in a shared library: Difference between revisions

From Rosetta Code
Content added Content deleted
(added racket)
m (Updated Julia)
Line 304: Line 304:


=={{header|Julia}}==
=={{header|Julia}}==
<lang julia>#ccall((symbol, library), RetType, (ArgType1, ...), ArgVar1, ...)
Julia has the `ccall` function which follows the form: ccall((symbol, library), RetType, (ArgType1, ...), ArgVar1, ...)
<lang julia>
ccall( (:GetDoubleClickTime, "User32"), stdcall,
#this example works on Windows
Uint, (), )</lang>
ccall( (:GetDoubleClickTime, "User32"), stdcall,
Uint, (), )

ccall( (:clock, "libc"), Int32, ())</lang>
For more information, see here [http://docs.julialang.org/en/latest/manual/calling-c-and-fortran-code.html]


=={{header|Maple}}==
=={{header|Maple}}==

Revision as of 19:59, 6 May 2013

Task
Call a function in a shared library
You are encouraged to solve this task according to the task description, using any language you may know.

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.

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.

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

Works with: [AutoHotkey.dll]


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>

BBC BASIC

The following shared libraries are automatically available: ADVAPI32.DLL, COMCTL32.DLL, COMDLG32.DLL, GDI32.DLL, KERNEL32.DLL, SHELL32.DLL, USER32.DLL and WINMM.DLL. <lang bbcbasic> SYS "MessageBox", @hwnd%, "This is a test message", 0, 0 </lang>

C

Works with: POSIX version .1-2001

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>

  1. include <stdlib.h>
  2. 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

Library: CFFI

<lang lisp>CL-USER> (cffi:load-foreign-library "libX11.so")

  1. <CFFI::FOREIGN-LIBRARY {1004F4ECC1}>

CL-USER> (cffi:foreign-funcall "XOpenDisplay"

                              :string #+sbcl (sb-posix:getenv "DISPLAY")
                                      #-sbcl ":0.0"
                              :pointer)
  1. .(SB-SYS:INT-SAP #X00650FD0)</lang>

D

<lang d>pragma(lib, "user32.lib");

import std.stdio, std.c.windows.windows;

extern(Windows) UINT GetDoubleClickTime();

void main() {

   writeln(GetDoubleClickTime());

}</lang>

500

Delphi

Static loading

Loads library on startup.

<lang Delphi>procedure DoSomething; external 'MYLIB.DLL';</lang>


Delayed loading

Loads library on first call to DoSomething.

<lang Delphi>procedure DoSomething; external 'MYLIB.DLL' delayed;</lang>


Dynamic loading

Loads and unloads library on demand.

<lang Delphi>var

 lLibraryHandle: THandle;
 lDoSomething: procedure; stdcall;

begin

 lLibraryHandle := LoadLibrary('MYLIB.DLL');
 if lLibraryHandle >= 32 then { success }
 begin
   lDoSomething := GetProcAddress(lLibraryHandle, 'DoSomething');
   lDoSomething();
   FreeLibrary(lLibraryHandle);
 end;

end;</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>

Library: JNA

<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>

Julia

Julia has the `ccall` function which follows the form: ccall((symbol, library), RetType, (ArgType1, ...), ArgVar1, ...) <lang julia>

  1. this example works on Windows

ccall( (:GetDoubleClickTime, "User32"), stdcall, Uint, (), )

ccall( (:clock, "libc"), Int32, ())</lang> For more information, see here [1]

Maple

<lang Maple>> cfloor := define_external( floor, s::float[8], RETURN::float[8], LIB = "libm.so" ): > cfloor( 2.3 );

                                  2.</lang>


Mathematica

This works on windows and on linux/mac too (through Mono) <lang Mathematica>Needs["NETLink`"]; externalFloor = DefineDLLFunction["floor", "msvcrt.dll", "double", { "double" }]; externalFloor[4.2] -> 4.</lang>

OCaml

As far as I know there is no solution in OCaml to load a function from a C library dynamically. 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>

OxygenBasic

<lang oxygenbasic> 'Loading a shared library at run time and calling a function.

declare MessageBox(sys hWnd, String text,caption, sys utype)

sys user32 = LoadLibrary "user32.dll"

if user32 then @Messagebox = getProcAddress user32,"MessageBoxA"

if @MessageBox then MessageBox 0,"Hello","OxygenBasic",0

'...

FreeLibrary user32 </lang>

PARI/GP

<lang parigp>install("function_name","G","gp_name","./test.gp.so");</lang> where "G" is the parser code; see section 5.7.3 in the User's Guide to the PARI library for more information.

Pascal

See Delphi

Perl 6

Works with: Rakudo Star version 2013-01

<lang perl6>use NativeCall;

constant libX11 = '/usr/lib/x86_64-linux-gnu/libX11.so.6';

sub XOpenDisplay(Str $s --> Int) is native(libX11) {*} sub XCloseDisplay(Int $i --> Int) is native(libX11) {*}

if try my $d = XOpenDisplay ":0.0" {

   say "ID = $d";
   XCloseDisplay($d);

} else {

   say "No library {libX11}!";
   say "Use this window instead --> ⬜";

}</lang>

Output:
ID = 124842864

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)

  1. 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))));

} /**/

  1. 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>

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>

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

Example that call User32.dll::GetDoubleClickTime() in windows. <lang python> import ctypes

user32_dll = ctypes.cdll.LoadLibrary('User32.dll') print user32_dll.GetDoubleClickTime() </lang>

Racket

<lang racket>#lang racket (require ffi/unsafe) (define libm (ffi-lib "libm")) ; get a handle for the C math library

look up sqrt in the math library. if we can't find it, return the builtin sqrt

(define extern-sqrt (get-ffi-obj 'sqrt libm (_fun _double -> _double)

                                (lambda () sqrt)))</lang>

Output:

> (extern-sqrt 42.0)
6.48074069840786

Ruby

Using the standard

Library: Ruby/DL

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

Library: RubyGems

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

Works with: GNU 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

Library: Ffidl

<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

  1. import flo

my_replacement = fleq/0.?/~& negative

abs = math.|fabs my_replacement</lang>