Теперь вы имеете в системе Visual FoxPro возможность использовать широкие функциональные преимущества технологии COM по реализации внешних Интерфейсов и Связыванию и обработке как внутренних, так и внешних Событий. В младших версиях Visual FoxPro тоже поддерживались простейшие процессы Привязки событий Серверов COM, однако реализовывалась только Клиентская часть Позднего Связывания. Теперь система Visual FoxPro выполняет поддержку Клиентской части Раннего Связывания. В данном параграфе обсуждаются вопросы внутренних механизмов Раннего и Позднего Связывания для обоих частей технологии COM (Серверной и Клиентской); рассматриваются вопросы производительности и эффективности объектов COM, характеристики их прозрачности и функциональной наполненности.

Введение

Технология COM разработана для обеспечения взаимодействия различных Приложений, рассматриваемых как отдельные независимые Объекты, которые могут обращаться к другим Объектам для выполнения требуемых функций. Связывание взаимодействующих Объектов может принимать самые различные формы. Простейшей схемой взаимодействия может являться: Обращение одного Объекта - Клиента к другому, внешнему Объекту - Серверу. Примерами более сложных схем взаимодействия, чем указанная выше схема Клиент-Сервер, могут яаляться схемы взаимодействия типа Peer-To-Peer, где взаимодействие осуществляется при прямом обращении Объекта к другому Объекту.

Если взаимодействующие Объекты не имеют предварительной Информации друг о друге, то необходим механизм надежного функционального и информационного Взаимодействия, объект должен знать, как ему обращаться к другому Объекту. Интерфейс Обработки Событий (Event Interfaces) является характерным примером для получения необходимой информации Вызывающего Объекта (Клиента) о статусе текущего взаимодействия с другим Объектом (Приложением). Подобные схемы Взаимодействия Объектов предполагают, что если Событие не обработано в Сервере, оно обрабатывается на Клиенте. Стандартные Контролы Microsoft ActiveX являются примерами объектов COM, имеющие достаточно развитые Интерфейсы взаимодействия с вешними Клиентами. Указанные Интерфейсы (как самого Контрола, так и Хоста (Host)) поддерживают доступ к изменяемым характеристикам Контрола, как внутри самого Контрола, так и на стороне Клиента, обращающегося к Объекту. На стороне Клиента поддерживаются Механизмы управления Событиями Контрола (связывания событий Контрола). Комбинация перечисленных функциональных преимуществ предоставляет Разработчику Мощный инструментарий разработки пользовательских Приложений.

В начале данного параграфа рассматривается самый простейший пример Сервера Microsoft Visual FoxPro COM и все его основные функциональные особенности и преимущества. Далее рассматриваются Библиотеки Типов, которые описывают процессы декларации компонент COM в окружающей среде, рассматриваются механизмы обнаружения и обработки Исключительных ситуаций (Ошибок). Наконец, обсуждаются вопросы производительности и реализации рассматриываемых Интерфейсов взаимодействия внешних Объектов.

С настоящего момента рассматриваются другие масштабы схем взаимодействия Объектов, как они обмениваются информацией друг с другом. В версии Visual FoxPro 6.0 выполнялась поддержка Раннего и Позднего Связывания для внешних Объектов (Клиентов), которые обращаются для обслуживания к Серверу Visual FoxPro 6.0; однако, для Сервера Visual FoxPro 6.0 поддерживалась только Позднее Связывание. Начиная с текущей версии (9.0) также выполняется поддержка Раннего Связывания при обращении к Серверу Visual FoxPro.

Создание простейших Серверов COM в системе Visual FoxPro

Далее представлен пример простейшего Сервера, программный код оформлен в виде модуля MYCLASS.PRG:

  CopyCode imageКопировать Код
*This entirely self-contained program will build a COM server 
* called "myserver.myclass"
* It will unregister a prior instance, if any
IF PROGRAM() != "MYCLASS"
?"this file MUST BE NAMED 'myclass.prg'"
return
ENDIF
IF FILE("myclass.dll")
DECLARE integer DllUnregisterServer IN myclass.dll
DllUnregisterServer()
CLEAR DLLS
ENDIF
BUILD PROJECT myserver FROM myclass
BUILD DLL myserver from myserver recomp
*now test this COM server:
ox = CreateObject("myserver.myclass") && create the server object
ox.mydocmd("USE home(1)+'samples\data\customer'") && use a table
?ox.myeval("RECCOUNT()") && get the record count

DEFINE CLASS myclass AS session OLEPUBLIC
PROCEDURE MyDoCmd(cCmd as String) as Variant ;
helpstring "Execute a VFP cmd"
&cCmd && just execute parm as if it were a fox command
FUNCTION MyEval(cExpr as String) ;
helpstring "Evaluate a VFP expression"
RETURN &cExpr && evaluate parm as if it were a fox expr
FUNCTION Error(nError, cMethod, nLine)
COMreturnerror(cMethod+' err#='+str(nError,5)+;
' line='+str(nline,6)+' '+message(),_VFP.ServerName)
&& this line is never executed
ENDDEFINE

Представленная программа описывает структуру Сервера COM и не нарушает целостность Реестра ОС. Рекомендуется выполнять данный модуль только во время построения Сервера. При Сборке (компиляции) Проекта сервера COM выполняется автоматическая регистрация полученной Компоненты в Реестре ОС. Повторное выполнение Сборки автоматически очищает предудущую Регистрацию. Тем не менее, флаги о необходимости перерегистрации Компоненты хранятся в Проекте (соответствующий файл .PJX). Если рассматриваемый Проект будет удален и построен заново, то при первой Сборке "старая" информация не будет обновлена. Требуется обратить на это особое внимание.

Далее. Вы построили ваш первый Сервер COM. При выполнении процесса Сборки Сервера в системе Visual FoxPro создается Библиотека Типов (Type Library); осуществляется Регистрация нового Объекта COM, включая разделы Реестра с именами ProgId, Type Library, Местоположения Сервера (File Location). В процессе Сборки Сервера создается файл с именем myserver.vbr, в котором отображаются изменяемые Секции Реестра при Регистрации собираемого Сервера.

Обратите внимание, как используется стандартный базовый класс SESSION, который появился только в версии Visual FoxPro 6.0 SP3. Указанный класс является невизуальным и достаточно простым и несложным; он имеет ключевое Свойство DataSession, которое обеспечивает работу при нескольких Сеансах Взаимодействия. Если построение Сервера COM осуществляется на базовом классе FORM, который также имеет рассмотренное выше свойство DataSession, но класс формы имеет и много других Свойств, которые являются несовместимыми с рассматриваемой технологией COM. К тому же, все незащищенные и открытые Свойства базового Класса прописываются в Библиотеке Типов, поэтому могут быть доступны внешнему Объекту, что может повлиять на безопасность и целостность создаваемого Сервера в процессе его функционарования.

Библиотеки Типов

