root/trunk/docsrc/dll.dd

Revision 1476, 15.7 kB (checked in by walter, 2 years ago)

lots of minor edits

  • Property svn:eol-style set to native
Line 
1 Ddoc
2
3 $(D_S Writing Win32 DLLs in D,
4
5     $(P DLLs (Dynamic Link Libraries) are one of the foundations
6     of system programming for Windows. The D programming
7     language enables the creation of several different types of
8     DLLs.
9     )
10
11     $(P For background information on what DLLs are and how they work
12     Chapter 11 of Jeffrey Richter's book
13     $(LINK2 http://www.amazon.com/exec/obidos/ASIN/1572315482/classicempire,
14     Advanced Windows) is indispensible.
15     )
16
17     $(P This guide will show how to create DLLs of various types with D.)
18
19     $(UL
20     $(LI <a href="#Cinterface">DLLs with a C interface</a>)
21     $(LI <a href="#com">DLLs that are COM servers</a>)
22     $(LI <a href="#Dcode">D code calling D code in DLLs</a>)
23     )
24
25 <h2><a name="Cinterface">DLLs with a C Interface</a></h2>
26
27     $(P A DLL presenting a C interface can connect to any other code
28     in a language that supports calling C functions in a DLL.
29     )
30
31     $(P DLLs can be created in D in roughly the same way as in C.
32     A $(TT DllMain())
33     is required, looking like:
34     )
35
36 --------------------------------
37 import std.c.windows.windows;
38 import core.dll_helper;
39
40 __gshared HINSTANCE g_hInst;
41
42 extern (Windows)
43 BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
44 {
45     switch (ulReason)
46     {
47     case DLL_PROCESS_ATTACH:
48         g_hInst = hInstance;
49         dll_process_attach( hInstance, true );
50         break;
51
52     case DLL_PROCESS_DETACH:
53         dll_process_detach( hInstance, true );
54         break;
55
56     case DLL_THREAD_ATTACH:
57         dll_thread_attach( true, true );
58         break;
59
60     case DLL_THREAD_DETACH:
61         dll_thread_detach( true, true );
62         break;
63     }
64     return true;
65 }
66
67 -------------------------------
68
69     $(P Notes:)
70     $(UL
71     $(LI DllMain simply forwards to the appropriate helper functions. These setup
72          the runtime, create thread objects for interaction with the garbage collector
73          and initialize thread local storage data.)
74     $(LI The DLL does not share its runtime or memory with other DLLs.)
75     $(LI The first boolean argument to the dll-helper functions specify whether all threads
76          should be controlled by the garbage collector. You might need more control over
77          this behaviour if there are threads in the process that must not be suspended.
78          In this case pass false to disable the automatic handling of all threads.)
79     $(LI The presence of $(TT DllMain()) is recognized by the compiler
80         causing it to emit a reference to
81         $(LINK2 http://www.digitalmars.com/ctg/acrtused.html, __acrtused_dll)
82         and the $(TT phobos.lib) runtime library.)
83     )
84
85     Link with a .def
86     (<a href="http://www.digitalmars.com/ctg/ctgDefFiles.html">Module Definition File</a>)
87     along the lines of:
88
89 $(MODDEFFILE
90 LIBRARY         MYDLL
91 DESCRIPTION     'My DLL written in D'
92
93 EXETYPE     NT
94 CODE            PRELOAD DISCARDABLE
95 DATA            WRITE
96
97 EXPORTS
98         DllGetClassObject       @2
99         DllCanUnloadNow         @3
100         DllRegisterServer       @4
101         DllUnregisterServer     @5
102 )
103
104     $(P The functions in the EXPORTS list are for illustration.
105     Replace them with the actual exported functions from MYDLL.
106     Alternatively, use
107     $(LINK2 http://www.digitalmars.com/ctg/implib.html, implib).
108     Here's an example of a simple DLL with a function print()
109     which prints a string:
110     )
111
112     <h4>mydll.d:</h4>
113 -------------------------------
114 module mydll;
115 import std.c.stdio;
116 export void dllprint() { printf("hello dll world\n"); }
117 -------------------------------
118
119     $(P Note: We use $(CODE printf)s in these examples
120     instead of $(CODE writefln)
121     to make the examples as
122     simple as possible.)
123
124     <h4>mydll.def:</h4>
125
126 $(MODDEFFILE
127 LIBRARY "mydll.dll"
128 EXETYPE NT
129 SUBSYSTEM WINDOWS
130 CODE SHARED EXECUTE
131 DATA WRITE
132 )
133
134     $(P Put the code above that contains $(CODE DllMain()) into a file
135     $(TT dll.d).
136     Compile and link the dll with the following command:
137     )
138
139 $(CONSOLE
140 C:>dmd -ofmydll.dll -L/IMPLIB mydll.d dll.d mydll.def
141 C:>
142 )
143
144     $(P which will create mydll.dll and mydll.lib.
145     Now for a program, test.d, which will use the dll:
146     )
147
148     <h4>test.d:</h4>
149 -------------------------------
150 import mydll;
151
152 int main()
153 {
154    mydll.dllprint();
155    return 0;
156 }
157 -------------------------------
158
159     $(P Create an interface file mydll.di that doesn't have the function bodies:)
160
161     <h4>mydll.di:</h4>
162 -------------------------------
163 export void dllprint();
164 -------------------------------
165
166     Compile and link with the command:
167
168 $(CONSOLE
169 C:>dmd test.d mydll.lib
170 C:>
171 )
172
173     and run:
174 $(CONSOLE
175 C:>test
176 hello dll world
177 C:>
178 )
179
180
181
182 <h3>Memory Allocation</h3>
183
184     $(P D DLLs use garbage collected memory management. The question is what
185     happens when pointers to allocated data cross DLL boundaries?
186     If the DLL presents a C interface, one would assume the reason
187     for that is to connect with code written in other languages.
188     Those other languages will not know anything about D's memory
189     management. Thus, the C interface will have to shield the
190     DLL's callers from needing to know anything about it.
191     )
192
193     $(P There are many approaches to solving this problem:)
194
195     $(UL
196
197     $(LI Do not return pointers to D gc allocated memory to the caller of
198     the DLL. Instead, have the caller allocate a buffer, and have the DLL
199     fill in that buffer.)
200
201     $(LI Retain a pointer to the data within the D DLL so the GC will not free
202     it. Establish a protocol where the caller informs the D DLL when it is
203     safe to free the data.)
204
205     $(LI Notify the GC about external references to a memory block by
206     calling GC.addRange.)
207
208     $(LI Use operating system primitives like VirtualAlloc() to allocate
209     memory to be transferred between DLLs.)
210
211     $(LI Use std.c.stdlib.malloc() (or another non-gc allocator) when
212     allocating data to be returned to the caller. Export a function
213     that will be used by the caller to free the data.)
214
215     )
216
217 <h2><a name="com">COM Programming</a></h2>
218
219     Many Windows API interfaces are in terms of COM (Common Object Model)
220     objects (also called OLE or ActiveX objects). A COM object is an object
221     who's first field is a pointer to a vtbl[], and the first 3 entries
222     in that vtbl[] are for QueryInterface(), AddRef(), and Release().
223     <p>
224
225     For understanding COM, Kraig Brockshmidt's
226     <a href="http://www.amazon.com/exec/obidos/ASIN/1556158432/classicempire">
227     Inside OLE</a>
228     is an indispensible resource.
229     <p>
230
231     COM objects are analogous to D interfaces. Any COM object can be
232     expressed as a D interface, and every D object with an interface X
233     can be exposed as a COM object X.
234     This means that D is compatible with COM objects implemented
235     in other languages.
236     <p>
237
238     While not strictly necessary, the Phobos library provides an Object
239     useful as a super class for all D COM objects, called ComObject.
240     ComObject provides a default implementation for
241     QueryInterface(), AddRef(), and Release().
242     <p>
243
244     Windows COM objects use the Windows calling convention, which is not
245     the default for D, so COM functions need to have the attribute
246     extern (Windows).
247
248     So, to write a COM object:
249
250 -------------------------------
251 import std.c.windows.com;
252
253 class MyCOMobject : ComObject
254 {
255     extern (Windows):
256     ...
257 }
258 -------------------------------
259
260     The sample code includes an example COM client program and server DLL.
261
262 <h2><a name="Dcode">D code calling D code in DLLs</a></h2>
263
264     Having DLLs in D be able to talk to each other as if they
265     were statically linked together is, of course, very desirable
266     as code between applications can be shared, and different
267     DLLs can be independently developed.
268     <p>
269
270     The underlying difficulty is what to do about garbage collection (gc).
271     Each EXE and DLL will have their own gc instance. While
272     these gc's can coexist without stepping on each other,
273     it's redundant and inefficient to have multiple gc's running.
274     The idea explored here is to pick one gc and have the DLLs
275     redirect their gc's to use that one. The one gc used here will be
276     the one in the EXE file, although it's also possible to make a
277     separate DLL just for the gc.
278     <p>
279
280     The example will show both how to statically load a DLL, and
281     to dynamically load/unload it.
282     <p>
283
284     Starting with the code for the DLL, mydll.d:
285 -------------------------------
286 /*
287  * MyDll demonstration of how to write D DLLs.
288  */
289
290 import core.runtime;
291 import std.c.stdio;
292 import std.c.stdlib;
293 import std.string;
294 import std.c.windows.windows;
295
296 HINSTANCE   g_hInst;
297
298 extern (C)
299 {
300     void  gc_setProxy(void* p);
301     void  gc_clrProxy();
302 }
303
304 extern (Windows)
305     BOOL $(B DllMain)(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
306 {
307     switch (ulReason)
308     {
309         case DLL_PROCESS_ATTACH:
310         printf("DLL_PROCESS_ATTACH\n");
311         Runtime.initialize();
312         break;
313
314         case DLL_PROCESS_DETACH:
315         printf("DLL_PROCESS_DETACH\n");
316         Runtime.terminate();
317         break;
318
319         case DLL_THREAD_ATTACH:
320         printf("DLL_THREAD_ATTACH\n");
321         return false;
322
323         case DLL_THREAD_DETACH:
324         printf("DLL_THREAD_DETACH\n");
325         return false;
326     }
327     g_hInst = hInstance;
328     return true;
329 }
330
331 export void $(B MyDLL_Initialize)(void* gc)
332 {
333     printf("MyDLL_Initialize()\n");
334     gc_setProxy(gc);
335 }
336
337 export void $(B MyDLL_Terminate)()
338 {
339     printf("MyDLL_Terminate()\n");
340     gc_clrProxy();
341 }
342
343 $(B static this)()
344 {
345     printf("static this for mydll\n");
346 }
347
348 $(B static ~this)()
349 {
350     printf("static ~this for mydll\n");
351 }
352
353 /* --------------------------------------------------------- */
354
355 class $(B MyClass)
356 {
357     char[] $(B concat)(char[] a, char[] b)
358     {
359     return a ~ " " ~ b;
360     }
361
362     void $(B free)(char[] s)
363     {
364     delete s;
365     }
366 }
367
368 export MyClass $(B getMyClass)()
369 {
370     return new MyClass();
371 }
372 -------------------------------
373
374     <dl>
375     <dt>$(B DllMain)
376     <dd>This is the main entry point for any D DLL. It gets called
377     by the C startup code
378     (for DMC++, the source is $(TT \dm\src\win32\dllstart.c)).
379     The $(B printf)'s are placed there so one can trace how it gets
380     called.
381     Notice that the initialization and termination code seen in
382     the earlier DllMain sample code is in this version as well.
383     This is because the same DLL should be usable from both C and
384     D programs, so the same initialization process should work
385     for both.
386     <p>
387
388     <dt>$(B MyDLL_Initialize)
389     <dd>
390     When the DLL is dynamically linked via $(B Runtime.loadLibrary)()
391     the runtime makes sure that any initialization steps required
392     by the D program are executed after the library is loaded.  If
393     the library is statically linked, this routine is not called by
394     the program, so to make sure the DLL is initialized properly we
395     have to do some of the work ourselves.  And because the library
396     is being statically linked, we need a function specific to this
397     DLL to perform the initialization.
398     This function takes one argument, a handle to the
399     caller's gc. We'll see how that handle is obtained later.
400     To pass this handle to the runtime and override the DLL's built-in
401     gc we'll call $(B gc_setProxy)().
402     The function is $(B export)ed as that is how a function is made
403     visible outside of a DLL.
404     <p>
405
406     <dt>$(B MyDLL_Terminate)
407     <dd>Correspondingly, this function terminates the DLL, and is
408     called prior to unloading it.
409     It has only one job: informing the runtime that the DLL will
410     no longer be using the caller's gc via $(B gc_clrProxy)().
411     This is critical, as the DLL will be unmapped from memory,
412     and if the gc continues to scan its data areas it will cause
413     segment faults.
414     <p>
415
416     <dt>$(B static this, static ~this)
417     <dd>These are examples of the module's static constructor
418     and destructor,
419     here with a print in each to verify that they are running
420     and when.
421     <p>
422
423     <dt>$(B MyClass)
424     <dd>This is an example of a class that can be exported from
425     and used by the caller of a DLL. The $(B concat) member
426     function allocates some gc memory, and $(B free) frees gc
427     memory.
428     <p>
429
430     <dt>$(B getMyClass)
431     <dd>An exported factory that allocates an instance of $(B MyClass)
432     and returns a reference to it.
433     <p>
434
435     </dl>
436
437     To build the $(TT mydll.dll) DLL:
438
439     $(OL
440     $(LI$(B $(TT dmd -c mydll -g))
441     <br>Compiles $(TT mydll.d) into $(TT mydll.obj).
442     $(B -g) turns on debug info generation.
443     )
444
445     $(LI $(B $(TT dmd mydll.obj mydll.def -g -L/map))
446     <br>Links $(TT mydll.obj) into a DLL named $(TT mydll.dll).
447     $(TT mydll.def) is the
448     <a href="http://www.digitalmars.com/ctg/ctgDefFiles.html">Module Definition File</a>,
449     and has the contents:
450
451 $(MODDEFFILE
452 LIBRARY         MYDLL
453 DESCRIPTION     'MyDll demonstration DLL'
454 EXETYPE     NT
455 CODE            PRELOAD DISCARDABLE
456 DATA            PRELOAD SINGLE
457 )
458     $(B -g) turns on debug info generation, and
459     $(B -L/map) generates a map file $(TT mydll.map).
460     )
461
462     $(LI $(B $(TT implib /noi /system mydll.lib mydll.dll))
463     <br>Creates an
464     <a href="http://www.digitalmars.com/ctg/implib.html">import library</a>
465     $(TT mydll.lib) suitable
466     for linking in with an application that will be statically
467     loading $(TT mydll.dll).
468     )
469
470     )
471
472     $(P Here's $(TT test.d), a sample application that makes use of
473     $(TT mydll.dll). There are two versions, one statically binds to
474     the DLL, and the other dynamically loads it.
475     )
476
477 -------------------------------
478 import core.runtime;
479 import std.stdio;
480 import std.gc;
481
482 import mydll;
483
484 //version=DYNAMIC_LOAD;
485
486 version (DYNAMIC_LOAD)
487 {
488     import std.c.windows.windows;
489
490     alias MyClass function() getMyClass_fp;
491
492     int main()
493     {   HMODULE h;
494         FARPROC fp;
495
496         getMyClass_fp getMyClass;
497         MyClass c;
498
499         printf("Start Dynamic Link...\n");
500
501         h = cast(HMODULE) Runtime.loadLibrary("mydll.dll");
502         if (h is null)
503         {
504             printf("error loading mydll.dll\n");
505             return 1;
506         }
507
508         fp = GetProcAddress(h, "D5mydll10getMyClassFZC5mydll7MyClass");
509         if (fp is null)
510         {   printf("error loading symbol getMyClass()\n");
511             return 1;
512         }
513
514         getMyClass = cast(getMyClass_fp) fp;
515         c = (*getMyClass)();
516         foo(c);
517
518         if (!Runtime.unloadLibrary(h))
519         {   printf("error freeing mydll.dll\n");
520             return 1;
521         }
522
523         printf("End...\n");
524         return 0;
525     }
526 }
527 else
528 {   // static link the DLL
529     extern (C)
530     {
531         void* gc_getProxy();
532     }
533
534     int main()
535     {
536     printf("Start Static Link...\n");
537     MyDLL_Initialize(gc_getProxy());
538     foo(getMyClass());
539     MyDLL_Terminate();
540     printf("End...\n");
541     return 0;
542     }
543 }
544
545 void foo(MyClass c)
546 {
547     char[] s;
548
549     s = c.concat("Hello", "world!");
550     writefln(s);
551     c.free(s);
552     delete c;
553 }
554 -------------------------------
555
556     $(P Let's start with the statically linked version, which is simpler.
557     It's compiled and linked with the command:
558     )
559
560 $(CONSOLE
561 C:>dmd test mydll.lib -g
562 )
563
564     $(P Note how it is linked with $(TT mydll.lib), the import library
565     for $(TT mydll.dll).
566     The code is straightforward, it initializes $(TT mydll.lib) with
567     a call to $(B MyDLL_Initialize)(), passing the handle
568     to $(TT test.exe)'s gc.
569     Then, we can use the DLL and call its functions just as if
570     it were part of $(TT test.exe). In $(B foo)(), gc memory
571     is allocated and freed both by $(TT test.exe) and $(TT mydll.dll).
572     When we're done using the DLL, it is terminated with
573     $(B MyDLL_Terminate)().
574     )
575
576     $(P Running it looks like this:)
577
578 $(CONSOLE
579 C:>test
580 DLL_PROCESS_ATTACH
581 Start Static Link...
582 MyDLL_Initialize()
583 static this for mydll
584 Hello world!
585 MyDLL_Terminate()
586 static ~this for mydll
587 End...
588 C:>
589 )
590
591     $(P The dynamically linked version is a little harder to set up.
592     Compile and link it with the command:
593     )
594
595 $(CONSOLE
596 C:>dmd test -version=DYNAMIC_LOAD -g
597 )
598     $(P The import library $(TT mydll.lib) is not needed.
599     The DLL is loaded with a call to
600     $(B Runtime.loadLibrary)(),
601     and each exported function has to be retrieved via
602     a call to
603     $(B GetProcAddress)().
604     An easy way to get the decorated name to pass to $(B GetProcAddress)()
605     is to copy and paste it from the generated $(TT mydll.map) file
606     under the $(B Export) heading.
607     Once this is done, we can use the member functions of the
608     DLL classes as if they were part of $(TT test.exe).
609     When done, release the DLL with
610     $(B Runtime.unloadLibrary)().
611     )
612
613     $(P Running it looks like this:)
614
615 $(CONSOLE
616 C:>test
617 Start Dynamic Link...
618 DLL_PROCESS_ATTACH
619 static this for mydll
620 Hello world!
621 static ~this for mydll
622 DLL_PROCESS_DETACH
623 End...
624 C:>
625 )
626
627 )
628
629 Macros:
630     TITLE=Writing Win32 DLLs
631     WIKI=DLLs
Note: See TracBrowser for help on using the browser.