www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Shared libraries/DLLs

reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
This might be a silly or simple question.  I haven't really used shared 
libraries very much, but have need to now.

On Linux with gdc, everything (seems to be) fine.  I can make everything 
work there fine.  My problem is with Windows and dmd.

Consider the following simple "library":

---
module library;

import loader;

// (the DllMain stuff from D's doc pages.)

extern (C)
export int foo()
{
	return 42 * loader.bar();
}
---

And also consider this "application":

---
module loader;

import std.c.windows.windows;
import std.string, std.stdio;

extern (C)
alias int function() example_f;

void main()
{
	HMODULE m = cast(HMODULE) LoadLibraryA(toStringz("library.dll"));

	example_f f = cast(example_f) GetProcAddress(m, toStringz("foo"));

	if (f)
		writefln("%d", f());

	FreeLibrary(m);
}

extern (C)
export int bar()
{
	return 2;
}
---

This appears to work on Linux.  I expected it might work (or there might 
be some way to coax it to work) on Windows.  It seems logical, even not 
having used the LoadLibrary/etc. stuff before.  The library needs to 
access the caller's functions.

It does appear I can make it at least *compile* (but not run) by adding 
an IMPORTS directive to the .def file of the library, but this clearly 
is intended for static linking.  I'm wanting to load the DLL as a 
plugin, dynamically.  Its name and location might change.

Am I making some hopefully obvious and stupid mistake?  Please tell me I 
don't actually have to send pointers to all the api functions to the DLL 
when calling it.

Thanks,
-[Unknown]
Jan 31 2008
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fnu7l0$2p5l$1 digitalmars.com...

 This appears to work on Linux.  I expected it might work (or there might 
 be some way to coax it to work) on Windows.  It seems logical, even not 
 having used the LoadLibrary/etc. stuff before.  The library needs to 
 access the caller's functions.

 It does appear I can make it at least *compile* (but not run) by adding an 
 IMPORTS directive to the .def file of the library, but this clearly is 
 intended for static linking.  I'm wanting to load the DLL as a plugin, 
 dynamically.  Its name and location might change.

 Am I making some hopefully obvious and stupid mistake?  Please tell me I 
 don't actually have to send pointers to all the api functions to the DLL 
 when calling it.

So, uh, what _exactly_ is the problem? I don't think you ever said..
Jan 31 2008
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Sorry, I want to be able to compile and run that.  It refuses to do so.

It wants to link against loader.obj, except that defeats the purpose of 
it being dynamic.  I can add IMPORTS to the def, but then it has to be 
static.

I want to dynamically link, and also call functions from the code that 
is dynamically linking.

Sorry I was unclear.

-[Unknown]


Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fnu7l0$2p5l$1 digitalmars.com...
 
 This appears to work on Linux.  I expected it might work (or there might 
 be some way to coax it to work) on Windows.  It seems logical, even not 
 having used the LoadLibrary/etc. stuff before.  The library needs to 
 access the caller's functions.

 It does appear I can make it at least *compile* (but not run) by adding an 
 IMPORTS directive to the .def file of the library, but this clearly is 
 intended for static linking.  I'm wanting to load the DLL as a plugin, 
 dynamically.  Its name and location might change.

 Am I making some hopefully obvious and stupid mistake?  Please tell me I 
 don't actually have to send pointers to all the api functions to the DLL 
 when calling it.

So, uh, what _exactly_ is the problem? I don't think you ever said..