Библиотека Типов представляет собой некоторый программный файл, который может быть либо автономным, либо внутренним ресурсом соответствующего файла Сервера COM: .EXE / .DLL. Рассматриваемый файл представляет из себя независимую от языка программирования Декларацию механизмов Интерфейса, описания Свойств и Методов создаваемого объекта COM. Данный файл может содержать вспомогательные строки Комментариев (краткой информации, Помощи), идентификаторы подсистемы Help (IDs), имена передаваемых / принимаемых параметров, полные названия Членов Объекта (Свойств и Методов). Если данный файл библиотеки типов не включен как внутренний ресурс исполняемых файлов EXE / DLL, то обычно он имеет расширение (тип) = .TLB / .OLB.

Начиная с версии Visual FoxPro 6.0 создаваемая Библиотека Типов содержит имена передаваемых параметров и соответствующих Методов, имеющих дополнительную характеристику OLE Public. Если при создании рассматриваемого Класса в свойстве Description приводится краткое назначение используемой Компоненты, которое хранится в соответствующем файле .VCX, то данная информация помещается в соответствующие строки комментариев (помощи) Библиотеки Типов (Type Library).

Вы можете просмотреть содержание Type Library при помощи соответствующего Инструментария. Например, для систем: Microsoft Excel, Microsoft Word - используется компонента Object Browser, для Visual FoxPro - Class Browser, OLE Viewer - для Visual C++. С помощью перечисленных инструментов вы можете просмотреть всю Объектную Модель для промышленных Серверных Приложений, которые могут иметь достаточно внушительные размеры.

Когда в текущий момент выполняется процесс Просмотра компоненты Библиотеки Типов (Type Library),  данный Сервер COM не может быть перестроен до тех пор, пока не закроется соответствующая Type Library. Аналогично, вы не можете создать новый экземпляр Клиента Объекта, пока не будет создана его Копия, или сервер не будет пересобран заново. Для решения перечисленных Конфликтов рекомендуется создавать Копию зарегистрированного Сервера DLL / EXE (1 копия - для разработки, 1 копия - для использования ).

Чтение Библиотек Типов

Обычный инструментарий чтения Библиотек Типов (TLBINF32.DLL) входит в стандартную поставку системы Microsoft Visual Studio. Данный инструмент применим для множества программных продуктов, в том числе и для Серверов COM. Далее приводится пример программного кода, иллюстрирующий создание простого инструмента чтения составляющих элементов Библиотеки Типов:

  CopyCode imageКопировать Код
clear
PUBLIC otlb
otli=NEWOBJECT('tli.tliapplication')
otlb=otli.TypeLibInfoFromFile("myserver.dll")
*otlb=otli.TypeLibInfoFromFile("tlbinf32.dll")
*otlb=otli.TypeLibInfoFromFile("c:\program files\microsoft office\office\excel9.olb")
?"CoClasses:"
FOR each oCoClass in otlb.CoClasses
?" ",oCoClass.name
*now each interface associated with this CoClass
for each oInterface in oCoClass.Interfaces
?" ",oInterface.name
endfor
endfor

?
?"Interfaces"

FOR each oInterface in otlb.Interfaces
?" ",oInterface.name
ENDFOR
?
?"Interface Members for 1st interface"
FOR each oMember IN otlb.Interfaces(1).Members
?" ", oMember.name
FOR each oParm in oMember.Parameters
?" ",oParm.name
ENDFOR
ENDFOR

Сначала создается экземпляр Объекта инструментария TLB, предназначенного для чтения Библиотеки Типов, смотри Метод TypeLibInfoFromFile. Составяющие компоненты Библиотеки типов представлены в виде разнообразных Коллекций, которые могут бать достаточно просто обработаны при помощи языковой конструкции Visual FoxPro: команда FOR EACH.

Коллекция Interface представляет собой множество интерфейсных элементов (Свойств), опубликованных в Библиотеке Типов. Каждый из представленных Интерфейсов должен поддерживаться либо самим Сервером, оказывающим Услуги, либо  интерфейс реализуется (создается) самим Клиентом, который обращается к Серверу; например, обычно Клиент реализует функционирование интерфейсов Events.

В коллекции CoClass обычно публикуются объекты COM, которые обычно создаются Клиентом. По-Умолчанию, реализация интерфейса CoClass представлена совместно с дополнительным интерфейсом Event Source interface.

Прочие Интерфейсы, отличные от стандартных: CoClass и Event, могут быть также опубликованы в Библиотеке Типов. Клиент может получить доступ к таким Интерфейсам посредством вызова специализированных Методов Объекта COM. Например, внутренний интерфейс ICell может быть получен как возвращаемое значение при вызове Метода GetCell (Сноска: при вызове метода ВзатьКлетку - возвращается индекс Ячейки).

Константы Объекта COM также могут быть опубликованы в Библиотеке Типов. Например, для получения значений некоторых Констант, к примеру xlMaximized, из соответствующей Библиотеке Типов Excel Type Library, загрузите Коллекцию otlb.Constants.

Для подробного изучения возможностей инструмента TLBINF32.DLL попытайтесь самостоятельно загрузить вашу Библиотеку Типов, которую вы сами хорошо знаете и сделайте сопоставления; изучите представленные Коллекции Свойств, Методов и Параметров, их корректные наименования.

Эффективность, Производительность, Быстродействие

Производительность Программных Компонент имеет важное значение для многих Разработчиков. В контексте программного обеспечения и Объектов COM, повышение производительности означает получение быстрых Результатов. Технология COM - это моделирование быстрого и надежного взаимодействия различного программного обеспечения друг с другом. Поэтому, тщательный Дизайн COM - прямой путь к повышению производительности всего комплекса программных средств, участвующих в получении конечных результатов.

Например, рассмортрим следующий Программный Код:

  CopyCode imageКопировать Код
ox=CreateObject("excel.application")
start = seconds()
ox.workbooks.add
SET EXCLUSIVE OFF
USE HOME()+'samples\data\customer'
ox.visible=1
FOR i= 1 TO RECCOUNT()
FOR J = 1 TO FCOUNT()
ox.Activesheet.cells(i,j).value = EVAL(FIELD(j))
NEXT
SKIP
NEXT
ox.Workbooks(1).Close(0) && close workbook, discarding changes
ox=0 && release Excel
?seconds() - start

В примере демонстрируется заполение Рабочего Листа Excel значениями из Таблицы VFP. Данный модуль выполняется в течение 30 секунд, обрабатывается 92 Записи Таблицы (Сноска: скорость относительно Рабочей Станции Автора).

Для повышения производительности представленного Процесса, вы должны точно знать как выполняются составные части данного программного модуля, выявить возможные "узкие" места, где есть возможности оптимизации и повышения производительности.

Отображение рассматриваемой Рабочей Книги Excel задерживается до тех пор, пока она не будет заполнена всеми Значениями. Оптимизация системы Excel нам недоступна, да и не требуется, отображение происходит почти мгновенно.

