8.39 Dynamic Linking to external code

8.39.1 C Type Constants

These C type constants are used when defining external C functions in a shared library file.

Example 1:

See define_c_proc

See Also:

define_c_proc, define_c_func, define_c_var

8.39.1.1 C_CHAR

include std/dll.e
namespace dll
public constant C_CHAR

char 8-bits

8.39.1.2 C_BYTE

include std/dll.e
namespace dll
public constant C_BYTE

byte 8-bits

8.39.1.3 C_UCHAR

include std/dll.e
namespace dll
public constant C_UCHAR

unsigned char 8-bits

8.39.1.4 C_UBYTE

include std/dll.e
namespace dll
public constant C_UBYTE

ubyte 8-bits

8.39.1.5 C_SHORT

include std/dll.e
namespace dll
public constant C_SHORT

short 16-bits

8.39.1.6 C_WORD

include std/dll.e
namespace dll
public constant C_WORD

word 16-bits

8.39.1.7 C_USHORT

include std/dll.e
namespace dll
public constant C_USHORT

unsigned short 16-bits

8.39.1.8 C_INT

include std/dll.e
namespace dll
public constant C_INT

int 32-bits

8.39.1.9 C_BOOL

include std/dll.e
namespace dll
public constant C_BOOL

bool 32-bits

8.39.1.10 C_UINT

include std/dll.e
namespace dll
public constant C_UINT

unsigned int 32-bits

8.39.1.11 C_SIZE_T

include std/dll.e
namespace dll
public constant C_SIZE_T

size_t 32-bits

8.39.1.12 C_LONG

include std/dll.e
namespace dll
public constant C_LONG

long 32-bits

8.39.1.13 C_ULONG

include std/dll.e
namespace dll
public constant C_ULONG

unsigned long 32-bits

8.39.1.14 C_POINTER

include std/dll.e
namespace dll
public constant C_POINTER

any valid pointer 32-bits

8.39.1.15 C_HANDLE

include std/dll.e
namespace dll
public constant C_HANDLE

handle 32-bits

8.39.1.16 C_HWND

include std/dll.e
namespace dll
public constant C_HWND

hwnd 32-bits

8.39.1.17 C_DWORD

include std/dll.e
namespace dll
public constant C_DWORD

dword 32-bits

8.39.1.18 C_WPARAM

include std/dll.e
namespace dll
public constant C_WPARAM

wparam 32-bits

8.39.1.19 C_LPARAM

include std/dll.e
namespace dll
public constant C_LPARAM

lparam 32-bits

8.39.1.20 C_HRESULT

include std/dll.e
namespace dll
public constant C_HRESULT

hresult 32-bits

8.39.1.21 C_FLOAT

include std/dll.e
namespace dll
public constant C_FLOAT

float 32-bits

8.39.1.22 C_DOUBLE

include std/dll.e
namespace dll
public constant C_DOUBLE

double 64-bits

8.39.1.23 C_DWORDLONG

include std/dll.e
namespace dll
public constant C_DWORDLONG

dwordlong 64-bits

8.39.2 External Euphoria Type Constants

These are used for arguments to and the return value from a Euphoria shared library file (.dll, .so or .dylib).

8.39.2.1 E_INTEGER

include std/dll.e
namespace dll
public constant E_INTEGER

integer

8.39.2.2 E_ATOM

include std/dll.e
namespace dll
public constant E_ATOM

atom

8.39.2.3 E_SEQUENCE

include std/dll.e
namespace dll
public constant E_SEQUENCE

sequence

8.39.2.4 E_OBJECT

include std/dll.e
namespace dll
public constant E_OBJECT

object

8.39.3 Constants

8.39.3.1 NULL

include std/dll.e
namespace dll
public constant NULL

C's NULL pointer

8.39.4 Routines

8.39.4.1 open_dll

include std/dll.e
namespace dll
public function open_dll(sequence file_name)

Open a Windows dynamic link library (.dll) file, or a Unix shared library (.so) file.

Parameters:
  1. file_name : a sequence, the name of the shared library to open or a sequence of filename's to try to open.
Returns:

An atom, actually a 32-bit address. 0 is returned if the .dll can't be found.

Errors:

The length of file_name (or any filename contained therein) should not exceed 1,024 characters.

Comments:

