D Programming Language 2.0

- Comments
- D
- Search
- Downloads
- Home

Last update Thu Apr 17 15:03:36 2008

# Writing Win32 DLLs in D

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.

- DLLs with a C interface
- DLLs that are COM servers
- D code calling D code in DLLs

## 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;
    HINSTANCE g_hInst;

    extern (C)
    {
    	void gc_init();
    	void gc_term();
    	void _minit();
    	void _moduleCtor();
    	void _moduleUnitTests();
    }

    extern (Windows)
    BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
    {
        switch (ulReason)
        {
    	case DLL_PROCESS_ATTACH:
    	    gc_init();			// initialize GC
    	    _minit();			// initialize module list
    	    _moduleCtor();		// run module constructors
    	    _moduleUnitTests();		// run module unit tests
    	    break;

    	case DLL_PROCESS_DETACH:
    	    gc_term();			// shut down GC
    	    break;

    	case DLL_THREAD_ATTACH:
    	case DLL_THREAD_DETACH:
    	    // Multiple threads not supported yet
    	    return false;
        }
        g_hInst=hInstance;
        return true;
    }

Notes:

- The `_moduleUnitTests()` call is optional.
- 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            PRELOAD SINGLE

    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:

#### mydll2.d:

    module mydll;
    export void dllprint() { printf("hello dll world\n"); }

Note: We use `printf`s 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 mydll2.d dll.d mydll.def
    C:>implib/system mydll.lib mydll.dll
    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 a clone of mydll2.d that doesn't have the function bodies:

#### mydll.d:

    export void dllprint();

Compile and link with the command:

    C:>dmd test.d mydll.lib
    C:>

and run:

    C:>test
    hello dll world
    C:>

### Memory Allocation

D DLLs use garbage collected memory management. The question is what happens when pointers to allocated data cross DLL boundaries? If the DLL presents a C interface, one would assume the reason for that is to connect with code written in other languages. Those other languages will not know anything about D's memory management. Thus, the C interface will have to shield the DLL's callers from needing to know anything about it.

There are many approaches to solving this problem:

- Do not return pointers to D gc allocated memory to the caller of the DLL. Instead, have the caller allocate a buffer, and have the DLL fill in that buffer.
- Retain a pointer to the data within the D DLL so the GC will not free it. Establish a protocol where the caller informs the D DLL when it is safe to free the data.
- Use operating system primitives like VirtualAlloc() to allocate memory to be transferred between DLLs.
- Use std.c.stdlib.malloc() (or another non-gc allocator) when allocating data to be returned to the caller. Export a function that will be used by the caller to free the data.

## COM Programming

Many Windows API interfaces are in terms of COM (Common Object Model) objects (also called OLE or ActiveX objects). A COM object is an object who's first field is a pointer to a vtbl[], and the first 3 entries in that vtbl[] are for QueryInterface(), AddRef(), and Release().

For understanding COM, Kraig Brockshmidt's Inside OLE is an indispensible resource.

COM objects are analogous to D interfaces. Any COM object can be expressed as a D interface, and every D object with an interface X can be exposed as a COM object X. This means that D is compatible with COM objects implemented in other languages.

While not strictly necessary, the Phobos library provides an Object useful as a super class for all D COM objects, called ComObject. ComObject provides a default implementation for QueryInterface(), AddRef(), and Release().

Windows COM objects use the Windows calling convention, which is not the default for D, so COM functions need to have the attribute extern (Windows). So, to write a COM object:

    import std.c.windows.com;

    class MyCOMobject : ComObject
    {
        extern (Windows):
    	...
    }

The sample code includes an example COM client program and server DLL.

## D code calling D code in DLLs

Having DLLs in D be able to talk to each other as if they were statically linked together is, of course, very desirable as code between applications can be shared, and different DLLs can be independently developed.

The underlying difficulty is what to do about garbage collection (gc). Each EXE and DLL will have their own gc instance. While these gc's can coexist without stepping on each other, it's redundant and inefficient to have multiple gc's running. The idea explored here is to pick one gc and have the DLLs redirect their gc's to use that one. The one gc used here will be the one in the EXE file, although it's also possible to make a separate DLL just for the gc.

The example will show both how to statically load a DLL, and to dynamically load/unload it.

