Last update March 7, 2012

Best Practices /
DLL



  • See Also: example in \dmd\samples\d\mydll
NOTE:
The following applies to at least DMD 0.127 and earlier. Later revisions may have changed the behavior documented here.

D has a lot of drawbacks when it comes to writing dll's. Thankfully, most of these issues are known and can be worked around.

Exporting functions and variables from a D-based dll requires knowledge of what the various extern() types do for the exported symbols. Stick to extern(Windows) or extern(C) as these create readable export names. The default, extern(D) is difficult to use due to how D mangles exports. [An alpha version of a reflection API called dflect that knows about D name mangling can help if you want to stick to extern(D) - BenHinkle]

Any top-level functions exported cannot, under any circumstances, throw an exception. This is becuase the top-level exception handler, as compiled into the dll by DMD, has no way to bind to an external client. The default behavior, is to terminate your program when one of your exports throws. Simply catch everything at the top level, and set a flag to be gathered elsewhere if you must give exception information back to the dll's client.

The GC will not be aware of anything happening outside the dll. If you are passing data from the dll back to the client, make sure that it has a reference held inside the dll so that data isn't prematurely collected. To help with this problem, D also provides a means for sharing a common garbage collector between a D exe and any DLL files it loads; this is documented in the DMD/Phobos specification and manual.

D doesn't provide a way to export an entire class, so you'll have to work around this by exporting static methods and global functions instead.

Keep in mind that the dll's runtime type information is a completely separate set of structures in memory than the client's runtime type information. If you are returning objects via exported functions, expect the client's attempt to cast those objects to fail miserably.

// in the dll
class Foobar{}
export extern(C) Object foobar(){ return new Foobar(); }

// in the client
extern(C) Object function() FoobarExport;
FoobarExport myFoobar = cast(FoobarExport)dll.getSymbol("foobar"); // gets the
export "foobar" from the dll
Foobar foo = cast(Foobar) myFoobar(); //** this cast will fail **

This limitation applies for objects and types passed to the dll as well.

In light of this, stick to scalar data when defining your exports (this also helps with GC issues). If you absolutely must pass a class, define your exports in terms of solitary interfaces (that don't inherit from other interfaces) to avoid casting; at least this way, you can reasonably expect an interface cast to fail. Also, make sure that both the client and the dll are using the same version of that interface.

There is one additional concern when passing classes between the dll and client.

Make sure you dispose of any classes that originate from the dll before the dll is unloaded if at all possible. In the case of a shared GC between the dll and client, this is critical, as it is possible for the GC to attempt to 'finalize' objects that no longer have a valid vtable (resulting a GPF or segfault), long after the dll hosting that vtable is gone.

- EricAnderton

FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: March 7, 2012 19:06 (diff))