views:

55

answers:

3

Imagine we have a solution with 2 projects: MakeDll (a dll app), which creates a dll, and UseDll (an exe app), which uses the dll. Now I know there are basically two ways, one is pleasant, other is not. The pleasant way is that UseDll link statically to MakeDll.lib, and just dllimports functions and classes and uses them. The unpleasant way is to use LoadLibrary and GetProcAddress which I don't even imagine how is done with overloaded functions or class members, in other words anything else but extern "C" functions.

My questions are the following (all regarding the first option)

  1. What exactly does the MakeDll.lib contain?
  2. When is MakeDll.dll loaded into my application, and when unloaded? Can I control that?
  3. If I change MakeDll.dll, can I use the new version (provided it is a superset of the old one in terms of interface) without rebuilding UseDll.exe? A special case is when a polymorphic class is exported and a new virtual function is added.

Thanks in advance.

P.S. I am using MS Visual Studio 2008

+3  A: 
  1. It basically contains a list of the functions in the DLL, both by name and by ordinal (though almost nobody uses ordinals anymore). The linker uses that to create an import table in UseDLL.exe -- i.e., a reference that says (in essence): "this file depends on function xxx from MakeDll.dll". When the loader loads that executable, it looks at the import table, and (recursively) loads all the DLLs it lists, and (at least conceptually) uses GetProcAddress to find the functions, so it can put their addresses into the executable where they're needed.

  2. It's normally loaded during the process of loading your executable. You can use the /delayload switch to delay its being loaded until a function from that DLL is called.

  3. In general, yes. In the specific case of adding a virtual function, it'll depend on the class' vtable layout staying the same other than the new function being added. Unless you take steps to either assure or verify that yourself, depending on it is a really bad idea.

Jerry Coffin
+1  A: 
  1. MakeDll.lib contains a list of studs for the exported functions and their RVAs into the MakeDll.dll

  2. MakeDll.dll is loaded into the application based on what type of loading is defined for the dll in question. (e.g. DELAYLOAD). Raymond Chen has an interesting article on that.

  3. You can use the new updated version of MakeDll.dll as long as all the RVA offsets used in UseDll.exe have not changed. In the event you change a vtable layout for a polymorphic class, as in add a new function in the middle of the previously defined vtable, you will need to recompile UseDll.exe. Other than that you can use the updated dll with the previously compiled UseDll.exe.

linuxuser27
+1  A: 

The unpleasant way is to use LoadLibrary and GetProcAddress which I don't even imagine how is done with overloaded functions or class members, in other words anything else but extern "C" functions.

Yes, this is unpleaseant but is not as bad as it sounds. If you choose to go through with this option, you'll want to do something like the following:

// Common.h: interface common to both sides.
// Note: 'extern "C"' disables name mangling on methods.
extern "C" class ISomething
{
    // Public virtual methods...

        // Object MUST delete itself to ensure memory allocator
        // coherence. If linking to different libraries on either
        // sides and don't do this, you'll get a hard crash or worse.
        // Note: 'const' allows you to make constants and delete
        // without a nasty 'const_cast'.
    virtual void destroy () const = 0;
};

// MakeDLL.c: interface implementation.
class Something : public ISomething
{
    // Overrides + oher stuff...

    virtual void destroy () const { delete this; }
};

extern "C" ISomething * create () { return new Something(); }

I've successfully deployed such setups with different C++ compilers on both ends (i.e. G++ and MSVC interchanged in all 4 possible combinations).

You may change Something's implementation all you want. However, you may not change the interface without re-compiling on both sides! When you think about it, this is faily intuitive: both sides rely on the other's definition of ISomething. What you may do to add this extra flexibility is use numbered interfaces (as DirectX does) or go with a set of interfaces and test for capabilities (as COM does). The former is really intuitive to set up but requires discipline and the second well... would be re-inventing the wheel!

André Caron