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