www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - For DLLs, what does export actually do?

reply Ben Davis <entheh cantab.net> writes:
Hi,

I'm working on a multimedia driver DLL, i.e. one that Windows loads on 
behalf of any program that uses the Windows multimedia API.

I'm using a .def file with an EXPORTS section, and I've also got all the 
relevant functions marked as 'export' in the code.

What I'm finding is, if I remove the 'export' attribute but keep the 
EXPORTS section, it all still works; but if I keep the 'export' 
attribute and remove the EXPORTS section, then it doesn't work.

Just wondering why this is, and whether the 'export' attribute is 
actually programmed to do anything at the moment :)

Thanks,

Ben :)
Feb 09 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 09.02.2013 21:35, schrieb Ben Davis:
 Hi,

 I'm working on a multimedia driver DLL, i.e. one that Windows loads on
 behalf of any program that uses the Windows multimedia API.

 I'm using a .def file with an EXPORTS section, and I've also got all the
 relevant functions marked as 'export' in the code.

 What I'm finding is, if I remove the 'export' attribute but keep the
 EXPORTS section, it all still works; but if I keep the 'export'
 attribute and remove the EXPORTS section, then it doesn't work.

 Just wondering why this is, and whether the 'export' attribute is
 actually programmed to do anything at the moment :)

 Thanks,

 Ben :)
Yes it is, but its only working properly for global functions. It works partially for classes and not at all for all remaining cases. The export attribute is currently only usefull if you want to do a C-Style interface for your Dll. Kind Regards Benjamin Thaut
Feb 09 2013
parent reply Ben Davis <entheh cantab.net> writes:
On 09/02/2013 20:39, Benjamin Thaut wrote:
 Am 09.02.2013 21:35, schrieb Ben Davis:
 Hi,

 I'm working on a multimedia driver DLL, i.e. one that Windows loads on
 behalf of any program that uses the Windows multimedia API.

 I'm using a .def file with an EXPORTS section, and I've also got all the
 relevant functions marked as 'export' in the code.

 What I'm finding is, if I remove the 'export' attribute but keep the
 EXPORTS section, it all still works; but if I keep the 'export'
 attribute and remove the EXPORTS section, then it doesn't work.

 Just wondering why this is, and whether the 'export' attribute is
 actually programmed to do anything at the moment :)

 Thanks,

 Ben :)
Yes it is, but its only working properly for global functions. It works partially for classes and not at all for all remaining cases. The export attribute is currently only usefull if you want to do a C-Style interface for your Dll.
My functions are "export extern (Windows)" - I think they're global...? For example: export extern(Windows) LRESULT DriverProc(DWORD_PTR dwDriverId, HDRVR hdrvr, UINT msg, LONG lParam1, LONG lParam2) nothrow { ... }
Feb 09 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 10.02.2013 03:03, schrieb Ben Davis:
 My functions are "export extern (Windows)" - I think they're global...?

 For example:

 export extern(Windows) LRESULT DriverProc(DWORD_PTR dwDriverId, HDRVR
 hdrvr, UINT msg, LONG lParam1, LONG lParam2) nothrow { ... }
Do you have a copy of visual studio around? If so you can use dumpbin /EXPORTS your.dll From a visual studio command shell to see the symbols the dll actually exports. Just compare the version where you manually listed them in the exports section with the version where you don't manually list exports. Kind Regards Benjamin Thaut -- Kind Regards Benjamin Thaut
Feb 10 2013
parent reply Ben Davis <entheh cantab.net> writes:
On 10/02/2013 08:17, Benjamin Thaut wrote:
 Am 10.02.2013 03:03, schrieb Ben Davis:
 My functions are "export extern (Windows)" - I think they're global...?

 For example:

 export extern(Windows) LRESULT DriverProc(DWORD_PTR dwDriverId, HDRVR
 hdrvr, UINT msg, LONG lParam1, LONG lParam2) nothrow { ... }
