2011-12-20 2 views
4

Delphi Xe.Как экспортировать функции перегрузки из DLL?

В модуле 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 Help, я вижу:

"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++)? Или лучше сделать две функции с разными именами?

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. Имеет ли значение, как писать «Функция MyFunc (const X: Integer): string; Overload; StdCall;" или «Функция MyFunc (const X: Integer): string; StdCall; Overload;"?

  3. Эта функция в проекте других языков (Vb, C++, C#) будет правильно вызвана?

ответ

11

Means, DLL can export functions with identical names.

Нет, это не так. 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; 

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

Мне никогда не приходилось это делать в моих DLL, но тогда я никогда не экспортирую перегрузки. Указание параметров позволяет компилятору различать, какой экспорт использует перегрузку, но, как показывает пример, эти перегрузки экспортируются разными именами, хотя они используют одно и то же имя в кодировке DLL.

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

Да.

+0

Ответьте, пожалуйста, на дополнительные вопросы –

+0

То, что вы показали сейчас, да, и другие языки смогут использовать эти функции как долго, так как они экспортируются с разными именами. –

+0

Никакие другие языки не могут использовать эти функции после строки использования. –

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 позволяет передавать «длинный ИНТ» или «указатель на долгое ИНТ». C и C++ с радостью позволят вам сделать то же самое. Но функция , которая называется такая же, в любом случае.

«Надежда, что помогает

+0

Другими словами, в Delphi нет возможности экспорта функций из DLL под одним именем? А на других языках тоже? –

+0

Я просто привел эту функцию в качестве примера, насколько меня интересует, можно сделать такой в ​​Delphi (экспорт Перегрузка функций из DLL). –

4

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. Вы не должны использовать string в экспортируемой функции, если функция должна быть вызвана с любого языка, кроме Delphi. Или действительно любая версия компилятора, кроме той, с которой вы построили DLL.

+0

Спасибо за подробный ответ –

Смежные вопросы