如何从DLL导出重载函数?

Gu.*_*u. 4 delphi dll overloading

德尔福Xe.

在模块Windows.pas中,我看到一种方法:

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';
Run Code Online (Sandbox Code Playgroud)

意味着,DLL可以导出具有相同名称的函数.

我试着重复一遍:

我创建了这个项目

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.
Run Code Online (Sandbox Code Playgroud)

它正常编译.我进一步创建DLL:

Library MyDll;

uses
  SimpleShareMem,
  DllUnit1 in 'DllUnit1.pas';

{$R *.res}

begin
//test
MyFunc(10);MyFunc(10.55);
end.
Run Code Online (Sandbox Code Playgroud)

......和模块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.
Run Code Online (Sandbox Code Playgroud)

但在编译时我收到一个错误:[DCC错误] DllUnit1.pas(7):E2273没有带有此参数列表的'MyFunc'的重载版本存在.

在Delphi帮助中,我看到:

"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.
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 如何在模块DllUnit1中导出这些函数是否正确,以及是否可以在Delphi(一个名称下导出)中一般地使用它从我的项目TestMyDll开始接收相同的调用(例如来自windows.pas)?

  2. 如果可以使用一个名称导出这些函数,那么通过调用其他语言的DLL(VB,C++)来处理它是否正确?或者最好使用不同的名称制作两个函数?

PS这里有点类似的问题(http://stackoverflow.com/questions/6257013/how-to-combine-overload-and-stdcall-in-delphi),但答案不适合我

PSS英语不好


ADD(已在答案后添加)

显然,谢谢.

已经这样做了:

在项目中:

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;
Run Code Online (Sandbox Code Playgroud)

在DllUnit1中

Exports
MyFunc (const X:Integer) Name 'MyFunc',
MyFunc (const X:Extended) Name 'MyFunc1';
Run Code Online (Sandbox Code Playgroud)

它编译并正常工作.

还是问题:

  1. 喜欢作品,但它是否正确?

  2. 是否有值如何写"Function MyFunc(const X:Integer):string; Overload; StdCall;" 或"函数MyFunc(const X:Integer):string; StdCall; Overload;"?

  3. 这个函数在其他语言的项目(Vb,C++,C#)中会被正确引起吗?

Rem*_*eau 11

意味着,DLL可以导出具有相同名称的函数.

不,不是的.Delphi InterlockedExchangeAdd()使用不同的参数声明2个重载,但kernel32.dll只导出一个InterlockedExchangeAdd()函数.两个Delphi声明正在导入相同的DLL函数.在运行时调用函数时,重载参数是等效的.换句话说,就功能而言Addend: PLongint,var Addend: Longint它们是相同的.在运行时,它们都是指向a的指针Longint.

第一个声明使用C风格的语法Addend通过显式指针传递参数:

var
  Value, Ret: Longint;
begin
  Ret := InterlockedExchangeAdd(@Value, 1); 
end;
Run Code Online (Sandbox Code Playgroud)

第二个声明使用Delphi风格的语法来通过Addend引用传递参数:

var
  Value, Ret: Longint;
begin
  Ret := InterlockedExchangeAdd(Value, 1); 
end;
Run Code Online (Sandbox Code Playgroud)

从动态可加载库中导出重载函数或过程时,必须在exports子句中指定其参数列表.

我从来没有在我的DLL中那样做,但是我也从不导出重载.指定参数允许编译器区分哪个导出使用哪个重载,但是如示例所示,这些重载由不同的名称导出,尽管它们在DLL的编码中使用相同的名称.

最好使用不同的名称制作两个函数?**

是.


Dav*_*nan 5

DLL按名称和序数值导出函数.每个都必须是独一无二的.您不能导出具有相同名称或相同序号的两个不同函数.

您的示例InterlockedExchangeAdd仅仅是两个具有不同但等效签名的函数,这些函数引用相同的函数.这样做是为了方便呼叫者.

让我们把序数留在一边,专注于名字.从上面的第一段可以清楚地看出,你必须为每个函数使用不同的名称.当然,您仍然可以在内部使用重载,但指定不同的名称作为exports子句的一部分.同样,在导入时,您可以声明导入的函数是重载,但使用name语法指定DLL名称.

因此,总而言之,您可以轻松地在接口的两侧内部使用重载,但在导出和导入函数时必须使用唯一的名称.这是一个简单的例子:

导出功能的库

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.
Run Code Online (Sandbox Code Playgroud)

导入功能的库

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.
Run Code Online (Sandbox Code Playgroud)

overload关键字可以在声明中任何地方出现.它出现在哪里并不重要.另一方面,调用约定必须出现在之前external.

请注意,不支持重载的语言(即VB6,C)显然无法导入函数并为它们使用相同的名称.同样,对于不支持在导入时重命名函数的语言(即C++).据我所知,只有Delphi才能在导入时允许这样的巧妙技巧.

对于支持重载的C++和C#等语言,您需要引入另一层间接.例如在C#中你会这样做:

[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);
}
Run Code Online (Sandbox Code Playgroud)

在C++中可以使用完全相同的方法.这种方法与我上面展示的Delphi代码之间唯一真正的区别是Delphi语言支持直接语法来实现这种映射.


关于你的问题中的各种例子,这些都使用字符串当然是私有的Delphi类型.string如果要从Delphi以外的任何语言调用该函数,则不得在导出函数中使用.或者实际上除了你用DLL构建DLL之外的任何编译器版本.