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.
问题:
如何在模块DllUnit1中正确导出这些函数,以及是否可以在Delphi(以一个名称导出)中一般地使用它来从我的项目TestMyDll接收与开头相同的调用(示例)来自windows.pas)?
如果可以使用一个名称导出这些函数,那么通过调用其他语言的DLL(VB,C ++)来处理它是否正确?或者最好使用不同的名称制作两个函数?
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';
它已编译并正常工作。
仍有问题:
喜欢作品,但是否正确?
是否有值如何写“Function MyFunc(const X:Integer):string; Overload; StdCall;”或“函数MyFunc(const X:Integer):string; StdCall; Overload;”?
这个函数在其他语言的项目(Vb,C ++,C#)中会被正确地引起吗?
答案 0 :(得分:11)
意味着,DLL可以导出名称相同的函数。
不,它没有。 Delphi使用不同的参数声明2个InterlockedExchangeAdd()
重载,但kernel32.dll只导出一个InterlockedExchangeAdd()
函数。两个Delphi声明正在导入相同的DLL函数。在运行时调用函数时,重载参数是等效的。换句话说,就函数而言,Addend: PLongint
和var 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 ++很乐意让你做同样的事情。但在任何一种情况下,被调用的函数都是相同的。
'希望有所帮助