home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / os / mswindo / programm / misc / 1294 < prev    next >
Encoding:
Text File  |  1992-08-12  |  9.2 KB  |  243 lines

  1. Newsgroups: comp.os.ms-windows.programmer.misc,comp.windows.ms.programmer
  2. Path: sparky!uunet!microsoft!wingnut!stevesi
  3. From: stevesi@microsoft.com (Steven Sinofsky)
  4. Subject: Re: How do you export a C++ CLASS?
  5. Message-ID: <1992Aug11.030445.24042@microsoft.com>
  6. Date: 11 Aug 92 03:04:45 GMT
  7. Organization: Microsoft Corporation
  8. References: <1992Aug8.095124.2322@swsrv1.cirr.com>
  9. Keywords: C++, exported classes, SDK
  10. Lines: 229
  11.  
  12.  
  13. This is a question that comes up rather often so I thought I
  14. would post this lengthly message regarding DLL support in MFC
  15. that I recently sent to a magazine writer.  There are a number
  16. of very interesting issues regarding DLLs and C++.  Have fun...
  17.  
  18. In article <1992Aug8.095124.2322@swsrv1.cirr.com> toma@swsrv1.cirr.com (Tom Armistead) writes:
  19. >Is there a way to export a C++ class (and all of it's member functions) so
  20. >that they can be called using C++ conventions (instead of normal C)?
  21. >
  22. >We would like to put som String and Container classes in a DLL???  I saw
  23. >a reference to this procedure in one of the technical notes (tn011?) that
  24. >came with the MFC stuff, but can find nothing related to in in the manuals?
  25. The only information for DLL support is in the technical note and there
  26. is also some information available via Compu$erve.
  27.  
  28. >
  29. > Note on DLL support in MFC:
  30.  
  31. > From my perspective the DLL issue is two big problems: (1)
  32. implementation issues with a C++ compiler, and (2) problems with the
  33. DLL mechanism and C++ (or why you don't want to be exporting class
  34. interfaces in DLLs).
  35.  
  36. For the first part, both Microsoft C/C++ and BCC have essentially the
  37. same (somewhat lame) implementation for exporting class interfaces. 
  38. Essentially you declare a class as _export and all of its members
  39. (public, private, protected, static) will be "exported," that is
  40. available for calling by client programs.  You must compile such
  41. interfaces large model, and you must be sure to export any non class
  42. functions that your clients might need (like binary operators in a
  43. string class).  BCC requires the -WD -ml switches, Microsoft C/C++
  44. requires the -GD -AL switches.  After building your .OBJ you must
  45. link it with LIBW and the large model DLL runtimes (in Microsoft
  46. C/C++ these are LDLLCEW.LIB) and create a .DLL.  Then compile any
  47. required resources and bind them to the DLL using RC.EXE.  If you
  48. wish to use an import library (as an aside, one isn't required if you
  49. use an explicit LoadLibrary and GetProcAddress calls, as is typically
  50. done in big apps) then you simply run MS implib over the DLL
  51. producing an import library for client code.
  52.  
  53. As an example, consider the following class that I'll do as an
  54. addition to the MFC sample application HELLOAPP.  First let's declare
  55. a class:
  56.  
  57. ==== File EXP.H ====
  58. class _export FOO  {
  59. public:
  60.     FOO();                // constructor
  61.     virtual void Member();        // virtual member function
  62.     static void SMember();    // static member
  63. };
  64.  
  65. void FAR PASCAL _export Global();    // requires export and far! 
  66. PASCAL is a convention
  67.  
  68.  
  69. === File EXP.CPP ====
  70. #include "windows.h"
  71. #include "exp.h"
  72.  
  73. void FOO::SMember()
  74. {
  75.     ::OutputDebugString("FOO::SMember called\r\n");
  76. }
  77.  
  78. void PASCAL FAR _export Global()
  79. {
  80.     ::OutputDebugString("Global function called\r\n");
  81. }
  82.  
  83. FOO::FOO()
  84. {
  85.     ::OutputDebugString("FOO::FOO called\r\n");
  86. }
  87.  
  88. void FOO::Member()
  89. {
  90.     ::OutputDebugString("FOO::Mem called\r\n");
  91. }
  92.  
  93. static HINSTANCE hInstLib = NULL; // probably use after we write more code
  94. int FAR PASCAL LibMain(HINSTANCE hInst, WORD wDS, WORD cbHeap, LPSTR
  95. lpCmdLine)
  96. {
  97.     hInstLib = hInst;
  98.     ::OutputDebugString("In EXP's LibMain\r\n");
  99.     return TRUE;
  100. }
  101.  
  102.  
  103. extern "C" int FAR PASCAL _WEP(int)
  104. {
  105.     ::OutputDebugString("My WEP has been called\r\n");
  106.     return 1;
  107. }
  108.  
  109. === File EXP.DEF ===
  110. LIBRARY      EXP
  111. DESCRIPTION  'Play around with DLLs and MFC'
  112.  
  113. EXETYPE      WINDOWS
  114. STUB         'WINSTUB.EXE'
  115.  
  116. CODE         PRELOAD MOVEABLE DISCARDABLE
  117. DATA         PRELOAD MOVEABLE SINGLE
  118.  
  119. EXPORTS
  120.     WEP     @1    RESIDENTNAME    
  121.  
  122.  
  123. === Compile EXP.BAT ===
  124. cl -c -GD -AL exp.cpp
  125. link /packcode /align:4 exp,exp.dll,exp.map/map,libw ldllcew,exp.def;
  126. rc /t /30 exp.dll
  127. implib exp.lib exp.dll
  128.  
  129. === Use the DLL ===
  130. Now just to see the code get used, let's add the following lines to
  131. the InitInstance of MFC's helloapp example:
  132.  
  133. BOOL CHelloApp::InitInstance()
  134. {
  135.     m_pMainWnd = new CHelloWindow();
  136.     m_pMainWnd->ShowWindow(m_nCmdShow);
  137.     m_pMainWnd->UpdateWindow();
  138.  
  139.     FOO* pFoo = new FOO;
  140.     pFoo->Member();
  141.     FOO::SMember();
  142.     ::Global();
  143.  
  144.     return TRUE;
  145. }
  146.  
  147. Modify the makefile to include EXP.LIB that you created above and add
  148. #include "exp.h" to helloapp.cpp.
  149.  
  150.  
  151. Things will then work (to see the ::OutputDebugString be sure to have
  152. DBWIN.EXE running or run under CodeView.)
  153.  
  154. So pretty easy you say....that brings us to the second issue,
  155. problems of C++ and DLLs.
  156.  
  157. The big reason we chose not to support a single DLL for MFC
  158. applications is that C++ interfaces and DLLs don't work well
  159. together.  The DLL mechanism was designed for C and works best for
  160. that.  There are a number of big problems:
  161.  
  162. 1. versioning and schema evolution: changing a C++ interface (public,
  163. protected, or private) will require updating the DLL, creating a new
  164. import lib, and relinking (and probably recompiling all client code. 
  165. Because of typesafe linkage, you can't even change a char* to a const
  166. char* without having to deal with this mess.  Thus you lost a *key*
  167. benefit of DLLs--the ability to replace parts of your application
  168. without replacing the whole thing.  If we were to provide MFC.DLL in
  169. version 1, and change even 1 API in version 1, even if that change
  170. were totally benign (like adding a const), you could not use the new
  171. DLL with an existing application.  We would then be forced to ship
  172. MFC2.DLL or make all of our customers recompile their applications. 
  173. Every user would have MFC.DLL and MFC2.DLL, which is a pretty big
  174. size hit and pretty soon that starts to add up and you lose the
  175. benefits of shared code (check out BCC 3.1 and OWL31.DLL).
  176.  
  177. 2. lack of encapsulation: even though you only want users to use the
  178. public interfaces of your class, the private interfaces will still be
  179. exported to the DLL.  This is a generic problem with packaging C++
  180. code, since the headers still contain the private and protected
  181. stuff, but now you are adding entry points to your DLL.
  182.  
  183. 3. inlines: where do they go?  If you want your DLL to be standalone,
  184. then you need to outline your inline functions and export each and
  185. every one in your DLL.  If you want the benefit of inlines, then you
  186. must ship interface files that include the inlines along with the DLL
  187. and the DLL is no longer a stand alone entity.  Just a mess...
  188.  
  189. 4. static objects: do you have one copy of static objects?  are they
  190. constructed only when the DLL is loaded?  what about static objects
  191. that you wish to have one per client of the running DLL?  Another
  192. whole can of worms that is made especially difficult because of the
  193. limits of DLL functionality in Windows (one datasegment, no per DLL
  194. instance data).  NT has addressed a number of these issues in
  195. enhancing the DLL mechanism.
  196.  
  197. 5. efficiency: MY FAVORITE!  If you built the DLL above you found
  198. that it was around 5K!  That's huge considering all it has is 4 null
  199. functions.  The reason for this size is that, first, the code is
  200. large model, which is always a big size hit (30-40% for an average
  201. application).  Second, each entry point in a DLL has a fixed overhead
  202. of 8 bytes + the name of the function (remember decorated names are
  203. really long).  Of course, in C you can refer to DLL entry points by
  204. ordinals, but C++ makes this nearly impossible because you want to
  205. automate the "def" file exports section and there is no way to get
  206. the ordinal into the syntax of the member function.  This name table
  207. is limited to 64K (about 2000 entry points, and MFC 1.0 has 1800!).
  208.     Then, recall that our DLL is large model.  That seems really
  209. bad on the face of it, but it is even worse.  These functions all
  210. have the DLL exit/entry _loadds sequence, which adds another 8 bytes
  211. or so to each function.  The problem is that you have no way of
  212. exporting only parts of your class, since DLLs can call across DLL
  213. boundaries and member functions can call other member functions it
  214. must be an all or nothing thing.  In C, you have the option of
  215. exporting a very narrow interface and implementing the rest of your
  216. code however you want.  In C, it is very common to implement a DLL
  217. interface in large model/exported function and then implement the
  218. real guts of the DLL in medium model standard Windows code.  This is
  219. a major savings in speed and size. 
  220.     Also, if you export a class then you must, by definition,
  221. export all of its base classes (yuck!).
  222.  
  223. So the problem we faced for MFC was that the previous five items were
  224. pretty significant.  These 5 items are the sum of the price paid for
  225. using a C++ interface DLL, and you can't choose to pay for only a few
  226. of them--you must pay all.  We found few people who would be willing
  227. to give up all five in order to have a DLL.  That is everyone finds
  228. at least one of these important enough to avoid exporting C++
  229. interfaces in a DLL.  That is why we urge customers to implement a
  230. DLL in MFC, but stick to exporting a straight C interfaces (see
  231. MFC\DOC\TN011.TXT).   My guess is that an MFC.DLL would be several
  232. hundred thousand bytes, which is pretty huge considering that the
  233. entire library is only 65K of medium model code.
  234.  
  235.  
  236.  
  237. Make sense,
  238. Steven
  239. -- 
  240. Steven Sinofsky
  241. stevesi@microsoft.com
  242. Disclaimer: I don't speak for Microsoft, BillG does that.
  243.