如何从DLL导出重载函数?

时间:2011-12-20 23:08:57

标签: delphi dll overloading

Delphi 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';

表示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.

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

Library MyDll;

uses
  SimpleShareMem,
  DllUnit1 in 'DllUnit1.pas';

{$R *.res}

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

...和模块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.

但在编译时我收到一个错误: [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.

问题:

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

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

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

    P.S.S。英语不好


    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;
    

    在DllUnit1中

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

    它已编译并正常工作。

    仍有问题:

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

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

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

3 个答案:

答案 0 :(得分:11)

  

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

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

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

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

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

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

从动态可加载库中导出重载函数或过程时,必须在其中指定其参数列表   出口条款。

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

  

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

答案 1 :(得分: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.

导入功能的库

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.

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);
}

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


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

答案 2 :(得分:4)

不,你错了。 Windows .dll中的函数都是C可调用的 - 它们重载。

以下是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
);

Windows.pas中的语法允许您传递“long int”或“指向long int”的指针。 C和C ++很乐意让你做同样的事情。但在任何一种情况下,被调用的函数都是相同的

'希望有所帮助