Для программирования структуры подключаемого модуля требуется контейнер подключаемого модуля для управления работой каждой DLL и организации каждой разделенной подсистемы в файл библиотеки DLL. Для каждой программы DLL функции интерфейса должны быть зарезервированы для контейнера. Обычно функции интерфейса включают в себя: функции, которые начинают вызывать библиотеку DLL, и функции, которые закрывают библиотеку DLL. Через функцию интерфейса подключаемый контейнер может передавать параметры модулю DLL для обеспечения динамического управления. Я объясню конкретные детали реализации и приведу код ответа ниже.
Возможно, вам придется сначала понять структуру UNIT и структуру проектов в DELPHI. В этой статье не обсуждаются подробно теоретические детали программирования DLL, а лишь демонстрируются некоторые практические коды. Я изучал книгу Лю И «Углубленное программирование DELPHI».
Я также нахожусь на вводной стадии DELPHI. Я просто чувствую, что есть некоторые вещи, которые стоит обсудить в этой разработке DLL, поэтому я пишу эту статью и надеюсь, что вы дадите щедрые советы о том, что у меня не получается.
Введение в пример программы
Для облегчения чтения я буду использовать часть программного кода MIS-системы для демонстрации некоторых методов программирования плагинов. Пример программы представляет собой типичное приложение СУБД со структурой C/S. Наше внимание будет сосредоточено на управляющих операторах программы-фреймворка (далее именуемой Холлом) и управлении ответом подключаемой программы dll.
1. Структура программы
Плагин-контейнер Hall создается с использованием независимого проекта. Главное окно Hall эквивалентно форме MDI-контейнера в программе MDI. Интерфейсные функции в Dll будут явно вызываться в Hall.
Каждая программа-плагин использует свой собственный проект независимо. В отличие от обычных проектов, проект DLL создает мастер Dll, а файлы, созданные в результате соответствующей компиляции, имеют суффикс DLL.
=550) window.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
2. Дизайн интерфейса
В примере программы Narcissus мы зарезервировали две функции интерфейса:
ПоказатьDLLForm
Эта функция передает дескриптор приложения дочернему окну DLL, и программа DLL динамически создаст экземпляр формы DLL. Вы также можете передать некоторую бизнес-логику в подокно DLL в виде параметров, таких как имя формы, имя текущего вошедшего в систему пользователя и т. д. Используйте эту функцию для создания экземпляра формы DLL при первом вызове.
FreeDLLForm
Эта функция будет отображать освобождение экземпляра окна DLL и вызывать метод FreeDLLForm каждой формы DLL, чтобы освободить созданный экземпляр при выходе из приложения, в противном случае это приведет к ошибке чтения только для памяти. Аналогичным образом вы также можете передать некоторую бизнес-логику, которую необходимо выполнить при выпуске формы, в форму DLL в виде параметров.
3. Метод отладки
Программа формы DLL не может быть выполнена напрямую, и для ее вызова требуется подключаемый контейнер. Поэтому нам нужно сначала реализовать базовую программу Холла, а затем сохранить Hall.exe в фиксированном каталоге. Сделайте следующие настройки для каждого проекта DLL:
1) Откройте проект DLL.
2) Выберите меню «Параметры запуска».
3) Во всплывающем окне перейдите к нашему контейнеру Hall.exe.
Таким образом, программа Холла будет автоматически вызываться при отладке программы DLL, а программа DLL будет отлаживаться с использованием вызывающего интерфейса, зарезервированного в Холле.
Базовая реализация плагина программы
Способ проектирования DLL-программы мало чем отличается от обычного WINAPP, за исключением того, что все окна сохраняются в DLL-библиотеке как специальный «ресурс» и их нужно вызывать вручную, в отличие от WINAPP, где проекты создаются автоматически. Метод объявления интерфейсных функций очень прост.
1) Объявите функцию в разделе «Реализация» Unit
2) Добавьте знак stdcall в конце оператора объявления функции.
3) Перед оператором начала кода проекта (источник представления проекта) используйте оператор экспорта, чтобы объявить интерфейс функции.
Чтобы сделать код кратким, я лично предпочитаю добавлять модуль Unit (File New -- Unit) независимо в проект, а затем определять все тела функций, которые будут выводиться в этом модуле. Не забудьте также использовать метод . Единица формы, на которую имеется ссылка. Я назвал этот модуль UnitEntrance, инициализировал отображаемое окно в функции ShowDLLForm и вызвал метод Show для его отображения. HALL передаст имя вошедшего в систему пользователя в качестве параметров. После получения имени пользователя можно выполнить некоторый контроль разрешений. отражается в инициализации интерфейса Superior.
Код выглядит следующим образом
модуль UnitOfficeEntrance;
интерфейс
использует
Windows, сообщения, SysUtils, варианты, классы, графика, элементы управления, формы;
функция ShowDLLForm (AHandle: THandle; ACaption: строка; AUserID: строка): логическое значение; stdcall;
функция FreeDLLForm (AHandle: THandle; ACaption: строка; AUserID: строка): логическое значение; stdcall;
выполнение
использует UnitOfficialMainForm //Изменить единицу измерения MAINFORM;
вар
DLL_Form:TFormOfficialMain; //Изменить имя MAINFORM;
//----------------------------------------
//Имя: ШоуДЛЛФорм
//Func: плагин DLL вызывает функцию ввода
//Para: AHandle прикрепленный дескриптор программы ACaption этой формы;
//Rtrn: Н/Д
//Аутентификация: CST
//Дата: 3 июня 2005 г.
//----------------------------------------
функция ShowDLLForm (AHandle: THandle; ACaption: строка; AUserID: строка): логическое значение;
начинать
результат:=истина;
пытаться
Application.Handle:=AHandle //Привязывается к основному контейнеру программы;
DLL_Form:=TFormOfficialMain.Create(Application); //Изменяем ИМЯ MAINFORM;
пытаться
с DLL_Form сделать
начинать
Заголовок := ACaption;
StatusBar.Panels.Items[0].Text := AUserID;
//Настраиваем пользовательский интерфейс
Показывать ;
конец;
кроме
на e:исключение сделать
начинать
dll_form.Бесплатно;
конец;
конец;
кроме
результат: = ложь;
конец;
конец;
//----------------------------------------
//Имя: FreeDLLForm
//Func: плагин DLL вызывает функцию экспорта
//Параметр: AHandle прикрепленный дескриптор программы
//Rtrn: правда/ложь
//Аутентификация: CST
//Дата: 11 июня 2005 г.
//----------------------------------------
функция FreeDLLForm (AHandle: THandle; ACaption: строка; AUserID: строка): логическое значение;
начинать
Application.Handle:=AHandle //Привязывается к основному контейнеру программы;
if DLL_Form.Showing then DLL_Form.Close //Если окно открывается и закрывается первым, запуск FORM.CLOSEQUERY может отменить процесс закрытия;
если не DLL_Form.Показывает, то
начинать
DLL_Form.Бесплатно;
результат:=истина;
end //Все еще открыто, что указывает на CLOSEQUERY.CANCLOSE=FALSE
еще
начинать
результат: = ложь;
конец;
конец;
конец.
Код файла проекта DLL выглядит следующим образом:
Сотрудник библиотеки;
{ Важное примечание об управлении памятью DLL: ShareMem должен быть
первый модуль в предложении USES вашей библиотеки И в вашем проекте (выберите
Project-View Source) USES, если ваша DLL экспортирует какие-либо процедуры или
функции, которые передают строки в качестве параметров или результатов функции.
применяется ко всем строкам, передаваемым в вашу DLL и из нее, даже к тем, которые
ShareMem — это интерфейсный модуль, вложенный в записи и классы.
менеджер общей памяти BORLNDMM.DLL, который необходимо развернуть вместе с
с вашей DLL. Чтобы избежать использования BORLNDMM.DLL, передайте строковую информацию.
используя параметры PChar или ShortString }
использует
СисУтилс,
Классы,
UnitOfficialDetailForm в 'UnitOfficialDetailForm.pas' {FormOfficialDetail},
UnitOfficialMainForm в 'UnitOfficialMainForm.pas' {FormOfficialMain},
UnitOfficeEntrance в «UnitOfficeEntrance.pas»,
UnitOfficialClass в '../../Public/Library/UnitOfficialClass.pas',
UnitMyDataAdatper в '../../Public/Library/UnitMyDataAdatper.pas',
UnitMyHeaders в '../../Public/Library/UnitMyHeaders.pas';
{$R *.res}
экспортирует ShowDLLForm,FreeDLLForm //функция интерфейса;
начинать
конец.
Как только программа плагина вызывает окно DLL, экземпляр окна останется поверх окна HALL, поэтому нет необходимости беспокоиться о окклюзии.
Реализация контейнерных программ
1. Введение функций интерфейса
В библиотеке DLL есть два способа вызова функций: явный и неявный. Явный вызов более гибок, поэтому мы используем явный вызов. В Delphi вам необходимо объявить тип функции для функции интерфейса, а затем создать экземпляр типа функции. Экземпляр на самом деле является указателем на функцию. С помощью указателя мы можем получить доступ к функции, передать параметры и получить. возвращаемое значение. Добавьте объявление класса функции в раздел «Интерфейс» файла модуля:
тип
//Определяем тип функции интерфейса, функция интерфейса берется из интерфейса DLL
TShowDLLForm = Функция (AHandle:THandle; ACaption: String; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Функция (AHandle:THandle; ACaption: String; AUserID:string):boolean;stdcall;
Отображение вызова библиотечных функций требует следующих шагов:
1) Загрузите файл библиотеки DLL.
2) Получить адрес функции
3) Выполнить функцию
4) Выпускаем библиотеку DLL.
Далее мы обсудим эти шаги подробно.
2. Загрузите файл библиотеки DLL.
Библиотеку DLL можно загрузить в память, вызвав функцию API LoadLibrary. Здесь мы не будем обсуждать влияние DLL на управление памятью. Параметр LoadLibrary — это адресный путь к файлу DLL. Если загрузка прошла успешно, переменная типа CARDINAL будет возвращена в качестве дескриптора библиотеки DLL, если целевой файл не существует или другие причины вызывают загрузку DLL. файл потерпит неудачу, будет возвращен 0.
3. Создание экземпляров интерфейсных функций
Функция API для получения указателя на функцию интерфейса — GetProcAddress (дескриптор файла библиотеки, имя функции). Если функция найдена, указатель функции будет возвращен. В случае сбоя будет возвращен NIL.
Определите переменную-указатель функции, используя тип функции, определенный выше, а затем используйте оператор @, чтобы получить адрес функции, чтобы вы могли использовать переменную-указатель для доступа к функции. Основной код выглядит следующим образом:
…
вар
ShowDLLForm: TShowDLLForm; //Экземпляр функции интерфейса DLL;
FreeDLLForm: TFreeDLLForm;
начинать
пытаться
начинать
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
если ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID), то
Результат: = Истина
…
4. Конкретный метод реализации
Чтобы структурировано управлять плагинами и облегчить будущее расширение системы, мы можем объединить доступную информацию DLL, записанную в базе данных, а затем динамически обращаться к программе DLL, запрашивая записи базы данных.
1) Конструкция таблицы системных модулей
Для систем MIS вы можете использовать существующие условия DBS для создания таблицы системных модулей для записи файлов DLL и связанной информации, сопоставленной с системными модулями.
Тип роли имени поля
Индекс автоидентификатораINT
modAlias псевдоним модуля VARCHAR
modName имя модуля VARCHAR
modWndClass формирует уникальный идентификатор VARCHAR
Путь к модфайлу DLL VARCHAR
modMemo заметка ТЕКСТ
・Псевдонимы модулей используются для унификации правил именования на этапе проектирования программирования, особенно для справки членов команды во время командной разработки.
・Имя модуля будет передано в качестве параметра ACAPTION функции SHOWDLLFORM в качестве заголовка окна DLL.
・Уникальным идентификатором формы является ИМЯ КЛАССА главного окна в субмодуле DLL, который используется для определения окна, которым нужно управлять во время выполнения.
・Путь к DLL сохраняет имя файла DLL, которое будет преобразовано в программе в абсолютный путь.
2) Структура информационных данных плагина
Определение интерфейса данных, который записывает информацию, связанную с плагином, позволяет централизованно управлять плагинами DLL. Добавьте следующий код в раздел Интерфейс:
тип
//Определяем информационный класс плагина
TMyPlugins = класс
Caption:String; //заголовок формы DLL
DllFileName:String //путь к файлу DLL
WndClass:String; //Идентификация формы
Идентификатор пользователя: строка; // Имя пользователя;
ProcAddr:THandle; //Дескриптор библиотеки, загруженный LOADLIBRARY;
FuncAddr:Pointer; //Указатель функции SHOWDLLFORM
FuncFreeAddr:Pointer; //Указатель функции FREEDLLFORM
конец;
…
Создайте экземпляр TMyPlugins для каждого плагина. Методы инициализации этих экземпляров будут обсуждаться ниже.
3) Функция загрузки плагина
В этом примере окно DLL загружается и отображается в событии, которое запускает открытие дочернего окна в HALL. После срабатывания события кнопки сначала определите, загружена ли DLL в соответствии с экземпляром структуры плагина. Если она загружена, управляйте отображением или закрытием окна, если оно не загружено, обращайтесь к таблице данных и назначьте поля структуре плагина, а затем выполните загрузку. Получение указателя работает.
Частичный код выглядит следующим образом
…
//----------------------------------------
//Имя: OpenPlugin
//Функция: Процесс управления информацией плагина: Инициализация==》Установка разрешений==》Загрузка окна DLL
// Параметр: APlugin-TMyPlugins; псевдоним sAlias; значение разрешения iFuncValue;
//Rtrn: Н/Д
//Аутентификация: CST
//Дата: 2 июня 2005 г.
//----------------------------------------
процедура TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
вар hWndPlugin: HWnd;
начинать
//Определяем, загрузилось ли окно плагина hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //Окно плагина загружено
начинать
если не IsWindowVisible(hWndPlugin), то
начинать
AFromActn.Checked := True;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //Дисплей
конец
еще
начинать
AFromActn.checked := Ложь;
ShowWindow(hWndPlugin,SW_HIDE);
конец;
Exit; //Выходим из процесса создания плагина
конец;
//Инициализируем экземпляр класса плагина
если не InitializeMyPlugins(APlugin,sAlias), то
начинать
showmessage('Ошибка инициализации класса плагина.');
Выход;
конец;
//Получаем текущее значение разрешения
APlugin.UserID := sUserID;
//Загружаем окно DLL
если не LoadShowPluginForm(APlugin), то
начинать
showmessage('Ошибка загрузки плагина центра.');
Выход;
конец;
конец;
//----------------------------------------
//Имя: ИнициализироватьМоиПлагины
//Функция: инициализация экземпляра MYPLUGIN (Заголовок | DllFileName | IsLoaded)
//Параметр: APlugin-TMyPlugins
//Rtrn: Н/Д
//Аутентификация: CST
//Дата: 2 июня 2005 г.
//----------------------------------------
функция TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
вар
стрSQL: строка;
myDA:TMyDataAdapter;
начинать
Результат: = Ложь;
myDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
пытаться
myDA.RetriveData(strSQL);
кроме
на E:Исключение сделать
начинать
результат: = ложь;
myDA.Free;
Выход;
конец;
конец;
пытаться
начинать
с myDA.MyDataSet сделайте
начинать
если Not IsEmpty, то
начинать
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
результат:=Истина;
конец;
Закрывать;
конец; //конец с...делать...
конец; //конец попытки
кроме
на E:Исключение сделать
начинать
Результат: = Ложь;
myDA.Free;
Выход;
конец; //конец исключения
конец; //конец попытки...кроме
myDA.Free;
конец;
//----------------------------------------
//Имя: LoadShowPluginForm
//Функция: загрузка плагина DLL и отображение окна
//Параметр: APlugin-TMyPlugins
//Rtrn: true создано успешно
//Аутентификация: CST
//Дата: 2 июня 2005 г.
//----------------------------------------
функция TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):boolean;
вар
ShowDLLForm: TShowDLLForm; //Экземпляр функции интерфейса DLL;
FreeDLLForm: TFreeDLLForm;
sPath:string; //Полный путь к файлу DLL
начинать
пытаться
начинать
sPath:=ExtractFilepath(Application.ExeName)+ 'plugins/' + APlugin.DllFileName;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
если ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID), то
Результат: = Истина
еще
Результат: = Ложь;
конец;
кроме
на E:Исключение сделать
начинать
Результат: = Ложь;
ShowMessage('Ошибка загрузки подключаемого модуля, проверьте, заполнены ли файлы в каталоге PLUGINS.');
конец;
конец;
конец;
…
4) Управление окном DLL
Как показано в коде 3), открытие и закрытие окна DLL происходит только на уровне представления. Закрытие окна фактически не освобождает окно DLL. Оно просто вызывает функцию API FindWindow для получения дескриптора формы в соответствии с окном. идентификатор (то есть Form.name). Использование Параметр nCmdShow функции SHOWWINDOW управляет отображением/скрытием окна.
На самом деле это слабое место в реализации моей программы. Если в окне DLL использовать метод Self.close, это вызовет ошибку памяти. Решить ее невозможно из-за ограниченности возможностей, так что это так. последнее средство. Поэтому кнопка закрытия главного окна каждой DLL-программы должна быть скрыта. :-П
5) Выпуск библиотеки DLL.
При выходе из программы библиотеки DLL должны быть освобождены одна за другой в соответствии с экземпляром информации о плагине. Функция освобождения библиотеки DLL выглядит следующим образом:
процедура TFormHall.ClosePlugin(aPLG:TMyPlugins);
вар
FreeDLLForm:TFreeDLLForm;
начинать
если aPLG.ProcAddr = 0, то выходим;
если aPLG.FuncFreeAddr = ноль, то выходим;
@FreeDLLForm:=aPLG.FuncFreeAddr;
если не FreeDLLForm(Application.Handle,'',''), то
showMessage('ошибка');
конец;
краткое содержание
Результат работы этого примера программы следующий:
=550) window.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
Среди вышеперечисленных методов, поскольку существует множество нерешенных проблем из-за ограниченных возможностей, я применил некоторые методы сокрытия, которые кажутся неразумными. Я надеюсь, что каждый сможет найти лучшее решение, немного попробовав. Я также надеюсь, что А. отличный способ узнать больше.