diff -uNr dmd-0.111/dmd/html/d/changelog.html dmd-0.112/dmd/html/d/changelog.html --- dmd-0.111/dmd/html/d/changelog.html 2005-01-18 16:37:04.000000000 +0100 +++ dmd-0.112/dmd/html/d/changelog.html 2005-01-27 01:03:32.000000000 +0100 @@ -1,7 +1,7 @@ +
+ +Note: This is a library only change, the dmd executables are still +at 0.111. + +
+ + 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. + +
+ ++ 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: +
+ 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: + +
+module mydll; +export void dllprint() { printf("hello dll world\n"); } ++ +
+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: + +
+import mydll; + +int main() +{ + mydll.dllprint(); + return 0; +} ++ + Create a clone of mydll2.d that doesn't have the function bodies: + +
+export void dllprint(); ++ + Compile and link with the command: +
+dmd test.d mydll.lib ++ + and run: +
+C:>test +hello dll world +C:> ++ + + +
+ + There are many approaches to solving this problem: + +
+ + 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. + +
+ + 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 + seperate 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(); +} ++ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ 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. +
+ +
+ +
+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: +
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: +
+DLL_PROCESS_ATTACH +Start Static Link... +MyDLL_Initialize() +static this for mydll +Hello world! +MyDLL_Terminate() +static ~this for mydll +End...+ + The dynamically linked version is a little harder to set up. + Compile and link it with the command: +
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: +
+Start Dynamic Link... +DLL_PROCESS_ATTACH +MyDLL_Initialize() +static this for mydll +Hello world! +MyDLL_Terminate() +static ~this for mydll +DLL_PROCESS_DETACH +End...+ + +
Floating literalss can have embedded '_' characters, which are ignored.
diff -uNr dmd-0.111/dmd/html/d/toc.html dmd-0.112/dmd/html/d/toc.html
--- dmd-0.111/dmd/html/d/toc.html 2004-12-30 01:56:36.000000000 +0100
+++ dmd-0.112/dmd/html/d/toc.html 2005-01-26 17:38:12.000000000 +0100
@@ -54,6 +54,7 @@
· Application Binary Interface
· Phobos (Runtime Library)
· D for Win32
+· Win32 DLLs in D
· C .h to D Modules
· FAQ
· Style Guide
@@ -64,6 +65,10 @@
· Acknowledgements