Jump to page: 1 2
Thread overview
Building a simple DLL
Feb 06, 2004
Michael
Feb 06, 2004
Scott Michel
Feb 06, 2004
Jan Knepper
Feb 06, 2004
Scott Michel
Feb 07, 2004
Michael
Feb 08, 2004
Michael
Feb 08, 2004
Scott Michel
Feb 09, 2004
Scott Michel
Feb 09, 2004
Scott Michel
Feb 10, 2004
Michael
Feb 10, 2004
Scott Michel
Feb 10, 2004
Michael
Feb 10, 2004
Scott Michel
February 06, 2004
I'm having the hardest time trying to build a simple 32-bit console only DLL. As a Unix dude, I can't fathom having to maintain a .DEF file of exported symbols. That is just too hard to swallow. I have over a hundred source files...I'm supposed to maintain a DEF file for these classes by hand? Why not just export everything and forget about it ala Unix. Much less headache and room for error.

Enough ranting...I have a module Foo.cpp with an accompanying header. I'd like
this to be a DLL called Foo.dll. I've defined DllMain() inside Foo.cpp and I've
added __declspec(dllexport) after each class declaration (how tedious). So for
Bar inside Foo.cpp:

#define EXPORT_DLL __declspec(dllexport)

class EXPORT_DLL Bar { ... };

I've got a simple driver called Driver.cpp which links against an import library derived from Foo.dll. When including Foo.h in Driver.cpp EXPORT_DLL becomes __declspec(dllimport). Again, tedious. Welcome to Windows I guess.

By explicitly using __declspec for each class, the theory goes that I won't need a .DEF file. Is this assumption correct? I'm new to Windows programming.

Here's how I'm building everything. By the way, everything does build without warnings or errors, but my executable does *nothing* (I have a method in Bar which just dumps a message to cout, yet I get no output...static link works fine and produces the expected output). The intention is for a simple 32-bit console application.

$ dmc -ND -DBUILD_DLL -I\dm\stlport\stlport -c Foo.cpp

Note that my BUILD_DLL macro just defines EXPORT_DLL to be __declspec(dllexport). Also, I want the -ND switch so that I link against the dynamic DMC RTLs.

$ dmc -oFoo.dll -ND -L/implib Foo.obj

Note here that I'm telling dmc to build me an import library to link against.
Output from dmc at this point (when the linker runs):
link Foo,Foo.dll,,user32+kernel32/noi/implib;

I do get a DLL and an import LIB. It appears that DMC saw the DllMain() entry point as I get no complaints.

Finally:

$ dmc -ND -I\dm\stlport\stlport -oDriver.exe Driver.cpp Foo.lib

The linker responds with:
link Driver,Driver.exe,,Foo+user32+kernel32/noi;

I run Driver and get no output (the Driver simply creates Foo and calls a method that just prints to cout...it works fine when statically linked but produces no output when I perform the above steps). Note that Driver is a console application so it has an entry point of main().

I have a feeling I need a DEF file which sucks, because I've gone through the pain of exporting/importing my class declarations with __declspec. Why should I now be bothered with maintaining a DEF file? Remember, this is a toy example...my real code has over a 100 modules and at least as many classes. Do people actually have to manually maintain DEF files? Again, I'm in disbelief that Windows programming could be this stupid.

Is there a way to dump the symbols from an import library so that I can at least check that my exports were correctly performed?

Also, what is the equivalent of UNIX ldd for listing the DLLs an executable is linked against? I would have expected this application to crash but I get no dialog or message...nothing appears to happen.

If I do need a DEF file, is there a tool that can generate this for me from the command line (I hate IDEs)? What would the minimum DEF file for my example look like for a 32-bit console DLL? Most importantly, is there a way to avoid manually putting the export symbols into the DEF file? If not, I'm going back to UNIX.

Thanks for any help!

Sorry for the ranting, but I paid $25 bucks for this. :-)


February 06, 2004
Michael <Michael_member@pathlink.com> wrote:
> I'm having the hardest time trying to build a simple 32-bit console only DLL. As a Unix dude, I can't fathom having to maintain a .DEF file of exported symbols. That is just too hard to swallow. I have over a hundred source files...I'm supposed to maintain a DEF file for these classes by hand? Why not just export everything and forget about it ala Unix. Much less headache and room for error.

Yeah, I can sympathize as a FreeBSD-er. Normally, you shouldn't need a .DEF file if you make use of __declspec properly. It's a bit of a shock coming from the Unix world, but it's not so bad once you get used to the platform.

