Click here to Skip to main content
15,880,796 members
Articles / Desktop Programming / MFC
Article

Dll Tips

Rate me:
Please Sign up or sign in to vote.
4.56/5 (17 votes)
4 Oct 2000 259.5K   89   45
Tips for writting Dynamic Link Libraries

Dynamic Link Libraries using with MFC

This document is intended to give a beginner knowledge that may be helpful when designing a project for the Windows platform. When I start a new project I try to put all code that has the greatest chance of changing into a DLL. This provides the ability to make updates to the project without having to recompile every piece. This allows for a minimum piece of code to be redistributed.

For this to work properly, there are some tips that must be followed. Much of you are asking “Why this @$&^# doesn’t use COM ?”. Of course COM is a good choice, but I’m here to show another way. Some of this code looks like COM (of course I use it’s architecture!!).

The problem with DLL’s (specially those that use MFC) is that the debug and release versions are incompatible. You have probably seen this when running a debug application version with the DLL release version. The whole world gets crazy!! The best way, in fact the way Microsoft does it, is to give different names to the different builds of the DLL. The release build of the DLL uses the [Project Name].DLL format while the debug version will use the [Project Name]D.DLL format. Note the "D" after the project name. Using this approach allows you to send the two DLLs to the installation directory with an application build and the application will work properly.

Example: Naming convention for different builds of the same DLL

These are the steps needed to achieve this (Assume the project name is AAA):

  1. Copy the AAA.def to AAAD.def and change all the AAA occurrences to AAAD.
  2. In the Project/Settings dialog, select Win32 Debug Build.
  3. Under the tab Link change the Output file name to AAAD.DLL.
  4. At the bottom of the property page you will see something like:
    /def:".\AAA.def" /out:"Debug/AAAD.DLL"
    Change to:
    /def:".\AAAD.def" /out:"Debug/AAAD.DLL"
  5. Now the debug version will create AAAD.lib and AAAD.DLL files.

When I create a DLL, I create an include header for it (I think everybody does), which I named DLL header. This header has all the exported class definitions. And to be more efficient I include the linking stuff in this file also. I would do this so you do not have to add the lib file to the Project Settings. My header looks like:

#ifndef DEF_MUDASDASDASDASDAS
#define DEF_MUDASDASDASDASDAS

#ifdef _DEBUG
#pragma comment(lib, AAAD.lib)
#else
#pragma comment(lib, AAA.lib)
#endif

... the class definitions go here

#endif //MUDASDASDASDASDAS


PROGRAMMING FOR CHANGES

The preferred kind of DLL used to export classes is a MFC extension DLL. By using this you can easily instantiate a class that is within a DLL. To do this you just declare the class as follows:

class AFX_EXT_CLASS CFoo
{
     ...

}

Evolving Implementations

In the application that uses this class you need to include the DLL header and everything is cool. The problem is: every time you need to include a member variable or a method to an exported class you have to change the DLL header. This means that every component that uses the dll must be recompiled. For new methods I don’t know a way to overcome this recompilation problem, but for new variables there’s a workaround.

Example: Handling Evolving Data Member Implmentation

Instead of declaring the member variables directly in the class body, you create a kind of implementation class, like the sample code:

class CFooImpl;
class CFoo
{
     ...

protected:
     CFooImpl* m_pThis;

};

The CFooImpl class doesn’t need to be exposed to the components that use the DLL. The implementation of CFooImpl would look like:

class CFooImpl
{
public:
    CString m_sName;
};

 

CFoo::CFoo()
{
    m_pThis = new CFooImpl;
    m_pThis->m_sName = _T("Unknown");
}


CFoo::~CFoo()
{
    delete m_pThis;
}

Evolving Data Structures

Another way to prepare for changes is to use intelligent structs the way that the Windows API does. You declare a method that has an LPVOID as in and out parameter. These pointers are address of struct instances. The trick is to define the first struct a member of DWORD type to hold it’s size. This way you can determine which data is available in the struct.

Example: Evolving Data Structures

typedef struct tagCHANGEABLE
{
     DWORD dwSize;
     long lBytes;
}CHANGEABLE, *LPCHANGEABLE;


BOOL CFoo::Method(LPVOID lpIn)
{
     LPCHANGEABLE lpChangeable = (LPCHANGEABLE)lpIn;

     if (lpChangeable->dwSize == sizeof(CHANGEABLE))
     {
         ...
         return TRUE;
     }
 
     return FALSE;
}

Using it:

CFoo myFoo;

CHANGEABLE changeable;
memset(&changeable, 0, sizeof(changeable));

changeable.dwSize = sizeof(changeable);

myFoo.Method(&changeable);


DLL LOADED WHEN NEEDED

Sometimes you have situations that requires you to call a dialog or create a class instance. So you decide to put those items in a DLL, but you don’t want the DLL to be loaded when the application gets executed. You want to load the DLL when needed (COM). This kind of DLL I call Dynamic DLL (stupid name I know “Dynamic Dynamic link libraries”).

Example: Loading A DLL As Needed

So you declare the exported function as:

__declspec( DLLexport )
void MyExportedFunc(DWORD dw)
{
     ...
}

We need to include this function in the def files (debug and release). The debug def file would look like this:

; AAAD.def : Declares the module parameters for the DLL.

LIBRARY      "AAAD"

DESCRIPTION  'AAAD Windows Dynamic Link Library'

EXPORTS
    MyExportedFunc @1
    ; Explicit exports can go here

Now to use this function we need to load the library, find the function entry point and call it.

typedef void (*MYFUNC)(DWORD);  

