home *** CD-ROM | disk | FTP | other *** search
-
-
-
-
-
-
-
-
-
-
-
-
- CHAPTER
- ___________________________________________________________________
-
- 1
-
-
- Dynamic link libraries
-
-
- A dynamic-link library (DLL) is a library of functions whose
- references are resolved at run time rather than at compile time.
- DLL functions are dynamically linked to a calling application.
-
- Each application using statically linked code has its own copy of
- the code. The code for functions defined in a DLL is shared by all
- calling applications; only one copy is placed in memory. Defining
- code shared by a group of applications in a DLL therefore reduces
- the size of the .EXEs of the calling applications.
-
- You might want to define complex windowing behavior, shared by a
- group of your applications, in an ObjectWindows DLL. In this
- chapter, you'll learn how to write and use an ObjectWindows DLL.
- You'll also learn how to use the ObjectWindows dynamic-link library
- (OWL.DLL).
-
-
- C++ DLLs
-
- DLLs can contain ordinary functions or a combination of functions
- and exported C++ classes. First, we'll examine DLLs in the context
- of a library of ordinary functions. This section contains an
- overview of Borland C++ DLLs; you'll learn how to write, export,
- and call a DLL function using Borland C++. For more detailed
- information, see "Dynamic link libraries" in Chapter 3, "Building a
- Windows application," of the Borland C++ User's Guide.
-
-
- Writing DLL functions
-
- Windows requires that two functions be defined in every DLL:
- LibMain and WEP (Windows Exit Procedure). (Borland C++ provides a
- default WEP automatically.)
-
-
-
-
-
-
- Chapter 1 Page 1
-
-
-
-
-
-
-
-
-
-
- LibMain and WEP
-
- You must supply the LibMain function, which is the main entry point
- for a Windows DLL. Windows calls LibMain once, when the library is
- first loaded. LibMain initializes the DLL; this initialization
- depends almost entirely on the particular DLL's function, but might
- include the following tasks:
-
- o unlocking the data segment with UnlockData, if it has been
- declared as MOVEABLE
-
- o setting up global variables for the DLL, if it uses any
-
- Note
- The DLL startup code C0Dx.OBJ initializes the local heap
- automatically.
-
- The following parameters are passed to LibMain:
-
- int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg,
- WORD cbHeapSize, LPSTR lpCmdLine);
-
- HANDLE, WORD, and LPSTR are defined in WINDOWS.H.
- HANDLE hInstance is the instance handle of the DLL.
-
- WORD wDataSeg is the value of the data segment (DS) register.
-
- WORD cbHeapSize is the size of the local heap specified in the
- module definition file for the DLL.
-
- LPSTR lpCmdLine is a far pointer to the command line specified when
- the DLL was loaded. This is almost always null, because typically
- DLLs are loaded automatically without parameters. It is possible,
- however, to supply a command line to a DLL when it is loaded
- explicitly.
-
- The return value for LibMain is either 1 (successful
- initialization) or 0 (unsuccessful initialization). Windows will
- unload the DLL from memory if the value is 0.
-
- WEP is the exit point of a DLL; Windows calls it prior to unloading
- the DLL. This function is not necessary in a DLL (because the
- Borland C++ run-time libraries provide a default one), but can be
- supplied by the writer of a DLL to perform any cleanup of the DLL
- before it is unloaded from memory.
-
- Under Borland C++, WEP does not need to be exported. This is the
- prototype for WEP:
-
- int FAR PASCAL WEP (int nParameter);
-
-
-
-
-
- Chapter 1 Page 2
-
-
-
-
-
-
-
-
-
-
- int nParameter is either WEP_SYSTEMEXIT (all of Windows shuts down)
- or WEP_FREE_DLL (just this DLL is unloaded).
-
- WEP returns 1 to indicate success. Note that Windows currently
- doesn't use this return value.
-
-
- DLL-specific functions
-
- The additional functions defined in your DLL depend on the services
- your DLL provides. When writing functions that will be called from
- an application, you need to keep these things in mind:
-
- o Make calls to DLL functions far calls, and make pointers
- specified as parameters and return values far pointers. This is
- because a DLL has different code and data segments than the
- calling application.
-
- o Static data defined in a DLL is global to all calling
- applications. Global data set by one caller can be accessed by
- another. If you need data to be private for a given caller, you
- need to dynamically allocate and manage the data yourself.
-
-
- Exporting DLL functions
-
- After writing your DLL functions, you must export the functions
- that you want available to a calling application. There are two
- steps involved: compiling your DLL functions as exportable
- functions and exporting them. You can do this in the following
- ways:
-
- o If you flag a function with the _export keyword, it's compiled as
- exportable and is then exported.
-
- o If you don't flag a function with _export and you use the -WD
- option when compiling, the function is compiled as exportable.
- However, the function only gets exported if you also list it in
- the EXPORTS section of the module definition (.DEF) file.
-
- o If you add the _export keyword to a class declaration, the entire
- class (data and function members) will be compiled as exportable
- and exported.
-
-
- Calling DLL functions
-
- You call a DLL function within an application just as you would
- call a function defined in the application itself. However, you
- must import the DLL functions that your application calls.
-
- There are two ways to import a DLL function:
-
-
-
- Chapter 1 Page 3
-
-
-
-
-
-
-
-
-
-
- 1. You can add an IMPORTS section to the calling application's
- module definition (.DEF) file and list the DLL function as an
- import.
-
- 2. You can link an import library that contains import information
- for the DLL function to the calling application. (Use IMPLIB to
- make the import library.)
-
- When your application is executed, the .DLL files for the DLLs that
- it calls must be in the directory of the calling application or on
- the path; otherwise, your application won't load.
-
-
- ObjectWindows DLLs
-
- ObjectWindows DLLs differ from ordinary DLLs in that, with Object-
- Windows DLLs, you can export classes derived from ObjectWindows classes
- as well as your own classes. This makes those classes and their
- member functions available to all .EXEs that dynamically link with
- your DLL.
-
- If you find that complex window behavior is shared by a group of
- your applications, you can define this behavior in an ObjectWindows
- DLL. For example, you might build a DLL that defines
-
- o a complex window used by related applications
-
- o useful dialogs
-
- o reusable custom controls
-
- o common drawable controls
-
-
- The module object
-
- An instance of TModule acts as an object-oriented representative for
- an ObjectWindows DLL. TModule member functions provide support for
- window and memory management and process errors.
-
- Construct a module object in the LibMain function of your DLL:
-
- PTModule TheModule;
-
- int FAR PASCAL LibMain(HANDLE hInstance, WORD,
- WORD cbHeapSize, LPSTR lpCmdLine)
- {
- int TheStatus;
-
- TheModule = new TModule("LibName", hInstance, lpCmdLine);
- TheStatus = TheModule->Status;
- if (TheStatus != 0)
-
-
-
- Chapter 1 Page 4
-
-
-
-
-
-
-
-
-
-
- {
- delete TheModule;
- TheModule = NULL;
- }
-
- return (TheStatus == 0);
- }
-
- In this example LibMain, an instance of TModule, is constructed.
- The hInstance and lpCmdLine parameters received from Windows are
- passed to the TModule constructor to initialize data members of the
- same name.
-
- After the module object is constructed, its Status data member is
- used to determine the success or failure of module initialization.
- If the Status member contains a nonzero value, the initialization
- of the module object is unsuccessful. At this point you should
- delete the module object in LibMain, set its pointer to NULL and
- return zero to Windows to indicate that an error occurred. Your
- module's constructor should set Status to an error that you define
- (a nonzero value) if your DLL cannot be successfully initialized.
-
- In WEP, the module object is deleted:
-
- int FAR PASCAL WEP(int)
- {
- if ( TheModule )
- delete TheModule;
- return 1;
- }
-
- As noted, Windows does not use WEP's return value.
-
- Usually, your DLL will require additional initialization and
- cleanup. You could perform this processing in your LibMain and WEP
- functions; better yet, derive a class from TModule. Perform the
- required initialization and cleanup in its constructor and
- destructor, and define data members for data global to your DLL.
-
-
- Shared classes
-
- You can share code that defines the behavior of a general-use
- dialog box by defining a dialog class in a DLL (a shared class).
- For example, a group of graphics applications might use a color
- dialog defined in a DLL to retrieve an RGB value from the user. To
- do so, you'll need to export the class from the DLL and import the
- class into your applications.
-
-
-
-
-
-
-
-
- Chapter 1 Page 5
-
-
-
-
-
-
-
-
-
-
- Declaring and defining a shared class
-
- You'll need to do the following in order to successfully define and
- declare a shared class.
-
- o pass an additional PTModule parameter to window constructors (when your
- ObjectWindows DLL can be used by non-ObjectWindows applications).
-
- o conditionally declare your class as either _export or huge.
-
- o use far pointers and far references in your declarations.
-
- If you declare a shared class in an include file that is included
- by both the DLL and an application using the DLL, the class must be
- declared _export when compiling the DLL and huge when compiling the
- application. You can do this with the _EXPORT macro, which is set
- to _export or huge as needed when building a DLL or using an .EXE
- with a DLL.
-
- class _EXPORT TColorControl : public TControl
- {
- .
- .
- .
- };
-
-
- Macro expansion
-
- The _EXPORT macro expands into _export if __DLL__ is defined.
- Borland C++ defines __DLL__ automatically when you supply the -WD
- or -WDE options to the command-line compiler, or when you specify
- that you are building a DLL in the IDE. If you are not building a
- DLL, then _EXPORT expands to _CLASSTYPE. _CLASSTYPE is a macro that
- expands into "huge", "far", or "near." See the Reference Guide for
- details.
-
- In order for _CLASSTYPE to expand into "huge" (which is required
- for classes shared between an application and DLLs), a third macro
- is used: _CLASSDLL. You must define _CLASSDLL on either the
- compiler command line or by including it in the "Defines" edit
- field in the compiler options dialog box of the Borland C++ IDE.
- When defined, _CLASSDLL communicates to both the compiler and to
- the _CLASSTYPE macro your intention to use the classes being
- compiled as shared classes.
-
- In summary, declare your shared classes with _EXPORT as in the
- example above. Compile your DLLs with the appropriate compiler
- directives and your classes will be exported. Compile your .EXE
- with the _CLASSDLL macro defined, and your classes will be compiled
- as huge and will therefore be suitable for sharing with DLLs.
-
-
-
- Chapter 1 Page 6
-
-
-
-
-
-
-
-
-
-
- Pointers and typedefs
-
- You must also declare the pointers and references in the header
- file of your shared class as far pointers. You can explicitly set
- the pointers to far, or you can use the _FAR macro to do this. _FAR
- is set to far only when _CLASSDLL is defined.
-
- ObjectWindows defines data type specifiers for each ObjectWindows
- class using the _FAR macro. The PTDialog typedef is shown here:
-
- typedef TDialog _FAR *PTDialog;
-
- ObjectWindows defines similar type specifiers for each Object-
- Windows class. Using these ObjectWindows typedefs will help improve
- the readability of your code.
-
- When referring to a pointer to an ObjectWindows class, use a type
- whose name is P followed by the name of a class. Similarly, for a
- reference to a class, use an R followed by the name of the class.
- For example, a pointer to a TWindow is a PTWindow. A reference to a
- TDialog is an RTDialog.
-
- All ObjectWindows classes automatically define those and other
- typedefs that make use of the _FAR macro. To automatically get
- those typedefs defined for your own classes, use the _CLASSDEF
- macro at the top of your header file for each class you define. For
- example, this code
-
- _CLASSDEF(TMyWindow)
- class _EXPORT TMyWindow:public TWindow
- {
-
- };
-
- will cause PTMyWindow, RTMyWindow, and other typedefs to be
- defined. See the documentation on _CLASSDEF in the ObjectWindows
- for C++ Reference Guide for details of the typedefs automatically
- defined.
-
- Due to the way the Borland C++ compiler treats classes and
- structures, if your shared classes contain, as data members,
- pointers to structures or other classes, you may have to declare
- any referenced structures and classes with the _CLASSTYPE macro.
- For example,
-
- struct _CLASSTYPE TMyStruct
- {
- };
- typedef TMyStruct _FAR * PTMyStruct;
-
- class _EXPORT TMyClass
- {
-
-
-
- Chapter 1 Page 7
-
-
-
-
-
-
-
-
-
-
- PTMyStruct ptms;
- };
-
- In the above example, TMyStruct may need to be declared with
- _CLASSTYPE (as shown), since pointers to it are contained in the
- huge class TMyClass. (_EXPORT implies "huge" when you are compiling
- a DLL or compiling an .EXE with the _CLASSDLL macro defined).
-
- The declaration of a TColorDialog shared class and the
- TColorControl class that it uses are shown next.
-
- This class definition is from COLORDLG.H.
-
- _CLASSDEF(TColorControl)
-
- class _EXPORT TColorControl : public TControl
- {
- public:
- COLORREF Color;
-
- TColorControl(PTWindowsObject AParent, int ResourceId, COLORREF
- AColor, PTModule AModule = NULL);
-
- protected:
- virtual LPSTR GetClassName();
- virtual void GetWindowClass(WNDCLASS _FAR & AWndClass);
- virtual void Paint(HDC, PAINTSTRUCT _FAR & PS);
- virtual WORD Transfer(Pvoid DataPtr, WORD TransferFlag);
- virtual void WMLButtonDown(RTMessage Msg) = [WM_FIRST +
- WM_LBUTTONDOWN];
- virtual void WMLButtonDblClk(RTMessage Msg) = [WM_FIRST +
- WM_LBUTTONDBLCLK];
-
- public:
- virtual void SetColor(COLORREF AColor);
- };
-
- _CLASSDEF(TColorDialog)
-
- class _EXPORT TColorDialog : public TDialog
- {
- protected:
- PTScrollBar ColorBar1;
- PTScrollBar ColorBar2;
- PTScrollBar ColorBar3;
- PTColorControl SelColor;
-
- public:
- TColorDialog(PTWindowsObject AParent, COLORREF _FAR & TheColor);
-
- protected:
-
-
-
- Chapter 1 Page 8
-
-
-
-
-
-
-
-
-
-
- virtual void DefChildProc(RTMessage Msg);
- virtual void SetupWindow();
- virtual void TransferData(WORD TransferFlag);
- virtual void UpdateBars(COLORREF AColor);
- };
-
- An RTMessage typedef is used in these declarations to improve code
- readability and to ensure the reference is a far reference when the
- class is used as a shared class. It is defined by ObjectWindows as
- follows:
-
- typedef TMessage _FAR & RTMessage;
-
- Various other macros that use the _FAR macro are defined in _DEFS.H in
- the BORLANDC\INCLUDE directory.
-
- After you've properly declared your shared class, you can define
- its implementation in your DLL as you would in an ObjectWindows
- application.
-
-
- Building a shared class in a DLL
-
- After declaring and defining your shared class, you need to
-
- o write LibMain and WEP functions
-
- o compile your DLL
-
- You can copy the LibMain and WEP functions previously shown to your
- DLL's .CPP source file, or you can use your customized versions.
-
- To compile your DLL, simply follow the normal procedures for
- generating DLLs. For the command-line compiler, specify either the
- -WDE or -WD options. For the IDE, ensure you are building a DLL by
- checking both the Options|Compiler|Entry/Exit Code and Options|
- Linker dialog boxes for the correct DLL options. See your Borland
- C++ documentation for general information about building DLLs.
-
- After you have compiled and linked your DLL, use IMPLIB to generate
- an import library for your DLL. This import library will list all
- exported member functions from your shared classes as well as any
- ordinary functions which you've exported.
-
- For more information on any of these procedures, see previous
- sections of this chapter, and "Dynamic link libraries" in Chapter
- 3, "Building a Windows application," of the Borland C++ User's
- Guide.
-
-
-
-
-
-
-
- Chapter 1 Page 9
-
-
-
-
-
-
-
-
-
-
- Using a shared class
-
- To use a shared class from a DLL in your application, you need to
- compile your application defining the _CLASSDLL macro (as discussed
- earlier). Then link your application with the import library you
- created for your DLL. Then you can use the class defined in the DLL
- as if the class had been defined by your application.
-
- In the following example code, TColorDialog is executed when a
- Color menu item is selected from TTestWindow's menu:
-
- void TTestWindow::CMColor(RTMessage) {
- COLORREF TheColor;
-
- if (GetApplication()->ExecDialog(new TColorDialog(this,
- TheColor)) == IDOK)
- ...
- }
-
- <this> is specified as the parent window object to the constructor
- of the TColorDialog.
-
- You could share code that defines the behavior of a window
- (perhaps, a complex help window) or of a control (perhaps a custom
- or drawable control). The procedure outlined here applies to the
- definition of any ObjectWindows shared class.
-
- You can use an ObjectWindows shared class as a base class for a
- class that is defined in an ObjectWindows application, or in
- another ObjectWindows DLL.
-
-
- Calling an ObjectWindows DLL from outside
-
- Up to this point, we've assumed that the applications calling your
- DLL were written using ObjectWindows. You can also call an Object-
- Windows DLL from a non-ObjectWindows application.
-
- An ObjectWindows child window requires some support from a parent
- window object. When a child window is created in a DLL and the
- parent window is created by the calling application, the parent-
- child relationship must be simulated in the DLL. This is done by
- constructing a window object in the ObjectWindows DLL that's
- associated with the parent window whose handle is specified on a
- DLL call. This object will be deleted by ObjectWindows when the
- window in the calling application is destroyed.
-
- Note that incoming messages for the parent window are first
- dispatched to its associated ObjectWindows object. The associated
- object only responds to those messages that support its child
-
-
-
-
- Chapter 1 Page 10
-
-
-
-
-
-
-
-
-
-
- windows. All messages are passed on to the window in the calling
- application.
-
- In the following code, the exported function CreateDLLWindow is in
- an ObjectWindows DLL. It is assumed to be called by a non-Object-
- Windows .EXE (although it will work from an ObjectWindows .EXE as
- well).
-
- BOOL _far _export CreateDLLWindow(HWND ParentHWnd)
- {
- PTWindowsObject AParentAlias;
- PTWindow TheWindow;
-
- AParentAlias = DLLHelloLib->GetParentObject(ParentHWnd);
- TheWindow = new TWindow(AParentAlias, "Hello from a DLL!",
- DLLHelloLib);
- TheWindow->Attr.Style |= WS_POPUPWINDOW | WS_CAPTION |
- WS_THICKFRAME | WS_MINIMIZEBOX |
- WS_MAXIMIZEBOX;
- TheWindow->Attr.X = 100;
- TheWindow->Attr.Y = 100;
- TheWindow->Attr.W = 300;
- TheWindow->Attr.H = 300;
- return (DLLHelloLib->MakeWindow(TheWindow) == TheWindow);
- }
-
- CreateDLLWindow is passed the handle to a window that potentially
- might not correspond with an ObjectWindows window. The call to
- GetParentObject returns an ObjectWindows object which is either the
- actual ObjectWindows TWindowsObject (if there is one) or a
- surrogate object created on the fly. You can use this
- TWindowsObject instance just as though you had created it in the
- DLL.
-
- In the above example, a child window of the supplied ParentHWnd is
- created as a popup window.
-
- Note that the DLL's module pointer, DLLHelloLib, is supplied as the
- last parameter to TWindow's constructor to provide an owning
- TModule for the window.
-
-
- Using ObjectWindows as a DLL
-
- To enable your ObjectWindows applications to share a single copy of
- the ObjectWindows library, you can dynamically link them to the
- ObjectWindows DLL. To do this, you'll need to be sure of the
- following:
-
- o When compiling, be sure to define the macro _CLASSDLL on the
- compiler command line or in the IDE.
-
-
-
-
- Chapter 1 Page 11
-
-
-
-
-
-
-
-
-
-
-
- o You must compile all modules of your application in large model
- (-ml on the BCC or BCCX command line.)
- o Instead of specifying the static link ObjectWindows library when
- linking (that is, OWLWS.LIB, OWLWM.LIB, or OWLWL.LIB), specify
- the ObjectWindows DLL import library (OWL.LIB) and the Borland C++
- run time DLL import library (CRTLL.LIB). Also, since the
- ObjectWindows DLL contains the container class library, don't link
- with the TCLASSWx.LIB libraries. See MANUAL.DOC for more updated
- information on linking with DLLs.
-