Mega Code Archive

 
Categories / Delphi / System
 

Calling a C++ DLL which exports a class

Title: Calling a C++ DLL which exports a class? Question: As I stated in an earlier article, it's possible to get an object-reference out from a DLL. This technique is known under the name DLL+. But how about the DLL is written in c++? What's about Name Mangling? Answer: First of all, you have to translate the header-file (should be delivered with the DLL), which is like an interface-section in ObjectPascal. Headers in c usually contain all sorts of definitions which are relevant outside the module. In our c++ example it looks like: /*FILE: income.h */ class CIncome { public: virtual double __stdcall GetIncome( double aNetto ) = 0 ; virtual void __stdcall SetRate( int aPercent, int aYear ) = 0 ; virtual void __stdcall FreeObject() = 0 ; } ; the export definition works like this extern "C" __declspec(dllexport) CIncome *CreateIncome(); CIncome *CreateIncome() { return new CIncomeImp ; } There are two ways to compile a function f1( ) in a DLL as exportable and then export it. Flag the function f1( ) with the _export keyword. Flag the function f1( ) with the __declspec(dllexport) keyword. Then you translate it to an Abstract Class in a unit of her own: //FILE: income.pas interface type IIncome = class public function GetIncome(const aNetto: double): double; virtual; stdcall; abstract; procedure SetRate(const aPercent:Integer; aYear:integer); virtual; stdcall; abstract; procedure FreeObject; virtual; stdcall; abstract; end; In the c++ dll, there is a procedure FreeObject this is necessary because of differences in memory management between C++ and ObjectPascal: void __stdcall FreeObject() { delete this ; } When you call the DLL written in C or C++, you have to use the stdcall or cdecl convention. Otherwise, you will end up in violation troubles and from time to time the application may crash. By the way the DLL, you are calling, should be on the search path;). So these conventions pass parameters from right to left. With this convention, the caller (that's Delphi)has to remove the parameters from the stack when the call returns. At least the DLL-call in Delphi is simple, except read name mangling below: incomeRef: IIncome; //member of the reference function CreateIncome: IIncome; stdcall; external('income_c.dll'); procedure TfrmIncome.FormCreate(Sender: TObject); begin incomeRef:=createIncome; end; procedure TfrmIncome.btncplusClick(Sender: TObject); var cIncome: Double; begin // this is the c++ dll+ call ;) incomeRef.SetRate(strToInt(edtZins.text), strToInt(edtJahre.text)); cIncome:= incomeRef.GetIncome(StrToFloat(edtBetrag.Text)); edtBetrag.text:= Format('%f',[cIncome]); end; Name Mangling Problems --------------------------------------------------------- The cause of diff. problems is the not standardised "name mangling" of different compilers, which decorates the signature of an method to guarantee overloading. So the vc++ compiler (linker) puts some information about types and parameters on the entry point which the caller doesnt know. in fact, it has been traditional, due to the changing nature of the defintion of C++, for major compiler releases to generate code incompatible with earlier releases from the same vendor. The vendor would therefore change the name mangling to prevent linking incompatible code.o. Decorated names were originally created to allow C++ to work with legacy linkers (that might not understand uppercase/lowercase, namespaces, class names, and overloading). In practice these "decorated names" are still around for reasons of compatibility with older versions of the same compiler. Note that there isn't any standard for how names get decorated, which is why it's nearly impossible to mix C++ object code from two different compilers. So, what can you do? 1. you can hack the map file of the linker http://groups.google.ch/groups?q=object+dll+c%2B%2B+mangling&hl=de&lr=&selm=1995Apr2.155609.28919%40emba.uvm.edu&rnum=9 So you could conceivably get around the problem by explicitly renaming Delphi export functions according to BC++'s name-mangling conventions, but this seems rather heavy handed and insecure. 2. You can work with an index instead of a name in a .def file(depends on your signature) C++: LIBRARY mxlump_dll EXPORTS FunctionName1 @1 FunctionName1 @2 ProcedureName1 @3 example: c++: EXPORTS WEP @1 @Triple$qi @2 ; Triple(int) Delphi: EXPORTS WEP @2 TRIPLE @1 3. Set an alias in the delphi external declaration: function CreateIncome2: CIncome; stdcall; external 'income.dll' name '_CreateIncome'; 4. an easy way to test the dll is on the dos shell: : rundll32 income.dll _SayHello2