How to export overload functions from a DLL?

Delphi Xe.

In the Windows.pas module, I see one of the methods:

function InterlockedExchangeAdd(Addend: PLongint; Value: Longint): Longint stdcall; overload; {$EXTERNALSYM InterlockedExchangeAdd} function InterlockedExchangeAdd(var Addend: Longint; Value: Longint): Longint stdcall; overload; {$EXTERNALSYM InterlockedExchangeAdd} ... function InterlockedExchangeAdd(Addend: PLongint; Value: Longint): Longint; external kernel32 name 'InterlockedExchangeAdd'; function InterlockedExchangeAdd(var Addend: Longint; Value: Longint): Longint; external kernel32 name 'InterlockedExchangeAdd'; 

This means that a DLL can export functions with the same name.

I am trying to repeat:

I am creating a project

 Program TestMyDll; {$APPTYPE CONSOLE} uses SimpleShareMem, SysUtils; Function MyFunc(const X:Integer):string; StdCall; External 'MyDll.dll' Name 'MyFunc'; Overload; Function MyFunc(const X:Extended):string; StdCall; External 'MyDll.dll' Name 'MyFunc'; Overload; begin try Writeln; Writeln('MyDll test'); Writeln('Int: ' + MyFunc(10)); Writeln('Real: ' + MyFunc(10.55)); Readln; except on E: Exception do Writeln(E.ClassName, ' : ', E.Message);end; end. 

It compiles fine. Next, I create the DLL:

 Library MyDll; uses SimpleShareMem, DllUnit1 in 'DllUnit1.pas'; {$R *.res} begin //test MyFunc(10);MyFunc(10.55); end. 

... and the DllUnit1.pas module

 Unit DllUnit1; Interface Function MyFunc(const X:Integer):string; Overload; StdCall; Function MyFunc(const X: Extended):string; Overload; StdCall; Exports MyFunc; // COMPILE ERROR Implementation Uses SysUtils; Function MyFunc(const X:Integer):string; begin result:=Inttostr(x); end; Function MyFunc(const X: Extended):string; begin result:=Floattostr(x); end; end. 

But when compiling, I get the error message: [DCC Error] DllUnit1.pas (7): E2273 There is no overloaded version of "MyFunc" with this list of parameters .

In the Delphi help, I see:

 "Delphi Language Reference"/"The exports clause" ... When you export an overloaded function or procedure from a dynamically loadable library, you must specify its parameter list in the exports clause. For example, exports Divide(X, Y: Integer) name 'Divide_Ints', Divide(X, Y: Real) name 'Divide_Reals'; On Windows, do not include index specifiers in entries for overloaded routines. 

Questions:

  • How to correctly export these functions to the DllUnit1 module, and is it even possible to make it in Delphi (export under the same name) to get the same call from my TestMyDll project as at the beginning (example from windows.pas)?

  • If such functions can be exported under the same name, will there be something that will work correctly in a DLL call from other languages โ€‹โ€‹(VB, C ++)? Or is it better to make two functions with different names?

