www.digitalmars.com [Home] [Search] [D]
Last update Nov 8, 2004

D for Win32

This describes the D implementation for 32 bit Windows systems. Naturally, Windows specific D features are not portable to other platforms.

Instead of the:

	#include <windows.h>
	
of C, in D there is:
	import std.c.windows.windows;
	

Calling Conventions

In C, the Windows API calling conventions are __stdcall. In D, it is simply:
	extern (Windows)
	{
		... function declarations ...
	}
	
The Windows linkage attribute sets both the calling convention and the name mangling scheme to be compatible with Windows.

For functions that in C would be __declspec(dllimport) or __declspec(dllexport), use the export attribute:

	export void func(int foo);
	
If no function body is given, it's imported. If a function body is given, it's exported.

Windows Executables

Windows GUI applications can be written with D. A sample such can be found in \dmd\samples\d\winsamp.d

These are required:

  1. Instead of a main function serving as the entry point, a WinMain function is needed.
  2. WinMain must follow this form:
    import std.c.windows.windows;
    
    extern (C) void gc_init();
    extern (C) void gc_term();
    extern (C) void _minit();
    extern (C) void _moduleCtor();
    extern (C) void _moduleUnitTests();
    
    extern (Windows)
    int WinMain(HINSTANCE hInstance,
    	HINSTANCE hPrevInstance,
    	LPSTR lpCmdLine,
    	int nCmdShow)
    {
        int result;
    
        gc_init();			// initialize garbage collector
        _minit();			// initialize module constructor table
    
        try
        {
    	_moduleCtor();		// call module constructors
    	_moduleUnitTests();	// run unit tests (optional)
    
    	result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
        }
    
        catch (Object o)		// catch any uncaught exceptions
        {
    	MessageBoxA(null, cast(char *)o.toString(), "Error",
    		    MB_OK | MB_ICONEXCLAMATION);
    	result = 0;		// failed
        }
    
        gc_term();			// run finalizers; terminate garbage collector
        return result;
    }
    
    int myWinMain(HINSTANCE hInstance,
    	HINSTANCE hPrevInstance,
    	LPSTR lpCmdLine,
    	int nCmdShow)
    {
        ... insert user code here ...
    }
    	
    The myWinMain() function is where the user code goes, the rest of WinMain is boilerplate to initialize and shut down the D runtime system.
  3. A .def (Module Definition File) with at least the following two lines in it:
    EXETYPE NT
    SUBSYSTEM WINDOWS
    	
    Without those, Win32 will open a text console window whenever the application is run.
  4. The presence of WinMain() is recognized by the compiler causing it to emit a reference to __acrtused_dll and the phobos.lib runtime library.

DLLs (Dynamic Link Libraries)

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: 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"); }
	

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:
dmd -ofmydll.dll mydll2.d dll.d mydll.def
implib/system mydll.lib mydll.dll
	
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:
dmd test.d mydll.lib
	
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? Other DLLs, or callers to a D DLL, may even be written in another language and may have no idea how to interface with D's garbage collector.

There are many approaches to solving this problem. The most practical approaches are to assume that other DLLs have no idea about D. To that end, one of these should work:

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().

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.

Feedback and Comments

Add feedback and comments regarding this page.
Copyright (c) 1999-2004 by Digital Mars, All Rights Reserved