Jan 31 2008
parent reply Neal Alexander <wqeqweuqy hotmail.com> writes:
Unknown W. Brackets wrote:
 Sorry, I want to be able to compile and run that.  It refuses to do so.
 
 It wants to link against loader.obj, except that defeats the purpose of 
 it being dynamic.  I can add IMPORTS to the def, but then it has to be 
 static.
 
 I want to dynamically link, and also call functions from the code that 
 is dynamically linking.
 
 Sorry I was unclear.
 
 -[Unknown]
 
 
 Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fnu7l0$2p5l$1 digitalmars.com...

 This appears to work on Linux.  I expected it might work (or there 
 might be some way to coax it to work) on Windows.  It seems logical, 
 even not having used the LoadLibrary/etc. stuff before.  The library 
 needs to access the caller's functions.

 It does appear I can make it at least *compile* (but not run) by 
 adding an IMPORTS directive to the .def file of the library, but this 
 clearly is intended for static linking.  I'm wanting to load the DLL 
 as a plugin, dynamically.  Its name and location might change.

 Am I making some hopefully obvious and stupid mistake?  Please tell 
 me I don't actually have to send pointers to all the api functions to 
 the DLL when calling it.

So, uh, what _exactly_ is the problem? I don't think you ever said..


If the settings used in your .def file aren't particular the DLL doesnt work as expected. IIRC the def files in the digital mars DLL tutorial diddnt work for me when i first looked at it. maybe try this instead: ----------------------------------- LIBRARY "x.dll" DESCRIPTION 'x.dll' EXETYPE NT CODE PRELOAD DATA PRELOAD
Jan 31 2008
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Thanks.  Actually, I have no trouble whatsoever making dlls.  I have a 
problem referencing functions in the program that loaded the DLL.

