Как подключить dll к проекту lazarus

Обновлено: 06.07.2024

Gecko
>При сборке вместо *.dll тупо получаю *.exe.
А если в настройках проекта выставить расширение .dll? Мне кажется, и будет нормальная dll. Проверь таблицу экспорта.
Lazarus - он же не только под винду, а в линухе свои библиотеки, не dll. Так зачем привязывать расширение?

Ну, положим, в настройках проекта есть параметр "Имя исполняемого файла", по умолчанию пустой. Подставляю туда mydll.dll.
Вроде получается, но dll какая-то странная. Вызывать из нее не получается.

Gecko
Хм, я по памяти немного не то сказал. Все гораздо проще: Проект -> Создать проект -> Library. Компилится *.dll без лишних настроек. Работает, только что проверил.

P.S.: Проверял не на твоем примере. А у тебя, я думаю, и с примером будут ошибки - PChar нельзя возвращать как результат. Это указатель. Кто его потом освобождать будет? Во всех WIN API функциях передается массив Char и размер этого массива, а функция уже его заполняет. В результате программа выделяет и освобождает память. Посмотри параметры GetWindowText или GetClassName.

Э, а кто мешает, вторую функйи экспортировать, которая будет чистить за этим безобразием?

Lord Stalker
>Э, а кто мешает, вторую функйи экспортировать, которая будет чистить за этим безобразием?
Ага, может еще garbage collector написать для собирания "мертвых" PChar? Зачем лишние функции, когда можно все организовать ресурсами приложения?
Кроме того, если писать именно так, как сделал Gecko, то область видимости Result, afaik, ограничится пределами самой функции, т.е. на выходе по адресу Result уже не будет выделенной памяти (хотя может повезти, и значение там еще не будет затерто - но это не правильно).

DragonMX
Создаю пустой проект, как ты сказал:

компилирую. все равно exe!
Не знаю, может у меня сборка Лазаруса какая-то кривая. Я поставил ту, в которой имеется
встроенный GLScene.

Gecko
>Создаю пустой проект, как ты сказал
Ты создал проект через меню? Я не знаю, как это в твоей версии выглядит. У меня стоит Lazarus 0.9.26, русский, вот только недавно скачал в sourceforge. У меня в главном окне программы (вверху, широкое которое) в меню есть пункты: Проект -> Создать проект. У тебя есть такие пункты? Там появляется окно выбора типа создаваемого проекта, штук 7-10 вариантов, среди них есть "Library". Когда его создаю, все уже само работает - компилируется .dll.

Да, у меня все то же самое. Но dll не компилируется.
0.9.26, говоришь? Попробую поставить.

Так как Delphi и Lazarus во многом очень схожи (а уж в плане написания dll и вовсе практически идентичны), рассказ будет разбит на 3 части:

подготовительная часть Delphi

подготовительная часть Lazarus

общая содержательная часть

2 Ответ от admin 2013-02-13 21:58:12

Delphi

Для начала скачиваем заголовочные pas-файлы с описанием интерфейсов Lua. Взять их можно из разных мест, в чем-то они будут немного отличаться, например из-за того, что сконвертированы из разных версий h-файлов, поставляемых с Lua. Кроме того, трансляция C-прототипов в общем случае тоже может быть выполнена в разной семантике при идентичном фактическом представлении скомпилированного кода.

Теперь собственно создание библиотеки. Запускаем Delphi, выбираем File -> New в случае Delphi5 или File -> New -> Other в случае Delphi 7. В других версия – что-то аналогичное.

https://quik2dde.ru/static-img/40/2web-pic-1.jpg

На вкладке New выбираем тип проекта Dll, жмем Ok.

https://quik2dde.ru/static-img/40/2web-pic-2.jpg

Добавим в проект интерфейсные файлы Lua: Project -> Add to project.

https://quik2dde.ru/static-img/40/2web-pic-3.jpg