file_name can be a relative or an absolute file name. Most operating systems will use the normal search path for locating non-relative files.

file_name can be a list of file names to try. On different Linux platforms especially, the filename will not always be the same. For instance, you may wish to try opening libmylib.so, libmylib.so.1, libmylib.so.1.0, libmylib.so.1.0.0. If given a sequence of file names to try, the first successful library loaded will be returned. If no library could be loaded, 0 will be returned after exhausting the entire list of file names.

The value returned by open_dll() can be passed to define_c_proc(), define_c_func(), or define_c_var().

You can open the same .dll or .so file multiple times. No extra memory is used and you'll get the same number returned each time.

Euphoria will close the .dll/.so for you automatically at the end of execution.

Example 1:
atom user32
user32 = open_dll("user32.dll")
if user32 = 0 then
   puts(1, "Couldn't open user32.dll!\n")
end if
Example 2:
atom mysql_lib
mysql_lib = open_dll({"libmysqlclient.so", "libmysqlclient.so.15", 
                     "libmysqlclient.so.15.0"})
if mysql_lib = 0 then
  puts(1, "Couldn't find the mysql client library\n")
end if
See Also:

define_c_func, define_c_proc, define_c_var, c_func, c_proc

8.39.4.2 define_c_var

include std/dll.e
namespace dll
public function define_c_var(atom lib, sequence variable_name)

Gets the address of a symbol in a shared library or in RAM.

Parameters:
  1. lib : an atom, the address of a Linux or FreeBSD shared library, or Windows .dll, as returned by open_dll().
  2. variable_name : a sequence, the name of a public C variable defined within the library.
Returns:

An atom, the memory address of variable_name.

Comments:

Once you have the address of a C variable, and you know its type, you can use peek() and poke() to read or write the value of the variable. You can in the same way obtain the address of a C function and pass it to any external routine that requires a callback address.

Example:

see euphoria/demo/linux/mylib.ex

See Also:

c_proc, define_c_func, c_func, open_dll

8.39.4.3 define_c_proc

include std/dll.e
namespace dll
public function define_c_proc(object lib, object routine_name, sequence arg_types)

Define the characteristics of either a C function, or a machine-code routine that you wish to call as a procedure from your Euphoria program.

Parameters:
  1. lib : an object, either an entry point returned as an atom by open_dll(), or "" to denote a routine the RAM address is known.
  2. routine_name : an object, either the name of a procedure in a shared object or the machine address of the procedure.
  3. argtypes : a sequence of type constants.
Returns:

A small integer, known as a routine id, will be returned.

Errors:

The length of name should not exceed 1,024 characters.

Comments:

Use the returned routine id as the first argument to c_proc() when you wish to call the routine from Euphoria.

A returned value of -1 indicates that the procedure could not be found or linked to.

On Windows, you can add a '+' character as a prefix to the procedure name. This tells Euphoria that the function uses the cdecl calling convention. By default, Euphoria assumes that C routines accept the stdcall convention.

When defining a machine code routine, lib must be the empty sequence, "" or {}, and routine_name indicates the address of the machine code routine. You can poke the bytes of machine code into a block of memory reserved using allocate(). On Windows, the machine code routine is normally expected to follow the stdcall calling convention, but if you wish to use the cdecl convention instead, you can code {'+', address} instead of address.

argtypes is made of type constants, which describe the C types of arguments to the procedure. They may be used to define machine code parameters as well.

The C function that you define could be one created by the Euphoria To C Translator, in which case you can pass Euphoria data to it, and receive Euphoria data back. A list of Euphoria types is shown above.

You can pass any C integer type or pointer type. You can also pass a Euphoria atom as a C double or float.

Parameter types which use 4 bytes or less are all passed the same way, so it is not necessary to be exact.

Currently, there is no way to pass a C structure by value. You can only pass a pointer to a structure. However, you can pass a 64 bit integer by pretending to pass two C_LONG instead. When calling the routine, pass low doubleword first, then high doubleword.

The C function can return a value but it will be ignored. If you want to use the value returned by the C function, you must instead define it with define_c_func() and call it with c_func().

Example 1:
atom user32
integer ShowWindow

-- open user32.dll - it contains the ShowWindow C function
user32 = open_dll("user32.dll")

