Mega Code Archive

 
Categories / Delphi / System
 

DLL Cross Independencies

Title: DLL Cross Independencies Question: Build a DLL more "other language friendly" and less IDE or platform dependent. Answer: First we have to decide between a DLL or a package: When we update a DLL (change function's implementation), we simply compile it, export some new routines and ship the new version. All the applications using this DLL will still work (unless, of course, you've removed existing exported routines). On the other hand, when updating a package, you cannot ship a new version of your package without also updating the executable. This is why we cannot use a unit compiled in D4 in a D5 project unless we have the unit's source; the compiler checks version information of DCU's and decides whether an unit has to be recompiled so any package that you provide for your application must be compiled using the same Delphi version used to compile the application. Note: You cannot provide a package written in D6 to be used by an application written in D5. The advantage of a DLL is: For most applications, packages provide greater flexibility and are easier to create than DLLs. However, there are several situations where DLLs would be better suited to your projects than packages: - Your code module will be called from non-Delphi applications. - You are extending the functionality of a Web server. - You are creating a code module to be used by 3.party developers. - Your project is an OLE container. DLL independencies whats all about? A Exception Handling B Include Files C Call Convention D Data alignment and Types E Name Mangling ------------------ A: For exception handling to work across multiple binary modules, all binary modules need to use the same copy of the shared exception handling code. To accomplish this, the following must be done: 1) All binary modules which use the Delphi RTL (CLX) must be built with the same version of the RTL runtime package. 2) All binary modules must dynamically link to the exception handling shared object libborunwind.so. This is accomplished by enabling the "Use dynamic RTL/STL" linker option. If this is not done, exceptions raised in one module may cause unintended side effects in other modules. ------------------ B: Include File, The $I parameter directive instructs the compiler to include the named file in the compilation. In effect, the file is inserted in the compiled text right after the {$I filename} directive. The default extension for filename is .pas. If filename does not specify a directory path, then, in addition to searching for the file in the same directory as the current module, unit recompile if file newer. To specify a filename that includes a space, surround the file name with single quotation marks: {$I 'My file'}. ------------------ C: Call Convention, 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. But for the rest of the world you can use pascal! By the way the DLL, you are calling, should be on the search path;). So these conventions (stdcall, cdecl) pass parameters from right to left. With cdecl, the caller (that's Delphi) has to remove the parameters from the stack when call returns, others clean-up by the routine. Tip in c++: DLL_IMPORT_EXPORT means after all stdcall. ------------------- D: Data Types and Alignment, To understand which operations can be performed on which expressions, we need to distinguish several kinds of compatibility among types and values. These include: - Type identity - Type compatibility - Assignment compatibility The common type system defines how types are declared, used, and managed in the runtime, and is also an important part of the runtime's support for cross-language integration. Records or pointer to records should have the right alignment: Structure members are stored sequentially in the order in which they are declared: the first member has the lowest memory address and the last member the highest. Applications should generally align structure members at addresses that are "natural" for the data type and the processor involved. For example, a 4-byte data member should have an address that is a multiple of four. #pragma pack Using packed in Delphi slows data access and, in the case of a character array, affects type compatibility ! In status {$A1} or {$A-} fields dont get an alignment. All records and structures of classes will be packed. type TTeststruct = record iVar: Integer; { 4 Byte } dVar: double; { 8 Byte } bVar: boolean; { 1 Byte } sVar: Array[1..50] of char; { n * 1 Byte } end; To avoid BORLNDMM.DLL in linker pass strings as PChar or ShortString: function SelectDir(Dest: PChar):Boolean; if dirSelect.ShowModal = idok then begin StrPCopy(Dest, dirSelect.DirectoryListBox1.Directory); caller: Var pathback: array[0..MAX_PATH] of Char; SelectDir(pathback) Deliver types others can understand: Dont use unknown objects or structs like string or stringlist between different compilers or class libraries in a DLL: Offsets and alignments are different! Ex.: C++ TStringList Delphi (mylist.add(hi c++dll) C++ TStringList It wont work to fill the stringlist from c++ DLL TStringList *MyStringList = new TStringList; ------------------------ E: Prevent Name Mangling, You can work with an index instead of a name in a .DEF file and export section (depends on your signature) C++: LIBRARY mxlump_dll EXPORTS FunctionName1 @1 FunctionName1 @2 ProcedureName1 @3 Solution: Set an alias in the Delphi external declaration: function CreateIncome2: CIncome; stdcall; external 'income.dll' name '_CreateIncome'; You can work with an block to prevent all functions from decorating: macro NoMangle means extern "C extern "C" { __declspec(dllexport) CIncome *CreateIncome(); void __EXPORT_TYPE SayHello2(); } function CreateIncome2: CIncome; stdcall; external 'income.dll' name '_CreateIncome'; You can work with an NoMangle decorator macro to prevent the decoration!: C: NoMangle long DLL_IMPORT_EXPORT csp2GetDeviceId(char szDeviceId[8], long nMaxLength); Call it from Pascal: function csp2GetDeviceId(szDeviceId: PChar; nMaxLength: Longint): Longint; stdcall; external 'csp2.dll' name 'csp2GetDeviceId'; var myBuffer: array [0..7] of Char; begin csp2GetDeviceId(@myBuffer[0], SizeOf(myBuffer));