#ifdef _DEBUG
    HINSTANCE hDLL = AfxLoadLibrary("AAADLLD");
#else
    HINSTANCE hDLL = AfxLoadLibrary("AAADLL");
#endif

    if (hDLL)
    {
        FARPROC pnProc = GetProcAddress(hDLL, "MyExportedFunc");
        MYFUNC pnMyfunc = (MYFUNC)pnProc;

        pnMyfunc(0);

        FreeLibrary(hDLL);
     }

Remember that to show a dialog you must take care of the resource stuff (AfxSetResource..). You can also use this approach to create class instances. The class definition must use pure virtual functions (to avoid an unresolved external symbol). This is just like COM.
The class definition should look like this:

class CFoo
{
public:
   virtual void Initialize (CString sName) = 0;
};

You implement this “interface” with another class that is not visible through the DLL header file.

class CFooImp  : public CFoo
{
public:
    CFooImp();
    virtual ~CFooImp();

    void Initialize (CString sName)
    {
        m_sName  = sName;
    }

protected:
    CString m_sName;
};

To create an instance of this class (interface) you create an exported function.

__declspec(DLLexport)

CFoo* CreateFoo(DWORD dwVersion)
{
     if (dwVersion == CURRENT_VERSION)
        return new CFooImp;

     return NULL;
}

The application creates the class instance like this:

typedef CFoo* (*MYFUNC)(DWORD);  

#ifdef _DEBUG
    HINSTANCE hDLL = AfxLoadLibrary("AAADLLD");
#else
    HINSTANCE hDLL = AfxLoadLibrary("AAADLL");
#endif

    if (hDLL)
    {
        FARPROC pnProc = GetProcAddress(hDLL, " CreateFoo");
        MYFUNC pnMyfunc = (MYFUNC)pnProc;

        CFoo* pFoo = pnMyfunc(0);

        pFoo->Initialize(_T("Hi"));

        delete pFoo;
 
        FreeLibrary(hDLL);
    }

Remember that you cannot free the library that contains the CFoo implementation until you have deleted the CFoo instance.



CONCLUSION

These examples explain the powers of well-designed DLLs. A good design is the first and most important step to the successful project. Unfortunately, if the whole project has a poor design no miracle will make your applications easy to change and update.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect VisionOne AG
Switzerland Switzerland
XicoLoko is a brazilian developer based in Switzerland.

Comments and Discussions

 
GeneralWriting dll's Pin
21-Jul-01 0:57
suss21-Jul-01 0:57 
Generalseting file atributes in normal(not MFC) DLL Pin
MIlan Chudik22-Mar-01 3:46
MIlan Chudik22-Mar-01 3:46 
GeneralAnother tip for evolving data structs Pin
Wes Jones17-Jan-01 18:37
Wes Jones17-Jan-01 18:37 
GeneralDetermining if class methods exist in exported object Pin
PeterK10-Oct-00 10:42
PeterK10-Oct-00 10:42 
QuestionAnd the allocation in DLL and deleting in main rpgram does not f*** up the heap? Pin
Mikko Mononen27-Jun-00 2:38
Mikko Mononen27-Jun-00 2:38 
AnswerRe: And the allocation in DLL and deleting in main rpgram does not f*** up the heap? Pin
xicoloko27-Jun-00 3:49
xicoloko27-Jun-00 3:49 
GeneralRe: And the allocation in DLL and deleting in main rpgram does not f*** up the heap? Pin
GBO27-Jun-00 6:02
GBO27-Jun-00 6:02 
GeneralRe: And the allocation in DLL and deleting in main rpgram does not f*** up the heap? Pin
Stephen Kellett3-Jul-00 6:32
Stephen Kellett3-Jul-00 6:32 
THere is no problem with the MFC heap. THe problem is
with your code example:

void Test1(CString s)
{
TRACE("%s\n", s);
}

should read like this:-
void Test1(CString s)
{
TRACE("%s\n", (const char *)s);
}

or, more portably, between UNICODE and MBCS:-

void Test1(CString s)
{
TRACE(_T("%s\n"), (const TCHAR *)s);
}

As for memory allocated in .exe and/or .dll not being shared
between allocators, that was for Win16. If you are using Win32 and all your DLLs and the .exe use the MFC shared library msvcrt.dll (msvcrtd or msvcrtud for debug and debug unicode), then there are no mismatches.

Stpehen (snail@objmedia.demon.co.uk)

GeneralRe: And the allocation in DLL and deleting in main rpgram does not f*** up the heap? Pin
kblees22-Aug-00 14:53
kblees22-Aug-00 14:53 
GeneralVery good advice - but be aware of extension DLLs Pin
Andy Metcalfe9-Oct-00 22:08
sussAndy Metcalfe9-Oct-00 22:08 
GeneralRe: And the allocation in DLL and deleting in main program does not f*** up the heap? Pin
Arnt Witteveen8-Mar-01 3:45
Arnt Witteveen8-Mar-01 3:45 
GeneralRe: And the allocation in DLL and deleting in main program does not f*** up the heap? Pin
Bernhard28-Apr-04 23:29
Bernhard28-Apr-04 23:29 
GeneralRe: And the allocation in DLL and deleting in main program does not f*** up the heap? Pin
Arnt Witteveen28-Apr-04 23:47
Arnt Witteveen28-Apr-04 23:47 
AnswerRe: And the allocation in DLL and deleting in main rpgram does not f*** up the heap? Pin
dumbo2-Jul-00 7:42
dumbo2-Jul-00 7:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.