Leave Comments, Critiques, and Suggestions Here?
Creating and using DLL files in Windows
Setting your Environment
In order to compile DLL file you'll have to modify your sc.ini file, add this to sc.ini file
[Environment] LIB="%@P%\..\lib" DFLAGS="-I%@P%\..\import" -version=Tango -defaultlib=tango-dmd.lib -debuglib=tango-dmd.lib tango-dmd.lib LINKCMD=%@P%\link.exe
main difference is that vanilla sc.ini file has "-L+tango-dmd.lib", while in order to compile and link DLL you got to leave out "-L+". So, if you want to create DLL files, it would be best to have two sc.ini files - one for usual compilation and one for DLL's.
Creating a simple DLL
Here is the source for simple DLL file:
module mydll; import tango.sys.win32.Types; import tango.io.Stdout; import tango.stdc.stdio; // The core DLL init code. extern (C) bool rt_init( void delegate( Exception ) dg = null ); extern (C) bool rt_term( void delegate( Exception ) dg = null ); HINSTANCE g_hInst; extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) { switch (ulReason) { case DLL_PROCESS_ATTACH: rt_init(); break; case DLL_PROCESS_DETACH: tango.stdc.stdio._fcloseallp = null; rt_term(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: // Multiple threads not supported yet return false; } g_hInst=hInstance; return true; } // End of core DLL Init export extern(C) void dllprint() { Stdout.formatln("hello dll world\n"); }
As you can see, this is a usual DLL structure, we have a rt_init and rt_term functions which are called when DLL is loaded and when it is detached, nothing fancy in here. Be sure to include import tango.sys.win32.Types; as it is needed for Windows types information. That is all you need for this program.
In this example DLL we have a simple function dllprint which prints "hello dll world" when called. It has a external C linkage and we imported tango.io.Stdout in order to use Stdout.formatln function. So, basically we have a hello world function in this dll.
Compiling your DLL
In order to compile this example to dll, we also need a .def file which defines our dll. Here it is:
LIBRARY "mydll.dll" EXETYPE NT SUBSYSTEM WINDOWS CODE SHARED EXECUTE DATA WRITE EXPORTS dllprint
You keep track, in your DLL, of EXPORTS where you define your function names and LIBRARY "mydll.dll" where name of the dll resides. Pretty simple, huh? So now, we have two files - mydll.d and mydll.def. On with the compilation.
To compile and link this file you have to set your environment as previously described and type this line to compile it:
dmd -ofmydll.dll mydll.d mydll.def
Using your sample DLL
import tango.sys.SharedLib; import tango.util.log.Trace; // declaring our function pointer typedef extern (C) void function() tdllprint; tdllprint dllprint; void main() { if (auto lib = SharedLib.load(`mydll.dll`)) { Trace.formatln("Library successfully loaded"); void* ptr = lib.getSymbol("dllprint"); if (ptr) { Trace.formatln("Symbol dllprint found. Address = 0x{:x}", ptr); // binding function address from DLL to our function pointer void **point = cast(void **)&dllprint; *point = ptr; // using our function dllprint(); } else { Trace.formatln("Symbol dllprint not found"); } lib.unload(); } else { Trace.formatln("Could not load the library"); } assert (0 == SharedLib.numLoadedLibs); }
As you can see, binding a function from DLL is trivial. Note that you should compile this example with default environment, that is with "-L+" in your sc.ini - as you compile other normal programs.