Снаружи подключаемый модуль выглядит как несколько пунктов в меню клиентского модуля. Начиная с версии 10, эти пункты меню могут быть добавлены в контекстное меню и на панели инструментов.
Внутри подключаемый модуль — это DLL, реализующая специальные функции. Есть два основных вида интерфейсов для взаимодействия подключаемого модуля с клиентом Лоцман: PAS-интерфейс и COM-интерфейс. Описание PAS-интерфейса отсутствует в официальной документации, не рекомендовано к использованию и создает множество проблем при использовании новых версий Delphi, поэтому мы будем писать подключаемый модуль с COM-интерфейсом. Описание его вы найдете в папке
SDK
дистрибутива в файле LoodsmanClientApi.chm
.
DLL подключаемого модуля должна экспортировать функции
InitUserDLLCom
, PgiCheckMenuItemCom
и функции, которые будут вызываться при выборе пользователем пунктов меню. Вот прототипы этих функций:// Используется ЛОЦМАН Клиент для построения меню, // которое поддерживает модуль function InitUserDLLCom(Value: Pointer): Integer; stdcall; // Используется ЛОЦМАН Клиент для определения // активности команд меню модуля function PgiCheckMenuItemCom(stFunction: PAnsiChar; PluginCall: IPluginCall): Integer; stdcall; // Функция, соответствующая команде меню procedure MenuClick(PluginCall: IPluginCall); stdcall;
Модуль может быть подключен к одной или нескольким базам данных, либо являться общим (с версии 10), то есть использоваться при работе с любой из баз данных. Информация о подключенных модулях хранится либо в реестре в ветке
HKCU\Software\ASCON\Loodsman\Client\PluginManager
(с версии 10), либо для старых версий она хранится в файле Loodsman.ini
.При открытии окна базы данных, ЛОЦМАН Клиент получает информацию о подключенных для нее модулях из реестра или INI-файла. После чего подключаемый модуль загружается и из него вызывается функция получения меню —
InitUserDLLCom
. Функция PgiCheckMenuItemCom
вызывается, когда ЛОЦМАН Клиент понадобится проверить доступность меню. Нужно отметить, что для пунктов меню, вынесенных на панели инструментов, PgiCheckMenuItemCom
может вызываться очень часто, и если в ней будет происходить обращение к серверу приложений, то это может существенно замедлить работу клиента. В старых версиях клиента DLL загружалась и выгружалась постоянно для вызова любой из функций. Так как DLL подключаемых модулей могут быть достаточно большими и иметь множество зависимостей, то постоянная их загрузка и выгрузка может отнимать заметное количество времени. В связи с тем, что в новых версиях ЛОЦМАН Клиент появилась возможность помещать кнопки вызова подключаемого модуля на панели инструментов, то вызываться функции подключаемого модуля стали чаще, и, чтобы ускорить работу, начиная с версии 10, подключаемый модуль по умолчанию выгружается только при закрытии клиента. Для целей отладки в версии 10 есть возможность вернуть режим, когда подключаемые модули постоянно выгружаются после вызова их функций. Для этого необходимо установить параметр системного реестра (типа
DWORD
) HKCU\Software\ASCON\Loodsman\Client\PluginManager\DebugMode = 1
.После загрузки подключаемого модуля клиент дважды вызывает функцию
InitUserDLLCom
. В первый раз, передавая в качестве Value
значение nil
, клиент запрашивает количество пунктов меню, которые хочет добавить подключаемый модуль. Затем клиент выделяет память для нужного количества пунктов меню и вызывает InitUserDLLCom
во второй раз, передавая ей в качестве Value
указатель на выделенную память.Каждый добавляемый пункт меню описывается следующей структурой данных:
const MAX_TEXT_LENGTH = 255; type TLoodsmanAddMenuCom = record stMenu: array [0..MAX_TEXT_LENGTH-1] of AnsiChar; stFunction: array [0..MAX_TEXT_LENGTH-1] of AnsiChar; end; PLoodsmanAddMenuCom = ^TLoodsmanAddMenuCom;
Где
stFunction
— это имя экспортируемой функции из DLL, которая будет вызвана, когда пользователь выберет данный пункт меню. А stMenu
— это названия пунктов меню, разделенные символом '#'
. Название первого пункта меню указывает, как меню подключаемого модуля будет расположено относительно собственного меню клиента. Возможные значения перечислены ниже:Меню клиента | Перед | Дочернее | После |
---|---|---|---|
Файл | BEFORE_MI_FILE | MI_FILE | AFTER_MI_FILE |
Вид | BEFORE_MI_VIEW | MI_VIEW | AFTER_MI_VIEW |
Правка | BEFORE_MI_EDIT | MI_EDIT | AFTER_MI_EDIT |
Инструменты | BEFORE_MI_TOOLS | MI_TOOLS | AFTER_MI_TOOLS |
Окно | BEFORE_MI_ZOOM | MI_ZOOM | AFTER_MI_ZOOM |
Справка | BEFORE_MI_HELP | MI_HELP | AFTER_MI_HELP |
Например, подключаемый модуль добавляет два пункта меню перед меню «Инструменты»:
'BEFORE_MI_TOOLS#Мои плагины#Тестовый#Список проектов'
и'BEFORE_MI_TOOLS#Мои плагины#Тестовый#Состав'
.Подключаемый модуль добавляет два пункта меню после меню «Инструменты»:
'AFTER_MI_TOOLS#Мои плагины#Тестовый#Список проектов'
и'AFTER_MI_TOOLS#Мои плагины#Тестовый#Состав'
.Подключаемый модуль добавляет два пункта меню внутрь меню «Инструменты»:
'MI_TOOLS#Мои плагины#Тестовый#Список проектов'
и'MI_TOOLS#Мои плагины#Тестовый#Состав'
.Пример реализации функции
InitUserDLLCom
:function InitUserDLLCom(AValue: Pointer): Integer; var LMenuItem: PLoodsmanAddMenuCom; begin if AValue <> nil then begin LMenuItem := AValue; StrLCopy(LMenuItem.stMenu, 'BEFORE_MI_TOOLS#LoodsmanPlugIn#Список проектов', SizeOf(LMenuItem.stMenu) - 1); StrLCopy(LMenuItem.stFunction, 'ProjectList', SizeOf(LMenuItem.stFunction) - 1); Inc(LMenuItem); StrLCopy(LMenuItem.stMenu, 'BEFORE_MI_TOOLS#LoodsmanPlugIn#Список', SizeOf(LMenuItem.stMenu) - 1); StrLCopy(LMenuItem.stFunction, 'LinkedFast', SizeOf(LMenuItem.stFunction) - 1); Inc(LMenuItem); end; Result := 2; end;
При необходимости, для пункта меню можно задать значок (как у пункта меню «Состав» на рисунке выше). Для этого в DLL нужно добавить ресурс типа
RT_BITMAP
, с именем как у экспортируемой функции, указанной в stFunction
.Проще всего это сделать, создав файл
PluginIcon.rc
и добавив его к проекту.
Например:
LinkedFast BITMAP Images\LinkedFast.bmp
Для определения доступности пунктов меню подключаемого модуля в зависимости от выбранного объекта, клиент будет вызывать функцию
PgiCheckMenuItemCom
, экспортируемую клиентским модулем. В PgiCheckMenuItemCom
передается имя функции для пункта меню (stFunction
) и интерфейс IPluginCall
, с помощью которого можно получить информацию о текущей базе данных и о выбранном объекте. На основании полученной информации подключаемый модуль должен принять решение, может он выполнить запрошенную функцию или нет, и вернуть соответствующий результат.
Пример реализации функции
PgiCheckMenuItemCom
:
function PgiCheckMenuItemCom(AFunction: PAnsiChar; APluginCall: IPluginCall): Boolean; var LFuncName: String; begin Result := False; try LFuncName := String(AFunction); if LFuncName = 'ProjectList' then begin Result := True; end; if LFuncName = 'LinkedFast' then begin Result := APluginCall.stType <> 'Документ'; end; except on E: Exception do begin Application.ShowException(E); end; end; end;
Если пункт меню доступен и пользователь выберет его, то клиент попытается найти среди экспортируемых из DLL функцию, указанную для данного пункта меню (в
stFunction
), и вызвать ее. В функцию будет передан интерфейс IPluginCall
, с информацией о текущей базе данных и о выбранном объекте.
Пример реализации функции:
procedure LinkedFast(APluginCall: IPluginCall); begin InitializeApplication(APluginCall.AppHandle); try ShowLinkedObjectsDialog(Application, APluginCall); except on E: Exception do begin Application.ShowException(E); end; end; FinalizeApplication; end;
Обратите внимание, что функции, вызываемые из клиента, не должны выбрасывать за свои пределы исключения — в каждой такой функции должен быть блок
try/except
. Дело в том, что формат объекта исключений (Exception
) изменялся в разных версия Delphi (например, в Delphi 2009 в нем произошли серьезные изменения связанные с поддержкой юникода и вложенных исключений).
Так же нельзя без дополнительных действий использовать
Application.Handle
, передаваемый из клиента в подключаемый модуль. Это опять же связано с тем, что в разных версиях Delphi объекты, передаваемые с помощью сообщений, будут отличаться.
Описанные выше проблемы решены в предлагаемом примере подключаемого модуля. Код предназначен для Delphi версии 7 и выше. Работоспособность примера проверялась в ЛОЦМАН Клиент версий 8.5, 10 и 11.
Исходный код примера подключаемого модуля.
Текущая версия кода на GitHub — github.com/achechulin/loodsman.
О том, как подключить модуль к клиенту написано в справке (файл
Loodsman.chm
), в разделе «Инструменты → Параметры → Подключаемые модули» или просто «Подключаемые модули», в зависимости от версии клиента.
Продолжение статьи: Подключаемые модули в новых версиях ЛОЦМАН Клиент.
Комментариев нет:
Отправить комментарий