и в открывшемся диалоге выбираем скачанные ранее файлы, жмем «Открыть»:

https://quik2dde.ru/static-img/40/2web-pic-4.jpg

Несколько забегая вперед скажу, что для компилябельности в Delphi пришлось немного модифицировать заголовочные файлы, в частности добавив определение не объявленных в Delphi производных типов (которые присутствуют в библиотеках FreePascal/Lazarus), и отключив (под условными директивами) интерфейсные функции Lua, которые содержат в определении слово varargs, т.к. не все версии Delphi поддерживают эту директиву; надеюсь, вам вполне удастся обойтись без этих функций, таких функций всего 3 или 4. Добавлено объявление типов:

Полные исходники с указанными исправлениями можно скачать по ссылке в самом первом посте этой темы.

Теперь сохраним проект File -> Save, в качестве имени проекта укажем SimpleDelphiLua.

3 Ответ от admin 2013-02-13 21:58:15

Lazarus

Т.к. в поставку Lazarus (вернее сказать, FreePascal) уже включены заголовочные pas-файлы с описанием интерфейсов для Lua, то достаточно просто добавить их в uses там, где это необходимо. Если же вы заголовочные файлы для Lua захотите модифицировать (например, чтобы использовать qlua.dll, о чем написано в конце общей части), то лучше скопировать эти файлы в папку с проектом и уже там изменять, не трогая файлы из поставки Lazarus.

У меня установился русифицированный Lazarus, поэтому картинки такие. Надеюсь, с вашей версией вы разберетесь.

Создадим новый проект

https://quik2dde.ru/static-img/40/2web-laz-1.jpg

и в открывшемся диалоге выберем тип проекта «Библотека» («Library»).

https://quik2dde.ru/static-img/40/2web-laz-2.jpg

Сохраним наш проект под именем SimpleDelphiLua (в названии проекта присутствует слово "Delphi" просто для удобства написания общей части).

Подготовительная часть для Lazarus на этом закончена.

4 Ответ от admin 2013-02-14 21:36:11

Общая часть

В основной файл проекта разместим код интерфейсной функции, которая будет вызываться при подключении нашего модуля к программе на Lua (через директиву require) и регистрировать доступные в нем для Lua-скриптов функции. Учитывая имя нашей библиотеки, такая интерфейсная функция должна называться luaopen_SimpleDelphiLua(). Здесь же в константном массиве ls_lib разместим список регистрируемых функций, которые буду доступны нам для вызова из Lua-скриптов.

( Строго говоря, написанное про единственно возможное имя функции не совсем верно; функция может называться и иначе, в этом случае надо будет изменить формат подключения нашего модуля в Lua. Но для простоты делаем так, как написано. )

Не забываем для интерфейсных функций добавлять модификатор соглашений о вызовах cdecl! Для остальных функции, которые используются только внутри DLL-библиотеки, добавлять модификаторы соглашений о вызовах не обязательно.

Т.к. функция инициализации должна экспортироваться из dll-ки, необходимо где-то после ее объявления добавить строчку:

Теперь добавим в библиотеку собственно те функции, которые будут вызываться из скриптов Lua. Все такие функции должны объявляться одинаково вот по такому шаблону:

На вход они принимают указатель на Lua стек, а возвращать должны количество результирующих значений. Т.к. это интерфейсные функции, которые будут вызываться из Lua-машины, не забывайте указывать для них модификатор cdecl!

Как видно из кода выше, в библиотеке мы реализуем две функции:

MultAllNumbers - перемножает все переданные в качестве аргументов числа и возвращает результат перемножения;

GetHostAppPath - возвращает полный путь, где расположено загрузившее эту DLL приложение; в нашем случае это будет папка, где расположен QUIK.

Сами функции я вынес в отдельный модуль LuaFuncImpl.pas, добавленный в проект. В него подключаем интерфейсы из заголовочных файлов Lua:

