|
|
PROG |
| 22 Haz 2001 | TIPS for USING DLL's | VC++ Articels |
|
But to do this
work properly there are some tips that must be followed. Many of you might be asking
asking Why not simply use COM ? The answer is that, of course, COM is a great choice in
certain situations. However, DLLs are still a very viable alternative as well. Therefore,
in this article. I hope to illustrate just when you should use DLLs and exactly how to use
them within the framework of an MFC application. A big problem of
DLLs (specially those that use MFC) are the debug and release version. These versions
are incompatible. You probably had the problem of running the debug application version
with the DLL release version. The whole world gets crazy!! The best way, in fact the way
Microsoft does, is to give diferent names to the DLLs. So the release DLL stays with the
Visual C++ project name and the debug version would look like [Project Name]D.DLL. Using
this approach you can send the two DLLs to the system directory and be happy. Those are
the steps needed to achieve this (suposing the project name is AAA): Copy the AAA.def
to AAAD.def and change all the AAA to AAAD; In the
Project/Settings, select Win32 Debug Under the tab Link
change the Output file name to AAAD.DLL; Below in this
property page you can see something like: Now the the debug
version will create AAAD.lib and AAAD.DLL files. When I create a DLL I create an include
header to it (I think everybody does), which I named DLL header. This header has all the
exported classes definitions. And to be more efficient I include the linking stuff in it,
so to use the DLL you doesnt 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 classes
definitions goes here #endif //MUDASDASDASDASDAS Programming for
Changes The prefered kind
of DLL used to export classes are the MFC extension DLLs. By using this you can easily
instanciate a classe that is within a DLL. To do this you just declare the class like
this: class
AFX_EXT_CLASS CFoo { //... } In the application
that uses this class you just include the DLL header and everything is cool. The problem
is: everytime you need to include a member variable or a method to an exported class you
have to change the DLL header, which means recompile all those who use the DLL. To new
methods I dont know a way to overcome this recompilation, but for new variables
theres a way. Instead of
declaring the member variables directly to the class body, you create a kind of
implementation class, like the sample code: class CFooImpl; class CFoo { protected: CFooImpl* m_pThis; }; So the CFooImpl
class doesnt need to be declare to those how use this DLL. The implementation of CFoo
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; } Another method to
be prepared for changes is to use inteligents structs the way the Windows API does. So you
declare a method that has an LPVOID as in and out parameter. Those pointers are address of
structs instances. The trick is to define as the first struct member a DWORD regardings
its size. This way you know which data is expected. 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
uncommon situations that you need to call a dialog or create a class instance. So you
decide to put those in a DLL, but you dont want it 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). So you declare the exported
function as: __declspec(
DLLexport ) void
MyExportedFunc(DWORD dw) { //... } We need to include
this function in the defs 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
use show a dialog you must take care of the resource stuffs (AfxSetResource..). You can
use this approach to create class instances. The class definition must use pure virtual
functions (to avoid unresolved external symbol). It 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 until you deleted the CFoo instance. Conclusion These examples
explained the powers of well designed DLLs. But if the whole project has a bad design no
miracle will make your applications easy to change and update. The good design is the
first and most important step to the successfull project. |
|
|
|
home | about me | links | projects | clean energy | programming | |