На этом шаге мы закончим изучение этого вопроса.
И, наконец, само приложение может экспонировать функции таким же способом, каким это делает DLL. В приложении можно создать секцию exports и объявить имена (и/или индексы) функций. После этого в DLL можно воспользоваться функцией GetProcAddress для получения указателя на функцию и вызвать ее. Для описанного на прошлом шаге примера код приложения выглядит следующим образом:
function GetNextValueExport:integer; stdcall; begin if NSum<10 then begin Inc(NSum); Result := NSum; end else Result := -1; end; function CalculateSumExport(MethodName:PChar):Integer; stdcall; external 'FirstLib.dll' name 'SumExport'; procedure TForm1.Button1Click(Sender: TObject); var N: Integer; begin NSum := 0; N := CalculateSumExport('GetNextValueExport'); Caption := IntToStr(N); end; exports {Эта секция объявлена в исполняемом файле} GetNextValueExport index 1 name 'GetNextValueExport';
Обратите внимание - теперь в приложении (в проекте ЕХЕ-файла) определена секция exports! Соответствующий код в DLL для тестирования данной функции выглядит следующим образом:
type TReturnNextMethodExport=function:integer; stdcall; function CalculateSumExport(MethodName:pchar):integer; stdcall; export; var ReturnNextExport:TReturnNextMethodExport; N:integer; begin Result := 0; N := 0; // Если параметр - нулевой, GetModuleHandle вернет // дескриптор файла, путем загрузки которого // создается вызывающий процесс. ReturnNextExport:=GetProcAddress(GetModuleHandle(NIL), MethodName); while N>=0 do begin N := ReturnNextExport; if N>=0 then Result:=Result+N; end; end;
При запуске этого проекта приложение автоматически загружает DLL и находит в DLL адрес функции CalculateNextExport (которая экспортируется по имени SumExport). При вызове этого проекта ему в качестве параметров передаются заголовок модуля приложения и имя функции, под которым она экспортируется из приложения. Далее, DLL использует функцию GetProcAddress для нахождения адреса функции, экспортируемой приложением. Для того чтобы был возвращен адрес функции, в приложении была объявлена секция exports, где описано внешнее имя функции. Этот пример иллюстрирует формально одинаковую структуру и способ формирования ЕХЕ- и DLL-файлов.
В COM DLL вызовы функций клиента осуществляются путем создания нотификационных интерфейсов. Однако если COM DLL должна работать на том же компьютере, что и клиентское приложение, то и сервер, и клиент имеют общее адресное пространство. В этом случае становятся значимыми указатели, в том числе указатели на функции. Поэтому серверу, реализованному в виде DLL, можно передавать указатели на функции клиента, и эти функции будут успешно вызываться.
На следующем шаге мы рассмотрим работу с объектами в DLL.