Функция MultAllNumbers представляет из себя «дословное переложение» аналогичной функции из примера Lua-библиотек на C. Вот она:

Реализацию функции GetHostAppPath() можете посмотреть в приложенных исходниках в самом первом посте темы, она совсем простая.

Скомпилируем проект, полученную библиотеку SipmleDelphiLua.dll скопируем в каталог, где установлен QUIK, и создадим тестовый скрипт на Lua:

https://quik2dde.ru/static-img/40/2web-pic-5.jpg

Вот у нас и готова библиотека на Delphi или Lazarus для Lua!

Если вдруг захочется использовать визуальные компоненты Delphi для построения GUI - то пожалуйста, конечно, но есть разные нюансы. Проще взять готовую библиотеку, например VCLua, которая как раз сделана в Delphi-подобной, только бесплатной среде разработки Lazarus.

В финале хотелось бы добавить еще один штрих. Начиная с версии 6.5.1.19 терминала QUIK, библиотека qlua.dll стала экспортировать из себя все функции поддержки Lua. Это значит, что вместо Lua5.1.dll можно использовать напрямую qlua.dll, т.е. никакая Lua5.1.dll для наших библиотек больше не нужна. Отличная новость!

Для поддержки такого варианта в файле Lua.pas я добавил возможность задания подключения интерфейсных Lua-функций непосредственно из qlua.dll в случае, если определена директива USEQLUA, и сразу включил именно такой режим сборки:

Можете проверить и убедиться, что библиотека SipmleDelphiLua.dll работает и при отсутствии файла lua5.1.dll.

5 Ответ от admin 2013-02-23 11:39:31

6 Ответ от admin 2013-02-23 11:39:34

7 Ответ от admin 2013-02-23 11:39:37

8 Ответ от admin 2013-02-23 11:39:58

Добавил описание для Lazarus.

9 Ответ от biznesman 2013-04-18 15:22:57

Благодарю за Ваш труд. Достойно уважения. Сэкономило немало времени и нервов… Не понятно с какой стороны было подступиться.
Интересует Ваше мнение как лучше реализовать связку Delphi + QLua.

Еще раз спасибо. С Уважением Владимир.

10 Ответ от admin 2013-04-18 19:28:35

biznesman,
Если вам нужно именно взаимодействие с отдельным приложением - то просто набрать в гугле "межпроцессное взаимодействие". Вариантов много: TCP-коннект, PIPE, выделенная общая область памяти между процессами (маппинг), WM_COPYDATA вроде все еще поддерживается, опять же COM, или даже пресловутый DDE-обмен. Выбирайте что вам знакомо, или хотя бы ближе. (Здесь явно не полный список вариантов)

С ходу пример накидать не возьмусь, надо самому повспоминать что и с чем.

11 Ответ от biznesman 2013-04-25 16:02:44

Доброго времени суток!

Спасибо за ответ, но хотелось бы узнить как реализовать связку через lua api.
На данный момент реализовал Application -> dll -> QLua. Пытался просто передать адрес стека из dll, но при попытке работать с ним из своего приложения получаю AV.
Как от сюда убрать dll, как из прложения получить адрес стека?

12 Ответ от admin 2013-05-01 17:11:45

Из другого приложения - никак, это разные адресные пространства.

13 Ответ от merabn 2015-04-05 04:08:22

Скрипт не хочет находить функции.
Кто-нибудь решал эту проблему?

14 Ответ от admin 2015-04-05 08:28:04

У вас dll совсем не загружается. Проверьте:
1) положите собранную dll в каталог с QUIK
2) проверьте в свойствах проекта, может вы собираете её под x64 платформу? Надо x86 собирать, т.е. 32-х битную библиотеку

15 Ответ от merabn 2015-04-05 14:19:59 (2015-04-05 14:52:29 отредактировано merabn)

