Site icon Embarcadero RAD Studio, Delphi, & C++Builder Blogs

How to call a C++ member function in a DLL

Author: Embarcadero USA

 Technical Information Database

TI2191C.txt   How to call a C++ member function in a DLL
Category   :General
Platform    :All
Product    :C/C++  All

Description:
Calling Member Functions in a DLL
=================================
How to call C++ member functions from a DLL is a question that
comes up quite often. A couple of issues tend to catch first
timers off-guard: name mangling and exporting.
Name Mangling
=============
The first problem is C++ name mangling.  In C++, the compiler
modifies function names to include an encoding of the function's
argument types. So, for example, for the function
foo(int, float): in the object file, the name is changed to
@foo$qif.  For more on name mangling, refer to online Help or any
good book on C++ programming.  One important point: name mangling
is part of the C++ language, but how names are mangled is
compiler implementation dependent.
You can approach the name mangling issue in two ways.  The
method recommended most often is to turn off name mangling.  This
is mainly for portability reasons, especially if your DLL is
going to be called from an application built with other compilers
or languages.  To turn off name mangling, use: extern "C".
Example:
    extern "C" int FAR PASCAL foo(int, float);
One problem with this method is that you can no longer overload
your function. Names will have to be different just as in
C programs.
The second method is to use the mangled name in the application
calling your DLL.  You can run the IMPDEF utility on your DLL,
and examine it with an editor.  The .DEF file contains a listing
of all the functions in your DLL and the new mangled names.
_export
=======
The second thing commonly overlooked when calling member
functions from a DLL is the exporting of the class or
function(s).  First, here are the basic facts about
exporting.
When you export a class:
* The class is huge, as if you declared class __huge CFoo{}.
* Member functions are exported.
* Static data is exported.
* Compiler-generated member functions are exported.
* Virtual tables (vtables) are exported.
When you export member functions:
* Have far this pointers for non-static member functions.
* Overridden exported virtual functions are exported.
* Exported C or C++ inline functions have exported local static
  data.
You use _export to export classes, functions, and data.  The
linker enters functions flagged with _export into an export
table for the module.  By using _export, you are guaranteed the
proper prologue code to make sure that exportable code is
generated. Example: int FAR PASCAL _export foo(int, float)
Using _export eliminates the need for an EXPORTS section in your
module definition file.  Whenever you declare a class as _export,
the compiler treats it as huge (with 32-bit pointers), and
exports all of its non-inline member functions and static data
members. You get a far this pointer, and your function and
overridden exported virtual functions are exported.  Exported
inline functions have exported local static data.
Another thing most programmers recommend is to export the entire
class versus individual member functions. But either method will
work.
Example:
  class _export TFooClass {...};
Exporting a class implies all member functions are exported and
the class is huge.  The static data and vtables are exported.
Compiler-generated members like the default and copy
constructor get exported also.  As you can see, a lot is done
automatically when you export the class.
When Calling Program isn't in C++
=================================
Most other languages do not support C++ object construction and
destruction.  For example, Visual Basic, Smalltalk, and even
programs written in C cannot use a DLL that exports C++
classes.  C++ member functions have an extra, hidden parameter
called the 'this' pointer that passes a pointer to the object's
instance.  Other programming languages are not able to handle
the this pointer.
If, on the other hand, you export a C-style interface, a C++
program can statically link to a wrapper class that calls the
DLL.  Using a C++ wrapper allows the DLL to be called by other
programs and still give the C++ user a C++ interface to the DLL.
Below is an simple example illustrating how to wrap a class.
// In the DLL
 class Exported
 {
   public:
     Exported() {}
     void func1();
     int func2();
     char *func3(int, int);
 };
 extern "C" DWORD FAR _export ClassAccess(void *p, int func, ...)
 {
   switch(func)
   {
     case 0 :
       return new Exported();
     case 1 :
       delete (Exported *) p;
       return 0;
     case 2 :
       ((Exported *)p)->func1();
       return 0;
     case 3 :
       return (DWORD) ((Exported *)p)->func2();
     case 4 :
     {
       va_list ap;
       va_start(ap, func);
       int arg1 = va_arg(ap, int);
       int arg2 = va_arg(ap, int);
       va_end(ap);
       return (DWORD) ((Exported *)p)->func3(arg1, arg2);
     }
   }
   return 0;
 }
 // In the caller:
 DWORD FAR (*ClassAccess)(void *, int, ...);
 class Imported
 {
   private:
     void *obj;
   public:
     Imported() : obj((void *) ClassAccess(0, 0) {}
     ~Imported()
     {
       ClassAccess(obj, 1);
     }
     void func1()
     {
       ClassAccess(obj, 2);
     }
     int func2()
     {
       return (int) ClassAccess(obj, 3);
     }
     char *func4(int a, int b)
     {
       return (char *) ClassAccess(obj, 4, a, b);
     }
 };
The last thing you need to do is to load the library and set
ClassAccess to the address of the exported function.
CAVEATS
=======
One last comment: if you use the Smart Callbacks IDE option at
compile time, callback functions do not need to be listed in the
EXPORTS statement or flagged with the _export keyword.
Functions compile them as callback functions.
BIBLIOGRAPHY
============
N/A


Reference:


7/2/98 10:40:38 AM
 

Article originally contributed by

Exit mobile version