| 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 |
|---|