Complete Guide to Using DLLs in OpenEuphoria

Download on Github

Complete Guide to Using DLLs in OpenEuphoria

Why

Over the years many programs have been written that take advantage of previously written code stored in dynamically linked libraries. On Windows these libraries have a .dll extension and on Linux/Unix they often end with a .so extension. These libraries can provide functionality for your program that would otherwise take years to achieve. These features include networking, graphics, sound, GUI, and much more.

What

This is a non-trivial topic and will need to be spread out over several sections. In this section we'll build a small DLL that will start our adventure into using libraries in our Euphoria programs. In the next few sections we'll explore some real world examples.

Prerequisites

This guide has prerequisites. I believe the lists below to be complete. If any other prerequisites pop up during the evolution of this guide I'll be sure to update this section.

Knowledge

At this point it's unavoidable. Some knowledge of C is required. While expert knowledge is not required you should be familiar with C header files and C data types. Additionally, I'll assume that you understand pointers and structs as many, perhaps all, DLLs that you'll want to use will take advantage of at least one of these features, probably both.

I like using Makefiles for tasks. If you copy my examples make sure you know that line indents in a Makefile are [TAB]s not [SPACE]s

Reading

  • Dynamic Linking to external code - Most of this page describes C type constants. There are only 7 functions in std/dll.e and you'll need 6 of them in this first guide.
  • Machine Level Access - For this guide you'll primarily use the poke and peek functions from ste/machine.e. There are several of these and you should know what each does.
  • UsingDLLs - This is a light and breezy introduction to this topic.

Tools

It is possible to use other tool chains. In fact people do it all the time. But this is the tool chain I'm using. If you prefer a different tool chain you may need to adapt the some parts of this guide to use your tools.

Additionally, I'm using Linux. I'll try hard to keep Linux-isms to a minimum as the entire concept is a near perfect match between Linux and other platforms.

How

The first step of this process is really about identifying the right library for the job. As you search for the right solution it's important to note that C is not CPP. It is possible to use CPP DLLs in OpenEuphoria but that first involves creating a C wrapper. So, if you want to skip wrapping CPP code into C wrappers, try to find the functionality you're looking for written in C in the first place.

So, What is a wrapper? "A wrapper function is a function in a computer program whose main purpose is to call a second function with little or no additional computation". In our case we are creating a DLL wrapper which I'll define as a collection of wrapper functions used to make working with DLLs practical and in some cases, possible. The goal is to translate the C header files to OpenEuphoria constructs and then use the wrapper in our programs.

Once your DLL is all wrapped up you'll want to distribute your new wrapper. This becomes a little sticky depending on what you're trying to do. Did you wrap your DLL for use in one of your own programs? Did you wrap the DLL so that other developers can use some cool C library? Is this a "standard" DLL that you expect to already exist on the target machine? In an attempt to solve some of these concerns this guide comes with a libhelper.e that will attempt to find a DLL in some reasonable locations based on OS. For me, I'll put DLLs in a directory called "ext" located right next to the wrapper. See the README for an example of installation instructions.

Example

Let's take a quick look at how the project is laid out.

├── guitar 
│   ├── ext 
│   │   └── guitar.so 
│   ├── guitar.e 
│   └── libhelper.e 
├── guitar.ex 
├── libsrc 
│   ├── guitar.c 
│   ├── guitar.h 
│   ├── guitar.so 
│   └── Makefile 
└── README 

  • guitar - directory is where the wrapper is located. This directory can be copied to install or bundled with your application.
  • guitar.e - is the wrapper file.
  • ext - directory is where the DLL goes.
  • guitar.ex - is a demo program using the DLL
  • libsrc - If you need to build the DLL the source and Makefile are here.
  • README - includes description, license and build/install info.

Full Source

guitar.h
typedef struct 
{ 
  int string_count; 
  char* brand; 
} Guitar; 
 
/* Initialize an new guitar struct  
 *  
 * returns a pointer to the new guitar 
 * */ 
Guitar * guitar_new(); 
 
/* Free up the guitar and release it from memory 
 * This function will also free up the guitar.brand memory 
 * */ 
