【问题标题】:How to export Overload functions from DLL?如何从 DLL 中导出重载函数?
【发布时间】:2012-01-24 20:56:00
【问题描述】:

德尔福 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 Error] 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. 如果这样的函数可以在一个名称下导出,那么从其他语言(VB、C++)调用DLL是否正确?还是做两个不同名字的函数比较好?

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

附言英语不好


添加(已在答案后添加)

很清楚,谢谢。

已经这样做了:

在项目中:

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#)项目中的这个函数会正确导致吗?

【问题讨论】:

    标签: delphi dll overloading


    【解决方案1】:

    意味着,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 的编码中使用相同的名称。

    最好是做两个不同名字的函数?**

    是的。

    【讨论】:

    • 请回答其他问题
    • 您现在展示的内容很好,是的,其他语言将能够使用这些函数,因为它们以不同的名称导出。
    • 自从使用字符串以来,没有其他语言可以使用这些函数。
    • C++Builder 就可以了。但你是对的。我没有注意到字符串的使用。其他语言将无法使用这些函数,除非将它们重写为仅对参数和返回值使用 POD 数据类型。
    【解决方案2】:

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

    您的InterlockedExchangeAdd 示例只是两个函数,它们具有不同但等效的签名,引用相同的函数。这样做是为了方便调用者。

    让我们将序数放在一边,专注于名称。从上面的第一段中可以清楚地看出,您必须为每个函数使用不同的名称。当然,您仍然可以在内部使用重载,但在导出子句中指定不同的名称。同样,在导入时,您可以将导入的函数声明为重载,但使用名称语法来指定 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 所用的编译器版本以外的任何编译器版本。

    【讨论】:

    • 感谢详细解答
    【解决方案3】:

    不,你错了。 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++ 很乐意让您做同样的事情。但无论哪种情况,调用的函数都是相同的

    '希望有帮助

    【讨论】:

    • 换句话说,在 Delphi 中,不可能以一个名称从 DLL 导出函数?还有其他语言吗?
    • 我只是简单地以这个函数为例,不管我是否感兴趣,是否可以在 Delphi 中实现(从 DLL 导出函数重载)。
    猜你喜欢
    • 2015-07-21
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    • 2021-10-08
    • 2010-10-25
    相关资源
    最近更新 更多