Using .lib and .dll in D applications

Mike Parker via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Jun 20 06:47:58 PDT 2016


On Monday, 20 June 2016 at 11:25:04 UTC, moe wrote:

>
> Where I still have a problem is with a plugin system. I would 
> like to write an app that is plugin based. So that I can write 
> a plugin to extend the functionality of the app. I imagine that 
> I could copy the plugin into a folder from the (compiled) app 
> and the app would be able to use the plugin without me having 
> to recompile the app. I have been looking at LoadLibrary 
> (https://dlang.org/library/core/runtime/runtime.load_library.html) and Object.factory (https://dlang.org/library/object/object.factory.html) but I don't quite get how to use them. I did find some info that was several years old. They mentioned that these functions are not yet ready for all platforms. I don't know if that has changed in the meantime. It is not clear from the documentation.
>
> I would like to find some snippets or examples but that sort of 
> resource for D is very hard to find online. Most of the time 
> there are only some partial snippets where it turns out they 
> only work under specific conditions or are restricted to one 
> platform. Does D not supply some infrastructure for this kind 
> of a task? It seams to me that is a very common approach to 
> build an app. I expected there would be some out of the box 
> solution for this in D. I would write the app and the plugins 
> purely in D (no C bindings if possible).
>

The problem is that shared library (DLL) support in D is not at a 
level where you can load one at runtime and treat it as the rest 
of your D code (at least on Windows -- I'm not sure how complete 
support is elsewhere). Until it is, there are a number of steps 
you can take to work around potential problems.

> I have tried to build a mini app which defines an interface for 
> the plugins. The plugins then implement that interface. But 
> when I want to load the plugin both LoadLibrary() and 
> Object.factory() fail (I am aware that Object.factory() 
> requires the full qualified class names).
>
> Is there a recommended way of writing a simple plugin system 
> that works on windows and linux? Maybe someone has a working 
> example or knows a book that covers how to write a plugin 
> system?

You've got the right idea in defining an interface, but don't 
bother with Object.factory. Here's a brief rundown of how I 
wouldn handle it.

Let's assume we'e got an interface like so:

enum PluginError {
    none,
    initFileSys,
    initAudio,
    initSomethingElse
}

class PluginException : Exception { ... }

interface Plugin {
    bool initialize();
    void terminate();
    Throwable getLastException();
    SomeObject getSomeObject();
    void returnSomeObject(SomeObject);
}

Now, I would take the following steps:

* Any free functions in the D code for the DLL would be marked as 
extern(C). This does not make them "C functions" in that you 
can't use D with them. It only turns off D symbol decoration so 
that they look like C functions to the linker and ensures that 
they have the cdecl calling convention. This makes them much 
easier to load, as the symbol names will match the function names.

* Any objects that need to be used in the application from the 
plugin should be allocated in the DLL. Be careful with this. You 
have to initialize the runtime yourself in your DLL, so you have 
two different GC instances running: one in the app and one in the 
DLL. I believe on Linux its possible to use Phobos + DRuntime as 
a shared library, so you have only one GC, but on Windows it is 
not. This means you should keep a reference to any objects you 
allocate in the DLL to make sure they aren't collected while the 
app is using them. When the app is finished with an instance, it 
should let the DLL know.

* Don't allocate Exceptions on the DLL side for use on the app 
side. You can't throw them across boundaries anyway, but you also 
have the issue of two separate GC instances. If you return an 
Exception somehow (like, plugin.getLastError or something) it 
will probably never be collected or, if the DLL terminates before 
the exception propagates fully, the memory will be invalid. Hence 
my use of PluginError above.

One possible use of the interface:

=================
// In the DLL code
export extern(C) nothrow Plugin getPluginImpl() {
     import core.memory : GC;
     auto plugin = new PluginImpl();

     // Make sure the GC doesn't collect the instance
     GC.addRoot(cast(void*)plugin);
     return plugin;
}

// In the app code
// You'll use this to load the plugin
alias GetPluginImpl = extern(c) nothrow function();
GetPluginImpl getPluginImpl;

// Using DerelictUtil [1] makes it easy.
// Use derelict-util version ~>2.0.6 as a dub dependency
import derelict.util.sharedlib;

// Keep this around if you need to unload the library manually
// at some point. It does not unload in the destructor, so you
// can let it go out of scope with no problems if you intend to
// keep the library open for the life of the program.
SharedLib lib;
lib.load(["plugin.dll"]);
getPluginImpl = 
cast(GetPluginImpl)lib.loadSymbol("getPluginImpl");

auto so = plugin.getSomeObject();

// Now get the plugin interface
auto plugin = getPluginImpl();

// And do some work
auto res = plugin.initialize();
if(res != PluginError.none) throw new PluginException(res);
===================================

Anyway, given my current level of knowledge about shared library 
support in D, this is the approach I would take. I may very well 
be behind the times, though, so if you discover this isn't all 
necessary, then great. But I'm pretty sure Linux has the most 
complete support of any of the platforms right now, while Windows 
and Mac are rather behind.

[1] 
https://github.com/DerelictOrg/DerelictUtil/blob/master/source/derelict/util/sharedlib.d



More information about the Digitalmars-d-learn mailing list