void guitar_destroy(Guitar * guitar); 
 
/*Sets the string_count value for the guitar 
 *  
 * Guitar * guitar The struct that needs a new string count value 
 * Int count the new number of string for the guitar 
 * */ 
void guitar_set_string_count(Guitar * guitar, int count); 
 
/*Gets the string_count value for the guitar 
 *  
 * Guitar * guitar The struct holds the string count value we want 
 *  
 * returns an integer value for the number of strings. 
 * */ 
int guitar_get_string_count(Guitar * guitar); 
 
/*Sets the brand value for the guitar.   
 * Frees the current brand char * 
 * Copies the const * brand into a new char * 
 * sets the guitar brand to this new copied value.  
 *  
 * Guitar * guitar The struct that needs a new brand value 
 * */ 
void guitar_set_brand(Guitar * guitar, const char * brand); 
 
/*Gets the brand pointer for the guitar 
 *  
 * Guitar * guitar The struct holds the brand value we want 
 *  
 * returns an char * for the guitar brand. 
 * */ 
const char * guitar_get_brand(Guitar * guitar); 

guitar.c
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include "guitar.h" 
 
void guitar_set_string_count(Guitar * guitar, int count) 
{ 
  guitar->string_count = count; 
} 
 
int guitar_get_string_count(Guitar * guitar) 
{ 
  return guitar->string_count; 
} 
 
void guitar_set_brand(Guitar * guitar, const char * brand) 
{ 
  if (guitar->brand != NULL) 
  { 
      free(guitar->brand); 
  } 
  char * new_brand = (char*)malloc(strlen(brand)+1); 
  strncpy(new_brand, brand, strlen(brand)); 
  new_brand[strlen(brand)] = '\0'; 
  guitar->brand = new_brand; 
} 
 
const char * guitar_get_brand(Guitar * guitar) 
{ 
  return guitar->brand; 
} 
 
void guitar_destroy(Guitar * guitar) 
{ 
  if (guitar->brand != NULL) 
  { 
      free(guitar->brand); 
      guitar->brand=NULL; 
  } 
   
  free(guitar); 
} 
 
Guitar * guitar_new() 
{ 
  Guitar * guitar; 
  guitar = (Guitar*)malloc(sizeof(Guitar)); 
  memset(guitar, '\0', sizeof(Guitar)); 
  return guitar; 
} 

Makefile
COPY=cp 
REMOVE=rm 
all: 
	gcc -shared -fPIC -o guitar.so guitar.c  
	${COPY} guitar.so ../guitar/ext 
 
clean: 
	${REMOVE} guitar.so 
	${REMOVE} ../guitar/ext/guitar.so 
 

libhelper.e

namespace libhelper 
include std/filesys.e 
 
public function include_derived_ext_paths(sequence libdir, sequence lib) 
    sequence inc_paths = include_paths(1) 
    sequence retval = {} 
    sequence dll_name = sprintf("%s.%s", {lib, SHARED_LIB_EXT}) 
    retval = append(retval, dll_name) 
    sequence current = join_path({current_dir(), dll_name}) 
    retval = append(retval, current) 
    for i = 1 to length(inc_paths) do 
        sequence inc_path = inc_paths[i] 
        sequence last_char = {inc_path[$]} 
        if equal(last_char, SLASH) then 
            inc_path = inc_path[1..$-1] 
        end if 
        retval = append(retval, join_path({inc_path, libdir, "ext", dll_name})) 
    end for 
    return retval 
end function 

guitar.e

namespace guitar 
include std/dll.e 
include std/machine.e 
include std/pretty.e 
include libhelper.e 
 
-- Globals 
atom  dllid_guitar_dll,  
      rid_guitar_new,  
      rid_guitar_destroy,  
      rid_guitar_set_brand, 
      rid_guitar_get_brand, 
      rid_guitar_set_string_count, 
      rid_guitar_get_string_count 
 
-- Wrapper functions.  So using the dll seems more Euphoria like. 
public function guitar_new() 
  return c_func(rid_guitar_new, {}) 