BTW: A "console DLL" is an oxymoron. A DLL is a DLL, a console app is a console app. Console apps can be linked with DLLs.

> Enough ranting...I have a module Foo.cpp with an accompanying header. I'd like
> this to be a DLL called Foo.dll. I've defined DllMain() inside Foo.cpp and I've
> added __declspec(dllexport) after each class declaration (how tedious). So for
> Bar inside Foo.cpp:
> 
> #define EXPORT_DLL __declspec(dllexport)

You should rephrase this as:

#ifdef BUILD_DLL
#define EXPORT_DLL __declspec(dllexport)
#else
#define EXPORT_DLL __declspec(dllimport)
#endif

> By explicitly using __declspec for each class, the theory goes that I won't need a .DEF file. Is this assumption correct? I'm new to Windows programming.

Yup.

> Here's how I'm building everything. By the way, everything does build without warnings or errors, but my executable does *nothing* (I have a method in Bar which just dumps a message to cout, yet I get no output...static link works fine and produces the expected output). The intention is for a simple 32-bit console application.
> 
> $ dmc -ND -DBUILD_DLL -I\dm\stlport\stlport -c Foo.cpp
> 
> Note that my BUILD_DLL macro just defines EXPORT_DLL to be __declspec(dllexport). Also, I want the -ND switch so that I link against the dynamic DMC RTLs.
> 
> $ dmc -oFoo.dll -ND -L/implib Foo.obj

missing '-WD -mn'. '-mn' is the default, but it's always good to be explicit to prevent later maintainer confusion. :-)

-ND tells the compiler to assume that the C/C++ runtime comes from DLLs, not the statically linked libraries. This is probably OK.

> $ dmc -ND -I\dm\stlport\stlport -oDriver.exe Driver.cpp Foo.lib
> 
> The linker responds with:
> link Driver,Driver.exe,,Foo+user32+kernel32/noi;
> 
> I run Driver and get no output (the Driver simply creates Foo and calls a method that just prints to cout...it works fine when statically linked but produces no output when I perform the above steps). Note that Driver is a console application so it has an entry point of main().

Recompile as above and see if you get output, I suspect you should. If not, you might want to consider using STLport for iostream by adding -I\dm\stlport\stlport to your compile line.

> I have a feeling I need a DEF file which sucks, because I've gone through the pain of exporting/importing my class declarations with __declspec. Why should I now be bothered with maintaining a DEF file? Remember, this is a toy example...my real code has over a 100 modules and at least as many classes. Do people actually have to manually maintain DEF files? Again, I'm in disbelief that Windows programming could be this stupid.

No. Once upon a time in Windows 3.1 we did have that problem. The only time one really needs a DEF file, technically, is if you export by ordinal. But few libs do that, other than MS's system libs.

Oh, before I forget, DLLs are much like "no undefined" shared objects -- if you end up with a lot of undefined symbols when you create a DLL:

a) You probably forgot a library when linking the DLL. msdn.microsoft.com can
   help you figure out which library you're missing

b) You forgot to __declspec(dllexport) a symbol or class. You'd be surprised
   at what has to be exported. But the linker will take care of creating the
   export library (w/o you creating a DEF file.)

> Is there a way to dump the symbols from an import library so that I can at least check that my exports were correctly performed?

Cygwin's objdump is a help, as is DMC's dumpobj.

> Also, what is the equivalent of UNIX ldd for listing the DLLs an executable is linked against? I would have expected this application to crash but I get no dialog or message...nothing appears to happen.

If you find one, I'd like to know about it.

> Sorry for the ranting, but I paid $25 bucks for this. :-)

Actually, compared to other tools available, it's **darned good** value for the money. At least you won't turn into a fossil waiting for gcc to get around to compiling your code and the runtime is a DLL (libstdc++ isn't.)



-scooter
February 06, 2004
> Yeah, I can sympathize as a FreeBSD-er.

FreeBSD huh?!... ;-)
Good choice! digitalmars.com is hosted on FreeBSD...
www.digitaldaemon.com



-- 
ManiaC++
Jan Knepper

But as for me and my household, we shall use Mozilla... www.mozilla.org
February 06, 2004
Jan Knepper <jan@smartsoft.us> wrote:
>> Yeah, I can sympathize as a FreeBSD-er.
> 
> FreeBSD huh?!... ;-)
> Good choice! digitalmars.com is hosted on FreeBSD...
> www.digitaldaemon.com

I once contributed to early versions of Linux, but decided to give up on it after it became apparent that Linux is a kernel and everything else is a mess. I've actually found my ancient (*) e-mail "scottm@intime.intime.com" and "scottm@intime.com" still in the ibcs2 source files.