Do you have a copy of visual studio around? If so you can use dumpbin /EXPORTS your.dll From a visual studio command shell to see the symbols the dll actually exports. Just compare the version where you manually listed them in the exports section with the version where you don't manually list exports.
Thanks, that helped expose what's going on. With the def, I get lines like "DriverProc = _DriverProc 20". Without it, I get lines like "_DriverProc 20 = _DriverProc 20". So the difference is that the export is done under a slightly mangled name if I only mark it in the code, and I need to use the def file to specify the exact name to export by. I suppose this is only necessary for weird things like driver entry points, and not for normal exported functions. A bit more Googling reveals that the n is the number of bytes taken by arguments, and is part of the stdcall == extern(Windows) convention. So Windows is making me use stdcall and then making me throw that information away in the export table. But hey - it wouldn't be the worst thing I've encountered with the Windows API. (._.'|||| :P) DllMain is a weird one - it creates all sorts of linker errors if I try it with extern(C) instead of extern(Windows) (which is different from the other methods, which compile fine with extern(C) and then crash at runtime). Also it doesn't matter what name I export it by or whether I export it at all. I'm getting the feeling this is what was implied by "The presence of DllMain() is recognized by the compiler". Good to know anyway - I like to keep stuff clean :)
Feb 10 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 10.02.2013 13:36, schrieb Ben Davis:
 With the def, I get lines like "DriverProc = _DriverProc 20".
 Without it, I get lines like "_DriverProc 20 = _DriverProc 20".
Then you did hit this 3 year old bug: http://d.puremagic.com/issues/show_bug.cgi?id=3956
Feb 10 2013
parent reply Ben Davis <entheh cantab.net> writes:
On 10/02/2013 12:39, Benjamin Thaut wrote:
 Am 10.02.2013 13:36, schrieb Ben Davis:
 With the def, I get lines like "DriverProc = _DriverProc 20".
 Without it, I get lines like "_DriverProc 20 = _DriverProc 20".
Then you did hit this 3 year old bug: http://d.puremagic.com/issues/show_bug.cgi?id=3956
I don't think I did. That bug applies to cases WITHOUT a .def file, and in that bug, the actual vs expected output is: _DriverProc mangledinsomeway (correct) modMessage mangledinsomeway (should be _modMessage mangledinsomeway) midMessage mangledinsomeway (should be _midMessage mangledinsomeway) In my case, when I build without a .def file, I get: _DriverProc mangledinsomeway _modMessage mangledinsomeway _midMessage mangledinsomeway not what I want, but correct as per the spec (I assume). Which would imply the bug was fixed at some point. My example of "DriverProc =" (without the _) was WITH a .def file, AND is what I want - there is no bug as far as I'm aware. For reference, when Andrej mentioned in earlier bug involving not having a .def, I thought of this one: http://www.digitalmars.com/d/archives/digitalmars/D/Windows_DLLs_and_TLS_177871.html Hope it's clear now? Ben :)
Feb 10 2013
parent Ben Davis <entheh cantab.net> writes:
On 10/02/2013 14:11, Ben Davis wrote:
 Which would imply the bug was fixed at some point.
...though of course it would need verifying with the example actually quoted in the bug, since there may be subtle differences. (Hopefully I'm just stating the obvious.)
Feb 10 2013
prev sibling parent reply "Regan Heath" <regan netmail.co.nz> writes:
On Sun, 10 Feb 2013 12:36:38 -0000, Ben Davis <entheh cantab.net> wrote:

 On 10/02/2013 08:17, Benjamin Thaut wrote:
 Am 10.02.2013 03:03, schrieb Ben Davis:
 My functions are "export extern (Windows)" - I think they're global...?

 For example:

 export extern(Windows) LRESULT DriverProc(DWORD_PTR dwDriverId, HDRVR
 hdrvr, UINT msg, LONG lParam1, LONG lParam2) nothrow { ... }
Do you have a copy of visual studio around? If so you can use dumpbin /EXPORTS your.dll From a visual studio command shell to see the symbols the dll actually exports. Just compare the version where you manually listed them in the exports section with the version where you don't manually list exports.
Thanks, that helped expose what's going on. With the def, I get lines like "DriverProc = _DriverProc 20". Without it, I get lines like "_DriverProc 20 = _DriverProc 20". So the difference is that the export is done under a slightly mangled name if I only mark it in the code, and I need to use the def file to specify the exact name to export by. I suppose this is only necessary for weird things like driver entry points, and not for normal exported functions. A bit more Googling reveals that the n is the number of bytes taken by arguments, and is part of the stdcall == extern(Windows) convention. So Windows is making me use stdcall and then making me throw that information away in the export table. But hey - it wouldn't be the worst thing I've encountered with the Windows API. (._.'|||| :P)
Some more background info: http://en.wikipedia.org/wiki/Name_mangling
 DllMain is a weird one - it creates all sorts of linker errors if I try  
 it with extern(C) instead of extern(Windows) (which is different from  
 the other methods, which compile fine with extern(C) and then crash at  
 runtime).
