Getting started with DLLs

Build dmd / druntime / phobos yourself

Use my binary distribution

You can get the binary distribution here: dmd-dll2.zip

Test your setup

Finally try a shared version of hello world:

hello.d:

module hello;

import std.stdio;

void main(string[] args)
{
	writefln("Hello World!");
}
dmd -m64 -defaultlib=phobos64s hello.d
hello.exe
or
dmd -m32mscoff -defaultlib=phobos32mscoffs hello.d
hello.exe
Specyfing the -defaultlib prameter here tells the compiler to use the shared version of phobos instead of the static one.

Your first shared library

libImpl.d:
module lib;

version(D_Shared)
{
	import core.sys.windows.dll;

	// This dll is going to be used from D code. 
	// Which means we don't have to hijack all threads in 
	// existence as we have a shared druntime that knowns 
	// about all D threads already.
	mixin SimpleDllMain!(DllIsUsedFromC.no);
}

export class LibClass
{
public:
	void doSomething() 
	{
		import std.stdio;
		writefln("Something");
	}
}


lib.d:
module lib;

public import libImpl;
Note that the lib.d in this case is a "module import list". It tells the compiler which modules must be dll-imported when linking against the library. Every module that is publicly imported from this import list module is considered to be inside a dll. Non public imports are ignored. The import list module is a bit like a package.d. But instead for a single package its for your entire dynamic library. The import list module needs to list all modules that are part of your librarys interface. Internal modules, which are not visible to the library user, don't need to be listed. When building the main program below you will see that the import list module is specified via the -import switch. The -import switch can appear multiple times.
The D_Shared version identifier is automatically set when building with the -shared flag. If you don't want to annotate your library with 'export' you can use the -shared=exportall switch. This will export all relevant symbols in a conservative way. This means a lot of symbols will be exported unnecessarily. It is recommended to properly annotate your code with 'export' but as this might be significant effort the -shared=exportall switch is provided.

program.d:
module program;
import lib;

void main(string[] args)
{
	auto inst = new LibClass();
	inst.doSomething();
}

Compile into a dll and executable:

dmd -m64 -shared lib.d libImpl.d -L/IMPLIB:lib-dynamic.lib
dmd -m64 -import=lib program.d lib-dynamic.lib -ofprogram-dynamic.exe
Compile without export annotation:
dmd -m64 -shared=exportall lib.d libImpl.d -L/IMPLIB:lib-dynamic.lib
dmd -m64 -import=lib program.d lib-dynamic.lib -ofprogram-dynamic.exe
Note that we don't specify -defaultlib=phobos64s here. This is not necessary as both -import and -shared automatically change the default library to the shared version of phobos. Also when building Dlls which are used from a D main program all D-Dlls and the main program must link against the shared version of phobos. Otherwise dynamic casts, throwing exceptions across dll boundaries and various other operations will not work.

Compile into a single statically linked executable:

dmd -m64 libImpl.d -lib -oflib-static.lib
dmd -m64 program.d lib-static.lib -ofprogram-static.exe

Standalone shared library used from C

plugin.d:
module plugin;

import core.sys.windows.dll;

mixin SimpleDllMain!(DllIsUsedFromC.yes);

export extern(C) void pluginEntry()
{
    //...
}
Compile:
dmd -m64 -shared plugin.d -ofplugin.dll