17. Подключение внешних библиотек DLL

Пример подключения функции из библиотеки:

function MessageBox(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal): Integer; 
external 'MessageBoxA@user32.dll stdcall delayload';

До слова external идет описание функции: имя и состав параметров. Имена могут быть любыми допустимыми идентификаторами. После слова external в кавычках указывается название функции в файле DLL. После символа @ идет имя самого файла dll. Stdcall – это соглашение о вызове функции. Слово delayload необязательное, оно позволяет загружать DLL не сразу при запуске программы, а при первом вызове функции.

Файл DLL должен находиться в папке с программой или в системной папке «system32». Текст подключения может быть в любом модуле. Если функция подключается в пользовательском модуле или модуле Main, то вы можете использовать эту функцию и в других модулях.

Проблема

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

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

Причины

Дело в том, что Free Pascal для распределения динамической памяти использует не функции операционной системы, а свои собственные, что позволяет значительно ускорить процесс. Однако это приводит к тому, что данные о состоянии памяти, выделенных и свободных блоках и т.д. хранятся в глобальных переменных исполняемого модуля. Таким образом, пул памяти динамической библиотеки отделен от пула памяти вызывающего ее приложения. А длинные строки и динамические массивы как раз и используют (неявно) динамическую память.

Решение

При передаче данных между приложением и модулем dll используйте только простые типы данных: integer, widestring, Pchar, variant и т.д. Не используйте тип string для текстовых данных - это приводит к тродноулавимым ошибкам при освобождении памяти. Вместо String используйте Widestring или PChar. Откажитесь от динамических массивов, сложных составных типов данных (record) и любых объектов (TObject, TStringList и т.д.) их использование обязательно приведёт к ошибке.