extern(C) will change the mangling.. so in this case I guess the linker is expecting DllMain but the mangling is incorrect. I wonder if the compiler detects the presence of DllMain and alters the linker line. DMD has a command line option to output the linker command line, maybe see if it changes with/without a DllMain in the code perhaps.
 Also it doesn't matter what name I export it by or whether I export it  
 at all. I'm getting the feeling this is what was implied by "The  
 presence of DllMain() is recognized by the compiler". Good to know  
 anyway - I like to keep stuff clean :)
Yep, DllMain isn't a requirement, but if is present should be called by the C runtime when the dll is loaded. You can hook into process start/stop and thread attach/detach with dll main. It's good for thread local storage initialisation - for example. R -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Feb 11 2013
parent reply Ben Davis <entheh cantab.net> writes:
On 11/02/2013 16:06, Regan Heath wrote:
 On Sun, 10 Feb 2013 12:36:38 -0000, Ben Davis <entheh cantab.net> wrote:
 DllMain is a weird one - it creates all sorts of linker errors if I
 try it with extern(C) instead of extern(Windows) (which is different
 from the other methods, which compile fine with extern(C) and then
 crash at runtime).
extern(C) will change the mangling.. so in this case I guess the linker is expecting DllMain but the mangling is incorrect. I wonder if the compiler detects the presence of DllMain and alters the linker line. DMD has a command line option to output the linker command line, maybe see if it changes with/without a DllMain in the code perhaps.
I think that's exactly what's going on. If it was only the mangling, I'd expect one error relating to that function, but instead I get at least 10 errors relating to various functions I've never heard of. I don't think there's any particular need to test further.
 Also it doesn't matter what name I export it by or whether I export it
 at all. I'm getting the feeling this is what was implied by "The
 presence of DllMain() is recognized by the compiler". Good to know
 anyway - I like to keep stuff clean :)
Yep, DllMain isn't a requirement, but if is present should be called by the C runtime when the dll is loaded. You can hook into process start/stop and thread attach/detach with dll main. It's good for thread local storage initialisation - for example.
Yep, sounds about right - and it sounds like something that gets resolved in a special way when the DLL is linked. Certainly DllMain isn't appearing in my export table, yet I've established (by calling MessageBoxA from inside DllMain - that was brave of me, wasn't it? :P) that it is being called. :)
Feb 16 2013
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/16/13, Ben Davis <entheh cantab.net> wrote:
 Certainly DllMain
 isn't appearing in my export table, yet I've established (by calling
 MessageBoxA from inside DllMain - that was brave of me, wasn't it? :P)
 that it is being called. :)
It's handled by the compiler itself, e.g.: https://github.com/D-Programming-Language/dmd/blob/master/src/backend/out.c#L1256
Feb 16 2013
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/9/13, Ben Davis <entheh cantab.net> wrote:
 Hi,

 I'm working on a multimedia driver DLL, i.e. one that Windows loads on
 behalf of any program that uses the Windows multimedia API.

 I'm using a .def file with an EXPORTS section, and I've also got all the
 relevant functions marked as 'export' in the code.
Export in code allows you to avoid listing exported functions in the DEF file. However you should likely still use the DEF file even if it lacks a list, as I recall there's a bug related to not using DEF files (I can't find the issue right now).
Feb 09 2013
parent Ben Davis <entheh cantab.net> writes:
On 09/02/2013 20:44, Andrej Mitrovic wrote:
 On 2/9/13, Ben Davis <entheh cantab.net> wrote:
 Hi,

 I'm working on a multimedia driver DLL, i.e. one that Windows loads on
 behalf of any program that uses the Windows multimedia API.

 I'm using a .def file with an EXPORTS section, and I've also got all the
 relevant functions marked as 'export' in the code.
Export in code allows you to avoid listing exported functions in the DEF file. However you should likely still use the DEF file even if it lacks a list, as I recall there's a bug related to not using DEF files (I can't find the issue right now).
(Oops, silly Thunderbird) I know which issue you mean, because I came across it earlier. It's something about needing "EXETYPE NT" and "SUBSYSTEM WINDOWS" or else something bad happens with thread-local storage. I don't think it had anything to do with exports. What you're telling me (about the purpose of export in code) is what I assumed was the intention, but the fact is it's not behaving that way. If I mark the functions as export in the code, but remove the EXPORTS section from the .def file (but keep the rest of the .def file), then the DLL doesn't work, which suggests that the export table did not contain those functions. Perhaps the exports in the code only apply if the .def is entirely missing? Is that the desired behaviour? (I won't be trying that, as I know it would break in other ways then.) Ben :)
Feb 09 2013