It turns out (from something I'm read) what I want is simply not 
possibly with DLLs.  I will have to send a pointer table/struct over on 
to the plugin for this to work the way I want.

Ain't Linux better?  Oi.

-[Unknown]


Neal Alexander wrote:
 If the settings used in your .def file aren't particular the DLL doesnt 
 work as expected. IIRC the def files in the digital mars DLL tutorial 
 diddnt work for me when i first looked at it.
 
 maybe try this instead:
 -----------------------------------
 LIBRARY "x.dll"
 DESCRIPTION 'x.dll'
 
 EXETYPE NT
 CODE PRELOAD
 DATA PRELOAD

Jan 31 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fnuiqt$ags$1 digitalmars.com...
 Thanks.  Actually, I have no trouble whatsoever making dlls.  I have a 
 problem referencing functions in the program that loaded the DLL.

You shouldn't need a .def file at all for this program. In fact, you should be able to compile the app the same way you compile any other app. That it loads a dynamic library at runtime means nothing to the compiler/linker.
 Ain't Linux better?  Oi.

DLLs are a pretty awful approximation of SOs, yes.
Feb 01 2008
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Well, the def being for the library, not for the program loading the 
library.  Otherwise I don't get exports named properly, etc.

-[Unknown]


Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fnuiqt$ags$1 digitalmars.com...
 Thanks.  Actually, I have no trouble whatsoever making dlls.  I have a 
 problem referencing functions in the program that loaded the DLL.

You shouldn't need a .def file at all for this program. In fact, you should be able to compile the app the same way you compile any other app. That it loads a dynamic library at runtime means nothing to the compiler/linker.
 Ain't Linux better?  Oi.

DLLs are a pretty awful approximation of SOs, yes.

Feb 01 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fnvd2q$229m$1 digitalmars.com...
 Well, the def being for the library, not for the program loading the 
 library.  Otherwise I don't get exports named properly, etc.

In that case, I'd be more interested to see how you're compiling all this code. The code itself looks very simplistic, and I don't see any apparent failure points.
Feb 01 2008
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Okay.

Example "plugin" example.d:

---
module example;

extern (C)
export int foo();

version (Windows)
	import utils.windows;

extern (C)
export int example()
{
	return 42 + foo();
}
---

Compiled with:

$(DMD) $(DFLAGS_DEBUG) -op -of$(OUTPUT) $(LIBS) $(wildcard utils/*.d) 
example.d example.def

Or:

$(GDMD) $(DFLAGS_DEBUG) -op -of$(OUTPUT) $(LIBS) $(wildcard utils/*.d) 
example.d -fPIC -q,-rdynamic,-shared


Plugin linker definition file:

---
LIBRARY         example.dll
DESCRIPTION     'Example'

EXETYPE         NT
SUBSYSTEM       WINDOWS 4.0
CODE            PRELOAD SHARED DISCARDABLE
DATA            PRELOAD SINGLE

EXPORTS
				example
---


Example host program for Windows, load_test.d:

---
module load_test;

import std.c.windows.windows;
import std.string, std.stdio;

extern (C)
	alias int function() example_f;

int main()
{
	HMODULE m = cast(HMODULE) LoadLibraryA(toStringz("example.dll"));

	example_f f = cast(example_f) GetProcAddress(m, toStringz("example"));

	if (f)
		writefln("%d", f());

	FreeLibrary(m);

	return 0;
}

extern (C)
export int foo()
{
	return 43;
}
---


Quick DllMain file from samples, utils/windows.d:

---
module utils.windows;

version (Windows):

import std.c.windows.windows;

extern (C)
{
	void gc_init();
	void gc_term();
	void _minit();
	void _moduleCtor();
	void _moduleUnitTests();

	HINSTANCE g_hInst;
}

extern (Windows)
BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
{
     switch (ulReason)
     {
	case DLL_PROCESS_ATTACH:
	    gc_init();			// initialize GC
	    _minit();			// initialize module list
	    _moduleCtor();		// run module constructors
	    _moduleUnitTests();		// run module unit tests
	    break;

	case DLL_PROCESS_DETACH:
	    gc_term();			// shut down GC
	    break;

	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	default:
	    // Multiple threads not supported yet
	    return false;
     }

     g_hInst = hInstance;
     return true;
}
---

I don't have the Linux loader in front of me but it's almost exactly the 
same and works just fine (LoadLibraryA => dlopen, GetProcAddress => 
dlsym, FreeLibrary => dlclose, with some dlerror logic in there.)  But 
as stated, it appears that there's really no "reverse linking" of DLLs.

-[Unknown]


Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fnvd2q$229m$1 digitalmars.com...
 Well, the def being for the library, not for the program loading the 
 library.  Otherwise I don't get exports named properly, etc.

In that case, I'd be more interested to see how you're compiling all this code. The code itself looks very simplistic, and I don't see any apparent failure points.

Feb 01 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fo0p7c$1tbq$1 digitalmars.com...
 Okay.

 Example "plugin" example.d:

 ---
 module example;

 extern (C)
 export int foo();

Ohhh, _there_ it is; I think this bit got cut out of the original post :)
 I don't have the Linux loader in front of me but it's almost exactly the 
 same and works just fine (LoadLibraryA => dlopen, GetProcAddress => dlsym, 
 FreeLibrary => dlclose, with some dlerror logic in there.)  But as stated, 
 it appears that there's really no "reverse linking" of DLLs.

As in having DLLs link to symbols exported from the app? Right, it's not possible without some hackery. I'm not sure how to do it but apparently it can be done. I think there are restrictions on it, though. DLLs really *are* an awful approximation of SOs :(
Feb 02 2008
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:fo1ufl$11if$1 digitalmars.com...
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo0p7c$1tbq$1 digitalmars.com...

 As in having DLLs link to symbols exported from the app?  Right, it's not 
 possible without some hackery.  I'm not sure how to do it but apparently 
 it can be done.  I think there are restrictions on it, though.

 DLLs really *are* an awful approximation of SOs :(

However there's hope: DDL. http://www.dsource.org/projects/ddl It's not entirely polished, but it's basically a sort of SO implementation for D on Windows, only it's entirely in userland rather than being part of the OS. Some simple tests have shown that it's more or less able to do the same stuff you can do with SOs. It takes a little more work but the end results are much, much better than DLLs could hope to be :)
Feb 02 2008
next sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Yes, I looked at that and tried it, but I'm afraid I may not have the 
time right now to port it from Mango to using Tango...

Thanks for the help and comments.

-[Unknown]


Jarrett Billingsley wrote:
 "Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
 news:fo1ufl$11if$1 digitalmars.com...
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo0p7c$1tbq$1 digitalmars.com...

 As in having DLLs link to symbols exported from the app?  Right, it's not 
 possible without some hackery.  I'm not sure how to do it but apparently 
 it can be done.  I think there are restrictions on it, though.

 DLLs really *are* an awful approximation of SOs :(

However there's hope: DDL. http://www.dsource.org/projects/ddl It's not entirely polished, but it's basically a sort of SO implementation for D on Windows, only it's entirely in userland rather than being part of the OS. Some simple tests have shown that it's more or less able to do the same stuff you can do with SOs. It takes a little more work but the end results are much, much better than DLLs could hope to be :)

Feb 02 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fo2i09$280r$1 digitalmars.com...
 Yes, I looked at that and tried it, but I'm afraid I may not have the time 
 right now to port it from Mango to using Tango...

It already uses Tango, and doesn't depend on Mango anymore, AFAIK. I was able to get it installed and working in a couple minutes.
Feb 02 2008
next sibling parent "Unknown W. Brackets" <unknown simplemachines.org> writes:
Ah, that's good news.  I just did the download, and didn't think to 
check out the latest trunk.

Thanks again.

-[Unknown]

Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo2i09$280r$1 digitalmars.com...
 Yes, I looked at that and tried it, but I'm afraid I may not have the time 
 right now to port it from Mango to using Tango...

It already uses Tango, and doesn't depend on Mango anymore, AFAIK. I was able to get it installed and working in a couple minutes.

Feb 02 2008
prev sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Hello again,

I dont't suppose you might tell me what revision?  I couldn't get the 
latest trunk to work for me... I was getting the same pointer back from 
getDExport whether I linked and registered or not.  I was trying the 
host.d and mule.d example files... perhaps I did something wrong?

Thanks,
-[Unknown]


Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo2i09$280r$1 digitalmars.com...
 Yes, I looked at that and tried it, but I'm afraid I may not have the time 
 right now to port it from Mango to using Tango...

It already uses Tango, and doesn't depend on Mango anymore, AFAIK. I was able to get it installed and working in a couple minutes.

Feb 04 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fo8t44$2v0j$1 digitalmars.com...
 Hello again,

 I dont't suppose you might tell me what revision?  I couldn't get the 
 latest trunk to work for me... I was getting the same pointer back from 
 getDExport whether I linked and registered or not.  I was trying the 
 host.d and mule.d example files... perhaps I did something wrong?

I have 286.
Feb 05 2008
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Okay, no love there.  What version of Tango are you using with it? 
Perhaps mine (latest download) is too new.

Thanks again.

-[Unknown]


Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo8t44$2v0j$1 digitalmars.com...
 Hello again,

 I dont't suppose you might tell me what revision?  I couldn't get the 
 latest trunk to work for me... I was getting the same pointer back from 
 getDExport whether I linked and registered or not.  I was trying the 
 host.d and mule.d example files... perhaps I did something wrong?

I have 286.

Feb 07 2008
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
news:fogp7r$1eas$1 digitalmars.com...
 Okay, no love there.  What version of Tango are you using with it? Perhaps 
 mine (latest download) is too new.

The last time I tried it out I think I was using 0.99.3; I'll admit I haven't tried since upgrading to 0.99.4.
Feb 08 2008
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jarrett Billingsley:
 However there's hope: DDL.  http://www.dsource.org/projects/ddl

It looks really nice. Regarding the Quick DDL Tutorial: http://www.dsource.org/projects/ddl/wiki/Tutorial/UsingDDL/Quick I think the interface may be eventually simplified to something like this (it may perform the "dmd -c plugin.d" too if needed): import std.stdio, ddl; void main(){ auto plugin = dlink("plugin.d"); if (plugin.hasFunction("helloWorld")) { auto hello = plugin.Func!(string function(), "helloWorld"); writefln(hello()); } } (Or/and equivalent for Tango). Bye, bearophile
Feb 04 2008
prev sibling parent reply Neal Alexander <wqeqweuqy hotmail.com> writes:
Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo0p7c$1tbq$1 digitalmars.com...
 Okay.

 Example "plugin" example.d:

 ---
 module example;

 extern (C)
 export int foo();

Ohhh, _there_ it is; I think this bit got cut out of the original post :)
 I don't have the Linux loader in front of me but it's almost exactly the 
 same and works just fine (LoadLibraryA => dlopen, GetProcAddress => dlsym, 
 FreeLibrary => dlclose, with some dlerror logic in there.)  But as stated, 
 it appears that there's really no "reverse linking" of DLLs.

As in having DLLs link to symbols exported from the app? Right, it's not possible without some hackery. I'm not sure how to do it but apparently it can be done. I think there are restrictions on it, though. DLLs really *are* an awful approximation of SOs :(

Ive never had problems resolving an .exe's exported symbols at runtime. Dunno why it would be an issue doing it from inside a DLL. x = GetModuleHandle(NULL); // handle to image that created the process GetProcAddress(x, "foo_"); // or "foo" depending how the compiler generates C symbols
Feb 03 2008
parent "Unknown W. Brackets" <unknown simplemachines.org> writes:
I'm not wanting to have to do that from the DLL.

Here's a practical example:

void plugin_function ()
{
	if (ask_host_for_something() != 42)
		tell_host_about_error("Oh no!");
}

I suppose you're suggesting I could either use GetModuleHandle(NULL) by 
passing it to the plugin, or from within the plugin (unclear but at 
least it makes this doable, though annoying.)  However, even then, .so 
files are simpler - it sets up the symbols for me.  I don't have to 
GetProcAddress any of them.

-[Unknown]


Neal Alexander wrote:
 Jarrett Billingsley wrote:
 "Unknown W. Brackets" <unknown simplemachines.org> wrote in message 
 news:fo0p7c$1tbq$1 digitalmars.com...
 Okay.

 Example "plugin" example.d:

 ---
 module example;

 extern (C)
 export int foo();

Ohhh, _there_ it is; I think this bit got cut out of the original post :)
 I don't have the Linux loader in front of me but it's almost exactly 
 the same and works just fine (LoadLibraryA => dlopen, GetProcAddress 
 => dlsym, FreeLibrary => dlclose, with some dlerror logic in there.)  
 But as stated, it appears that there's really no "reverse linking" of 
 DLLs.

As in having DLLs link to symbols exported from the app? Right, it's not possible without some hackery. I'm not sure how to do it but apparently it can be done. I think there are restrictions on it, though. DLLs really *are* an awful approximation of SOs :(

Ive never had problems resolving an .exe's exported symbols at runtime. Dunno why it would be an issue doing it from inside a DLL. x = GetModuleHandle(NULL); // handle to image that created the process GetProcAddress(x, "foo_"); // or "foo" depending how the compiler generates C symbols

Feb 03 2008
prev sibling parent Bjoern <nanali nospam-wanadoo.fr> writes:
Hm,
1-
__declspec(dllimport) int __stdcall foo();
becomes :
export extern(Windows) int foo();

so, I would give extern(Windows) instead of extern(C) a try to avoid 
name mangeling problems.

2-
Just guessing :
You have module library "DLLMain()" and loader "main()" try to place 
main() in a seperate file.

3-
UNKNOWN WROTE :
"I want to dynamically link, and also call functions from the code that 
is dynamically linking."

I can imagine that using callbacks is an interesting option to establish 
plugings, like :

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


export extern(Windows)
void ExecuteProcess(DisplayCallBack cb, char* _dir, char* _command, 
char* _args)
{
   //do someting and call CallBack function defined in Application
   cb(tok, tok_size)
}

HTH Bjoern
Feb 01 2008