У вас dll совсем не загружается. Проверьте:
1) положите собранную dll в каталог с QUIK
2) проверьте в свойствах проекта, может вы собираете её под x64 платформу? Надо x86 собирать, т.е. 32-х битную библиотеку

dll загружается, если в скрипте кроме require нет ничего, то выполняется без ошибок. Как я понимаю, как раз require и подключает dll. Значит скрипт и находит dll и, по всей видимости загружает список функций. Но указатели на функции в последствии оказываются недействительными. Это мои предположения, я не специалист по lua и плохо знаком.
1. dll в каталоге с QUIK
2. У меня стоит Delphi 2010. Она сама 32 битная. В свойствах компилятора стоит 32 (можно еще выбрать 16 и windows 3.1 )

Уже столько времени на все это убил, лучше поставить Delphi 7 и сделать нужную DLL.

16 Ответ от sam063rus 2015-04-05 16:03:36 (2016-09-29 11:30:53 отредактировано sam063rus)

17 Ответ от admin 2015-04-05 20:03:46

потому что для того, чтобы написать "обратный порт" с лазаруса на дельфи - надо малость подшаманить проект.

Это никак не связано с описываемой проблемой.
Потому как, если почитать внимательно, человек делает простейшую DLL на дельфи.

"Проблемы" же при портированием из Lazarus - сугубо косметические и связаны лишь с этапом компиляции, а не выполнения.

18 Ответ от admin 2015-04-05 20:06:59

merabn,
Попробуйте еще такой вариант:

19 Ответ от merabn 2015-04-05 20:15:33 (2015-04-05 22:47:19 отредактировано merabn)

20 Ответ от merabn 2015-04-05 22:45:19 (2015-04-05 23:05:28 отредактировано merabn)

потому что для того, чтобы написать "обратный порт" с лазаруса на дельфи - надо малость подшаманить проект. О том, как это сделать - вы можете почитать во "всезнающем" гугле.

----
p.s. в свою очередь, для перевода с дельфи в лазарус - ничего делать не надо - т.к. в "лазаре" уже есть необходимая опция импорта проекта.

К сожалению, информации не нашел, которая помогла бы "подшаманить".
С Delphi в Lazarus много чего есть, хоть и в самом Lazarus есть возможность импорта из Delphi.

Но тут все-таки дело не в этом. На Delphi 7 DLL собирается и она работает в Quik. На Embarcadero Delhpi 2010 и Delphi XE7 - собранная DLL не работает. В самом деле, в проекте простейшая DLL, там практически все - объявления типов и функций. Самая важная вещь - регистрация функций в lua. По поводу изменений в объявлении указателей на функции в Embarcadero по сравнению с Delphi 7 ничего не нашел, относящегося к делу.
Склоняюсь к мысли, что дело в опциях компилятора и компоновщика. Причем, на Delphi 7 - размер DLL 90kb, Delphi 2010 - 250kb, XE7 - 900kb.
Пробовал и разные опции компиляции, но не помогло. И мой уже чисто спортивный интерес к проблеме компоновки DLL в продуктах Embarcadero (или проблеме регистрации DLL в lua) угасает Рано выкидывать Delphi 7

Эта страница содержит информацию о том, как создавать библиотеки с помощью Lazarus/FPC и как использовать их в проектах и пакетах.

Contents

Схожие темы

    - Как преобразовать заголовочные файлы языка C (с расширением .h) в модули на паскале.

Основное

Статическая компоновка: FPC по умолчанию компилирует и компонует статичные исполняемые файлы. Это значит, что компилятор дает команду компоновщику взять все объектные файлы (с расширением .о) в проекте и все пакеты, а затем собрать их в один большой исполняемый файл. Преимущества: нет внешних зависимостей. Недостатки: Разные программы на одном компьютере не могут использовать общий код, и вы не сможете загружать/выгружать подключаемые модули.