Имеется стандартная рекомендация при выполнении строки Кода, где происходит вычисление значения Поля и загрузка его всоответствующую Ячейку Активного Листа Excel. Вы можете исключить лишние вызовы Сервера COM, заменив указанную строку на следующую:

  CopyCode imageКопировать Код
 oa= EVAL(FIELD(j))

Данная корректировка представленного программного Кода позволяет снизить затраты времени на выполнение двойного цикла до Одной секунды. Это показывает, что остальные 29 секунд тратились для вычисления конструкции ox.Activesheet.cells (а это работа внешнего COM Сервера (Excel)).

Анализируем данную строку Кода далее, Visual FoxPro пытается идентифиуцировать требуемый элемент ox.Activesheet, а результат записать в некоторую временную переменную VFP. Далее, выполняется уточнение косвенной ссылки на требуемую Ячейку, в которую требуется записать вычисленное Значение. Каждая косвенная ссылка (".") в результирующем выражении порождает создание временных переменных VFP, переписывание значений из одного в другое. При следующем проходе Цикле - вычисление и переопределение временных переменных повторяется.

Каждая косвенная ссылка (".") порождает прямое обращение к соответствующему методу Сервера COM (Excel), который выполняет возврат требуемого локального Объекта. Рекомендуется следующий подход: сначала выполняем вычисление Активного Листа IApplication.ActiveSheet, который сохраняем во временной переменной Visual FoxPro. Далее, вычисляем Коллекцию Ячеек Активного Листа (от временной переменной). Наконец, идентификация требуемой Ячейки осуществляется при помощи индексов (I,J), которые передаются как параметры. После получения ссылки на конкретную Ячейку, заносим в нее требуемое значение, (следующий вызов инструментария Сервера COM). Всего получаем 4 (четыре ) вызова Сервера. Повышение Производительности - очевидно.

Выводы: замена объектной ссылки ox.Activesheet через временную переменную VFP, вычисляемую до Двойного Цикла и использование процессов Кэширования внешних Объектов позволяет повысить Эффективность выполнения представленного  программного модуля до 50 процентов  (17 секунд). Смотрите новый контекст:

  CopyCode imageКопировать Код
oa = ox.Activesheet && get an object reference
FOR i= 1 TO RECCOUNT()
FOR J = 1 TO FCOUNT()
oa.cells(i,j).value = EVAL(FIELD(j))
NEXT
SKIP
NEXT

Поскольку внутренний объект Activesheet независим от выполняемого Двойного Цикла, вы должны вычислять его и сохранять в Кэше временной переменной до начала выполнения представленных циклов. Это позволяет исключить еще одно обращение к Серверу; в Итоге получаем = 3 (Три) вызова методов Сервера COM (Excel).

Уменьшение количества вызовов методов Сервера снижает временные затраты еще на 25 процентов, от ранее уменьшенного времени на 50 процентов. Однако, снижение количества обращений к серверу COM не пропорционально линейному снижению временных затрат при выполнении программного кода. Затраты на обработку Кэша, создания временных переменных и прочее вносят свои нелинейные факторы в общий процесс выполнения программного Кода при участии Объектов COM.

NoteПримечание

В представленном Программном Коде можно совсем не использовать внутренний объект Activesheet. Требуемая Коллекция Ячеек более эффективно может быть доступна при помощи имеющегося Интерфейса Iapplication. Используемый внутренний объект ActiveSheet был представлен исключительно для иллюстрации возможностей Анализа и Способов повышения Производительности при работе с внешними Объектами COM (Excel).

Использование системы Visual Basic, как Клиента COM

Система Visual Basic (как Клиент COM) может быть использована для решения аналогичной Задачи (рассмотренной в предыдущем разделе).

Для использования Visual Basic, как Клиента (подсистемы Запроса на Обслуживание):

  1. Загрузите систему MS Excel.

  2. В системном меню Tools, выберите подпункт Macro.

  3. Нажмите кнопку Macros, введите временное имя: t.

  4. Нажмите Кнопку Create.

  5. В системном Меню Tools, выберите подпункт References, и укажите ранее созданный Сервер COM (VFP): myserver, точнее его Библиотеку Типов (Type Library), в которой опубликованы Основные Интерфейсы.

    Выполнение описанного Шага обеспечивает необходимой информацией из Библиотеки Типов сервера myserver создаваемый далее Макрос.

  6. Введите (или скопируйте) в тело Макроса следующий Програмный Код (VB):

      CopyCode imageКопировать Код
    Sub t()
    Dim ox As New myserver.myclass
    ox.mydocmd ("SET EXCLUSIVE OFF")
    ox.mydocmd ("USE C:\Program Files\Microsoft Visual FoxPro VersionNumber\Samples\Data\Customer")
    n = ox.MyEval("reccount()")
    nflds = ox.MyEval("fcount()")
    nsecs = ox.MyEval("seconds()")
    For i = 1 To n
    For j = 1 To nflds
    cc = "evaluate(field(" & j & "))"
    Application.Sheets(1).Cells(i, j).Value = ox.MyEval(cc)
    Next
    ox.mydocmd ("skip")
    Next
    MsgBox (ox.MyEval("seconds()") - nsecs)
    End Sub

Обработка и Управление Ошибоками

Контроль и Управление исключительными ситуациями (Ошибками) является очень важным составным элементом в Серверах COM, особенно это касается DLL-Серверов. Если подсистема-Клиент пытается выполнить некоторый Метод на Сервере, что приводит к некоторой Ошибке, например, файл Отсутствует (File Not Found) или отказано в Доступе (Access Denied), то Сообщение Сервера об указанной Ошибке не будет являться хорошим тоном в Программировании. Разработчик должен использовать соответствующий Метод Error для класса с атрибутами OLE Public, который более корректно разрешает проблему Ошибочных ситуаций. Система Visual FoxPro имеет стандартную функцию COMReturnError(), параметрами которой являются объект порождения Ошибки COM Error, и соответствующее Сообщение об Ошибке, которые возвращаются Клиенту COM. Далее представлен Пример использования указанной функции, вместе с указанием основных параметров: Source / Description. Данный Фрагмент может быть использован при построении Сервера MyServer (смотрите выше)

  CopyCode imageКопировать Код
