Using .lib and .dll in D applications

moe via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Jun 21 16:59:54 PDT 2016


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!




More information about the Digitalmars-d-learn mailing list