It's always a source of amusement to the sysadmins here at CS that in the eight years I've been here, I haven't been knocked over, whereas all of the Linux boxen get hacked regularly.

But, the "consensus" is that BSD is dead, it's no longer useful or pertinent. Funny thing is that FreeBSD is quite popular at MS Research.

I've been pretty dedicated to FreeBSD since the 2.x series a few years ago.
But it's been a while since I did kernel hacking. The closest I came to doing
kernel stuff was 2002, where I did some extensive hacking on a Conexant
huMMingbird cordless telephone chip that was being used by a pair of pico-
satellites launched off the back of Endeavor. It's is 6502-based system
that does spread spectrum. Pictures at http://mordred.cs.ucla.edu/STS-113/


-scooter

(*) My company's connection to the "Internet" at the time was via UUCP
    through randvax!smc!intime (Santa Monica College and RAND Corporation)
-- 
Scott Michel                          | No research proposal ever survives
UCLA Computer Science                 |    contact with implementation.
PhD Graduate Student                  | !!  Futuaris nisi irrisus ridebis  !!

February 07, 2004
Hi Scott,

Thanks for the help, but it's still a no go. I took your advice and
added "-WD
-mn" to explicitly set the memory model. Now at least the build
breaks when
creating the DLL. That's progress. :-)

 $ dmc -ND -DBUILD_DLL
-I\dm\stlport\stlport -c Foo.cc
 $ dmc -oFoo.dll -ND -WD -mn -L/implib
-I\dm\stlport\stlport Foo.obj
  link Foo,Foo.dll,,,Foo/noi/implib;
..OPTLINK header/copyright elided...

  C:\dm\bin\..\lib\SND.lib(dllstart)
Error 42: Symbol Undefined _GetVersion@0
  C:\dm\bin\..\lib\SND.lib(semlock)
Error 42: Symbol Undefined _GetTickCount@0
  ...
  and so on ad nauseum. All
undefined symbol messages from SND.lib which I
  assume is the dynamic C++ RTL
that I'm supposed to be linking to via -ND?

  By the way, the SND.lib import
library is where it's supposed to be:

  Directory of C:\dm\lib
09/08/2003  09:27PM   482,816 SND.lib

  One clue which doesn't look
good...DMC generated the following DEF file:

  LIBRARY "Foo.dll"
DESCRIPTION 'Foo as a DLL'
  EXETYPE NT
  SUBSYSTEM WINDOWS
  CODE SHARED
EXECUTE
  DATA WRITE

  Shouldn't the SUBSYSTEM be CONSOLE and not WINDOWS? I
tried adding
  -L/su:console to the link line but was no better off.
Driver.cpp includes a
  definition of main() not WinMain(). Shouldn't DMC
figure this out?

  Any more clues?

  Thanks,
  Michael


February 08, 2004
Ok, I figured this one out. I had to link with kernel32.lib explicitly when building the DLL. For building an exe, DMC links against kernel32.lib and user32.lib implicitly.

Things are working now...thanks for the help! I think this should be in the FAQ. I can't believe more people haven't run into these problems. Maybe a "DMC for UNIX developers" section. :-)

Michael


February 08, 2004
Michael <Michael_member@pathlink.com> wrote:
> Ok, I figured this one out. I had to link with kernel32.lib explicitly when building the DLL. For building an exe, DMC links against kernel32.lib and user32.lib implicitly.

Not completely unexpected, since a DLL, like a Unix shared object/library, has slightly different link semantics than an ordinary application, console or windows.

There are times and places where you might not want to link against kernel32 and user32. I can't think of any off the top of my head at the moment. But having the control over linking is desirable.

> Things are working now...thanks for the help! I think this should be in the FAQ. I can't believe more people haven't run into these problems. Maybe a "DMC for UNIX developers" section. :-)

:-) :-) :-)


-scooter
February 09, 2004
Scott Michel <scottm@mordred.cs.ucla.edu> wrote:
> Michael <Michael_member@pathlink.com> wrote:
>> Ok, I figured this one out. I had to link with kernel32.lib explicitly when building the DLL. For building an exe, DMC links against kernel32.lib and user32.lib implicitly.
> 
> Not completely unexpected, since a DLL, like a Unix shared object/library, has slightly different link semantics than an ordinary application, console or windows.
> 
> There are times and places where you might not want to link against kernel32 and user32. I can't think of any off the top of my head at the moment. But having the control over linking is desirable.
> 
>> Things are working now...thanks for the help! I think this should be in the FAQ. I can't believe more people haven't run into these problems. Maybe a "DMC for UNIX developers" section. :-)
> 
> :-) :-) :-)

