Создание статических и динамических библиотек

Давайте для начала разберем понятия. Любая из библиотек представляет собой функцию или несколько функций с различными дополняющими их данными.

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

Динамическая библиотека представляет собой тоже хранилище функции(ий), но используется библиотека уже после компилирования вашего проекта. Или если проще, она подключается дополнительно к вашему исполняемому модулю во время его работы. Пример динамических библиотек в системной папке windows/system32. Можете взглянуть на любую объемную по размеру игру. Ее составляющие в большинстве своем ресурсы (картинки, музыка) и динамические библиотеки.

Лишь редкие разработчики создают объемные по размеру исполняемые модули, это не экономично по отношению к ресурсам компьютера. Правильней иметь небольшой исполняемый модуль, который в нужное время подключает динамическую библиотеку,  вызывает нужные функции и освобождает (отключает) библиотеку. Еще одним преимуществом динамической библиотеки является возможность вызывать ее несколькими программами. К примеру ваша программа вызвала ее, а другой тоже понадобилась эта же библиотека. Тогда другая программа просто получает начальный адрес этой библиотеки и использует ее так же как ваша программа без ограничений, не создавая отдельную копию функций в памяти. То есть динамическая для всех программ общая.

Любая из библиотек имеет хотя бы одну функцию и может иметь дополнительно вынесенные за тело функций объявления структур, переменных, массивов и своих типов данных. Пример простой библиотеки из одной функции:

Function UIppOO Alias "UIppOO" () As Integer Export
    Return 25
End Function


Как видите, это обычная функция, разница лишь в том, чтобы ее вызывать из других модулей, надо добавлять в объявлении функции ключевую команду export. Если вы не будете добавлять эту команду, то функция будет видна только внутри файла библиотеки и из другого модуля ее уже не вызвать. Это тоже используется, когда у экспортируемой функции есть необходимость иметь вспомогательную внутреннюю функцию, которая не должна быть видима для других модулей. Ключевая команда Alias задает внутреннее имя функции, которое и указывается после нее. Она имеет значение для программ, которые вызывают модуль DLL динамически (ниже об этом написано). Если не использовать эту команду, то все имена функций будут в верхнем регистре. В итоге вместо красивого названия GetModuleHandle, придется при вызове функции писать GETMODULEHANDLE, что как видите не очень красиво, но на производительность это не влияет.  По сути можно было записать и так:

Function UIppOO() As Integer Export
    Return 25
End Function


Вы можете скомпилировать этот или выше код как любую из библиотек. Я скомпилировал динамическую библиотеку под именем d.dll и статическую d.a
Не обращайте внимания на то, что файл статической библиотеки на диске создается как libd.a вместо d.a это нормально.

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

Declare Function UIppOO Lib "d.dll" Alias "UIppOO"() As Integer

Или если создавалась dll без команды Alias:

Declare Function UIppOO Lib "d.dll"() As Integer

а далее вызывать как обычно, пример:

Declare Function UIppOO Lib "d.dll"() As Integer
Print UIppOO
Sleep


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

Второй способ:

Dim Memdll As Any Ptr
Dim func As Function() As Integer
Memdll=DylibLoad("d.dll")'получаем хендл библиотеки
If Memdll=0 Then End
func=DylibSymbol(Memdll, "UIppOO")'получаем указатель на функцию
If func=0 Then End
Print func()
Sleep
Dylibfree Memdll ' освобождаем DLL


Сначала мы получаем хендл загруженной библиотеки с помощью функции DyLibLoad , у которой один параметр: имя нашего файла DLL.
Этот хендл мы присваиваем нашей переменной-указателю.
Далее во избежании проблем с использованием вашей программы, идет проверка на результат работы функции, которая возвратит нуль, если файл DLL не будет найден. В таком случае программа просто завершит свою работу.
Далее мы получаем указатель на функцию с помощью функции DyLibSymbol, у которой два параметра указатель на хендл библиотеки и внутреннее имя функции. Если Alias не использовался, то тут надо писать имя заглавными буквами. Далее опять проверка на результат работы функции. Если данную функцию не удалось найти, то выход из программы. Далее можно вызывать функцию столько раз, сколько требуется. И освобождается(отключается) библиотека с помощью функции DyLibFree , у которой только один параметр: указатель на хендл библиотеки.

А теперь давайте подключим файл DLL из папки windows\system32 и вызовем одну из функций. Пример ничем практически не будет отличаться, разве что количеством параметров у функции. Для начала откроем отличный инструмент ApiViewer. Если у вас его нет, можете взять в разделе Скачать. Открыли справочник, находим поиском функцию Beep:

ApiViewer

В ней вы видите что функция находится в файле Kernel32.dll.  Далее смотрим сколько параметров у функции, и какого они типа. Как видите, у функции два параметра с типом Long . Тип Long  эквивалентен типу Integer в FreeBasic. Более подробно о том, что делает каждый параметр, нужно смотреть в справочнике по API функциям. Если используете мою сборку, то она есть в ней. Вам нужно нажать в меню Справка->Win32. Если у вас нет справки, вы можете взять ее в разделе Скачать на этом сайте.  Первый параметр очевидно частота звука в герцах, второй длительность звучания в миллисекундах. Мы узнали все, что нам нужно. Теперь напишем пример, который будет воспроизводить звук частотой 3000 гц и длительностью 1 сек :

Dim Memdll As  Any Ptr
Dim func As Function(As Integer,As Integer) As Integer
Memdll=DylibLoad("C:\Windows\System32\kernel32.dll")
If Memdll=0 Then End
func=DylibSymbol(Memdll, "Beep")
If func=0 Then End
func(3000,1000)
Dylibfree Memdll


Для того, что закрепить ваши знания, я советую вам самим вызвать функцию MessageBox. Но все же кое что хочу уточнить, чтобы у вас не возникло проблем:
Внимательно смотрите какое внутреннее имя функции MessageBox, оно указано в ApiViewer в кавычках после Alias.

Я надеюсь у вас не возникло проблем и вы без труда вызвали функцию MessageBox. А теперь давайте учиться вызывать статическую библиотеку.

Для вызова функции из статической библиотеки, нужно сначала эту библиотеку подключить с помощью команды #inclib. После этой команды пишется только имя без расширения статической библиотеки. Затем нужно продекларировать функцию(и) обычным образом, только без команды EXPORT:

#INCLIB "d"
 Declare Function UIppOO() As Integer
Print UIppOO()
Sleep

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

 

При использовании файла .bi , записи о вызове и декларации функций:

#INCLIB "d"
Declare Function UIppOO() As Integer

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

#INCLUDE "d.bi"
Print UIppOO()
Sleep

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

Type qq
   a As integer=33
End Type

Dim Shared b As qq

Function hh() As Integer
     Return b.a+17
 End Function
Function UIppOO() As Integer Export
    Return hh()
End Function


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

 Всего доброго!

содержание | назад | вперед