Starting with the code for the DLL, mydll.d:

    /*
     * MyDll demonstration of how to write D DLLs.
     */

    import std.c.stdio;
    import std.c.stdlib;
    import std.string;
    import std.c.windows.windows;
    import std.gc;

    HINSTANCE   g_hInst;

    extern (C)
    {
    	void _minit();
    	void _moduleCtor();
    	void _moduleDtor();
    	void _moduleUnitTests();
    }

    extern (Windows)
        BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
    {
        switch (ulReason)
        {
            case DLL_PROCESS_ATTACH:
    	    printf("DLL_PROCESS_ATTACH\n");
    	    break;

            case DLL_PROCESS_DETACH:
    	    printf("DLL_PROCESS_DETACH\n");
    	    std.c.stdio._fcloseallp = null; // so stdio doesn't get closed
    	    break;

            case DLL_THREAD_ATTACH:
    	    printf("DLL_THREAD_ATTACH\n");
    	    return false;

            case DLL_THREAD_DETACH:
    	    printf("DLL_THREAD_DETACH\n");
    	    return false;
        }
        g_hInst = hInstance;
        return true;
    }

    export void MyDLL_Initialize(void* gc)
    {
        printf("MyDLL_Initialize()\n");
        std.gc.setGCHandle(gc);
        _minit();
        _moduleCtor();
    //  _moduleUnitTests();
    }

    export void MyDLL_Terminate()
    {
        printf("MyDLL_Terminate()\n");
        _moduleDtor();			// run module destructors
        std.gc.endGCHandle();
    }

    static this()
    {
        printf("static this for mydll\n");
    }

    static ~this()
    {
        printf("static ~this for mydll\n");
    }

    /* --------------------------------------------------------- */

    class MyClass
    {
        char[] concat(char[] a, char[] b)
        {
    	return a ~ " " ~ b;
        }

        void free(char[] s)
        {
    	delete s;
        }
    }

    export MyClass getMyClass()
    {
        return new MyClass();
    }

DllMain
  This is the main entry point for any D DLL. It gets called by the C startup code (for DMC++, the source is `\dm\src\win32\dllstart.c`). The printf's are placed there so one can trace how it gets called. Notice that the initialization and termination code seen in the earlier DllMain sample code isn't there. This is because the initialization will depend on who is loading the DLL, and how it is loaded (statically or dynamically). There isn't much to do here. The only oddity is the setting of std.c.stdio._fcloseallp to null. If this is not set to null, the C runtime will flush and close all the standard I/O buffers (like stdout, stderr, etc.) shutting off further output. Setting it to null defers the responsibility for that to the caller of the DLL.

MyDLL_Initialize
  So instead we'll have our own DLL initialization routine so exactly when it is called can be controlled. It must be called after the caller has initialized itself, the Phobos runtime library, and the module constructors (this would normally be by the time main() was entered). This function takes one argument, a handle to the caller's gc. We'll see how that handle is obtained later. Instead of gc_init() being called to initialize the DLL's gc, std.gc.setGCHandle() is called and passed the handle to which gc to use. This step informs the caller's gc which data areas of the DLL to scan. Afterwards follows the call to the _minit() to initialize the module tables, and _moduleCtor() to run the module constructors. _moduleUnitTests() is optional and runs the DLL's unit tests. The function is exported as that is how a function is made visible outside of a DLL.

MyDLL_Terminate
  Correspondingly, this function terminates the DLL, and is called prior to unloading it. It has two jobs; calling the DLL's module destructors via _moduleDtor() and informing the runtime that the DLL will no longer be using the caller's gc via std.gc.endGCHandle(). That last step is critical, as the DLL will be unmapped from memory, and if the gc continues to scan its data areas it will cause segment faults.

static this, static ~this
  These are examples of the module's static constructor and destructor, here with a print in each to verify that they are running and when.

MyClass
  This is an example of a class that can be exported from and used by the caller of a DLL. The concat member function allocates some gc memory, and free frees gc memory.

getMyClass
  An exported factory that allocates an instance of MyClass and returns a reference to it.

To build the `mydll.dll` DLL:

1. `dmd -c mydll -g`
   Compiles `mydll.d` into `mydll.obj`. -g turns on debug info generation.
2. `dmd mydll.obj \dmd\lib\gcstub.obj mydll.def -g -L/map`
   Links `mydll.obj` into a DLL named `mydll.dll`. `gcstub.obj` is not required, but it prevents the bulk of the gc code from being linked in, since it will not be used anyway. It saves about 12Kb. `mydll.def` is the Module Definition File, and has the contents:

       LIBRARY         MYDLL
       DESCRIPTION     'MyDll demonstration DLL'
       EXETYPE		NT
       CODE            PRELOAD DISCARDABLE
       DATA            PRELOAD SINGLE

   -g turns on debug info generation, and -L/map generates a map file `mydll.map`.
