COM-объекты в Visual FoxPro
« Назад COM-объект создается с использование модели многокомпонентных объектов. VFP, с одной стороны, имеет средства доступа к COM-объектам, созданным другими приложениями, а с другой, может строить COM-объекты, доступные другим приложениям. COM-объекты имеют такую же организацию, что и рассмотренные выше объекты VFP-классов. То есть обладают свойствами, методами и событиями. Доступ к методам, событиям и свойствам COM-объекта осуществляется через его интерфейс, реализуемый как группа взаимосвязанных функций. В общем случае объект может поддерживать несколько интерфейсов. Приложение, осуществляющее доступ к COM-объектам, называется клиентом COM-объекта. Приложение, создавшее COM-объект и употребляемое для его редактирования, называется сервером COM-объекта. В качестве экземпляра COM-объекта могут выступать ActiveX-элемент управления, COM-компонент или OLEBound-объект (поле типа General). Ссылку на COM-объект возвращают функции CREATEOBJECT( ), CREATEOBJECTEX( ) и NEWOBJECT( ). Пример. Открывается лист Microsoft Excel. public x set oleobject on x = NewObject('Excel.Sheet') x.Application.Visible = .T. Функция CREATEOBJECTEX(cCLSID | cPROGID, cComputerName [, cIID])создает экземпляр зарегистрированного COM-объекта на удаленном компьютере. Возвращает ссылку на созданный экземпляр. Тип результата – Object. Параметры: cCLSID | cPROGID – идентификатор класса (CLSID) или программный идентификатор (PROGID) COM-объекта. Если задается CLSID, то COM-объект должен быть зарегистрирован на удаленном сервере, заданном параметром cComputerName. Если – PROGID, то COM-объект должен быть зарегистрирован как на локальном компьютере пользователя, так и на удаленном. Попытка использовать PROGID без регистрации сервера на локальном компьютере приведет к генерации OLE-ошибки "Invalid Class String" с кодом 0x800401f3. Для VFP серверов Автоматизации, созданных на локальном компьютере, можно использовать свойства CLSID и PROGID для задания локальных CLSID и PROGID значений. cComputerName – имя удаленного компьютера, на котором создается экземпляр COM-объекта. Если cComputerName – это пустая строка, то экземпляр COM-объекта создается на локальном компьютере. Параметр cComputerName поддерживает UNC-имена (universal naming convention), такие, как "\\myserver" и "myserver," и DNS-имена (domain system names). cIID – задает GUID (глобальный уникальный идентификатор) интерфейса CLSID или PROGID, когда создается рано связываемый экземпляр класса. Если в качестве cIID передается пустая строка, то VFP пытается получить заданный по умолчанию интерфейс CLSID или PROGID. Пример: public x x = CreateObjectEx("Excel.Application", "", ; "{000208D5-0000-0000-C000-000000000046}") && Следующий вызов использует заданный по умолчанию интерфейс Excel.Application && x = CreateObjectEx("Excel.Application", "", "") && && Открываем Microsoft Excel x.Visible = .T. && или: && x.Application.Visible = .T. Функция CREATEOBJECTEX( ) не может быть использована вместо функции CREATEOBJECT( ). CREATEOBJECTEX( ) поддерживает раннее связывание (на этапе компиляции) через cIID, что помогает улучшить производительность за счет отказа от многочисленных IDispatch-вызовов. Функция GETINTERFACE(oObject [, cIID | cInterface [, cTypelib | cProgID]])обеспечивает посредством раннего связывания доступ к свойствам, методам и событиям COM-объекта. Возвращает ссылку на интерфейс COM-объекта. Параметры: oObject – ссылка на COM-объект. cIID – задает GUID интерфейса объекта oObject. Параметр cIID может быть таким интерфейсом, как "IContextState" или GUID, например "{94631BEC-EE81-479A-AE64-A6CFC37B4799}". Если параметр – это "IDispatch", то функция GETINTERFACE( ) возвращает ссылку на поздно связываемый (на этапе исполнения) интерфейс IDispatch. Если параметр cIID не задан, то функция вернет рано связываемый интерфейс объекта. cInterface – имя интерфейса. cTypelib – имя библиотеки типа, содержащей класс объекта oObject. cProgID – имя программы для поиска библиотеки типа. Функция GETINTERFACE( ) не может быть использована для объектов VFP. Если DLL строится на платформе Windows 9x (Windows95, Windows98 и Windows ME), то библиотека типа не включается в DLL. Когда GETINTERFACE( ) ссылается на DLL, созданную на одной из этих платформ, то в качестве третьего параметра следует использовать имя библиотеки типа, а не имя DLL, например: oX = GetInterface(x, "IMyClass", "MyClass1.tlb") Если DLL построена на платформе Windows XP, Windows 2000 или Windows NT, то допустим следующий вызов: oX = GetInterface(x, "IMyClass", "MyClass1.DLL") Пример. Пример, демонстрирующий, как можно использовать VFP COM-сервер для управления транзакциями COM+ приложения. COM-сервер, содержащий этот код, должен быть добавлен в COM+ приложение до его вызова клиентом. local oMTX, oContext, oContextState local lTxnState, lGetTxnState, lDone, lGetDone lGetDone = .F. && Начальные установки lGetTxnState = 0 && oMTX = CreateObject("MTXAS.APPSERVER.1") oContext = oMTX.GetObjectContext( ) oContextState = GetInterface(oContext, "IContextState") && && Установки активизации: && .T. – сделать неактивным, .F. – оставить активным lDone = .T. oContextState.SetDeactivateOnReturn(lDone) oContextState.GetDeactivateOnReturn(@lGetDone) … && Установки транзакции: && 0 – выполнить, 1 – прервать lTxnState = 1 oContextState.SetMyTransactionVote(lTxnState) oContextState.GetMyTransactionVote(@lGetTxnState) Функция GETOBJECT(cFileName | Moniker [, cClassName])возвращает ссылку на объект Автоматизации. Тип возвращаемого значения – Object. cFileName – полное имя активизируемого файла. Приложение указывать не нужно, поскольку динамическая библиотека OLE находит приложение по имени указанного файла. Пример. Переменной mSineVar присваивается ссылка на объект Sine.xls. mSineVar = GetObject('d:\Sine.xls') XLApp = mSineVar.Application XLApp.WorkBooks.Open('d:\Sine.xls') XLApp.Visible = .T. Moniker – идентификатор COM-объекта, имеющего IMoniker-интерфейс. cClassName – имя класса, которому принадлежит получаемый объект. Задается, когда приложение сохраняет в одном файле несколько разнотипных объектов. Если имя файла или класса указано неверно, то генерируется OLE-ошибка и функция возвращает пустую строку. Пример 1. Состав массива someArray отображается в Microsoft Excel. declare someArray(7, 4) && Протяженности массива someArray по первому и второму измерениям m = Alen(someArray, 1) n = Alen(someArray, 2) && Инициализация массива someArray someArray(1, 1) = "" for j = 2 to n someArray(1, j) = "C" + Transform(j - 1) next for i = 2 to m someArray(i, 1) = "X" + Transform(i) for j = 2 to n someArray(i, j) = i + j next next && Массив someArray:
&& && Ссылка на Excel.Application XLApp = GetObject("", "Excel.Application") && Делаем объект видимым, добавляем рабочую книгу, && получаем ссылку на активный лист Excel XLApp.Visible = .T. XLApp.WorkBooks.Add( ) XLSheet = XLApp.ActiveSheet && Переносим данные массива в ячейки листа Excel && Результат приведен на рис. 13.7 for i = 1 to m for j = 1 to n XLSheet.Cells(i, j).Value = someArray(i, j) next next Рис. 13.7. Массив someArray в Microsoft Excel Пример 2. По данным массива someArray строится диаграмма Microsoft Excel. public oXLChart1 declare someArray(7, 4) && Протяженности массива someArray по первому и второму измерениям m = Alen(someArray, 1) n = Alen(someArray, 2) && Код инициализации массива someArray см. в примере 1 … && && Получаем ссылку на объект Excel.Chart oXLChart1 = GetObject("", "Excel.Chart") XLApp = oXLChart1.Application XLApp.Visible = .T. XLSheet = XLApp.WorkSheets(1) && Передаем данные массива someArray первому листу Microsoft Excel && Результат приведен на рис. 13.8 for i = 1 to m for j = 1 to n XLSheet.Cells(i, j).Value = someArray(i, j) next next Рис. 13.8. Диаграмма Microsoft Excel по данным массива someArray Пример 3. В Microsoft Word передается строка текста. public oWDdoc oWDdoc = GetObject("", "Word.Basic") && Или: oWDdoc = NewObject("Word.Basic") oWDdoc.AppShow oWDdoc.FileNewDefault oWDdoc.Insert('Строка из приложения VFP') Функция COMCLASSINFO(oObject [, nInfoType])возвращает регистрационную информацию о COM-объекте. Тип результата – Character. Функция COMCLASSINFO( ) вернет пустую строку, если регистрационная информация для указанного объекта недоступна. Параметры: oObject – ссылка на COM- или OLE-объект; может быть также ссылкой и на объект, созданный на основе класса VFP, например класса Form. nInfoType – состав возвращаемой информации. Принимает приведенные в табл. 13.6 значения. Таблица 13.6 Значения параметра nInfoType
Пример: mSineVar = GetObject("d:\Sine.xls") ? ComClassInfo(mSineVar, 1) && Напечатает: Excel.Sheet.8 ? ComClassInfo(mSineVar, 2) && Напечатает: Excel.Sheet ? ComClassInfo(mSineVar, 3) && Напечатает: Лист Microsoft Excel ? ComClassInfo(mSineVar, 4) && Напечатает: {00020820-…-000000000046} ? ComClassInfo(mSineVar, 5) && Напечатает: 3 Замечания:
x = CreateObject("MSComCtl2.MonthView.2") то при nInfoType = 5 COMCLASSINFO( ) вернет 3 (COM-компонент). Функция COMPROP(oCOMObject, cProperty [, eValue])устанавливает и возвращает значения свойств UTF8 и PUTREF COM-объекта. Если параметр eValue опущен, то функция вернет текущее значение свойства cProperty. Параметры: oCOMObject – ссылка на COM-объект. Используется полное имя, например oForm.OLEControl1. cProperty – имя свойства COM-объекта. Может принимать приведенные в табл. 13.7 значения. Таблица 13.7 Значения cProperty
eValue – задает вариант поведения cProperty. Принимает следующие значения:
Пример: ComProp(oForm.OLEControl1, 'PUTREF', 1) Функция COMARRAY(oObject [, nNewValue])задает способ передачи массива COM-объекту. Возвращает, вызванная без параметров, текущую COMARRAY-установку. Тип результата – Numeric. Параметры: oObject – ссылка на COM-объект. nNewValue – число, определяющее способ передачи массива COM-объекту. Принимает приведенные в табл. 13.8 значения. Таблица 13.8 Значения параметра nNewValue
Замечание. Индекс первого элемента 0-массива равен 0; индекс первого элемента 1-массива равен 1. Значения 100 и 1000 добавляются к значению 0, 1, 10 или 11. Функция COMARRAY( ) применяется, только когда массив передается COM-объекту с использованием следующего синтаксиса: oComObject.Method(@MyArray) Когда передается Byte-массив (VT_UI1), то VFP преобразовывает его в строку, если только к nNewValue не добавлено число 1000. Если клиент передает Byte-массив по ссылке VFP COM-серверу, то в VFP необходимо добавить к nNewValue значение 1000. Это можно выполнить в Init-событии VFP COM-сервера, например так: ComArray(This, 1000) Если префикс @ опущен, то COM-объекту будет передан только первый элемент массива; установка COMARRAY( ) не действует. Пример. Массив передается COM-объекту с nNewValue = 11 + 100. Поэтому попытка изменить его размер методом RedimensionArray класса ArrayHandler приводит к ошибке. Перед запуском примера, для проекта, содержащего определение класса define class ArrayHandler as Custom OLEPUBLIC procedure RedimensionArray(aArray, nRows) dimension aArray[nRows] endproc endDefine строится DLL-файл T1.dll: build dll T1.dll from d:\Projects\ArrayHandler При таком вызове команды BUILD DLL файл T1.dll запишется в директорию, из которой стартует VFP, возвращаемую функцией HOME(0). Код программы, запускаемой из VFP: local loSvr, laTest loSvr = NewObject("t1.ArrayHandler") dimension laTest[10] laTest = 3 && Инициализация массива laTest ? ComArray(loSvr, 11 + 100) && COM-сервер вернет ошибку (рис. 13.9), поскольку выполняется попытка изменить && размер массива laTest, переданного с параметром nNewValue = 11 + 100 loSvr.RedimensionArray(@laTest, 4) ? Alen(laTest) Рис. 13.9. Ошибка, полученная от COM-сервера Ошибки не возникнет, если в качестве второго параметра взять число 10: loSvr.RedimensionArray(@laTest, 10) Функция COMRETURNERROR(cExceptionSource, cExceptionText)заполняет структуру COM-исключения информацией, которую клиент Автоматизации может использовать для определения источника ошибки Автоматизации. Параметры: cExceptionSource – текст, обозначающий источник ошибки. cExceptionText – текст для описания исключения. При выполнении функции COMRETURNERROR( ) указанный текст помещается в структуру COM-исключения; работа текущего метода сервера Автоматизации прекращается; процесс управления передается клиенту. Текст, помещенный в структуру COM-исключения отобразит функция AERROR( ). Функция EVENTHANDLER(oCOMObject, oVFPObject [, lUnbind])связывает события COM-объекта с методами объекта VFP; состав методов и их интерфейс определяется составом и интерфейсом событий COM-объекта. При наличии связи вместо события COM-объекта будет выполняться соответствующий метод VFP-объекта. Функция вернет .T., если связь установлена, или .F. – в противном случае. Параметры: oCOMObject – ссылка на COM-объект. oVFPObject – ссылка на объект VFP. Класс VFP, экземпляром которого объект является, должен содержать соответствующим образом оформленную опцию IMPLEMENTS и методы с интерфейсами, повторяющими интерфейсы событий COM-объекта. lUnbind – если употребляется со значением .T., то функция разрывает связь событий COM-объекта с методами объекта VFP. В общем случае один COM-объект можно связать с несколькими объектами VFP и несколько COM-объектов VFP могут быть связаны с одним объектом VFP. Связь событий автоматически прекращается, когда COM- или VFP-объект освобождается. Пример. Связываются события COM-объекта oRS класса RecordSet библиотеки классов ADODB. Библиотека расположена в файле c:\Program Files\Common Files\System\Ado\msAdo15.dll, представляющем Microsoft ActiveX Data Objects 2.7 Library. Библиотека классов ADODB имеет приведенные на рис. 13.10 классы; на том же рисунке приведены и события класса RecordSet. Рис. 13.10. Классы библиотеки ADODB и события класса RecordSet Библиотека позволяет управлять данными удаленной базы данных. В примере создается соединение с источником данных BookSaleDataBase, имеющим в частности таблицу Authors, второе поле которой содержит фамилию и имя авторов (подробно база данных BookSale.mdb описана в разд. 16.3.2). Выполняется запрос к таблице Authors. С результатом запроса ассоциируется объект oRS. Затем события объекта oRS связываются с методами объекта oEvents – экземпляра определенного в программе класса MyClass. При изменении значения поля Authors в первой записи результата запроса выполняются соответствующие обработчики событий объекта oEvents и выводятся сообщения, генерируемые методом SayProgAndDateTime – имя обработчика и дата и время возникновения события (все обработчики событий обращаются к этому методу). После изменения значения поля связь событий объектов разрывается. Осуществляется переход к следующей записи (oRS.MoveNext) и вновь изменяется значение поля Authors. Однако поскольку связь обработчиков событий разорвана, никаких сообщений не выводится: обработчики событий объекта oEvents не выполняются. local oEvents local oRS as adodb.RecordSet local oConn as adodb.Connection oEvents = NewObject("MyClass") oConn = NewObject("adodb.Connection") && Устанавливаем соединение с источником данных oConn.ConnectionString = "DSN=BookSaleDataBase" oConn.Open && Выполняем запрос к таблице Authors oRS = oConn.Execute("select * from Authors") && Устанавливаем связь между обработчиками событий объектов oRS и oEvents EventHandler(oRS, oEvents) && Выводимые обработчиками сообщения см. после текста программы ? Upper(oRS.Fields(1).Value) && Разрываем связь между обработчиками событий объектов oRS и oEvents && Вывод сообщений прекращается EventHandler(oRS, oEvents, .T.) oRS.MoveNext ? Upper(oRS.Fields(1).Value) oConn.Close && Определение класса MyClass define class MyClass as Session OLEPUBLIC implements RecordsetEvents in "adodb.RecordSet" && && Необходимо описать методы, соответствующие обработчикам && всех событий класса RecordSet && procedure RecordSetEvents_WillChangeField(cFields as Number @, ; Fields as Variant @, adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_FieldChangeComplete(cFields as Number @, ; Fields as Variant @, pError as Variant @, adStatus as Variant @, ; pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_WillChangeRecord(adReason as Variant @, ; cRecords as Number @, adStatus as Variant @, ; pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_RecordChangeComplete(adReason as Variant @, ; cRecords as Number @, pError as Variant @, adStatus as Variant @, ; pRecordset as Variant @) AS Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_WillChangeRecordSet(adReason as Variant @, ; adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_RecordSetChangeComplete(adReason as Variant @, ; pError as Variant @, adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_WillMove(adReason as Variant @, ; adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_MoveComplete(adReason as Variant @, ; pError as Variant @, adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_EndOfRecordset(fMoreData as Logical @, ; adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_FetchProgress(Progress as Number @, ; MaxProgress as Number @, adStatus as Variant @, ; pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && procedure RecordSetEvents_FetchComplete(pError as Variant @, ; adStatus as Variant @, pRecordset as Variant @) as Variant This.SayProgAndDateTime(Program( )) endProc && && Функция, вызываемая всеми обработчиками событий класса function SayProgAndDateTime(ProgName) ? ProgName + ' ' + Transform(DateTime( )) endFunc endDefine Результат:
Замечание. При перетаскивании интерфейса из браузера COM-объекта (рис. 13.11) в программу VFP генерируется следующий код (приводится его фрагмент): x=NEWOBJECT("MyClass") DEFINE CLASS MyClass AS session OLEPUBLIC IMPLEMENTS RecordsetEvents IN ; "C:\PROGRAM FILES\COMMON FILES\SYSTEM\ADO\MSADO15.DLL" PROCEDURE RecordsetEvents_WillChangeField(cFields AS Number, ; Fields AS VARIANT, adStatus AS VARIANT @, ; pRecordset AS VARIANT) AS VOID * add user code here ENDPROC … ENDDEFINE Рис. 13.11. Интерфейс RecordsetEvents можно перетащить в программу VFP Команда APPEND GENERAL GeneralFieldName [FROM FileName]
|