Динамические библиотеки: Идея динамических библиотек в том, чтобы разделять общий одинаковый код между программами, сохраняя таким образом память и сокращая время на запуск часто используемых библиотек и подключаемых модулей. Недостатками такого подхода являются снижение скорости редко используемых библиотек, сложное внутреннее устройство (это больше проблема для компилятора) и то, что инициализация раздельна (см. ниже) и требуется система версий, чтобы получить совместимый код.

Операционные системы

FreeBSD

macOS

Linux

Имя файла динамической библиотеки всегда имеет вид 'lib+'имя'+'.so'+версия. Пример: libz.so.1 и libz.so.1.2.2. Linux ищет библиотеку в местах, путь к которым прописан в переменной окружения LD_LIBRARY_PATH, затем в /lib, /usr/lib и наконец в директориях, пути к которым прописаны в файле /etc/ld.so.conf.

Чтобы была возможность разделять память (использовать GetMem/FreeMem, строки, динамические массивы и т.д.) с другими библиотеками (написанными не в FPC) в Linux, необходимо в раздел uses главного файла проекта (обычно .lpr) добавить модуль cmem (нужно вписать его самым первым, до инициализации других модулей!).

Windows

Windows ищет библиотеку в текущей директории, затем системной директории и в переменной окружения PATH.

ppumove, .ppu, .ppl

FPC обычно создает для каждого модуля файлы с расширением .ppu и .o. файл .ppu содержит всю важную информацию из файла .pas/.pp (типы, требуемые объектные файлы .о), тогда как файл .о содержит ассемблерный код и измененные имена, понимаемые текущей системой. Инструмент ppumove, входящий в любую установку FPC, конвертирует файлы .ppu и .o в динамическую библиотеку. Это достигается засчет вызова компоновщика для сборки всех объектных файлов с расширением .о в файл с расширением .so (или .dll в Windows) с последующим удалением записей об объектных файлах из файла .ppu. Обычно такой новый .ppu файл переименовывается в .ppl. Пример: Перейдите в директорию с пакетом (ту, в которой находятся файлы .ppu): ppumove -o имя_пакета -e ppl *.ppu Эта команда конвертирует все файлы .ppu в .ppl и создаст файл libимя_пакета.so (в windows - имя_пакета.dll). Запомните, что в Linux префикс 'lib' добавляется всегда.

Эта новая библиотека может использоваться и другими языками программирования, например C или C++. Или в других программах FPC, с помощью использования external модификаторов. Но секции инициализации/финализации должны вызываться автоматически. Включая инициаллизацию/финализацию менеджера "кучи". Это означает, что не получится использовать строки или GetMem/FreeMem. Но программисты могут добиться большего какими-то своими способами.

Loadlibrary - загрузка динамической библиотеки

Загрузка динамической библиотеки проще с функцией dlopen Loadlibrary из модуля unit dl dynlibs. С версии 1.9.4, dynlibs предоставляет портируемую альтернативу модулю dl. Запомните, что использование модуля dl вместо dynlibs обычно приводит к невозможности портирования между разными системами. Главная проблема - получить имя файла, которое зависит от версии и операционной системы. С версии 2.2.2, в модуле dynlibs появилась константа sharedsuffix, которая упростит этот процесс. Она позволяет получить правильное расширение (dll/so/dylib).

Инициализация

Любой модуль содержит раздел инициализации. Порядок инициализации записан в разделе uses каждого модуля.

Как инициализировать динамическую библиотеку: в разработке.

Финализация

Каждый модуль содержит раздел финализации. Порядок обратный по отношению к порядку инициализации.

Версии, распространение

Библиотеки имеют тенденцию разрастаться и меняться постоянно. Добавление новых возможностей не будет проблемой, а вот удаление метода или изменение его параметров вызовет несовместимость разных версий библиотек. Это значит, любая установленная библиотека (.so, .dll, .dylib) замещается совместимой или новая бибиотека должна быть добавлена в систему. Поэтому каждая библиотека и содержит номер версии. Чтобы загрузить динамическую библиотеку (dlopen из unit dl), должно быть известно правильное имя файла. Под Linux это значит, что вы должны знать и номер версии (т.к. библиотеки оканчиваются на .so.номер_версии). В разработке: IDE сможет создавать номера версий.