end function 
 
public procedure guitar_destroy(atom guitar) 
  c_proc(rid_guitar_destroy, {guitar}) 
end procedure 
 
public procedure guitar_set_brand(atom guitar, sequence brand) 
  atom brand_ptr = allocate_string(brand) 
  c_proc(rid_guitar_set_brand, {guitar, brand_ptr} ) 
end procedure 
 
public function guitar_get_brand(atom guitar) 
  atom brand_ptr = c_func(rid_guitar_get_brand, {guitar}) 
  sequence brand = {} 
  brand = peek_string(brand_ptr) 
  return brand   
end function 
 
public procedure guitar_set_string_count(atom guitar, integer cnt) 
  c_proc(rid_guitar_set_string_count, {guitar, cnt} ) 
end procedure 
 
public function guitar_get_string_count(atom guitar) 
  integer cnt = c_func(rid_guitar_get_string_count, {guitar}) 
  return cnt 
end function 
 
-- Pseduo Link -- 
-- Make sure we can open the dll before we go any further -- 
dllid_guitar_dll = open_dll(include_derived_ext_paths("guitar", "guitar")) 
if dllid_guitar_dll = 0 then 
  puts(2, "Could not find or open dynamic lib guitar.  Tried the following: ") 
  pretty_print(2, include_derived_ext_paths("guitar", "guitar"), {2}) 
  abort(1) 
end if 
 
-- We need an identifier that represents our c routine.  This is accomplished by 
-- mapping the dll function prototype (or signature if you prefer) to something  
-- Euphoira will know how to handle. 
rid_guitar_new = define_c_func(dllid_guitar_dll, "guitar_new", {}, C_POINTER) 
rid_guitar_destroy = define_c_proc(dllid_guitar_dll, "guitar_destroy", {C_POINTER}) 
rid_guitar_set_brand = define_c_proc(dllid_guitar_dll, "guitar_set_brand", {C_POINTER, C_POINTER}) 
rid_guitar_get_brand = define_c_func(dllid_guitar_dll, "guitar_get_brand", {C_POINTER}, C_POINTER) 
rid_guitar_set_string_count = define_c_proc(dllid_guitar_dll, "guitar_set_string_count", {C_POINTER, C_INT}) 
rid_guitar_get_string_count = define_c_func(dllid_guitar_dll, "guitar_get_string_count", {C_POINTER}, C_INT) 
 

guitar.ex

#!/usr/bin/env eui 
include guitar.e 
atom guitar = guitar_new() 
guitar_set_brand(guitar, "fender") 
guitar_set_string_count(guitar, 6) 
printf(1, "Your guitar is a %d string %s\n", {guitar_get_string_count(guitar), guitar_get_brand(guitar)}) 
guitar_destroy(guitar) 

Copyright (c) 2015 Ronald Weidner 
 
Permission is hereby granted, free of charge, to any person obtaining a  
copy of this software and associated documentation files (the "Software"),  
to deal in the Software without restriction, including without limitation  
the rights to use, copy, modify, merge, publish, distribute, sublicense,  
and/or sell copies of the Software, and to permit persons to whom the  
Software is furnished to do so, subject to the following conditions: 
 
The above copyright notice and this permission notice shall be included  
in all copies or substantial portions of the Software. 
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF  
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE  
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 
Description: 
An example of wrapping up and packaging a DLL for deployment. 
 
Building: 
$> cd libsrc 
$> make 
 
If you're using another OS other than Linux, you may need to modify 
the Makefile such that the REMOVE file command and the COPY file command 
match your OS.  (ie "del" and "copy") 
 
Installing: 
To install this library globally, simply copy the directory "guitar" 
to your Euphoria include directory.  For example: 
 
$> cp -R guitar /usr/local/share/euphoria/include 
 
Bundling with your application: 
 
To bundle with your application you have at least 3 choices: 
1. provide download instructions for your user 
2. place the guitar directory next to your executable program 
3. place the guitar directory in the include paths 
 
Thanks. 
 

Search



Quick Links

User menu

Not signed in.

Misc Menu