2.4.12. Использование OLE (ActiveX компонентов)¶
2.4.12.1. Получить экземпляр OLE-объекта¶
В Pascal-скрипте, чтобы получить экземпляр ole-объекта (создать),
необходимо воспользоваться функцией CreateOleObject
. На вход принимается
строка - идентификатор сласса объета, зарегистрированный в системе,
например: ‘Word.Application’, или ‘OPOS.Scanner’. Возвращается методом
интерфейс IDispatch, который в скриптере сразу заворачивается в Variant
для хранения и дальнейших манипуляций. Обращение к методам и свойствам
ole-объекта реализуется синтаксисом совершенно аналогичным синтаксису
обращения к методам и свойствам обычного скриптового объекта.
2.4.12.2. Утилизация OLE-объекта¶
Время жизни объекта управляется счётчиком ссылок. Всё что требуется от разработчика - занулить все ссылки на полученный им объект, и полученные в процессе эксплуатации объекта ссылки на его составные части.
Пример:
oleobject := CreateOleObject('SomeRegisteredOLE.Factory');
nestedObject := oleobject.NestedObject;
nestedObject.DoSomthing;
oleobject := null;
nestedObject := null;
Если полученный объект не передавался в какое-либо постоянное хранилище, только использовался в операции, - зануление, в принципе, не обязательно, т.к. по окончанию выполнения операции все имевшиеся в выполнявшем её экземпляре скриптера значения всё равно будут занулены. И, соответственно, наоборот: если требуется распространить время жизни полученного ole-объекта за пределы времени выполнения операции - следует сохранить ссылку где-либо.
Например:
Selection.AddVar('MyOleObject', oleobject, ftVariant);
В этом случае время жизни ole-объекта, будет идентично времени жизни выборки, если значение MyOleObject не будет изменено раньше.
2.4.12.3. Обработка событий возбуждаемых OLE-объектом¶
2.4.12.3.1. Общее описание работы с обработчиками событий¶
В технологии OLE как подмножество технологии COM обработка событий реализована через обращение к переданному(подключенному) от владельца объекта интерфейсу. Интерфейс callback-ов (зачастую в один интерфейс включены несколько обработчиков - методов, которые вызывает ole-объект при наступлении соответствующего события, объединённых смысловой нагрузкой) в каждом отдельном случае реализации COM-объекта имеет чёткую однозначную спецификацию (описанную в документации объекта и, в случае ActiveX компонента, в TypeLibrary библиотеки) и идентификатор (GUID. Так же описан в документации или TypeLibrary).
COM-объект может специфицировать несколько разных callback-интерфейсов. К COM-объекту может быть подключено разом несколько callback-интерфесов, как разных, так и однотипных.
Для OLE-объектов интерфейсы обработчиков(callback-ов) специфицируются как наследник IDispatch - dispinterface. Dispinterface описывает какие методы и свойства содержатся в OLE-объекте, их индексы (OLE практикует индексное обращение к методам и свойствам), параметры и типы возвращаемых значений.
2.4.12.3.2. Реализация обработчика в скриптере¶
В скриптере имеется возможность подключить операцию выборки в качестве обработчика событий к OLE-объекту. Возможность реализована по средствам скриптового класса TbtkScriptComEvents, он реализует IDispatch и перенаправляет все вызовы в операцию выборки.
Свойства и методы TbtkScriptComEvents:
class function Create(EventsIID: String): TbtkStriptComEvents;
Создаёт новый экземпляр адаптера обработки событий: события генерируемые COM-объектом вызывают операцию выборки. Экземпляр представляющий заданный в параметре EventsIID класс COM-обработчика. EventsIID - GUID представленный строкой вида {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}. GUID dispinterface-а обработчика событий COM-объекта.
procedure Connect(ComObject: Variant; Operataion: TbtkScriptOperationObject);
Установить обработчик событий COM-объекту (подключить к COM-объекту). COM-объекту передаётся ссылка на IDispatch реализуемый данным экземпляром TbtkScriptComEvents, также ссылка остаётся на COM-объект у экземпляра TbtkScriptComEvents. Ссылка на COM-объект (после успешного подключения) доступна через свойство ComObject. Проверить успех подключения можно через свойство Connected. В последствии вызовы событий данного интерфейса COM-объектом будут перенаправляться в операцию выборки переданную как параметр Operation.
Параметры передаваемые в операцию-обработчик:
- Sender - COM(OLE)-объект вызвавший обработчик.
- DispId - индекс вызванного метода интерфейса-обработчика (dispinterface)
- Params - параметры переданные при вызове. VariantArray of Variant.
procedure Disconnect;
Отключить обработчик от COM-объекта. Убрать ссылку на себя из COM-объекта и занулить ссылку на COM-объект.
property Connected: Boolean;
Свойство сообщает о состоянии обработчика, True - подключен (успешно выполнен метод
Connect
).property ComObject: Variant;
COM-объект к которому подключен обработчик (для которого выполнен метод Connect, он же передаётся как Sender в операцию-обработчик).
Время жизни экземпляров TbtkScriptComEvents в скриптере управляется счётчиком ссылок.
// Создать новый экземпляр COM-объекта
device := OPOS_OpenNewDevice('Scanner', 'MyScanner1', True);
//Создать адаптер обработки событий COM-объекта
events := TbtkScriptComEvents.Create('{CCB90183-B81E-11D2-AB74-0040054C3719}');
//Подключить обработчик к COM-объекту
events.Connect(device, Selection.OperationByName('TestOp'));
if events.Connected then
//передать ссылку выборке, чтобы иметь возможность управлять им в последствии.
Selection.AddVar('scanner', events)
else
Raise('Невозможно установить обработчик. Объект не соответствует спецификации.');
2.4.12.3.2.1. Примечания:¶
- Экземпляр обработчика(TbtkScriptComEvents) будучи успешно
подключенным к COM(или OLE)-объекту требует обязательного
отключения (вызова
Disconnect
), в противном случае ни COM-объект, ни экземпляр обработчика никогда не будут уничтожены до самого завершения программы - они будут ссылаться друг на друга и их счётчики не занулятся.
2.4.12.4. Примеры работы с OLE¶
Библиотеки компонентов ActiveX (*.ocx) имею исчерпывающую информацию о способах взаимодействия с OLE-объектами реализованными в данной библиотеке - TypeLibrary - ресурс самой библиотеки (OCX). Microsoft Windows SDK имеет инструменты для чтения библиотеки типов - “OleView”. Его использование может стать серьёзным подспорьем при разработке методов взаимодействия с тем или иным ActiveX компонентом.
На корпоративном FTP есть локальная копия Windows SDK, пакет непосредственно содержащий “OleView”
2.4.12.4.1. Работа с компонентами OPOS (OLE POS)¶
Скачать дистрибутив ActiveX компонентов OPOS
Документация OPOS (В современности описывается стандартом UnifiedPOS)
Пример создания/инициализации OPOS-устройства
function OPOS_OpenNewDevice(DeviceClass: String; DeviceName: String; Exclusive: Boolean): Variant;
begin
device := CreateOleObject('OPOS.' + DeviceClass);
if device.Open(DeviceName) <> 0 then
Raise('OPOS.' + DeviceClass + '.Open error ' + IntToStr(device.OpenResult));
if Exclusive and (device.ClaimDevice(1000) <> 0) then
begin
Device.Close;
Raise('OPOS.' + DeviceClass + '.ClaimDevice error');
end;
device.DeviceEnabled := True;
if not device.DeviceEnabled then
begin
Device.ReleaseDevice;
Device.Close;
if not Exclusive then
Raise('device not Enabled. May Exclusive needed.')
else
Raise('device not Enabled');
end;
Result := device;
end;
device := OPOS_OpenNewDevice('Scanner', 'ScannerName', True);
Имена классов OPOS устройств можно наблюдать среди зарегистрированных в системе утилитой “OleView”
Имена устройств(и настройки) хранятся по пути реестра “HKEY_LOCAL_MACHINE\SOFTWARE\OLEForRetail\ServiceOPOS\”
ключ ServiceOPOS содержит классы зарегистрированных в системе устройств, такие как “Scanner”. ключ конкретного типа устройств содержит ключи с Именами зарегистрированных устройств В приведённом примере подключается устройство описанное в ключе “HKEY_LOCAL_MACHINE\SOFTWARE\OLEForRetail\ServiceOPOS\Scanner\ScannerName”
Пример завершения работы с OPOS-устройством
procedure CloseDevice(Device: Variant);
begin
Device.DeviceEnabled := False;
Device.ReleaseDevice;
Device.Close;
end;
Пример работы со сканером штрихкодов
<PASCAL ScannerDeviceName>
var events: TbtkScriptComEvents;
begin
Selection.ExecOpScript('CloseScanner');
device := OPOS_OpenNewDevice('Scanner', ScannerDeviceName, True);
events := TbtkScriptComEvents.Create('{CCB90183-B81E-11D2-AB74-0040054C3719}');
events.Connect(device, Selection.OperationByName('OnScanner'));
if not events.Connected then
begin
CloseDevice(device);
Raise('Сканер отверг обработчик событий');
end;
device.DecodeData := True;
device.DataEventEnabled := True;
if Selection.VarExists('StaticScannerVariableName') then
Selection.SetVar('StaticScannerVariableName', events)
else
Selection.AddVar('StaticScannerVariableName', events, ftVariant);
end;
</PASCAL>
<PASCAL>
var events: TbtkScriptComEvents;
begin
events := Selection.GetVar('StaticScannerVariableName')
if VarIsNull(events) then
Exit;
CloseDevice(events.ComObject);
events.Disconnect;
Selection.GetVar('StaticScannerVariableName', Null);
end;
</PASCAL>
begin
try
case ADispID of
1:
begin
ShowMessage('Данные сканирования:
'+ ScanDataType2String(ASender.ScanDataType)+'
'+ ASender.ScanDataLabel);
end;
finally
ASender.DataEventEnabled := True;
end;
end;
</PASCAL>
function ScanDataType2String(AType: Integer): String;
begin
case AType of
101: Result := 'Digits';
102: Result := 'Digits';
103: Result := 'EAN 8';
104: Result := 'EAN 13';
105: Result := 'Discrete 2 of 5) Digits';
106: Result := 'Interleaved 2 of 5) Digits';
107: Result := 'Digits, -, $, :, /, ., +; 4 start/stop characters (a, b, c, d)';
108: Result := 'Full ASCII feature';
109: Result := 'Same characters as Code 39';
110: Result := '128 data characters';
111: Result := 'UPC-A with supplemental barcode';
112: Result := 'UPC-E with supplemental barcode';
113: Result := 'UPC-D1';
114: Result := 'UPC-D2';
115: Result := 'UPC-D3';
116: Result := 'UPC-D4';
117: Result := 'UPC-D5';
118: Result := 'EAN 8 with supplemental barcode';
119: Result := 'EAN 13 with supplemental barcode';
120: Result := 'EAN 128';
121: Result := 'OCR "A"';
122: Result := 'OCR "B"';
131: Result := 'Reduced Space Symbology - 14 digit GTIN';
132: Result := 'RSS - 14 digit GTIN plus additional fields';
131: Result := 'GS1 DataBar Omnidirectional (normal or stacked)';
132: Result := 'GS1 DataBar Expanded (normal or stacked)';
133: Result := 'Interleaved 2 of 5 check digit verified and transmitted';
134: Result := 'GS1 DataBar Limited';
135: Result := 'Ames Code';
136: Result := 'Matrix 2 of 5';
137: Result := 'Code 39 with check character verified and transmitted';
138: Result := 'Code 39 with Mod 32 check character';
139: Result := 'Code 39 CIP';
140: Result := 'Tri-Optic Code 39';
141: Result := 'ISBT-128';
142: Result := 'Code 11';
143: Result := 'MSI Code';
144: Result := 'Plessey Code';
145: Result := 'Telepen';
151: Result := 'Composite Component A.';
152: Result := 'Composite Component B.';
153: Result := 'Composite Component C.';
154: Result := 'TLC-39';
201: Result := 'PDF 417';
202: Result := 'Maxicode';
203: Result := 'Data Matrix';
204: Result := 'QR Code';
205: Result := 'Micro QR Code';
206: Result := 'Aztec';
207: Result := 'Micro PDF 417';
208: Result := 'GS1 DataMatrix';
209: Result := 'GS1 QR Code';
210: Result := 'Code 49';
211: Result := 'Code 16K';
212: Result := 'Codablock A';
213: Result := 'Codablock F';
214: Result := 'Codablock 256';
215: Result := 'Han Xin Code';
end else
Result := 'Unknown(' + IntToStr(AType) + ')';
end;