Динамическое связывание происходит намного сложнее статического, причем динамическое связывание в Lazarus несколько отличается от такого же связывания в Delphi, поэтому примеры Delphi тут работать не будут.

Итак, начнем с того, что процедуру динамического связывания можно разбить на четыре этапа:

  1. Загрузка динамической библиотеки функцией LoadLibrary и получение её дескриптора - числового идентификатора библиотеки в системе. Такой идентификатор представляет собой целое число. Если загрузка библиотеки была неудачной, то в дескриптор запишется ноль.
  2. Получение адреса нужной функции (или функций) с помощью GetProcAddress .
  3. Непосредственно работа с нужной функцией из DLL.
  4. Выгрузка DLL из памяти.

Причем вы можете выполнять все действия по загрузке библиотеки в память и её выгрузке как для вызова каждой процедуры или функции отдельно, так и для вызова группы подпрограмм. Например, пользователь захотел выполнить некую операцию. Для этой операции требуется вызвать, скажем, десять подпрограмм (процедур и функций) из DLL . Будет глупо открывать и закрывать DLL отдельно для каждой подпрограммы, правильней будет открыть DLL , выполнить все эти десять подпрограмм, а затем закрыть DLL .

Но в нашем примере для наглядности мы будем загружать и выгружать DLL отдельно для каждой операции .

Для этого приложения нам потребуются точно такие же компоненты на форме, как и у предыдущего. Если вам не лень, то можно вернуться назад, и подготовить форму таким же образом, как и у проекта Proba. Однако можно сделать проще. Загрузите в Lazarus проект Proba из папки 26-02, для этого вам нужно загрузить файл Proba.lpi или Proba.lpr. Далее выведите на передний план Редактор формы. Щелкните правой кнопкой мыши по свободному месту формы и выберите команду "Выделить всё". При этом окажутся выделенными все компоненты формы. Затем выберите команду главного меню "Правка -> Копировать".

Теперь начнем новый проект. Выберите команду "Файл -> Создать -> Приложение", и у вас откроется новый проект с пустой формой. Растяните форму по ширине и высоте, чтобы на ней легко уместились все компоненты, позже размеры формы можно будет подкорректировать. Теперь выберите команду главного меню "Правка -> Вставить". При этом все нужные компоненты появятся на форме на том же самом месте. Кроме того, они сохранят свои имена и прочие настройки. Подкорректируйте размер формы. Теперь переименуйте форму в fMain, в Caption напишите " Динамическое связывание DLL ", в BorderStyle выберите bsDialog , а в Position - poDesktopCenter . Сохраните проект в папку 26-03 под именем Proba2, модулю формы дайте имя Main . Не забудьте скопировать файл MyFirstDLL.dll и в эту папку.

Теперь нам потребуется некоторая подготовка перед тем, как мы начнем динамически связывать нашу DLL . Прежде всего, в раздел uses добавьте еще один модуль - Dynlibs . Именно в нём описаны необходимые инструменты для динамического подключения библиотек.

Далее, перед глобальным разделом var нам нужно описать типы наших подключаемых функций, а в самом разделе var добавить переменную- дескриптор библиотеки, а также по переменной на каждую функцию:

Обратите внимание, как мы описываем тип функций:

Тип мы назвали TCode , вы можете дать другое имя, но для типов и классов традиционно принято начинать имя с большой буквы "T". В этом типе мы указали, что создается функция с такими то параметрами (как в DLL ), которая возвращает тип PChar , и будет использовать соглашение stdcall . При этом имени самой функции мы не указываем. Таким же образом мы создаем еще три типа.

