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)