FUNCTION Error(nError, cMethod, nLine)
COMreturnerror(cMethod+' err#='+str(nError,5)+' line='+str(nline,6)+'
'+message(),_VFP.ServerName)
&& this line is never executed

Вы можете проТестировать представленный Фрагмент Программного Кода если выполните следующие шаги:

  CopyCode imageКопировать Код
ox = CreateObject("myserver.myclass") && create the server object
?ox.mydocmd("illegal command") && causes an Error to occur

Когда Ошибка возникает в Сервере, она перехватывается (обрабатывается) нашим Методом MyClass::Error, что заставляет Сервер прервать выполнение поставленной Задачи, и вернуть Клиенту COM соответствующий объект Error и комментарий по данной Ошибке. Смотрите ниже:

  CopyCode imageКопировать Код
?aerror(myarray)
list memo like myarray
MYARRAY Pub A
( 1, 1) N 1429 ( 1429.00000000)
( 1, 2) C "OLE IDispatch exception code 0 from mydocmd
err#= 16 line= 2 Unrecognized command v
erb.: c:\fox\test\myserver.exe.."
( 1, 3) C "c:\fox\test\myserver.exe"
( 1, 4) C "mydocmd err#= 16 line= 2 Unrecognized
command verb."
( 1, 5) C ""
( 1, 6) N 0 ( 0.00000000)
( 1, 7) N 0 ( 0.00000000)

Общий Интерфейс

На самом деле, Интерфейс COM представляет из себя набор указателей в соответствующей Таблице функциональных Адресов Сервера. Обычно указанная Таблица называется VTable, или Таблицей Виртуальных Функций. Декларация Интерфейса COM - это указание номера (точки) входа в указанную Таблицу, сопоставление каждого Метода Класса с соответствующим индексом Таблицы, описание Сигнатуры каждой функции из Таблицы: количества параметров, их Типов, возвращаемое значение.

Все Интерфейсы COM наследуются от типа IUnknown. Это означает, что первыми тремя (3) Входами в Таблицу VTable каждого интерфейса COM являются следующие базовые методы реализации функций Сервера: IUnkown::QueryInterface, IUnkown::AddRef, IUnkown::Release.

Когда Интерфейс наследуется из другого Интерфейса, все последующие Интерфейсы содержат вложенные таблицы vtable начальных интерфейсов.

Двойной Интерфейс (Dual Interface) COM порождается от интерфейса типа IDispatch (интерфейс Позднего Связывания). IDispatch имеет только Четыре метода: GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, и Invoke. Таким образом, первые 7 (семь) (3+4) Интерфейсов (входы в Таблицу) полностью описывают IDispatch, и все пораждаемые от него Интерфейсы IDispatch.

Для ранее построенного сервера Myserver.dll, двойной интерфейс IMyClass может иметь следующий вид:

  CopyCode imageКопировать Код
 IMyClass
QueryInterface(QI params) (from IUnknown)
Addref (from IUnknown)
Release (from IUnknown)
GetTypeInfoCount() (from IDispatch)
GetTypeInfo() (from IDispatch)
GetIDsOfNames() (from IDispatch)
Invoke() (from IDispatch)
MyDoCmd(cCmd) (from IMyClass)
MyEval(cExpr) (from IMyClass)

Предположим, Клиент пытается выполнить метод Activesheet с помощью двойного интерфейса IApplication Сервера Excel.Application. Фактически, данный Метод может быть выполнен двумя путями: с помощью Раннего и Позднего Связывания (Early/Late Binding). Раннее Связывание обычно называется Связыванием VTtable, поскольку, Клиент вызывает требуемый метод Activesheet данного Сервера определяя его индексный ключ входа из представленной Таблицы VFable; естественно, номер данного метода болше 7. По найденному адресу размещен фиксированный программный Код требуемого Метода-функции, который включается в Класс Клиента во время Компиляции. Если в последующих версиях Сервера изменяется номер рассматриваемого Метода в Таблице Интерфейса, тогда Класс Клиента будет выполняться с Ошибкой (адрес метода-функции изменился).

При Позднем Связывании используется интерфейс IDispatch. Клиент выполняет вызов IDispatch::GetIDsOfNames, которому передается в виде параметра имя требуемого Метода: Activesheet, отсюда получаем идентификатор функции ID. (Данный идентификатор требуемой функции ID может быть кэширован (зарезервирован, сохранен) Клиентом для последующего использования). Далее Клиент размещает все необходимые параметры метода Activesheet в простейшую структуру DISPPARAMS, и вызывается функция IDispatch::Invoke, которой передается известный идентификатор ID и структура параметров DISPPARAMS. Функция IDispatch::Invoke выполняется на стороне Сервера, она распаковывает параметры из структуры DISPPARAMS, вызывает актуальную копию метода Activesheet, получает возвращаемое значение, передает последнее вызывающему Клиенту.

Поскольку при Позднем Связывании на Клиенте не фиксируется программный Код метода Сервера (точнее индекс требуемой функции из V-Таблицы),  заново собранные Методы Клиента остаются работоспособными независимо от перекомпоновки Сервера (изменения индекса таблицы интерфейса). Однако, процессы упаковки передаваемых параметров на стороне Клиента и распаковки этих параметров на стороне Сервера замедляют выполнение требуемых методов при Позднем связывании; чего не происходит при Раннем Связывании (там процессы упаковки/распаковки параметров отсутствуют).

Реализация Интерфейсов

Реализация интерфейса означает, что ваш модуль проверяет Свойства, События и Методы Объекта COM и создает новый экземпляр Объекта с указанными характеристиками. Данный процесс реализует любые параметры, их соответствующие типы и возвращаемые Значения. Другими словами, если некоторый Объект умеет и знает как использовать и вызывать компоненты другого Объекта с помощью специального Интерфейса, значит Он может использовать любой Объект, реализуемых этим Интерфейсом.

Реализация Интерфейса обеспечивает использование Клиентом любого Метода представленного Интерфейса. Это означает, что если вызван следующий Метод: Sample(parm1 as int, parm2 as string, parm3 as variant @) as int, тогда соответствующий уникальный Идентификатор должен быть найден в Объекте.

В следующем далее примере использования COM-объекта ADO, если при вызове некоторого Метода не указан (опущен) один из параметров, то следует появление следующего Сообщения (Класс не может идентифицировать Значение, поскольку член 'RECORDSETEVENTS_WillChangeField' имеет неверное количество параметров):

NoteПримечание

Class can not be instantiated because Member 'RECORDSETEVENTS_WillChangeField' has wrong # of parameters

Соответственно, при исключении (удалении) Метода появляется другое сообщение об Ошибке

Как описывалось ранее в данном Разделе, соответствующие интерфейсы описываются в Библиотеке Типов, которую можно просмотреть. Инструментальное средство системы  Visual FoxPro Object Browser (вызываемый из системного меню Tools) обеспечивает Разработчика необходимым инструментом для просмотра Библиотек Типов требуемых Объектов COM. С помощью простейших операций Редактора Visual FoxPro (Drag-Drop) вы можете скопировать требуемую Сигнатуру Метода в соответствующий файл .PRG, тем самым выполните действительную Реализацию Интерфейса.

Связывание Событий

Функциональная возможность Реализации Интерфейсов объектов COM позволяет использовать разнообразные возможности системы Microsoft Office. Представленный далее пример демонстрирует реализацию обработки Событий подсистем Microsoft Outlook, Excel, Word. Тем самым Разработчик получает доступ к разнообразным Методам, реализуемым подсистемами Office. Новые возможности стандартной функции версии VFP-9 EventBinding позволяют создавать Классы Реализации Интерфейсов по обработке "перехватываемых" Событий внешних Объектов COM, которые опубликованы и декларированы для внешнего исспользования.

Представленная Модель обработки Событий называется "Плотно связанные События" (Tightly Coupled Events). Взаимодействующие между собой Клиент и Сервер должны знать друг о друге достаточно много (быть тесно взаимосвязаны); Объекты должны иметь взаимооднозначное Соответствие. К новым моделям событийного взаимодействия Объектов можно отнести модель "Соводно связанные События" (Loosely Coupled Events),  в которых Объект-Сервер публикует (рекламирует) свои Открытые События, а Объект-Клиент подписывается на указанные События (берется их обрабатывать Сам).

  CopyCode imageКопировать Код
CLEAR
CLEAR all
PUBLIC ox as Excel.Application, ;
ow as word.application, ;
oOutlook as Outlook.Application

oOutlookEvents= NEWOBJECT('OutlookEvents')

oOutlook = NEWOBJECT("Outlook.Application")
oOutlookEvents.oo = oOutlook
? "Outlook",EVENTHANDLER( oOutlook, oOutlookEvents)

oWordEvents = NEWOBJECT("WordEvents")
ow = NEWOBJECT("word.application")
oWordEvents.ow = ow
?"Word",EVENTHANDLER(ow,oWordEvents)
ow.visible = .t.
ow.Activate
ow.Documents.Add

oExcelEvents = NEWOBJECT("ExcelEvents")
oex = NEWOBJECT("excel.application")
oex.Workbooks.Add
?"Excel",EVENTHANDLER(oex, oExcelEvents)
oex.visible = .t.

_screen.WindowState= 1

DEFINE CLASS OutlookEvents AS SESSION OLEPUBLIC
IMPLEMENTS ApplicationEvents IN Outlook.Application
oo = .null.
PROCEDURE ApplicationEvents_ItemSend(ITEM AS VARIANT, ;
CANCEL AS LOGICAL) AS VOID
?PROGRAM()
m.item.Body=STRTRAN(m.item.Body,"good","bad") + ;
CHR(13)+CHR(10)+TRANSFORM(DATETIME())+" Fox was here!"
* if Recipients fails, it could be outlook security
* m.item.Recipients.Add("someone@example.com")
PROCEDURE ApplicationEvents_NewMail() AS VOID
?PROGRAM()
PROCEDURE ApplicationEvents_Reminder(ITEM AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE ApplicationEvents_OptionsPagesAdd(PAGES AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE ApplicationEvents_Startup() AS VOID
?PROGRAM()
PROCEDURE ApplicationEvents_Quit() AS VOID
?PROGRAM()
PROCEDURE destroy
?PROGRAM()
IF !ISNULL(this.oo)
?EVENTHANDLER(this.oo,this,.t.)
ENDIF
ENDDEFINE

DEFINE CLASS WordEvents as Custom
implements applicationevents2 in "word.application"
ow = .null.
PROCEDURE applicationevents2_startup()
?PROGRAM()
PROCEDURE applicationevents2_quit
?PROGRAM()
procedure applicationevents2_DocumentBeforeClose(Cancel,Doc)
?PROGRAM()
procedure DocumentBeforeClose(Cancel,Doc)
?PROGRAM()
procedure applicationevents2_DocumentBeforePrint(Cancel,Doc)
?PROGRAM()
procedure applicationevents2_DocumentBeforeSave(Doc,SaveAsUI,Cancel)
?PROGRAM()
procedure applicationevents2_DocumentChange
?PROGRAM()
procedure applicationevents2_DocumentOpen(Doc)
?PROGRAM()
procedure applicationevents2_NewDocument(Doc)
?PROGRAM()
procedure applicationevents2_WindowActivate(Doc,Wn)
?PROGRAM()
procedure applicationevents2_WindowBeforeDoubleClick(Sel,Cancel)
?PROGRAM()
procedure applicationevents2_WindowBeforeRightClick(Sel,Cancel)
?PROGRAM()
procedure applicationevents2_WindowDeactivate(Doc,Wn)
?PROGRAM()
procedure applicationevents2_WindowSelectionChange(Sel)
?PROGRAM(),sel.text
IF sel.start < sel.end
sel.InsertAfter("Fox!")
*!* mtmp = sel.text
*!* sel.text=STRTRAN(mtmp,"good","Great!")
endif
PROCEDURE destroy
?PROGRAM()
IF !ISNULL(this.ow)
?EVENTHANDLER(this.ow,this,.t.)
ENDIF
ENDDEFINE

DEFINE CLASS ExcelEvents AS session OLEPUBLIC
IMPLEMENTS AppEvents IN "excel.application"
PROCEDURE AppEvents_NewWorkbook(Wb AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetSelectionChange(Sh AS VARIANT, ;
Target AS VARIANT) AS VOID
LOCAL mtmp,mcell
mcell = m.target.Cells(1,1)
IF !ISNULL(mcell)
mtmp = m.target.Cells(1,1).Value
?PROGRAM(),VARTYPE(mtmp)
DO case
case ISNULL(mtmp)
* m.target.Cells(1,1).Value = "Fox is great"
CASE VARTYPE(mtmp)='C'
m.target.Cells(1,1).Value = ;
STRTRAN(mtmp,"good","great!")
CASE VARTYPE(mtmp)='N'
m.target.Cells(1,1).Value = mtmp + 1
ENDCASE
ENDIF
PROCEDURE AppEvents_SheetBeforeDoubleClick(Sh AS VARIANT, ;
Target AS VARIANT, Cancel AS LOGICAL) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetBeforeRightClick(Sh AS VARIANT, ;
Target AS VARIANT, Cancel AS LOGICAL) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetActivate(Sh AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetDeactivate(Sh AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetCalculate(Sh AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetChange(Sh AS VARIANT, Target AS VARIANT) AS
VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookOpen(Wb AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookActivate(Wb AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookDeactivate(Wb AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookBeforeClose(Wb AS VARIANT, ;
Cancel AS LOGICAL) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookBeforeSave(Wb AS VARIANT, ;
SaveAsUI AS LOGICAL, Cancel AS LOGICAL) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookBeforePrint(Wb AS VARIANT, ;
Cancel AS LOGICAL) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookNewSheet(Wb AS VARIANT, ;
Sh AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookAddinInstall(Wb AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookAddinUninstall(Wb AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WindowResize(Wb AS VARIANT, Wn AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WindowActivate(Wb AS VARIANT, Wn AS VARIANT) AS
VOID
?PROGRAM()
PROCEDURE AppEvents_WindowDeactivate(Wb AS VARIANT, Wn AS VARIANT) AS
VOID
?PROGRAM()
PROCEDURE AppEvents_SheetFollowHyperlink(Sh AS VARIANT, ;
Target AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_SheetPivotTableUpdate(Sh AS VARIANT, ;
Target AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookPivotTableCloseConnection(Sh AS VARIANT, ;
Target AS VARIANT) AS VOID
?PROGRAM()
PROCEDURE AppEvents_WorkbookPivotTableOpenConnection(Sh AS VARIANT, ;
Target AS VARIANT) AS VOID
?PROGRAM()
ENDDEFINE

Далее представлен пример Реализации интерфейса для Событий объекта ADO. В данном случае, Пользователь не взаимодействует с Приложением, поэтому События не обрабатываются, как это было для простых случаев в подсистемах Office. В данном случае, Клиент напрямую вызывает необходимые Методы объекта ADO (Сервера), который возвращает необходимые Значения при использовании Интерфейса Обработки Событий. Смотрите Далее:

  CopyCode imageКопировать Код
clear
CLEAR all
local ox as adodb.recordset
local oc as ADODB.Connection
oe = NEWOBJECT("myclass")
oe2 = NEWOBJECT("myclass")

oc=NEWOBJECT("adodb.connection")
connstr = "Driver={Microsoft Visual FoxPro Driver};UID=;PWD=;SourceDB=" + ;
HOME(1)+"samples\data\testdata.dbc" + ;
";SourceType=DBC;Exclusive=No;BackgroundFetch=No;Collate=Machine;"
*
oc.ConnectionString= connstr
oc.Open
ox = oc.Execute("select * from customer")
* Now enable event handling
?EVENTHANDLER(ox,oe)
?EVENTHANDLER(ox,oe2)

?
?PADR(ox.Fields(0).Value,20)

?EVENTHANDLER(ox,oe2,.f.) && Turn off 2nd obj event handling
ox.MoveNext
?PADR(ox.Fields(0).Value,20)
ox.MoveNext
CLEAR all
retu
for i = 0 to ox.Fields.Count-1
* ?PADR(ox.Fields(i).Name,20)
* ?ox.Fields[i].value
endfor

DEFINE CLASS myclass AS session
implements RecordsetEvents IN "adodb.recordset"
* 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
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_FieldChangeComplete(;
cFields AS Number @, ;
Fields AS VARIANT @, pError AS VARIANT @, ;
adStatus AS VARIANT @, pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_WillChangeRecord(adReason AS VARIANT @, ;
cRecords AS Number @, adStatus AS VARIANT @, ;
pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_RecordChangeComplete(adReason AS VARIANT @, ;
cRecords AS Number @, pError AS VARIANT @, ;
adStatus AS VARIANT @, pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_WillChangeRecordset(adReason AS VARIANT @, ;
adStatus AS VARIANT @, pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
?adreason,adstatus,precordset.recordcount
PROCEDURE Recordsetevents_RecordsetChangeComplete(;
adReason AS VARIANT @, ;
pError AS VARIANT @, adStatus AS VARIANT @, ;
pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_WillMove(adReason AS VARIANT @, ;
adStatus AS VARIANT @, pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_MoveComplete(adReason AS VARIANT @, ;
pError AS VARIANT @, adStatus AS VARIANT @, ;
pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_EndOfRecordset(fMoreData AS LOGICAL @, ;
adStatus AS VARIANT @, pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_FetchProgress(Progress AS Number @, ;
MaxProgress AS Number @, adStatus AS VARIANT @, ;
pRecordset AS VARIANT @) AS Void
? " "+program() + ' ' + TRANSFORM(DATETIME())
PROCEDURE Recordsetevents_FetchComplete(pError AS VARIANT @, ;
adStatus AS VARIANT @, pRecordset AS VARIANT @) AS void
? " "+program() + ' ' + TRANSFORM(DATETIME())
ENDDEFINE

Использование Функциональных Тэгов Office XP

Новая версия Office XP обладает новыми возможностями, называемыми Smart Tags (интелектуальные Тэги). Типичным примером в работе каждой Компании является использование разнообразного Программного Обеспечения, обрабатывающего  одни и те же информационные Данные. Например, обычно Компания имеет Генеральный Список своих Клиентов, имеющих индивидуальные электронные адреса (e-mail), в процессе своей работы создаются разнообразные финансовые Документы и Таблицы, которые либо публикуются на серверах Web, либо рассылаются Клиентам. Предположим, необходимо отправить Сообщение на адрес  (e-mail) Клиенту с Идентификатором ALFKI, при этом требуется указать Номер Телефона или Лимит Сссуды. Обычно, сотруднику Компании требуется переключиться в другое Приложение, обеспечивающее требуемой Информацией, найти Клиента ALFKI, перенести указанную информацию в первое Приложение.

Технология Smart Tags обеспечивает взаимодействие нескольких Приложений на основе Распознавания Ключевых Идентификаторов; пользователь-Сотрудник выделяет в тексте уникальный Идентификатор Клиента, активирует Контекстное Меню (Right-Click), получает несколько возможностей реализовать поставленные Задачи. С помощью Ключевого Идентификатора ALFKI могукт быть выполнены разнообразные функции: уточнены параметры Кредитной Линии, детали юридического Адреса, отправлены документы на соответствующий электронный Адрес, выполнены доступные финансовые / бухгалтерские Транзакции, или даже - может быть набран номеер Телефона Клиента. Сотрудник Компании может работать как с постоянными Идентификаторами Клиентов, или использовать временные Идентификаторы, которые удаляются при Завершении Рабочего Дня.

В следующем далее Примере построения Smart Tags используются уникальные идентификаторы Клиентов IDs из соответствующей Таблицы Customer. Smart Tag представляют из себя Поля из указанной Таблицы, с использованием которых осуществляется доступ к Страницам Web данного Клиента. Если Сотрудник выполняет работу в редакторе Word, то указанные Поля используются для вставки их содержания в финансовые Документы Word. Для Приложения Excel, рассматриваемые Поля вставляются в Ячейки, ширина которых автоматически выравнивается. Для Приложения Internet Explorer, создается соответствующий Диалоговый Бокс (в системе Visual FoxPro рекомендуется использовать DLL, реализующий функцию MessageBox, которая объявляется соответственно командой Declare DLL).

Технологические компоненты Smart Tag должны только регистрироваться в секциях ProgID соответствующих Разделов Реестра ОС Windows. Подробное описание Smart Tag SDK (представленое для свободного использования на Microsoft Web Site) описывает различные возможности данных Компонентов.

Рассматриваемые Smart Tag должы реализовываться двумя Интерфейсами: ISmartTagRecognizer и ISmartTagAction. Первый выполняет распознавание и детализацию Ключевых Элементов, возвращает их ссылки запрашиваемому Клиенту. Второй - описывает выполняемые действия.

NoteПримечание

Если в текущий момент Приложения Office являются открытыми (загруженными), то соответствующие, привязанные модули .DLL будут также открыты. Вы не сможете редактировать и выполнять Сборку указанных .DLL, пока не закроете приложения Office.

Метод Logit выполняет поддержку Протокола Приложения, что обеспечивает удобный механизм отслеживания выполняемых Операций. При помощи Текстового Редактора вы можете просмотреть создаваемый Журнал.

  CopyCode imageКопировать Код
CLEAR ALL
clear
SET excl off
?PROGRAM()

*Smart tags in Office XP. Just change the DATAPATH,STAGPATH if necessary
IF PROGRAM() != "STAG"
?"this file MUST BE NAMED 'stag.prg'"
ENDIF
#define STNAME "MynameSpaceURI#MyLocalName"
#define DATAPATH HOME(1)+"samples\data\"
#define STAGPATH "C:\Program Files\Common Files\Microsoft Shared\Smart Tag\mstag.tlb"
IF .t.
IF FILE("stag.dll")
DECLARE integer DllUnregisterServer IN stag.dll
DllUnregisterServer()
CLEAR DLLS
ENDIF
BUILD PROJECT stag FROM stag
BUILD mtDLL stag from stag recomp
STRTOFILE("","d:\t.txt") && null log file
endif

#DEFINE HKEY_CURRENT_USER -2147483647 && BITSET(0,31)+1
oFoxReg=NEWOBJECT("foxreg", HOME(1)+"FFC\registry")
oFoxReg.OpenKey("Software\Microsoft\Office\Common\Smart Tag\Actions\stag.MyStag", ;
HKEY_CURRENT_USER, .T.)
oFoxReg.OpenKey(;
"Software\Microsoft\Office\Common\Smart Tag\Recognizers\stag.MyStag", ;
HKEY_CURRENT_USER, .T.)

DEFINE CLASS MyStag AS Session OLEPUBLIC
IMPLEMENTS ISmartTagRecognizer IN STAGPATH
IMPLEMENTS ISmartTagAction IN STAGPATH
PROCEDURE ISmartTagRecognizer_get_ProgId() AS STRING
logit()
RETURN "stag.MyStag"
PROCEDURE ISmartTagRecognizer_get_Name(LocaleID AS Integer) AS STRING
logit()
RETURN "VFP NorthWind Customer Recognizer"
PROCEDURE ISmartTagRecognizer_get_Desc(LocaleID AS Integer) AS STRING
logit()
RETURN "VFP NorthWind Customer ID Recognizer"
PROCEDURE ISmartTagRecognizer_get_SmartTagCount() AS Integer
logit()
RETURN 1
PROCEDURE ISmartTagRecognizer_get_SmartTagName(;
SmartTagID AS Integer) AS STRING
logit()
If SmartTagID = 1
RETURN STNAME
EndIf
RETURN ""
PROCEDURE ISmartTagRecognizer_get_SmartTagDownloadURL(;
SmartTagID AS Integer) AS STRING
logit()
RETURN ""
PROCEDURE ISmartTagRecognizer_Recognize(cText AS STRING, ;
DataType AS Integer, ;
LocaleID AS Integer, RecognizerSite AS VARIANT) AS VOID
logit(ctext+' '+TRANSFORM(LEN(CTEXT)))
LOCAL i, mat,cWord,propbag
i = 1
DO WHILE i <= LEN(cText)
IF ISALPHA(SUBSTR(cText,i))
mst = i
DO WHILE i <= LEN(cText) AND ;
(ISALPHA(SUBSTR(cText,i)) or ;
ISDIGIT(SUBSTR(cText,i)))
i=i+1
ENDDO
IF mst # i
cWord = SUBSTR(cText,mst,i-mst)
IF SEEK(cWord,"Customer")
* Ask for a property bag
propbag = ;
RecognizerSite.GetNewPropertyBag()
* Commit the smart tag
propbag.write("test","value")
propbag.write("test2","value2")
RecognizerSite.CommitSmartTag(STNAME, ;
mst, LEN(cWord), propbag)
propbag=.null.
ENDIF
ENDIF
ENDIF
i=i+1
ENDDO
***********************
PROCEDURE ISmartTagAction_get_ProgId() AS STRING
logit()
RETURN "stag.MyStag"
PROCEDURE ISmartTagAction_get_Name(LocaleID AS Integer) AS STRING
logit()
RETURN "Customer Actions"
PROCEDURE ISmartTagAction_get_Desc(LocaleID AS Integer) AS STRING
logit()
RETURN "Provides actions for VFP Customer data"
PROCEDURE ISmartTagAction_get_SmartTagCount() AS Integer
logit()
RETURN 1
PROCEDURE ISmartTagAction_get_SmartTagName(SmartTagID AS Integer) AS
STRING
logit()
IF SmartTagID = 1
RETURN STNAME
EndIf
RETURN ""
PROCEDURE ISmartTagAction_get_SmartTagCaption(SmartTagID AS Integer, ;
LocaleID AS Integer) AS STRING
logit(TRANSFORM(SmartTagID ))
RETURN "Customer Lookup"
PROCEDURE ISmartTagAction_get_VerbCount(SmartTagName AS STRING) AS
Integer
logit(SmartTagName )
If SmartTagName = STNAME
RETURN FCOUNT()+1
ENDIF
RETURN 0
PROCEDURE ISmartTagAction_get_VerbID(SmartTagName AS STRING, ;
VerbIndex AS Integer) AS Integer
logit(SmartTagName +', '+ TRANSFORM(VerbIndex ))
RETURN VerbIndex
PROCEDURE ISmartTagAction_get_VerbCaptionFromID(VerbID AS Integer, ;
_ApplicationName AS STRING, LocaleID AS Integer) AS STRING
logit(TRANSFORM(VerbID )+' '+_ApplicationName +;
' '+TRANSFORM(LocaleID))
IF VerbId <= FCOUNT()
RETURN "View "+FIELD(VerbID)
ENDIF
RETURN "Visit customer Web site"
PROCEDURE ISmartTagAction_get_VerbNameFromID(VerbID AS Integer) AS
STRING
logit(TRANSFORM(VerbID))
IF VerbId <= FCOUNT()
RETURN FIELD(VerbID)
ENDIF
RETURN "Visit Web site"
PROCEDURE ISmartTagAction_InvokeVerb(VerbID AS Integer, ;
cApplicationName AS STRING, ;
Target AS VARIANT, oProperties AS VARIANT, ;
cText AS STRING, Xml AS STRING) AS VOID
logit(TRANSFORM(VerbID )+' '+cApplicationName +' '+cText+' ';
+Xml+' '+TRANSFORM(oProperties.count))
LOCAL i,cProp
oProperties.write("iitest","iivalue")
oProperties.write("iitest2","iivalue2")
FOR i = 1 to oProperties.count
cProp = oProperties.keyfromindex(i-1)
logit(cProp)
logit(oProperties.read(cprop))
endfor
LOCAL fExcel,fWord
DO case
CASE capplicationname = "Excel.Application.10"
fExcel = .t.
logit(m.target.cells[1,1].value)
CASE capplicationname = "Word.Application.10"
fWord = .t.
* logit(m.target.range
ENDCASE
IF verbId > FCOUNT()
LOCAL oie as internetexplorer.application
oie = NEWOBJECT("internetexplorer.application")
oie.navigate2("localhost/"+ctext+".html")
oie.visible=1
else
IF SEEK(cText,"customer")
DO case
case fExcel
target.cells[1,2].value = ;

ALLTRIM(TRANSFORM(EVALUATE(FIELD(VerbID))))
target.Columns(2).columnWidth = 25
case fWord
target.insertAfter(' '+;

ALLTRIM(TRANSFORM(EVALUATE(FIELD(VerbID)))))
otherwise
DECLARE Integer MessageBox IN WIN32API ;
as msgbox ;
Integer,string,string, integer
msgbox(0,;

ALLTRIM(TRANSFORM(EVALUATE(FIELD(VerbID)))), ;
ctext+"="+company,0)
endcase
ELSE
logit("not found")
ENDIF
ENDIF
PROCEDURE Init
logit()
SET EXACT ON
SET EXCLUSIVE off
SET PATH TO DATAPATH
USE customer order cust_id shared
PROCEDURE Destroy
logit()
PROCEDURE MyDoCmd(cCmd as String)
&cCmd
PROCEDURE MyEval(cExp as String)
RETURN &cExp
PROCEDURE Error(nError, cMethod, nLine)
logit(TRANSFORM(nError)+' '+TRANSFORM(nLine)+' '+MESSAGE())
ENDDEFINE
#if .f.
DEFINE CLASS STagAction AS StagRecognizer OLEPUBLIC
PROCEDURE Error(nError, cMethod, nLine)
logit(PROGRAM()+" "+TRANSFORM(nError)+' '+TRANSFORM(nLine)+' '+MESSAGE())

ENDDEFINE
#endif
FUNCTION Logit(cStr)
TEXT TO mystr TEXTMERGE NOSHOW
<<DATETIME()>> <<PROGRAM(PROGRAM(-1)-1)>> <<cStr>>

ENDTEXT
STRTOFILE(myStr,"D:\t.TXT",.T.)

Разработка обратных вызовов Visual FoxPro (Callback Design)

Система Visual FoxPro позволяет создавать такие Объекты COM, функционал которых обеспечивает обработку Событий обслуживающего Сервера на стороне Клиента. Подобные сценарии Обратных Вызовов (CallBack) аналогичны механизмам Visual FoxPro по перехвату Событий Объектов (обработке их в других пользовательских классах - обработке события на стороне Клиента). В представленном далее Примере Программного Кода используется Сервер COM, декларированный в модуле IDEMO (.DLL), где описан весь механизм Event Interface, реализуемый на стороне Клиента с помощью Методов Класса DemoEvents.

В Коде Клиента используется единственный вызов объекта cCallBack, с помощью которого осуществляется интерфейс DemoEvents.

Базовый Объект IDEMO имеет декларированный Метод BuyStock, который может быть вызван на Стороне Клиента. Изначально, в Коде метода BuyStock размещен только Комментарий, который может быть Заменен на стороне Клиента конкретным программным кодом (например, по покупке Клиентом Акций). Однако, Перед и После выполнения Клиентского Программного Кода выполняется вызов соответствующего Кода из сопоставимого Объекта в классе DemoEvents. Если в программном Коде Клиента отсутствует Обратный Вызов, использующий объявленный Метод SetCallBack, то вызов методов Объекта DemoEvents не выполняется. Тем не менее, если Клиент использует право Обратного Вызова, то  рассматриваемые Методы выполняют возврат управления на сторону Клиента.

  CopyCode imageКопировать Код
CLEAR ALL
IF PROGRAM() != "IDEMO"
?"this file MUST BE NAMED 'idemo.prg'"
RETURN
ENDIF
IF .t.
IF FILE("idemo.dll")
DECLARE integer DllUnregisterServer IN idemo.dll
DllUnregisterServer()
CLEAR DLLS
ENDIF
BUILD PROJECT idemo FROM idemo
BUILD DLL idemo from idemo recomp
endif
clear

oCallback = NEWOBJECT("cCallback") && the event callback
ostock=newOBJECT("idemo.idemo") && the business COM obj
ostock.setcallback(oCallBack) && like BindEvents
?ostock.BuyStock("MSFT",10000 ) && invoke a method
ostock.setcallback(.null.) && like UnBindEvents
?ostock.BuyStock("MSFT",20000 ) && this one doesn't fire events

*This is the actual implementation of the event interface
DEFINE CLASS cCallback as session
implements iDemoEvents in idemo.dll
procedure iDemoEvents_BeforeBuyStock(cStock as String, qty AS Number)
?program(),cstock,qty
procedure iDemoEvents_AfterBuyStock(cStock as String, qty AS Number)
?program(),cstock,qty

enddefine
*the rest of this file is used in the COM server
DEFINE CLASS idemo as session olepublic
oc = .null.
PROCEDURE init
this.SetCallBack(.null.) && set callback to default
PROCEDURE setcallback(oC as Variant)
IF ISNULL(oc)
&& dummy instance that does nothing: virtual function
this.oc = NEWOBJECT("DemoEvents")
else
IF VARTYPE(oc) != 'O'
COMRETURNERROR(PROGRAM(),"callback must be obj")
ENDIF
this.oc = GETINTERFACE(oC,"iDemoEvents","idemo.idemo")
endif
procedure MyDoCmd(cCmd as String) as Variant
&cCmd
procedure MyEval(cExpr as String) as Variant
return &cExpr
procedure BuyStock(cStock as String, qty AS Number) as Boolean
this.oc.BeforeBuyStock(cStock, qty)
*here we buy the stock
this.oc.AfterBuyStock(cStock, qty)
FUNCTION Error(nError, cMethod, nLine)
COMreturnerror(cMethod+' err#='+str(nError,5)+' line='+;
str(nline,6)+' '+message(),_VFP.ServerName)
&& this line is never executed

enddefine

*Just an interface definition that should be implemented by outside callers
DEFINE CLASS DemoEvents as session olepublic
procedure BeforeBuyStock(cStock as String, qty AS Number)
procedure AfterBuyStock(cStock as String, qty AS Number)
enddefine

Технология COM допускает самые разнообразные схемы взаимодействия внешних Объектов. Каждое новое дополнение в функциональные возможности COM сразу находит поддержку в системе Visual FoxPro, включая поддержку простейших Клиентов COM, обслуживание Интерфейсов (обработку Событий Сервера), поддержку Раннего Связывания для Сервера и Клиента, их изменяемых функциональных возможностей и расширений.

См. также