home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0040 - 0049 / ibm0040-0049 / ibm0040.tar / ibm0040 / BCPPOWL1.ZIP / OWLDOC.ZIP / DLL.DOC < prev    next >
Encoding:
Text File  |  1991-08-28  |  20.9 KB  |  740 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13. CHAPTER
  14. ___________________________________________________________________
  15.  
  16.                                                                   1
  17.  
  18.  
  19.                                              Dynamic link libraries
  20.  
  21.  
  22. A dynamic-link library (DLL) is a library of functions whose
  23. references are resolved at run time rather than at compile time.
  24. DLL functions are dynamically linked to a calling application.
  25.  
  26. Each application using statically linked code has its own copy of
  27. the code. The code for functions defined in a DLL is shared by all
  28. calling applications; only one copy is placed in memory. Defining
  29. code shared by a group of applications in a DLL therefore reduces
  30. the size of the .EXEs of the calling applications.
  31.  
  32. You might want to define complex windowing behavior, shared by a
  33. group of your applications, in an ObjectWindows DLL. In this
  34. chapter, you'll learn how to write and use an ObjectWindows DLL.
  35. You'll also learn how to use the ObjectWindows dynamic-link library
  36. (OWL.DLL).
  37.  
  38.  
  39. C++ DLLs
  40.  
  41. DLLs can contain ordinary functions or a combination of functions
  42. and exported C++ classes. First, we'll examine DLLs in the context
  43. of a library of ordinary functions. This section contains an
  44. overview of Borland C++ DLLs; you'll learn how to write, export,
  45. and call a DLL function using Borland C++. For more detailed
  46. information, see "Dynamic link libraries" in Chapter 3, "Building a
  47. Windows application," of the Borland C++ User's Guide.
  48.  
  49.  
  50. Writing DLL functions
  51.  
  52. Windows requires that two functions be defined in every DLL:
  53. LibMain and WEP (Windows Exit Procedure). (Borland C++ provides a 
  54. default WEP automatically.)
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61. Chapter 1 Page 1
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72. LibMain and WEP
  73.  
  74. You must supply the LibMain function, which is the main entry point
  75. for a Windows DLL. Windows calls LibMain once, when the library is
  76. first loaded. LibMain initializes the DLL; this initialization
  77. depends almost entirely on the particular DLL's function, but might
  78. include the following tasks:
  79.  
  80. o unlocking the data segment with UnlockData, if it has been
  81.   declared as MOVEABLE
  82.  
  83. o setting up global variables for the DLL, if it uses any
  84.  
  85. Note
  86. The DLL startup code C0Dx.OBJ initializes the local heap
  87. automatically.
  88.  
  89. The following parameters are passed to LibMain:
  90.  
  91.   int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg,
  92.                          WORD cbHeapSize, LPSTR lpCmdLine);
  93.  
  94. HANDLE, WORD, and LPSTR are defined in WINDOWS.H.
  95. HANDLE hInstance is the instance handle of the DLL.
  96.  
  97. WORD wDataSeg is the value of the data segment (DS) register.
  98.  
  99. WORD cbHeapSize is the size of the local heap specified in the
  100. module definition file for the DLL.
  101.  
  102. LPSTR lpCmdLine is a far pointer to the command line specified when
  103. the DLL was loaded. This is almost always null, because typically
  104. DLLs are loaded automatically without parameters. It is possible,
  105. however, to supply a command line to a DLL when it is loaded
  106. explicitly.
  107.  
  108. The return value for LibMain is either 1 (successful
  109. initialization) or 0 (unsuccessful initialization). Windows will
  110. unload the DLL from memory if the value is 0.
  111.  
  112. WEP is the exit point of a DLL; Windows calls it prior to unloading
  113. the DLL. This function is not necessary in a DLL (because the
  114. Borland C++ run-time libraries provide a default one), but can be
  115. supplied by the writer of a DLL to perform any cleanup of the DLL
  116. before it is unloaded from memory.
  117.  
  118. Under Borland C++, WEP does not need to be exported. This is the
  119. prototype for WEP:
  120.  
  121.   int FAR PASCAL WEP (int nParameter);
  122.  
  123.  
  124.  
  125.  
  126.  
  127. Chapter 1 Page 2
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138. int nParameter is either WEP_SYSTEMEXIT (all of Windows shuts down)
  139. or WEP_FREE_DLL (just this DLL is unloaded).
  140.  
  141. WEP returns 1 to indicate success. Note that Windows currently
  142. doesn't use this return value.
  143.  
  144.  
  145. DLL-specific functions
  146.  
  147. The additional functions defined in your DLL depend on the services
  148. your DLL provides. When writing functions that will be called from
  149. an application, you need to keep these things in mind:
  150.  
  151. o Make calls to DLL functions far calls, and make pointers
  152.   specified as parameters and return values far pointers. This is
  153.   because a DLL has different code and data segments than the
  154.   calling application.
  155.  
  156. o Static data defined in a DLL is global to all calling
  157.   applications. Global data set by one caller can be accessed by
  158.   another. If you need data to be private for a given caller, you
  159.   need to dynamically allocate and manage the data yourself.
  160.  
  161.  
  162. Exporting DLL functions
  163.  
  164. After writing your DLL functions, you must export the functions
  165. that you want available to a calling application. There are two
  166. steps involved: compiling your DLL functions as exportable
  167. functions and exporting them. You can do this in the following
  168. ways:
  169.  
  170. o If you flag a function with the _export keyword, it's compiled as
  171.   exportable and is then exported.
  172.  
  173. o If you don't flag a function with _export and you use the -WD
  174.   option when compiling, the function is compiled as exportable.
  175.   However, the function only gets exported if you also list it in
  176.   the EXPORTS section of the module definition (.DEF) file.
  177.  
  178. o If you add the _export keyword to a class declaration, the entire
  179.   class (data and function members) will be compiled as exportable
  180.   and exported.
  181.  
  182.  
  183. Calling DLL functions
  184.  
  185. You call a DLL function within an application just as you would
  186. call a function defined in the application itself. However, you
  187. must import the DLL functions that your application calls.
  188.  
  189. There are two ways to import a DLL function:
  190.  
  191.  
  192.  
  193. Chapter 1 Page 3
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204. 1. You can add an IMPORTS section to the calling application's
  205.    module definition (.DEF) file and list the DLL function as an
  206.    import.
  207.  
  208. 2. You can link an import library that contains import information
  209.    for the DLL function to the calling application. (Use IMPLIB to
  210.    make the import library.)
  211.  
  212. When your application is executed, the .DLL files for the DLLs that
  213. it calls must be in the directory of the calling application or on 
  214. the path; otherwise, your application won't load.
  215.  
  216.  
  217. ObjectWindows DLLs
  218.  
  219. ObjectWindows DLLs differ from ordinary DLLs in that, with Object-
  220. Windows DLLs, you can export classes derived from ObjectWindows classes
  221. as well as your own classes. This makes those classes and their
  222. member functions available to all .EXEs that dynamically link with
  223. your DLL.
  224.  
  225. If you find that complex window behavior is shared by a group of
  226. your applications, you can define this behavior in an ObjectWindows
  227. DLL. For example, you might build a DLL that defines
  228.  
  229. o a complex window used by related applications
  230.  
  231. o useful dialogs
  232.  
  233. o reusable custom controls
  234.  
  235. o common drawable controls
  236.  
  237.  
  238. The module object
  239.  
  240. An instance of TModule acts as an object-oriented representative for
  241. an ObjectWindows DLL. TModule member functions provide support for
  242. window and memory management and process errors.
  243.  
  244. Construct a module object in the LibMain function of your DLL:
  245.  
  246.   PTModule TheModule;
  247.  
  248.   int FAR PASCAL LibMain(HANDLE hInstance, WORD,
  249.                          WORD cbHeapSize, LPSTR lpCmdLine)
  250.   {
  251.     int TheStatus;
  252.  
  253.     TheModule = new TModule("LibName", hInstance, lpCmdLine);
  254.     TheStatus = TheModule->Status;
  255.     if (TheStatus != 0)
  256.  
  257.  
  258.  
  259. Chapter 1 Page 4
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.     {
  271.       delete TheModule;
  272.       TheModule = NULL;
  273.     }
  274.  
  275.     return (TheStatus == 0);
  276.   }
  277.  
  278. In this example LibMain, an instance of TModule, is constructed.
  279. The hInstance and lpCmdLine parameters received from Windows are
  280. passed to the TModule constructor to initialize data members of the
  281. same name.
  282.  
  283. After the module object is constructed, its Status data member is
  284. used to determine the success or failure of module initialization.
  285. If the Status member contains a nonzero value, the initialization
  286. of the module object is unsuccessful. At this point you should 
  287. delete the module object in LibMain, set its pointer to NULL and 
  288. return zero to Windows to indicate that an error occurred. Your 
  289. module's constructor should set Status to an error that you define 
  290. (a nonzero value) if your DLL cannot be successfully initialized.
  291.  
  292. In WEP, the module object is deleted:
  293.  
  294.   int FAR PASCAL WEP(int) 
  295.   {
  296.     if ( TheModule )
  297.       delete TheModule;
  298.     return 1;
  299.   }
  300.  
  301. As noted, Windows does not use WEP's return value.
  302.  
  303. Usually, your DLL will require additional initialization and
  304. cleanup. You could perform this processing in your LibMain and WEP
  305. functions; better yet, derive a class from TModule. Perform the
  306. required initialization and cleanup in its constructor and
  307. destructor, and define data members for data global to your DLL.
  308.  
  309.  
  310. Shared classes
  311.  
  312. You can share code that defines the behavior of a general-use
  313. dialog box by defining a dialog class in a DLL (a shared class).
  314. For example, a group of graphics applications might use a color
  315. dialog defined in a DLL to retrieve an RGB value from the user. To
  316. do so, you'll need to export the class from the DLL and import the
  317. class into your applications.
  318.  
  319.  
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326. Chapter 1 Page 5
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337. Declaring and defining a shared class
  338.  
  339. You'll need to do the following in order to successfully define and
  340. declare a shared class.
  341.  
  342. o pass an additional PTModule parameter to window constructors (when your
  343.   ObjectWindows DLL can be used by non-ObjectWindows applications).
  344.  
  345. o conditionally declare your class as either _export or huge.
  346.  
  347. o use far pointers and far references in your declarations.
  348.  
  349. If you declare a shared class in an include file that is included
  350. by both the DLL and an application using the DLL, the class must be
  351. declared _export when compiling the DLL and huge when compiling the
  352. application. You can do this with the _EXPORT macro, which is set
  353. to _export or huge as needed when building a DLL or using an .EXE
  354. with a DLL.
  355.  
  356.   class _EXPORT TColorControl : public TControl 
  357.   {
  358.   .
  359.   .
  360.   .
  361.   };
  362.  
  363.  
  364. Macro expansion
  365.  
  366. The _EXPORT macro expands into _export if __DLL__ is defined.
  367. Borland C++ defines __DLL__ automatically when you supply the -WD
  368. or -WDE options to the command-line compiler, or when you specify
  369. that you are building a DLL in the IDE. If you are not building a
  370. DLL, then _EXPORT expands to _CLASSTYPE. _CLASSTYPE is a macro that
  371. expands into "huge", "far", or "near." See the Reference Guide for
  372. details.
  373.  
  374. In order for _CLASSTYPE to expand into "huge" (which is required
  375. for classes shared between an application and DLLs), a third macro
  376. is used: _CLASSDLL. You must define _CLASSDLL on either the
  377. compiler command line or by including it in the "Defines" edit
  378. field in the compiler options dialog box of the Borland C++ IDE.
  379. When defined, _CLASSDLL communicates to both the compiler and to
  380. the _CLASSTYPE macro your intention to use the classes being
  381. compiled as shared classes.
  382.  
  383. In summary, declare your shared classes with _EXPORT as in the
  384. example above. Compile your DLLs with the appropriate compiler
  385. directives and your classes will be exported. Compile your .EXE
  386. with the _CLASSDLL macro defined, and your classes will be compiled
  387. as huge and will therefore be suitable for sharing with DLLs.
  388.  
  389.  
  390.  
  391. Chapter 1 Page 6
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.                  Pointers and typedefs
  403.  
  404. You must also declare the pointers and references in the header
  405. file of your shared class as far pointers. You can explicitly set
  406. the pointers to far, or you can use the _FAR macro to do this. _FAR
  407. is set to far only when _CLASSDLL is defined.
  408.  
  409. ObjectWindows defines data type specifiers for each ObjectWindows
  410. class using the _FAR macro. The PTDialog typedef is shown here:
  411.  
  412.   typedef TDialog _FAR *PTDialog;
  413.  
  414. ObjectWindows defines similar type specifiers for each Object-
  415. Windows class. Using these ObjectWindows typedefs will help improve
  416. the readability of your code.
  417.  
  418. When referring to a pointer to an ObjectWindows class, use a type
  419. whose name is P followed by the name of a class. Similarly, for a
  420. reference to a class, use an R followed by the name of the class.
  421. For example, a pointer to a TWindow is a PTWindow. A reference to a
  422. TDialog is an RTDialog.
  423.  
  424. All ObjectWindows classes automatically define those and other
  425. typedefs that make use of the _FAR macro. To automatically get
  426. those typedefs defined for your own classes, use the _CLASSDEF
  427. macro at the top of your header file for each class you define. For
  428. example, this code
  429.  
  430.   _CLASSDEF(TMyWindow)
  431.   class _EXPORT TMyWindow:public TWindow
  432.   {
  433.  
  434.   };
  435.  
  436. will cause PTMyWindow, RTMyWindow, and other typedefs to be
  437. defined. See the documentation on _CLASSDEF in the ObjectWindows
  438. for C++ Reference Guide for details of the typedefs automatically
  439. defined.
  440.  
  441. Due to the way the Borland C++ compiler treats classes and
  442. structures, if your shared classes contain, as data members,
  443. pointers to structures or other classes, you may have to declare
  444. any referenced structures and classes with the _CLASSTYPE macro.
  445. For example,
  446.  
  447.   struct _CLASSTYPE TMyStruct
  448.   {
  449.   };
  450.   typedef TMyStruct _FAR * PTMyStruct;
  451.  
  452.   class _EXPORT TMyClass
  453.   {
  454.  
  455.  
  456.  
  457. Chapter 1 Page 7
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.      PTMyStruct ptms;
  469.   };
  470.  
  471. In the above example, TMyStruct may need to be declared with
  472. _CLASSTYPE (as shown), since pointers to it are contained in the
  473. huge class TMyClass. (_EXPORT implies "huge" when you are compiling
  474. a DLL or compiling an .EXE with the _CLASSDLL macro defined).
  475.  
  476. The declaration of a TColorDialog shared class and the
  477. TColorControl class that it uses are shown next.
  478.  
  479.   This class definition is from COLORDLG.H.
  480.  
  481.   _CLASSDEF(TColorControl)
  482.  
  483.   class _EXPORT TColorControl : public TControl
  484.   {
  485.   public:
  486.     COLORREF Color;
  487.  
  488.     TColorControl(PTWindowsObject AParent, int ResourceId, COLORREF
  489.                   AColor, PTModule AModule = NULL);
  490.  
  491.   protected:
  492.     virtual LPSTR GetClassName();
  493.     virtual void GetWindowClass(WNDCLASS _FAR & AWndClass);
  494.     virtual void Paint(HDC, PAINTSTRUCT _FAR & PS);
  495.     virtual WORD Transfer(Pvoid DataPtr, WORD TransferFlag);
  496.     virtual void WMLButtonDown(RTMessage Msg) = [WM_FIRST +
  497.                  WM_LBUTTONDOWN];
  498.     virtual void WMLButtonDblClk(RTMessage Msg) = [WM_FIRST +
  499.                  WM_LBUTTONDBLCLK];
  500.  
  501.   public:
  502.     virtual void SetColor(COLORREF AColor);
  503.   };
  504.  
  505.   _CLASSDEF(TColorDialog)
  506.  
  507.   class _EXPORT TColorDialog : public TDialog
  508.   {
  509.   protected:
  510.     PTScrollBar ColorBar1;
  511.     PTScrollBar ColorBar2;
  512.     PTScrollBar ColorBar3;
  513.     PTColorControl SelColor;
  514.  
  515.   public:
  516.     TColorDialog(PTWindowsObject AParent, COLORREF _FAR & TheColor);
  517.  
  518.   protected:
  519.  
  520.  
  521.  
  522. Chapter 1 Page 8
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.     virtual void DefChildProc(RTMessage Msg);
  534.     virtual void SetupWindow();
  535.     virtual void TransferData(WORD TransferFlag);
  536.     virtual void UpdateBars(COLORREF AColor);
  537.   };
  538.  
  539. An RTMessage typedef is used in these declarations to improve code
  540. readability and to ensure the reference is a far reference when the
  541. class is used as a shared class. It is defined by ObjectWindows as
  542. follows:
  543.  
  544.   typedef TMessage _FAR & RTMessage;
  545.  
  546. Various other macros that use the _FAR macro are defined in _DEFS.H in
  547. the BORLANDC\INCLUDE directory.
  548.  
  549. After you've properly declared your shared class, you can define
  550. its implementation in your DLL as you would in an ObjectWindows
  551. application.
  552.  
  553.  
  554. Building a shared class in a DLL
  555.  
  556. After declaring and defining your shared class, you need to
  557.  
  558. o write LibMain and WEP functions
  559.  
  560. o compile your DLL
  561.  
  562. You can copy the LibMain and WEP functions previously shown to your
  563. DLL's .CPP source file, or you can use your customized versions.
  564.  
  565. To compile your DLL, simply follow the normal procedures for
  566. generating DLLs. For the command-line compiler, specify either the
  567. -WDE or -WD options. For the IDE, ensure you are building a DLL by
  568. checking both the Options|Compiler|Entry/Exit Code and Options|
  569. Linker dialog boxes for the correct DLL options. See your Borland
  570. C++ documentation for general information about building DLLs.
  571.  
  572. After you have compiled and linked your DLL, use IMPLIB to generate
  573. an import library for your DLL. This import library will list all
  574. exported member functions from your shared classes as well as any
  575. ordinary functions which you've exported.
  576.  
  577. For more information on any of these procedures, see previous
  578. sections of this chapter, and "Dynamic link libraries" in Chapter
  579. 3, "Building a Windows application," of the Borland C++ User's
  580. Guide.
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588. Chapter 1 Page 9
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599. Using a shared class
  600.  
  601. To use a shared class from a DLL in your application, you need to
  602. compile your application defining the _CLASSDLL macro (as discussed
  603. earlier). Then link your application with the import library you
  604. created for your DLL. Then you can use the class defined in the DLL
  605. as if the class had been defined by your application.
  606.  
  607. In the following example code, TColorDialog is executed when a
  608. Color menu item is selected from TTestWindow's menu:
  609.  
  610.   void TTestWindow::CMColor(RTMessage) {
  611.     COLORREF TheColor;
  612.  
  613.     if (GetApplication()->ExecDialog(new TColorDialog(this,
  614.                                      TheColor)) == IDOK)
  615.     ...
  616.   }
  617.  
  618. <this> is specified as the parent window object to the constructor
  619. of the TColorDialog.
  620.  
  621. You could share code that defines the behavior of a window
  622. (perhaps, a complex help window) or of a control (perhaps a custom
  623. or drawable control). The procedure outlined here applies to the
  624. definition of any ObjectWindows shared class.
  625.  
  626. You can use an ObjectWindows shared class as a base class for a
  627. class that is defined in an ObjectWindows application, or in
  628. another ObjectWindows DLL.
  629.  
  630.  
  631. Calling an ObjectWindows DLL from outside
  632.  
  633. Up to this point, we've assumed that the applications calling your
  634. DLL were written using ObjectWindows. You can also call an Object-
  635. Windows DLL from a non-ObjectWindows application.
  636.  
  637. An ObjectWindows child window requires some support from a parent
  638. window object. When a child window is created in a DLL and the
  639. parent window is created by the calling application, the parent-
  640. child relationship must be simulated in the DLL. This is done by
  641. constructing a window object in the ObjectWindows DLL that's
  642. associated with the parent window whose handle is specified on a
  643. DLL call. This object will be deleted by ObjectWindows when the
  644. window in the calling application is destroyed.
  645.  
  646. Note that incoming messages for the parent window are first
  647. dispatched to its associated ObjectWindows object. The associated
  648. object only responds to those messages that support its child
  649.  
  650.  
  651.  
  652.  
  653. Chapter 1 Page 10
  654.  
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.  
  662.  
  663.  
  664. windows. All messages are passed on to the window in the calling
  665. application.
  666.  
  667. In the following code, the exported function CreateDLLWindow is in
  668. an ObjectWindows DLL. It is assumed to be called by a non-Object-
  669. Windows .EXE (although it will work from an ObjectWindows .EXE as
  670. well).
  671.  
  672.   BOOL _far _export CreateDLLWindow(HWND ParentHWnd)
  673.   {
  674.     PTWindowsObject AParentAlias;
  675.     PTWindow TheWindow;
  676.  
  677.     AParentAlias = DLLHelloLib->GetParentObject(ParentHWnd);
  678.     TheWindow = new TWindow(AParentAlias, "Hello from a DLL!",
  679.                             DLLHelloLib);
  680.     TheWindow->Attr.Style |= WS_POPUPWINDOW | WS_CAPTION |
  681.                              WS_THICKFRAME | WS_MINIMIZEBOX |
  682.                              WS_MAXIMIZEBOX;
  683.     TheWindow->Attr.X = 100;
  684.     TheWindow->Attr.Y = 100;
  685.     TheWindow->Attr.W = 300;
  686.     TheWindow->Attr.H = 300;
  687.     return (DLLHelloLib->MakeWindow(TheWindow) == TheWindow);
  688.   }
  689.  
  690. CreateDLLWindow is passed the handle to a window that potentially
  691. might not correspond with an ObjectWindows window. The call to
  692. GetParentObject returns an ObjectWindows object which is either the
  693. actual ObjectWindows TWindowsObject (if there is one) or a
  694. surrogate object created on the fly. You can use this
  695. TWindowsObject instance just as though you had created it in the
  696. DLL.
  697.  
  698. In the above example, a child window of the supplied ParentHWnd is
  699. created as a popup window.
  700.  
  701. Note that the DLL's module pointer, DLLHelloLib, is supplied as the
  702. last parameter to TWindow's constructor to provide an owning
  703. TModule for the window.
  704.  
  705.  
  706. Using ObjectWindows as a DLL
  707.  
  708. To enable your ObjectWindows applications to share a single copy of
  709. the ObjectWindows library, you can dynamically link them to the
  710. ObjectWindows DLL. To do this, you'll need to be sure of the
  711. following:
  712.  
  713. o When compiling, be sure to define the macro _CLASSDLL on the
  714.   compiler command line or in the IDE.
  715.  
  716.  
  717.  
  718.  
  719. Chapter 1 Page 11
  720.  
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731. o You must compile all modules of your application in large model 
  732.   (-ml on the BCC or BCCX command line.)
  733. o Instead of specifying the static link ObjectWindows library when
  734.   linking (that is, OWLWS.LIB, OWLWM.LIB, or OWLWL.LIB), specify
  735.   the ObjectWindows DLL import library (OWL.LIB) and the Borland C++
  736.   run time DLL import library (CRTLL.LIB). Also, since the 
  737.   ObjectWindows DLL contains the container class library, don't link
  738.   with the TCLASSWx.LIB libraries. See MANUAL.DOC for more updated
  739.   information on linking with DLLs.
  740.