Далее, в глобальном разделе var мы добавляем такую переменную:

Это - дескриптор . Когда мы загрузим DLL в память , в эту переменную попадет идентификатор нашей библиотеки. Ведь в памяти всё время загружено множество самых разных DLL , используемых системой, и Windows как-то нужно различать, к которой из них вы обращаетесь. По умолчанию сразу же выставляем нулевое значение . Если попытка считать DLL в память будет неудачной, например, эта DLL отсутствует, то дескриптор также будет нулевым.

Далее мы создаем по переменной для каждой вызываемой из DLL функции. Например, для функции Code мы создаем переменную

Тип TCode мы описали выше, здесь просто используем его же. В принципе, эти переменные необязательно делать глобальными, можно было описать их в разделе var той процедуры, откуда будем вызывать. Но в больших проектах одну и ту же DLL -функцию обычно вызывают из множества различных процедур, поэтому удобней описывать переменные, как глобальные.

Описав дескриптор и переменные для функций, мы сделали всю подготовительную работу.

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

Сгенерируйте событие OnClick для кнопки bBeforeBirthday. Код события отличается от прошлого проекта, он более сложный:

Давайте подробно разбирать весь этот код. Для начала, как и в прошлом проекте, мы сделали проверку - есть ли что-то в строке DE1 , и если нет, то выходим, так как в дальнейших действиях смысла нет. Если текст есть ( пользователь вписал или выбрал дату рождения), то выполняется дальнейший код. Прежде всего, мы загружаем в память библиотеку, сразу же получая дескриптор на неё:

Функция LoadLibrary описана в модуле Dynlibs , который мы подключили в разделе uses , и пытается открыть указанную в параметре библиотеку. Если имя библиотечного файла указано без адреса, как в нашем случае, то файл библиотеки ищется в той же папке, откуда запущена программа . Если его там нет, то библиотека ищется в текущей папке (она может отличаться от папки с программой), затем в системных каталогах Windows и, наконец, в папках, указанных в системной переменной Path . Поэтому DLL лучше устанавливать в папку с программой, или в системную папку Windows , если эту DLL будут использовать несколько приложений. Такой папкой может быть, например,

C:\Windows\system32

Далее, мы получаем адрес в памяти, где находится нужная нам процедура:

Для этого мы указываем имя типа этой функции и используем функцию GetProcAddress . Эта функция работает так. В качестве параметров она получает дескриптор нужной библиотеки и название функции (процедуры), которую нам требуется оттуда вызвать. Затем она возвращает адрес этой функции в памяти, который попадает в нашу переменную BeforeBirthday . Кстати, необязательно давать этим переменным такие же имена, как и у функций, просто так удобней.

Если по какой то причине адрес функции получить не удалось, например, в памяти находится устаревшая версия DLL , где эта функция отсутствует, то GetProcAddress вернет значение nil , то есть, ничего. Именно поэтому мы делаем ещё одну проверку:

Поскольку переменная BeforeBirthday является, на самом деле, указателем, то в ней находится не функция из DLL с аналогичным именем, а адрес этой функции в памяти. Чтобы посмотреть, указывает ли на что-то этот указатель , мы используем оператор получения адреса @ , указанный перед именем указателя:

Иначе функция недоступна, о чем мы и сообщаем пользователю, не обращаясь при этом к функции:

В принципе, в нашем небольшом проекте можно было бы обойтись и без всех этих многочисленных проверок, но я хотел показать, каким образом динамически подключаются DLL в реальных проектах. Это на первый взгляд весь этот код кажется сложным, а когда разберетесь и опробуйте его, он станет достаточно понятным. Под конец мы выгружаем DLL из памяти:

После этого, обращаться к её функциям уже будет нельзя. Код обращения к остальным функциям мы так подробно разбирать не будем, поскольку он аналогичен рассмотренному выше коду. Код события OnClick кнопки bArToRom следующий:

Читайте также: