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

Когда компилятор создает исполняемый файл, исходные файлы сначала преобразуются в объектные файлы. Затем объектные файлы линкуются, для создания исполняемого файла. Динамическая библиотека так же, как статическая библиотека содержит файлы объектов. Но динамическая библиотека также как исполняемый файл загружается при запуске исполняемого файла.

Библиотека является динамической (или общей) потому, что код библиотеки загружается  из исполняемого файла во время выполнения, более того, библиотека может быть загружена несколькими исполняемыми файлами, несмотря на то, что используется одна копия динамической библиотеки.

Когда библиотека создана, мы можем использовать ее код, как если бы мы скомпилировали его в нашу программу.

Пример динамической библиотеки
Использование динамической библиотеки в Windows
Использование динамической библиотеки в Linux
Исполняемые файлы, которые экспортируют символы
Загрузка динамических библиотек динамически

Пример динамической библиотеки


Ниже приводится простой пример создания динамической (общей) библиотеки с помощью этих трех файлов:

  • mylib.bas - исходный файл для библиотеки
  • mylib.bi - заголовок для библиотеки
  • mytest.bas - тестовая программа

Наша библиотека будет иметь один модуль, с одной функцией:

'' mylib.bas
'' компиляция: fbc -dll mylib.bas

'' Сложение двух чисел и возвращение результата
Public Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer Export
  Return( x + y )
End Function


Компиляция библиотеки:
fbc -dll mylib.bas

Опция -dll говорит компилятору о том, что надо взять исходный код mylib.bas, и превратить его в объектный файл mylib.o, а затем сохранить файл объекта в файл динамической библиотеки. Имя библиотеки будет содержать расширение .so или .dll в зависимости от платформы, на которой библиотека собирается (Linux или Windows соответственно). Библиотека может содержать множество модулей (исходных файлов), каждый модуль может иметь множество функций, но для этого простого примера, мы использовали один модуль.

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

Чтобы использовать библиотеку в каком-то другом исходном коде, мы должны каким-то образом сообщить компилятору, что именно в библиотеке. Хороший способ сделать это: положить декларации (также называемые интерфейс или API) для библиотеки в файл заголовка.

'' mylib.bi
#inclib "mylib"
Declare Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer


Нет необходимости писать это в заголовке. Мы можем использовать данную запись и в исходном коде нашей программы. Заголовок же поможет избежать лишнего кода, если данную библиотеку нужно будет включить и для других исходных файлов. Заявление #inclib сообщит компилятору имя динамической библиотеки, которую надо будет вызвать из нашего исполняемого файла во время выполнения программы.

С нашей библиотекой (.dll/.so файлом) и заголовком (.bi файлом) мы можем попробовать протестировать нашу программу:

'' mytest.bas
'' компиляция: fbc mytest.bas
#include once "mylib.bi"
Print Add2(1,2)


Оператор #include сообщает компилятору включить исходный код mylib.bi так, как если бы мы ввели его содержимое в исходном источнике. То есть все что мы в нем написали (запись о библиотеке) будет передана компилятору.

Компиляция:
fbc mytest.bas

Затем, когда мы запускаем наш исполняемый файл mytest, мы должны получить результат:
3

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

Динамические библиотеки могут по желанию содержать отладочную информацию, указанную с опцией -g командной строки.

Объектные файлы, и библиотеки, являются платформозависимыми, а в некоторых случаях конкретизируются для определенной версии компилятора и FreeBASIC RinTime.

Использование динамической библиотеки в Windows


В Windows динамические библиотеки должны находится либо в глобальных каталогах, либо рядом с исполняемым файлом, который ее использует .

Операционная система может найти следующие каталоги , в которых можно положить динамическую библиотеку:

  • Каталог, из которого был загружен исполняемый файл.
  • Текущий каталог.
  • Папка Windows и системная папка Windows.
  • Каталоги, указанные с помощью PATH в переменных средах.

Порядок, в котором производится поиск каталогов может зависеть от используемой версии Windows и ее настроек.

Использование динамической библиотеки в Linux


По умолчанию Linux обычно не ищет в текущем каталоге или каталоге, из которого исполняемый файл был загружен. Вам нужно будет выполнить одно из двух действий:

  • Скопировать .so файл в каталог с динамическими библиотеками (т.е. /usr/lib) и запустить ldconfig для настройки библиотеки.
  • Изменить переменную окружения LD_LIBRARY_PATH для поиска текущей папки или конкретного каталога для вновь созданной динамической библиотеки.

Чтобы запустить исполняемый файл ./mytest/ и временно сказать Linux искать библиотеку в текущем каталоге, используйте следующую команду в командной строке:
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./mytest

Исполняемые файлы, которые экспортируют символы


Если исполняемый файл имеет символы, которые должны быть доступны для других динамических библиотек, когда эти динамические библиотеки загружаются, используйте декларацию процедур со спецификатором Export, и параметр командной строки -export при создании (линковании) исполняемого файла.

Опция -export не имеет дополнительного эффекта при использовании параметров командной строки с -dylib или -dll

Загрузка динамических библиотек динамически


Динамически подключаемые библиотеки могут быть загружены и использованы во время выполнения путем динамической загрузки библиотеки и ее символов во время выполнения.

  • DyLibLoad может использоваться для загрузки и получения дескриптора динамической библиотеки.
  • DyLibSymbol используется для получения адреса символа в загруженной динамической библиотеке.
  • DyLibFree используется, чтобы выгрузить динамическую библиотеку, когда она больше не нужна.

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

'' mydll.bas
'' компилировать как: fbc -dll mydll.bas
'' Это создаст mydll.dll (и libmydll.dll.a библиотеку импорта) на Windows,
'' и libmydll.so на Linux.
''
'' Примечание: libmydll.dll.a представляет собой библиотеку импорта , 
'' которая нужна только при создании исполняемого файла 
'' Не нужно включать ее в конечный продукт,
'' она бесполезна для конечных пользователей.

'' Простая экспортируемая функция; Запись <alias "..."> отключает искажение имени,
'' которое в FB по умолчанию в верхнем регистре; если не использовать alias ,
''
то вместо красивого названия AddNumbers() будет ADDNUMBERS()

Function AddNumbers Alias "AddNumbers"( ByVal a As Integer, ByVal b As Integer ) As Integer Export
    Function = a + b
End Function


'' load.bas: Загрузка mydll.dll (или libmydll.so) во время выполнения, вызывает из mydll
'' одну функцию и печатает результат. mydll не требуется во время компиляции.
'' компилировать как: fbc test.bas
''
'' Примечание: Ожидается, что скомпилированная библиотека mydll.dll (или libmydll.so)
'' будет доступна в текущем каталоге.

'' Обратите внимание, что мы указываем просто «mydll» в качестве имени файла библиотеки;
'' Это необходимо для обеспечения совместимости между Windows и Linux, поскольку в разных
'' системах динамические библиотеки имеют разное имя файла и расширение.
Dim As Any Ptr library = DyLibLoad( "mydll" )
If( library = 0 ) Then
    Print "Failed to load the mydll dynamic library, aborting program..."
    End 1
End If

'' Этот указатель на функцию , он будет использоваться для вызова функции из mydll, после
'' того, как был найден адрес. Примечание: Он должен иметь ту же конвенцию вызова и те же параметры.
Dim AddNumbers As Function( ByVal As Integer, ByVal As Integer ) As Integer
AddNumbers = DyLibSymbol( library, "AddNumbers" )
If( AddNumbers = 0 ) Then
    Print "Could not retrieve the AddNumbers() function's address from the mydll library, aborting program..."
    End 1
End If

Randomize Timer

Dim As Integer x = Rnd * 10
Dim As Integer y = Rnd * 10

Print x; " +"; y; " ="; AddNumbers( x, y )

'' Закончили работать с библиотекой; ОС автоматически выгружает библиотеки,
'' загруженные процессом когда процесс завершается, но мы можем также сами
'' выгрузить библиотеку во время выполнения программы , тем самым освобождая
'' ненужные более ресурсы. Это как раз делается в следующей строке.
'' Помните, как только вы выгрузите ранее загруженную библиотеку, все символы
 ''
полученные с помощью dylibsymbol становятся недействительными
'' и доступ к ним приведет к сбою приложения.

DyLibFree( library )


См. также