-- It has 2 parameters that are both C int.
ShowWindow = define_c_proc(user32, "ShowWindow", {C_INT, C_INT})
-- If ShowWindow used the cdecl convention, 
-- we would have coded "+ShowWindow" here

if ShowWindow = -1 then
    puts(1, "ShowWindow not found!\n")
end if
See Also:

c_proc, define_c_func, c_func, open_dll

8.39.4.4 define_c_func

include std/dll.e
namespace dll
public function define_c_func(object lib, object routine_name, sequence arg_types,
        atom return_type)

Define the characteristics of either a C function, or a machine-code routine that returns a value.

Parameters:
  1. lib : an object, either an entry point returned as an atom by open_dll(), or "" to denote a routine the RAM address is known.
  2. routine_name : an object, either the name of a procedure in a shared object or the machine address of the procedure.
  3. argtypes : a sequence of type constants.
  4. return_type : an atom, indicating what type the function will return.
Returns:

A small integer, known as a routine id, will be returned.

Errors:

The length of name should not exceed 1,024 characters.

Comments:

Use the returned routine id as the first argument to c_proc() when you wish to call the routine from Euphoria.

A returned value of -1 indicates that the procedure could not be found or linked to.

On Windows, you can add a '+' character as a prefix to the function name. This indicates to Euphoria that the function uses the cdecl calling convention. By default, Euphoria assumes that C routines accept the stdcall convention.

When defining a machine code routine, x1 must be the empty sequence, "" or {}, and x2 indicates the address of the machine code routine. You can poke the bytes of machine code into a block of memory reserved using allocate(). On Windows, the machine code routine is normally expected to follow the stdcall calling convention, but if you wish to use the cdecl convention instead, you can code {'+', address} instead of address for x2.

The C function that you define could be one created by the Euphoria To C Translator, in which case you can pass Euphoria data to it, and receive Euphoria data back. A list of Euphoria types is contained in dll.e:

  • E_INTEGER = #06000004
  • E_ATOM = #07000004
  • E_SEQUENCE= #08000004
  • E_OBJECT = #09000004

You can pass or return any C integer type or pointer type. You can also pass a Euphoria atom as a C double or float, and get a C double or float returned to you as a Euphoria atom.

Parameter types which use 4 bytes or less are all passed the same way, so it is not necessary to be exact when choosing a 4-byte parameter type. However the distinction between signed and unsigned may be important when you specify the return type of a function.

Currently, there is no way to pass a C structure by value or get a C structure as a return result. You can only pass a pointer to a structure and get a pointer to a structure as a result. However, you can pass a 64 bit integer as two C_LONG instead. On calling the routine, pass low doubleword first, then high doubleword.

If you are not interested in using the value returned by the C function, you should instead define it with define_c_proc() and call it with c_proc().

If you use euiw to call a cdecl C routine that returns a floating-point value, it might not work. This is because the Watcom C compiler (used to build euiw) has a non-standard way of handling cdecl floating-point return values.

Passing floating-point values to a machine code routine will be faster if you use c_func() rather than call() to call the routine, since you won't have to use atom_to_float64() and poke() to get the floating-point values into memory.

Example 1:
atom user32
integer LoadIcon

-- open user32.dll - it contains the LoadIconA C function
user32 = open_dll("user32.dll")

-- It takes a C pointer and a C int as parameters.
-- It returns a C int as a result.
LoadIcon = define_c_func(user32, "LoadIconA",
                         {C_POINTER, C_INT}, C_INT)
-- We use "LoadIconA" here because we know that LoadIconA
-- needs the stdcall convention, as do
-- all standard .dll routines in the WINDOWS API. 
-- To specify the cdecl convention, we would have used "+LoadIconA".

if LoadIcon = -1 then
    puts(1, "LoadIconA could not be found!\n")
end if
See Also:

demo\callmach.ex, c_func, define_c_proc, c_proc, open_dll

8.39.4.5 c_func

<built-in> function c_func(integer rid, sequence args={})

Call a C function, or machine code function, or translated/compiled Euphoria function by routine id.

Parameters:
  1. rid : an integer, the routine_id of the external function being called.
  2. args : a sequence, the list of parameters to pass to the function
Returns:

An object, whose type and meaning was defined on calling define_c_func().

Errors:

If rid is not a valid routine id, or the arguments do not match the prototype of the routine being called, an error occurs.

