Как экспортировать функции перегрузки из библиотеки 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.
Вопросы:
Как правильно экспортировать эти функции в модуль DllUnit1 и можно ли вообще сделать это в Delphi (экспорт под одним именем), чтобы получить тот же вызов из моего проекта TestMyDll, что и в начале (пример из windows.па)?
Если такие функции можно экспортировать под одним именем, то правильно ли будет работать вызовом 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';
Он составлен и работать нормально.
Еще вопросы:
Вроде работает, но правильно ли?
Имеет ли значение как написать " Function MyFunc (const X: Integer): string; Overload; StdCall;" или "Function MyFunc (const X:Integer): string; StdCall; Overload;"?
Правильно ли будут вызваны эти функции в проекте других языков (Vb, C ++, C #)?
3 ответов:
Значит, DLL может экспортировать функции с одинаковыми именами.
Нет, это не так. Delphi объявляет 2 перегрузки
InterlockedExchangeAdd()с разными параметрами, но kernel32.dll экспортирует только одну функциюInterlockedExchangeAdd(). Два объявления Delphi импортируют одну и ту же функцию DLL. Перегруженные параметры равнозначны при вызове функции во время выполнения. Другими словами,Addend: PLongintиvar Addend: Longintидентичны в том, что касается функции. Во время выполнения они оба являются указатель на aLongint.Первое объявление использует синтаксис в стиле 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 экспортируют функции по имени и порядковому значению. Каждый из них должен быть уникальным. Нельзя экспортировать две разные функции с одинаковым именем или порядковым номером.
Ваш пример с
Оставим порядковый номер в стороне и сосредоточимся на именах. Очень ясно из первого абзаца выше, что вы должны использовать различные имена для каждой функции. Конечно, вы все еще можете использовать перегрузку внутри, но указать различные имена в предложении exports. Аналогично при импорте можно объявить импортируемые функции перегруженными, но использовать синтаксис name для указания имени DLL. Таким образом, вы можете легко использовать внутреннюю перегрузку с обеих сторон интерфейса, но вам придется использовать уникальные имена при экспорте и импорте функций. Вот простой пример:InterlockedExchangeAdd- это просто две функции с разными, но эквивалентными сигнатурами, относящимися к одной и той же функции. Это делается для удобства звонящего.Библиотека, что экспорт функций
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.Ключевое слово
Обратите внимание, что языки, которые не поддерживают перегрузку (например, VB6, C), очевидно, не смогут импортировать функции и использовать для них одинаковые имена. Аналогично для языков, которые не поддерживают переименование функции в импорт (то есть C++). Насколько мне известно, только Delphi позволяет такие ловкие трюки во время импорта.overloadможет появиться в любом месте объявления. Не имеет значения, где он появляется. С другой стороны, соглашение о вызове должно появиться передexternal.Для таких языков, как 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