Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

DLLMain

Moderators: larsivi kris

Posted: 01/17/08 14:42:41

How to create DLLMain using Tango ?
Just have added one exported function. Do I need a DEF file even when I have no need for a .lib. I just need the DLL ? What options should I pass to the linker ?
Thanks Bjoern

import tango.sys.win32.UserGdi;

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:
		// I guess rt_init()  
/* What about this stuff ?
	    _minit();			// initialize module list
	    _moduleCtor();		// run module constructors
	    _moduleUnitTests();		// run module unit tests
*/
	    break;

	case DLL_PROCESS_DETACH:
	    // Still guessing rt_term()
	    break;

	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	    // Multiple threads not supported yet
	    return false;
    }
    g_hInst=hInstance;
    return true;
}


alias extern(Windows) void function(char[] token, bool eof = false) LexerCallBack; 
extern(Windows) export bool Lexer(LexerCallBack cb, char* code)
{
	char[] tok;
    // tokenize
	cb(tok);
	return true;
}
Author Message

Posted: 02/20/08 09:39:14

Maybe you'd like to add this to the Tango FAQ
How to create a DLL using Tango. I've added a simple callback function, 'cause this is probabely a matter of interrest.

module sampledll;
import tango.sys.win32.UserGdi;  // This is all you need to import.
// Just required for the sample DLL callback function.
import tango.core.Exception;
import tango.stdc.string;
import tango.stdc.stdarg;
import tango.sys.Process;
import tango.text.stream.LineIterator;
import Text = tango.text.Util;
import tango.text.convert.Integer;


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

// Describing the callback function
alias extern(Windows) void function(char* token, size_t siz = 0, bool error =false) DisplayCallBack; 

// A DLL function using Tango's Process class...redirecting the output to the callbackfunction.  
export extern(Windows) 
void ExecuteProcess(DisplayCallBack cb, char* _dir, char* _command, char* _args)
{
	try
    {
		char[] dir = _dir[0 .. strlen(_dir)];
		char[] command = _command[0 .. strlen(_command)];
		char[] args =  _args[0 .. strlen(_args)];
		char[] msg;
		char[256] buffer;

        Process p = new Process(command, null);
		p.workDir = dir;
		
		msg = Text.layout(buffer, "Executing %0 ", p.toString);
        cb(msg.ptr, msg.length);
		
        p.execute();
		
		foreach (line; new LineIterator!(char)(p.stdout))
			cb(line.ptr, line.length, false);

        // Show the errors (if there were any)			
		foreach (line; new LineIterator!(char)(p.stderr))
			cb(line.ptr, line.length, true);
				
        Process.Result result = p.wait();

        msg = Text.layout(buffer, "Process %0  [pid %1]  finished. %2",p.programName,toString(p.pid),result.toString);
		cb(msg.ptr, msg.length, false);
    }
    catch (ProcessException e)
    {
		char[] msg;
        msg = "Process execution failed : " ~ e.toString;
		cb(msg.ptr, msg.length, true);
	}
    catch (IOException e)
    {
		char[] msg;
        msg = "Input/output exception caught : " ~ e.toString;
		cb(msg.ptr, msg.length, true);
    }
    catch (Exception e)
    {
		char[] msg;
        msg = "Unexpected exception caught : "~ e.toString;
		cb(msg.ptr, msg.length, true);
    }	
}

Okay : You need a DEF file in order to compile

LIBRARY "sampledll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE
EXPORTS
	ExecuteProcess

last but not least a simple batch file, :

cls
dmd -odc:\dmd\projects\bsdutil\obj -ofSAMPLEDLL.DLL sampledll.d sampledll.def


Unfortunately, your project will not compile... But you have two options
Modify sc.ini

[Environment]
LIB="%@P%\..\lib"
DFLAGS="-I%@P%\..\import" -version=Tango -defaultlib=tango-base-dmd.lib -debuglib=tango-base-dmd.lib tango-user-dmd.lib
LINKCMD=%@P%\link.exe

You see I've remove the -L flag.

Second option modify the batch file :

link all+my+object+files+separated+with+pluses,my.dll,,user32+kernel32+tango-base-dmd+tango-user-dmd,mydll.def/noi/CODEVIEW

i think the lib file may not be added at the end after the noi option, but has to added in the libraries-section (sections are separated by commas),

HTH Bjoern

Posted: 02/27/08 05:55:20

It would be nice if Tango had some way of initializing with existing garbage collector, like Phobos.

Posted: 03/04/08 01:21:17

That's tricky, since Phobos doesn't provide any means for registering external threads to be scanned by its GC. Tango does, however (thread_attachThis/thread_detachThis), so there is some basic support for this already.

Posted: 03/24/08 16:53:41

so, if I got it right, the following code is correct? I mean it compiles and everything.. I'm not sure where does GC fall into this scheme, what should I be carefull of?

module mydll;
import tango.sys.win32.Types;

import tango.io.Stdout;

// 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:
		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"); }

Posted: 03/24/08 17:09:24

and here is the loader modified from the examples:

import tango.sys.SharedLib;
import tango.util.log.Trace;

typedef 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);
            
            void **point = cast(void **)&dllprint;
            *point = ptr;
            
            dllprint();
            
        } else {
            Trace.formatln("Symbol dllprint not found");
        }

        lib.unload();
    } else {
        Trace.formatln("Could not load the library");
    }

    assert (0 == SharedLib.numLoadedLibs);
}

and here is the def for previous dll

LIBRARY "mydll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE
EXPORTS
	dllprint

Posted: 03/24/08 20:43:07

I've added entry to the Tutorial section: http://dsource.org/projects/tango/wiki/TutDLL It would be nice if someone writes a GC section to it and explain how GC fits to this and what should one be aware of.

Posted: 03/24/08 20:44:43

That looks like it should work. And for thread attach/detach you could call thread_attach/detachThis if the thread was going to be holding references to GCed data.

Posted: 03/24/08 22:31:27

I also have one more question :) If DLL has C++ Functions, when I try to call them I get exceptions. So is it possible to load and use C++ functions from DLL at all?

Posted: 03/24/08 22:53:41

Only if the used compilers belong in the same world - for D that means that unless the DLL is created with DMC, you're not likely to have a chance in hell, and even then you'll have problems. By creating wrappers via C, you may be able to do it indirectly though. But it probably gotta be one heck of a library to be worth it :)

Posted: 03/30/08 19:02:54

I'll try/investigate how to import/link library+DLL with extern C linkage (originally written in C++). It would be AWESOME to write maya plugins with D instead of C++, even if I had to write a C++ wrapper. Any help/experience with simillar linkage is appreciated.

Posted: 03/30/08 19:26:26

Keyframe: Jascha's plugin for Kate presumably use some C++ linking/wrapping - you could try to ask him he proceded.

Posted: 04/04/08 15:54:25

Any idea how to have classes in DLL and use them when dynamically linking to it? I had no luck with using DDL whatsoever, so I opted for DLLs instead.

Posted: 04/05/08 11:37:05

I seem to remember something from a long time ago - I think easiest will be some sort of factory setup where you use a function to return a class instance. You should try using "export" keyword on your classes first though, maybe that is enough.

Posted: 04/06/08 13:56:26 -- Modified: 04/06/08 13:59:02 by
Keyframe

hmm here is where I'm at now:

DLL

module mydll;
import tango.sys.win32.Types;

import tango.io.Stdout;

// 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:
		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"); }
class A {
	void testor() {
		Stdout.formatln("here comes the class");
	}	
}
export extern(C) A getA() { A x = new A; return x; }

.def for it:

LIBRARY "dll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE
EXPORTS
	dllprint
	getA

and finally DLL client:

import tango.sys.SharedLib;
import tango.util.log.Trace;
import tango.io.Stdout;

// declaring our function pointer
typedef void function() tdllprint;
tdllprint dllprint;

extern {
class A {
	void testor() { }	
};
}
typedef A function() tgetA;
tgetA getA;


void main() {
    if (auto lib = SharedLib.load(`dll.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");
        }


        void* ptrA = lib.getSymbol("getA");
        if (ptrA) {
            Trace.formatln("Symbol getA found. Address = 0x{:x}", ptrA);
            
            // binding function address from DLL to our function pointer
            void **pointA = cast(void **)&getA;
            *pointA = ptrA;
            
            // using our function
            auto x = getA();
            Stdout.formatln("{}", typeof(x).stringof);
            x.testor();
            
        } else {
            Trace.formatln("Symbol getA not found");
        }



        lib.unload();
    } else {
        Trace.formatln("Could not load the library");
    }

    assert (0 == SharedLib.numLoadedLibs);
}

it looks like it works, however I'm worried about two things - who is in charge of GC in this case? How do I propagate my methods outside of the class? Don't tell me I have to manually declare them via extern :(

edit: I've included a client with extern block that loads a method, looks like it works - if someone can explain what happens to GC in this case - looks like a leakage to me.