Win32 DLLs in D

From D Wiki
Revision as of 11:06, 24 February 2014 by Verax (talk | contribs) (mydll.def:)
Jump to: navigation, search

DLLs (Dynamic Link Libraries) are one of the foundations of system programming for Windows. The D programming language enables the creation of several different types of DLLs.

For background information on what DLLs are and how they work Chapter 11 of Jeffrey Richter's book Advanced Windows is indispensible.

This guide will show how to create DLLs of various types with D.

Compiling a DLL

Use the -shared switch to tell the compiler that the generated code is to be put into a DLL. Code compiled for an EXE file will use the optimization assumption that _tls_index==0. Such code in a DLL will crash.

DLLs with a C Interface

A DLL presenting a C interface can connect to any other code in a language that supports calling C functions in a DLL.

DLLs can be created in D in roughly the same way as in C. A DllMain() is required, looking like:

import std.c.windows.windows;
import core.sys.windows.dll;

__gshared HINSTANCE g_hInst;

extern (Windows)
BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
{
    switch (ulReason)
    {
	case DLL_PROCESS_ATTACH:
	    g_hInst = hInstance;
	    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;
}

Notes:

  • DllMain simply forwards to the appropriate helper functions. These setup the runtime, create thread objects for interaction with the garbage collector and initialize thread local storage data.
  • The DLL does not share its runtime or memory with other DLLs.
  • The first boolean argument to the dll-helper functions specify whether all threads should be controlled by the garbage collector. You might need more control over this behaviour if there are threads in the process that must not be suspended. In this case pass false to disable the automatic handling of all threads.
  • The presence of DllMain() is recognized by the compiler causing it to emit a reference to __acrtused_dll and the phobos.lib runtime library.

Link with a .def (Module Definition File) along the lines of:

LIBRARY         MYDLL
DESCRIPTION     'My DLL written in D'

EXETYPE		NT
CODE            PRELOAD DISCARDABLE
DATA            WRITE

EXPORTS
		DllGetClassObject       @2
		DllCanUnloadNow         @3
		DllRegisterServer       @4
		DllUnregisterServer     @5

The functions in the EXPORTS list are for illustration. Replace them with the actual exported functions from MYDLL. Alternatively, use implib. Here's an example of a simple DLL with a function print() which prints a string:

mydll.d:

module mydll;
import std.c.stdio;
export void dllprint() { printf("hello dll world\n"); }

Note: We use printfs in these examples instead of writefln to make the examples as simple as possible.

mydll.def:

LIBRARY "mydll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE

Put the code above that contains DllMain() into a file dll.d. Compile and link the dll with the following command:

C:>dmd -ofmydll.dll -L/IMPLIB mydll.d dll.d mydll.def
C:>

which will create mydll.dll and mydll.lib. Now for a program, test.d, which will use the dll:

test.d:

import mydll;

int main()
{
   mydll.dllprint();
   return 0;
}

Create an interface file mydll.di that doesn't have the function bodies.