Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to export Overload functions from DLL?

Delphi Xe.

In module Windows.pas I see one of 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';

Means, DLL can export functions with identical names.

I try to repeat:

I create the 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 is compiled normally. Further I create DLL:

Library MyDll;

uses
  SimpleShareMem,
  DllUnit1 in 'DllUnit1.pas';

{$R *.res}

begin
//test
MyFunc(10);MyFunc(10.55);
end.

...and module DllUnit1.pas

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 at compilation I receive an error: [DCC Error] DllUnit1.pas(7): E2273 No overloaded version of 'MyFunc' with this parameter list exists.

In 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:

  1. How correctly to export these functions in module DllUnit1 and whether it is possible to make it in general in Delphi (export under one name) to receive the same call from the my project TestMyDll as in the beginning (an example from windows.pas)?

  2. If such functions can be exported under one name, whether that it will be correct to work by call DLL from other languages (VB, C ++)? Or it is better to make two functions with different names?

P.S. Little bit similar question has found here (http://stackoverflow.com/questions/6257013/how-to-combine-overload-and-stdcall-in-delphi), but the answer did not suit me

P.S.S. Bad english


ADD (Has added after answers)

Clearly, thanks.

Has made so:

In the 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 is compiled and works normally.

Still questions:

  1. Like works, but whether it is correct?

  2. Whether has value how to write "Function MyFunc (const X:Integer):string; Overload; StdCall;" or "Function MyFunc (const X:Integer):string; StdCall; Overload;"?

  3. This functions in project of other languages (Vb, C ++, C #) will be correctly caused?

like image 576
Gu. Avatar asked Dec 01 '22 07:12

Gu.


2 Answers

Means, DLL can export functions with identical names.

No, it does not. Delphi is declaring 2 overloads of InterlockedExchangeAdd() with different parameters, but kernel32.dll only exports one InterlockedExchangeAdd() function. The two Delphi declarations are importing the same DLL function. The overloaded parameters are equivilent when calling the function at runtime. In other words, Addend: PLongint and var Addend: Longint are identical as far as the function is concerned. At runtime, they are both a pointer to a Longint.

The first declaration uses a C-style syntax for passing the Addend parameter by explicit pointer:

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

The second declaration uses a Delphi-style syntax for passing the Addend parameter by reference instead:

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

When you export an overloaded function or procedure from a dynamically loadable library, you must specify its parameter list in the exports clause.

I have never had to do that in my DLLs, but then I never export overloads, either. Specifying the parameters allows the compiler to differentiate which export uses which overload, but as the example also shows, those overloads are exported by different names, though they use the same name in the DLL's coding.

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

Yes.

like image 56
Remy Lebeau Avatar answered Dec 05 '22 07:12

Remy Lebeau


DLLs export functions by name and ordinal value. Each of these must be unique. You cannot export two different functions with the same name or the same ordinal.

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

Let us leave the ordinal to one side and concentrate on names. It is very clear from the first paragraph above that you have to use distinct names for each function. Of course you can still use overloading internally but specify distinct names as part of the exports clause. Likewise when importing you can declare the imported functions to be overloads but use the name syntax to specify the DLL name.

So, in summary, you can easily use overloading internally on both sides of the interface but you have to use unique names when exporting and importing the functions. 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 keyword can appear anywhere in the declaration. It doesn't matter where it appears. On the other hand, the calling convention has to appear before external.

Note that languages that do not support overloading (i.e. VB6, C) will obviously not be able to import the functions and use the same names for them. Likewise for languages that do not support renaming the function at import (i.e. C++). To the best of my knowledge it's only really Delphi that allows such neat tricks at import time.

For languages like C++ and C# which do support overloading you would need to introduce another layer of indirection. For example in C# you would do this:

[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 could be used in C++. The only real difference between this approach and the Delphi code I showed above is that the Delphi language supports direct syntax to effect this mapping.


Regarding the various examples in your question, these all use string which of course is a private Delphi type. You must not use string in an exported function if the function is to be callable from any language other than Delphi. Or indeed any compiler version other than the one you built the DLL with.

like image 37
David Heffernan Avatar answered Dec 05 '22 06:12

David Heffernan