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



Delphi Xe.



В окне модуля.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.па)?



  2. Если такие функции можно экспортировать под одним именем, то правильно ли будет работать вызовом DLL с других языков (VB, C ++)? Или лучше сделать две функции с разными именами?



P.S. Немного похожий вопрос нашел здесь (http://stackoverflow.com/questions/6257013/how-to-combine-overload-and-stdcall-in-delphi), но ответ меня не устроил



P. S. S. плохой английский





Добавить (добавил После ответов)



Ясно, спасибо.



Сделал так:



В проекте:



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;" или "Function MyFunc (const X:Integer): string; StdCall; Overload;"?



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


599   3  

3 ответов:

Значит, DLL может экспортировать функции с одинаковыми именами.

Нет, это не так. Delphi объявляет 2 перегрузки InterlockedExchangeAdd() с разными параметрами, но kernel32.dll экспортирует только одну функцию InterlockedExchangeAdd(). Два объявления Delphi импортируют одну и ту же функцию DLL. Перегруженные параметры равнозначны при вызове функции во время выполнения. Другими словами, Addend: PLongint и var Addend: Longint идентичны в том, что касается функции. Во время выполнения они оба являются указатель на a 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.

Лучше сделать две функции с разными именами?**

Да.

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

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

" Надеюсь, это поможет

Comments

    Ничего не найдено.