Comments:

rid must have been returned by define_c_func(), not by routine_id(). The type checks are different, and you would get a machine level exception in the best case.

If the function does not take any arguments then args should be {}.

If you pass an argument value which contains a fractional part, where the C function expects a C integer type, the argument will be rounded towards 0. e.g. 5.9 will be passed as 5, -5.9 will be passed as -5.

The function could be part of a .dll or .so created by the Euphoria To C Translator. In this case, a Euphoria atom or sequence could be returned. C and machine code functions can only return integers, or more generally, atoms (IEEE floating-point numbers).

Example 1:
atom user32, hwnd, ps, hdc
integer BeginPaint

-- open user32.dll - it contains the BeginPaint C function
user32 = open_dll("user32.dll")

-- the C function BeginPaint takes a C int argument and
-- a C pointer, and returns a C int as a result:
BeginPaint = define_c_func(user32, "BeginPaint",
                           {C_INT, C_POINTER}, C_INT)

-- call BeginPaint, passing hwnd and ps as the arguments,
-- hdc is assigned the result:
hdc = c_func(BeginPaint, {hwnd, ps})
See Also:

c_proc, define_c_proc, open_dll, Platform-Specific Issues

8.39.4.6 c_proc

<built-in> procedure c_proc(integer rid, sequence args={})

Call a C void function, or machine code function, or translated/compiled Euphoria procedure by routine id.

Parameters:
  1. rid : an integer, the routine_id of the external function being called.
  2. args : a sequence, the list of parameters to pass to the function
Errors:

If rid is not a valid routine id, or the arguments do not match the prototype of the routine being called, an error occurs.

Comments:

rid must have been returned by define_c_proc(), not by routine_id(). The type checks are different, and you would get a machine level exception in the best case.

If the procedure does not take any arguments then args should be {}.

If you pass an argument value which contains a fractional part, where the C void function expects a C integer type, the argument will be rounded towards 0. e.g. 5.9 will be passed as 5, -5.9 will be passed as -5.

Example 1:
atom user32, hwnd, rect
integer GetClientRect

-- open user32.dll - it contains the GetClientRect C function
user32 = open_dll("user32.dll")

-- GetClientRect is a VOID C function that takes a C int
-- and a C pointer as its arguments:
GetClientRect = define_c_proc(user32, "GetClientRect",
                              {C_INT, C_POINTER})

-- pass hwnd and rect as the arguments
c_proc(GetClientRect, {hwnd, rect})
See Also:

c_func, define_c_func, open_dll, Platform-Specific Issues

8.39.4.7 call_back

include std/dll.e
namespace dll
public function call_back(object id)

Get a machine address for an Euphoria procedure.

Parameters:
  1. id : an object, either the id returned by routine_id for the function/procedure, or a pair {'+', id}.
Returns:

An atom, the address of the machine code of the routine. It can be used by Windows, or an external C routine in a Windows .dll or Unix-like shared library (.so), as a 32-bit "call-back" address for calling your Euphoria routine.

Errors:

The length of name should not exceed 1,024 characters.

Comments:

By default, your routine will work with the stdcall convention. On Windows, you can specify its id as {'+', id}, in which case it will work with the cdecl calling convention instead. On non-Microsoft platforms, you should only use simple IDs, as there is just one standard calling convention, i.e. cdecl.

You can set up as many call-back functions as you like, but they must all be Euphoria functions (or types) with 0 to 9 arguments. If your routine has nothing to return (it should really be a procedure), just return 0 (say), and the calling C routine can ignore the result.

When your routine is called, the argument values will all be 32-bit unsigned (positive) values. You should declare each parameter of your routine as atom, unless you want to impose tighter checking. Your routine must return a 32-bit integer value.

You can also use a call-back address to specify a Euphoria routine as an exception handler in the Linux/FreeBSD signal() function. For example, you might want to catch the SIGTERM signal, and do a graceful shutdown. Some Web hosts send a SIGTERM to a CGI process that has used too much CPU time.

A call-back routine that uses the cdecl convention and returns a floating-point result, might not work with euiw. This is because the Watcom C compiler (used to build euiw) has a non-standard way of handling cdecl floating-point return values.

Example 1:

See: demo\win32\window.exw, demo\linux\qsort.ex

See Also:

routine_id