The biggest one is getting __declspec(dllexport) and __declspec(dllimport) right.

The other big one is DLLs can't have undefined symbols. You have to specify all of the additional libs that link against the DLL. It's like specifying "-no-undefined" to the GNU ld linker and requiring that all libraries linked are shared libraries.

Other than those two, it's all about what's in the SDKs provided by MS. That's more than an FAQ.


-scooter
February 09, 2004
Michael <Michael_member@pathlink.com> wrote:
> Hi Scott,
> 
> Thanks for the help, but it's still a no go. I took your advice and
> added "-WD
> -mn" to explicitly set the memory model. Now at least the build
> breaks when
> creating the DLL. That's progress. :-)
> 
> $ dmc -ND -DBUILD_DLL
> -I\dm\stlport\stlport -c Foo.cc

This should also have "-WD -mn" in the compiler flags. Think of "-WD" as "-fpic -fPIC -DPIC" to generate position indep. code.

> $ dmc -oFoo.dll -ND -WD -mn -L/implib
> -I\dm\stlport\stlport Foo.obj
>  link Foo,Foo.dll,,,Foo/noi/implib;
> ..OPTLINK header/copyright elided...

"-WD -mn" isn't needed here since you're linking.

> 
>  C:\dm\bin\..\lib\SND.lib(dllstart)
> Error 42: Symbol Undefined _GetVersion@0
>  C:\dm\bin\..\lib\SND.lib(semlock)
> Error 42: Symbol Undefined _GetTickCount@0
>  ...
>  and so on ad nauseum. All
> undefined symbol messages from SND.lib which I
>  assume is the dynamic C++ RTL
> that I'm supposed to be linking to via -ND?
> 
>  By the way, the SND.lib import
> library is where it's supposed to be:
> 
>  Directory of C:\dm\lib
> 09/08/2003  09:27PM   482,816 SND.lib
> 
>  One clue which doesn't look
> good...DMC generated the following DEF file:
> 
>  LIBRARY "Foo.dll"
> DESCRIPTION 'Foo as a DLL'
>  EXETYPE NT
>  SUBSYSTEM WINDOWS
>  CODE SHARED
> EXECUTE
>  DATA WRITE
> 
>  Shouldn't the SUBSYSTEM be CONSOLE and not WINDOWS? I
> tried adding

Nope. You can think of DLLs as a special kind of EXE. A console mode EXE has a different entry point that creates the console window for you. Regular EXEs and DLLs don't create console windows ordinarily.

That's why "console mode DLL" is an oxymoron, as previously noted.

>  -L/su:console to the link line but was no better off.
> Driver.cpp includes a
>  definition of main() not WinMain(). Shouldn't DMC
> figure this out?

Vide supra.

>  Any more clues?
> 
>  Thanks,
>  Michael
February 10, 2004
>> $ dmc -ND -DBUILD_DLL
>> -I\dm\stlport\stlport -c Foo.cc
>
>This should also have "-WD -mn" in the compiler flags. Think of "-WD" as "-fpic -fPIC -DPIC" to generate position indep. code.

Thanks Scott. I've done some experimenting and learned a few things...

I left off "-mn" since it seems to be the default on Windows XP at least, and I found that "-WD" doesn't seem to have the semantics of -fPIC for GCC. I was able to leave off "-WD" when compiling the object files that would be used to build the DLL. This implies "-WD" doesn't generate position independent code, unless I was very lucky (or unlucky). :-)

>> $ dmc -oFoo.dll -ND -WD -mn -L/implib
>> -I\dm\stlport\stlport Foo.obj
>>  link Foo,Foo.dll,,,Foo/noi/implib;
>> ..OPTLINK header/copyright elided...
>
>"-WD -mn" isn't needed here since you're linking.

Again, my experiments show just the opposite. When linking to build the DLL from object files I *had* to add "-WD" or I would get strange behavior at runtime (in my case, no output). Even when I added "-WD" to compile (which I found was unnecesary) I did need to use it for linking the DLL.

Thanks for helping out...it gave me enough insight to get through the initial quirks. I'm actually finding the control I have over exported symbols has a benefit. I don't need to export *all* of my classes, which helps with encapsulation. I can prevent clients from accessing internal classes. It's not as tedious as I had anticipated and forced me to consider what the public interface of my library should be. I still like developing on UNIX flavors better. ;-)

Thanks,
Michael


« First   ‹ Prev
1 2