I had some time to try it out and I finally got it to work. I have only tried in windows so far but there was a pitfall in windows. Your dll need a DllMain entry to compile. This was the only thing that was missing from your information. The rest worked perfectly. This may be obvious to most around here, but I did not know before. So, I thought it might make sense to show my working solution in case someone else stumbles upon the same problem.

I wanted the app and the plugin to be independent projects form one another. So they both need a shared project containing the interface for the plugin. That makes the 3 projects shown below. I can than after compiling the app simply copy the plugin dll into a plugins folder and the app will find it on demand. So that I could later add more plugins if desired. Obviously in that case I would have to get a list of available plugins by reading the filenames in the plugins directory instead of a hard coded path in the app. I tried to reduce it to the bare minimum with a working solution. Even with the DllMain entry (which is necessary for windows) it turns out to be surprisingly compact.

There are 3 distinct projects. I have the following directories (omitting some generated by dub):

- PluginTest

-- PluginContract // all files for the shared plugin interface (separate project)
---- source
------ iplugin.d // the interface for the plugin
---- dub.json // the project file for the shared plugin interface

-- TestApp // all files for the app (separate project)
---- packages
------ DerelictUtil-master // contains the project for derelict
---- source
------ app.d // the app
---- dub.json // the project file for the app

-- TestPlugin // all files for the plugin (separate project)
---- source
------ someplugin.d // the plugin
---- dub.json // the project file for the plugin


Here are the files:

Shared plugin interface (Project 1)
===================================
Note, these are necessary for the linker to find the files:
"targetType": "library"
"targetPath": "lib"

PluginTest/PluginContract/dub.json
----------------------------------
{
        "name": "plugincontract",
        "authors": ["root"],
        "description": "A minimal D application.",
        "copyright": "Copyright © 2016, root",
        "license": "proprietary",
        "platforms": ["windows"],
        "versions": ["DesktopApp"],
        "targetType": "library",
        "configurations": [
        {
                "name": "debug",
                "targetPath": "lib",
                "buildOptions": ["debugMode", "debugInfo"],
        },
        {
                "name": "release",
                "targetPath": "lib",
                "buildOptions": ["releaseMode", "optimize", "inline"],
        }
        ]
}


PluginTest/PluginContract/source/iplugin.d
------------------------------------------
module iplugin;

interface IPlugin
{
        void Talk(string msg);
}





TestApp (Project 2)
===================

PluginTest/TestApp/dub.json
---------------------------
{
        "name": "testapp",
        "authors": ["root"],
        "description": "A minimal D application.",
        "copyright": "Copyright © 2016, root",
        "license": "proprietary",
        "platforms": ["windows"],
        "versions": ["DesktopApp"],
        "targetType": "executable",
        "dependencies": {
                "derelict-util":  {"path": "packages/DerelictUtil-master"},
                "plugincontract": {"path": "../PluginContract"}
        },
        "configurations": [
        {
                "name": "debug",
                "targetPath": "bin/debug",
                "buildOptions": ["debugMode", "debugInfo"],
        },
        {
                "name": "release",
                "targetPath": "bin/release",
                "buildOptions": ["releaseMode", "optimize", "inline"],
        }
        ]
}


PluginTest/TestApp/source/app.d
-------------------------------
import std.stdio;
import derelict.util.sharedlib;
import iplugin;

alias GetPluginImpl = extern(C) nothrow IPlugin function();
GetPluginImpl getPlugin;

void main()
{
        SharedLib lib;
        lib.load(["plugins/testplugin.dll"]);
        getPlugin = cast(GetPluginImpl)lib.loadSymbol("getPlugin");

        auto plugin = getPlugin();
        plugin.Talk("Hello World.");

        writeln("End of app.");
}




TestPlugin (Project 3)
======================

PluginTest/TestPlugin/dub.json
------------------------------
{
        "name": "testplugin",
        "authors": ["root"],
        "description": "A minimal D application.",
        "copyright": "Copyright © 2016, root",
        "license": "proprietary",
        "platforms": ["windows"],
        "versions": ["DesktopApp"],
        "targetType": "dynamicLibrary",
        "importPaths": ["../PluginContract"],
        "dependencies": {
                "plugincontract": {"path": "../PluginContract"}
        },
        "configurations": [
        {
                "name": "debug",
                "targetPath": "bin/debug",
                "buildOptions": ["debugMode", "debugInfo"],
        },
        {
                "name": "release",
                "targetPath": "bin/release",
                "buildOptions": ["releaseMode", "optimize", "inline"],
        }
        ]
}

PluginTest/TestPlugin/source/SomePlugin.d
-----------------------------------------
module someplugin;
import std.stdio;
import iplugin;


export extern(C) nothrow IPlugin getPlugin()
{
        import core.memory : GC;
        auto plugin = new SomePlugin();

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


class SomePlugin : IPlugin
{
        //      this() {}
        void Talk(string msg)
        {
                writefln("SomePlugin: %s", msg);
        }
}

//shared static this() { printf("plugin shared static this\n"); }
//shared static ~this() { printf("plugin shared static ~this\n"); }


version(Windows)
extern(Windows) bool DllMain(void* hInstance, uint ulReason, void*)
{
        import std.c.windows.windows;
        import core.sys.windows.dll;
        switch (ulReason)
        {
                default: assert(0);
                case DLL_PROCESS_ATTACH:
                                 dll_process_attach( hInstance, true );
                                 break;

                case DLL_PROCESS_DETACH:
                                 dll_process_detach( hInstance, true );
                                 break;

                case DLL_THREAD_ATTACH:
                                 dll_thread_attach( true, true );
                                 break;

                case DLL_THREAD_DETACH:
                                 dll_thread_detach( true, true );
                                 break;
        }
        return true;
}


Hope it helps someone. And again thanks for the help!


Reply via email to