FAQ - Как написать свой PLUGIN (типа поддержки различных форматов файлов ...)
http://z-ol.chat.ru/
Типовая задача - разрабатывается некая задача и при этом
- Некоторые ее компоненты могут не инсталлироваться баз ущерба для работоспособности
- Некоторые компоненты предполагается изготавливать впоследствии и рассылать пользователям
- Некоторые компоненты могут разрабатываться другими программистами и распространяться независимо от программы
- .....
Классические примеры - фильтры для совместимости по форматам файлов с другими программами, некоторые расширения и дополнительные возможности. Примеры и моей практики - приведу парочку
- Программа управления программатором ПЗУ. Заранее неизвестно, с каким железом она будет работать и как им управлять. Необходимо было дать возможнось разработчику железа написать для него поддержку
- Программа печати отчетов. Она должна печатать в любой кодировке на любой принтере, в т.ч. и экзотическом типа АЦПУ. Заранее неизвестно, какие принтеры будуп применяться совместно с ней и как ими управлять (известно только одно - драйверов под них нет и не будет) - переделывать программу под каждый принтер - неинтересно ...
Итак, все это можно реализовать в DLL, однако обычное ее подключение приведет к тому, что при запуске программа будет искать все подключенне к ней DLL и в случае отсутствия хотя-бы одной откажется запускаться. Это не приемлемо, но к счастю есть возможность и весьма удобный набор сервисных функций для динамической загрузки, использования и выгрузки DLL.
Пример (приложение имеет одно окно, на нем кнопка):
UNIT UNIT1; INTERFACE USES WINDOWS, MESSAGES, SYSUTILS, CLASSES, GRAPHICS, CONTROLS, FORMS, DIALOGS, STDCTRLS; TYPE TFORM1 = CLASS(TFORM) BUTTON1: TBUTTON; PROCEDURE BUTTON1CLICK(SENDER: TOBJECT); PROCEDURE FORMCREATE(SENDER: TOBJECT); PRIVATE PUBLIC END; // Тип "процедура". Естественно, можно определит типы // "функция" или "функция с параметрами" ... TDLLPROC = PROCEDURE; VAR FORM1: TFORM1; DLLPROCPTR : TDLLPROC; LIBINSTANCE : HMODULE; // Логический номер модуля DLL IMPLEMENTATION {$R *.DFM} PROCEDURE TFORM1.BUTTON1CLICK(SENDER: TOBJECT); BEGIN // Проверим, загружена ли DLL IF LIBINSTANCE=0 THEN BEGIN // Не загружена, попробуем загрузить LIBINSTANCE := LOADLIBRARY('PLUG_IN.DLL'); // Проверим, успешна ли загрузка (LIBINSTANCE=0 - неуспешно) IF LIBINSTANCE=0 THEN BEGIN SHOWMESSAGE('Ошибка загрузки библиотеки PLUG_IN.DLL'); EXIT; END; // Ищем функцию по ее имени (имя должно точно совпадать) DLLPROCPTR := TDLLPROC(GETPROCADDRESS(LIBINSTANCE,'MYPROC')); // Проверим, нашли ли (если нашли, то ASSIGNED вернет TRUE) IF NOT ASSIGNED(DLLPROCPTR) THEN BEGIN // Не нашли - выгружаем DLL из памяти FREELIBRARY(LIBINSTANCE); LIBINSTANCE:=0; SHOWMESSAGE('Ошибка: функция MYPROC не найдена'); EXIT; END; // Непосредственно вызов функции DLLPROCPTR; // Выгрузка библиотеки FREELIBRARY(LIBINSTANCE); LIBINSTANCE:=0; END; END; PROCEDURE TFORM1.FORMCREATE(SENDER: TOBJECT); BEGIN DLLPROCPTR:=NIL; LIBINSTANCE:=0; END; END.
Естественно, в реальной задаче имеет смысл создать свой класс, который при инициализации будет загружать библиотеку, а при уничтожении - выгружать. Кроме того, он должен иметь функцию типа "Перезагрузить библиотеку", которая будет выгружать текущую и загружать новую. DLL - обычная, естественно может иметь неограниченное количество процедур и функций.
Особенности:
- Пока библиотека загружена, ее файл нельзя ни удалить, ни переименовать. Поэтому при возникновении ошибок следует выгружать библиотеку, иначе пользователь не сможет ее заменит (без перезагрузки ПК).
- Обычно имеет смысл создать ряд функции типа GETINFO, GETAUTOR, GETCOPYRIGHT ..., чтобы вызывающая программа могла получить информацию о назначении данной DLL
- Расширение DLL не является обязательным, поэтому можно применять свои расширения (например DRV)