3. `implib /noi /system mydll.lib mydll.dll`
   Creates an import library `mydll.lib` suitable for linking in with an application that will be statically loading `mydll.dll`.

Here's `test.d`, a sample application that makes use of `mydll.dll`. There are two versions, one statically binds to the DLL, and the other dynamically loads it.

    import std.stdio;
    import std.gc;

    import mydll;

    //version=DYNAMIC_LOAD;

    version (DYNAMIC_LOAD)
    {
        import std.c.windows.windows;

        alias void function(void*) MyDLL_Initialize_fp;
        alias void function() MyDLL_Terminate_fp;
        alias MyClass function() getMyClass_fp;

        int main()
        {	HMODULE h;
    	FARPROC fp;
    	MyDLL_Initialize_fp mydll_initialize;
    	MyDLL_Terminate_fp  mydll_terminate;

    	getMyClass_fp  getMyClass;
    	MyClass c;

    	printf("Start Dynamic Link...\n");

    	h = LoadLibraryA("mydll.dll");
    	if (h == null)
    	{   printf("error loading mydll.dll\n");
    	    return 1;
    	}

    	fp = GetProcAddress(h, "D5mydll16MyDLL_InitializeFPvZv");
    	if (fp == null)
    	{   printf("error loading symbol MyDLL_Initialize()\n");
    	    return 1;
    	}

    	mydll_initialize = cast(MyDLL_Initialize_fp) fp;
    	(*mydll_initialize)(std.gc.getGCHandle());

    	fp = GetProcAddress(h, "D5mydll10getMyClassFZC5mydll7MyClass");
    	if (fp == null)
    	{   printf("error loading symbol getMyClass()\n");
    	    return 1;
    	}

    	getMyClass = cast(getMyClass_fp) fp;
    	c = (*getMyClass)();
    	foo(c);

    	fp = GetProcAddress(h, "D5mydll15MyDLL_TerminateFZv");
    	if (fp == null)
    	{   printf("error loading symbol MyDLL_Terminate()\n");
    	    return 1;
    	}

    	mydll_terminate = cast(MyDLL_Terminate_fp) fp;
    	(*mydll_terminate)();

    	if (FreeLibrary(h) == FALSE)
    	{   printf("error freeing mydll.dll\n");
    	    return 1;
    	}

    	printf("End...\n");
    	return 0;
        }
    }
    else
    {   // static link the DLL

        int main()
        {
    	printf("Start Static Link...\n");
    	MyDLL_Initialize(std.gc.getGCHandle());
    	foo(getMyClass());
    	MyDLL_Terminate();
    	printf("End...\n");
    	return 0;
        }
    }

    void foo(MyClass c)
    {
        char[] s;

        s = c.concat("Hello", "world!");
        writefln(s);
        c.free(s);
        delete c;
    }

Let's start with the statically linked version, which is simpler. It's compiled and linked with the command:

    C:>dmd test mydll.lib -g

Note how it is linked with `mydll.lib`, the import library for `mydll.dll`. The code is straightforward, it initializes `mydll.lib` with a call to MyDLL_Initialize(), passing the handle to `test.exe`'s gc. Then, we can use the DLL and call its functions just as if it were part of `test.exe`. In foo(), gc memory is allocated and freed both by `test.exe` and `mydll.dll`. When we're done using the DLL, it is terminated with MyDLL_Terminate().

Running it looks like this:

    C:>test
    DLL_PROCESS_ATTACH
    Start Static Link...
    MyDLL_Initialize()
    static this for mydll
    Hello world!
    MyDLL_Terminate()
    static ~this for mydll
    End...
    C:>

The dynamically linked version is a little harder to set up. Compile and link it with the command:

    C:>dmd test -version=DYNAMIC_LOAD -g

The import library `mydll.lib` is not needed. The DLL is loaded with a call to LoadLibraryA(), and each exported function has to be retrieved via a call to GetProcAddress(). An easy way to get the decorated name to pass to GetProcAddress() is to copy and paste it from the generated `mydll.map` file under the Export heading. Once this is done, we can use the member functions of the DLL classes as if they were part of `test.exe`. When done, release the DLL with FreeLibrary().

Running it looks like this:

    C:>test
    Start Dynamic Link...
    DLL_PROCESS_ATTACH
    MyDLL_Initialize()
    static this for mydll
    Hello world!
    MyDLL_Terminate()
    static ~this for mydll
    DLL_PROCESS_DETACH
    End...
    C:>

Copyright © 1999-2008 by Digital Mars, All Rights Reserved | Page generated by Ddoc. | Comments