PS I found a slightly similar question here (http://stackoverflow.com/questions/6257013/how-to-combine-overload-and-stdcall-in-delphi), but I did not like the answer

PSS Bad English


ADD (added after replies)

Thanks, thanks.

Did so:

In project:

 Function MyFunc (const X:Integer):string; StdCall; External 'MyDll.dll' Name 'MyFunc'; Overload; Function MyFunc (const X:Extended):string; StdCall; External 'MyDll.dll' Name ' MyFunc1'; Overload; 

In DllUnit1

 Exports MyFunc (const X:Integer) Name 'MyFunc', MyFunc (const X:Extended) Name 'MyFunc1'; 

It compiled and works fine.

More questions:

  • How it works, but is it true?

  • Does it matter how to write "Function MyFunc (const X: Integer): string; Overload; StdCall;" or "Function MyFunc (const X: Integer): string; StdCall; Overload;"?

  • Will these functions in the project of other languages โ€‹โ€‹(Vb, C ++, C #) be called correctly?

+4
source share
3 answers

This means that a DLL can export functions with the same name.

No, it is not. Delphi declares 2 InterlockedExchangeAdd() overloads with various parameters, but kernel32.dll only exports one InterlockedExchangeAdd() function. Two Delphi declarations import the same DLL function. Overloaded parameters are equivalent when a function is called at run time. In other words, Addend: PLongint and var Addend: Longint identical with respect to the function. At runtime, both of them are pointers to Longint .

The first declaration uses C-style syntax to pass an Addend parameter Addend explicit pointer:

 var Value, Ret: Longint; begin Ret := InterlockedExchangeAdd(@Value, 1); end; 

The second declaration uses Delphi-style syntax to pass an Addend parameter by reference:

 var Value, Ret: Longint; begin Ret := InterlockedExchangeAdd(Value, 1); end; 

When exporting an overloaded function or procedure from a dynamically loaded library, you must specify its list of parameters in the export clause.

I have never had to do this in my DLLs, but I never export overloads. Specifying the parameters allows the compiler to distinguish which export uses the overload, but, as an example, these overloads are exported with different names, although they use the same name in the DLL encoding.

is it better to make two functions with different names? **

Yes.

+11
source

The DLL exports functions by name and ordinal value. Each of them must be unique. You cannot export two different functions with the same name or the same serial number.

Your InterlockedExchangeAdd example is just two functions with different but equivalent signatures related to the same function. This is done for the convenience of the caller.

Leave the ordinal one way and focus on the names. From the first paragraph it is very clear that you need to use different names for each function. Of course, you can still use overloading internally, but specify individual names as part of the export clause. Similarly, when importing, you can declare the imported functions as overloaded, but use the name syntax to specify the name of the DLL.

So, in general, you can easily use overloading internally on both sides of the interface, but when exporting and importing functions, you need to use unique names. Here is a simple example:

Library that exports functions

 library liba; procedure F(X: Integer); stdcall; overload; begin end; procedure F(X, Y: Integer); stdcall; overload; begin end; exports F(X: Integer) name 'F1', F(X, Y: Integer) name 'F2'; begin end. 

Library that imports functions

 library libb; procedure F(X: Integer); stdcall; overload; external 'liba.dll' name 'F1'; procedure F(X, Y: Integer); stdcall; overload; external 'liba.dll' name 'F2'; begin end. 

The overload can appear anywhere in the ad. It doesn't matter where he appears. On the other hand, the calling convention must appear before external .

Note that languages โ€‹โ€‹that do not support overloading (i.e. VB6, C), obviously, will not be able to import functions and use the same names for them. Similarly for languages โ€‹โ€‹that do not support renaming a function upon import (i.e. C ++). As far as I know, only Delphi actually allows such neat tricks during import.

For languages โ€‹โ€‹like C ++ and C # that support overloading, you will need to introduce another layer of indirection. For example, in C # you do the following:

 [DllImport("liba.dll")] private static extern void F1(int X); [DllImport("liba.dll")] private static extern void F2(int X, int Y); public static void F(int X) { F1(X); } public static void F(int X, int Y) { F2(X, Y); } 

Exactly the same approach can be used in C ++. The only real difference between this approach and the Delphi code shown above is that the Delphi language supports direct syntax for this mapping.


As for the various examples in your question, they all use a string, which, of course, is a private Delphi type. You should not use string in the exported function if the function should be called from any language other than Delphi. Or really, any version of the compiler other than the one with which you built the DLL.

+4
source

No, you are mistaken. The functions in Windows.dll are all C-called - they are not overloaded.

Here is the correct prototype for InterlockedExchangeAdd:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683590%28v=vs.85%29.aspx

 LONG __cdecl InterlockedExchange( __inout LONG volatile *Target, __in LONG Value ); 

The syntax in Windows.pas allows you to pass a "long int" or a "pointer to a long int". C and C ++ will happily let you do the same. But the called function in one case is the same.

'Hope that helps

+3
source

Source: https://habr.com/ru/post/1387287/


All Articles