Описание функций работы с COM-объектами в HomeLisp

Одним из краеугольных камней операционной системы Windows является компонентная модель объекта (Component Object Model; COM). Эта модель стандартизует правила построения и манипулирования объектами. Подавляющее большинство стандартных элементов оконного интерфейса Windows на самом деле являются COM-объектами (другое название - ActiveX-компонент). Так, командная кнопка, поле ввода, список являются ActiveX-компонентами. Однако понятие COM-объекта существенно шире. COM-объект может не иметь графического визуального интерфейса, а может быть полнофункциональным Windows-приложением. Так большинство офисных продуктов Microsoft (Excel, Word, PowerPoint и др.) могут рассматриваться, как COM-объекты.

COM-объекты "живут" в файлах типа dll, exe или ocx. Чтобы источник COM-объектов мог упешно функционировать, он должен быть зарегистрирован на компьютере. Суть регистрации заключается в том, что информация о COM-объекте заносится в системный реестр. После регистрации источник COM-объектов готов к работе.

С точки зрения HomeLisp-а COM-объект - это атом, у которого установлен определенный список свойств и стандартный индикатор COM. При создании объектов или при возврате объектов из методов и свойств необходимо на месте соответствующих параметров задавать атом без списка своиств и без индикаторов. Будем далее называть такие атомы чистыми.

Все функции манипулирования COM-объектами являются функциями типа SUBR (их аргументы вычисляются).

Имена функций манипулирования COM-объектами в HomeLisp для удобства начинаются префиксом COM. Эти функции доступны во всех режимах работы HomeLisp, кроме WEB-компоненты.

Имя функции К-во аргументов Тип аргументов Выполняемое действие
  COMCREATEOBJECT 2 1-Атом; 2-STRING Создает COM-объект, имя которого представлено значением ВТОРОГО аргумента. Атом-значение первого аргумента становится ссылкой на созданный объект
  COMDESTROYOBJECT 1 Атом Атом-значение аргумента должен иметь стандартный индикатор COM. Функция уничтожает COM-объект, созданный ранее вызовом ComCreateObject
  COMINTERFACE 1 STRING Возвращает интерфейс COM-объекта, заданного значением первого параметра.
  COMMETHOD 2 и более 1-COM; 2-STRING; 3 и далее FIXED, FLOAT, BITS, STRINGS Вызов метода объекта
  COMMETHODO 2 и более 1-COM; 2-STRING; 3 и далее FIXED, FLOAT, BITS, STRINGS Вызов метода объекта (возвращающего объектную ссылку)
  COMPROPGET 2 и более 1-COM; 2-STRING; 3 и далее FIXED, FLOAT, BITS, STRINGS Вызов PROPERTY GET объекта.
  COMPROPGETO 2 и более 1-COM; 2-STRING; 3 и далее FIXED, FLOAT, BITS, STRINGS Вызов PROPERTY GET объекта (c возвратом объектной ссылки)
  COMPROPLET 2 и более 1-COM; 2-STRING; 3 и далее FIXED, FLOAT, BITS, STRINGS Вызов PROPERTY LET объекта.
COMCREATEOBJECT  

Функция COMCREATEOBJECT служит для создания COM-объекта. Функция принимает два параметра: первым параметром должен быть "чистый" атом (т.е. атом, не содержащий в списке свойств никаких индикаторов). Вторым параметром должна быть строка вида "Имя_Сервера.Имя_Объекта". Если COM-сервер, заданный именем, зарегистрирован на компьютере, будет создан COM-объект, а атом, заданный первым параметром вызова получит в список свойств все необходимые индикаторы. В дальнейшем все действия с COM-объектом выполняются посредством ссылки на этот атом (который далее будем называть идентификатором объекта).

Если COM-объект создан успешно, функция COMCREATEOBJECT возвращает идентификатор объекта.

Вот примеры вызова функции COMCREATEOBJECT:


(comCreateObject 'xlsApp "Excel.Application")

==> xlsApp

(proplist 'xlsApp)

==> (COM Lib "Excel.Application" COMHANDLE 1)

(comPropLet 'xlsApp "Visible" -1)

==> T

(comDestroyObject 'xlsApp)

==> T

(comCreateObject 'o "a.a")

comCreateObject: Ошибка при создании COM-объекта ERRCode=429
==> ERRSTATE

Здесь создается объект-приложение Excel. Запрос списка свойст у идентификатора объекта показыает, что объект создан успешно. Далее, свойству Visible присваивается значение True (-1). После этого на экране возникает главное меню Excel. Далее вызов COMDESTROYOBJECT уничтожает приложение Excel. Последняя команда делает попытку создать незарегистрированный (несуществующий) объект. Возбуждается состояние ошибки с кодом VB, равным 429.

Следует также отметить, что попытка модифицировать список свойств идентификатора COM-объекта вызовет ошибку - список свойств защищен от модификации.

COMDESTROYOBJECT  

Функция COMDESTROYOBJECT уничтожает созданный ранее COM-объект. Единственный аргумент этой функции должен быть идентификатором COM-объекта. При успешном завершении функция возвращает T, COM-объект выгружается из памяти, а идентификатор объекта лишается списка свойств. Если на вход функции COMDESTROYOBJECT подан атом, не имеющий в списке свойств флага COM, то ошибки не возникает, но возвращается Nil.

Все это иллюстрируется следующим примером:


(comCreateObject 'xlsApp "Excel.Application")

==> xlsApp

(proplist 'xlsApp)

==> (COM Lib "Excel.Application" COMHANDLE 1)

(comDestroyObject 'xlsApp)

==> T

(proplist 'xlsApp)

==> NIL

(comDestroyObject 'z)

==> NIL

Видно, что после уничтожения объекта идентификатор объекта лишается всех флагов, специфичных для COM-объекта. Видно, также, что попытка уничтожить несуществующий объект не приводит к ошибке (возвращается Nil).

COMINTERFACE  

Функция COMINTERFACE позволяет получить в виде списка интерфейс COM-объекта (т.е. описание его свойств и методов). Функция использует стандартный Microsoft-компонент TLBInf32.dll (если этот компонент отсутствует - будет возбуждено состояние ошибки).

Функция принимает один параметр типа STRING, значение которого должно иметь вид: Имя_библиотеки.Имя_класса. Имя библиотеки - это имя файла с расширением dll/ocx/exe, который зарегистрирован на компьютере пользователя. Функция возвращает интерфейс заданного класса в виде списка сложной структуры. На верхнем уровне этого списка находятся описания элементов интерфейса (методов и свойств). В свою очередь, каждый элемент интерфейса является списком, включающим: Имя элемента, Тип (метод, свойство), тип возвращаемого значения, и описание списка параметров. Если свойство или метод не имеют параметров, то на месте списка параметров возвращается атом Nil.

Рассмотрим конкретный пример использования функции COMINTERFACE. Как отмечается в разделе, посвященном Web-компоненте, для связи web-страницы и ядра лиспа используется коммуникационный объект. Этот объект "живет" в библиотеке Tpw.dll c именем класса - clsPipes. Ниже приводится полный интерфейс этого класса (вывод для наглядности отформатирован):


(cominterface "tpw.clsPipes")

==> ((QueryInterface Method VOID ("riid" EMPTY "ppvObj" VOID))
     (AddRef Method UI4 NIL) 
     (Release Method UI4 NIL) 
     (GetTypeInfoCount Method VOID ("pctinfo" UINT)) 
     (GetTypeInfo Method VOID ("itinfo"  UINT 
                               "lcid"    UI4 
                               "pptinfo" VOID))
     (GetIDsOfNames Method VOID ("riid"  EMPTY 
                                 "rgszNames" I1 
                                 "cNames"    UINT 
                                 "lcid"      UI4 
                                 "rgdispid"  LONG))
     (Invoke Method VOID ("dispidMember" LONG 
                          "riid"         EMPTY 
                          "lcid"         UI4 
                          "wFlags"       UI2 
                          "pdispparams"  EMPTY
                          "pvarResult"   VARIANT
                          "pexcepinfo"   EMPTY 
                          "puArgErr"     UINT)
                         ) 
     (SendRequest Method VOID ("Req" BSTR)) 
     (GetResp Method BSTR NIL) 
     (InitPipe Method VOID ("ExeName" BSTR))
    )

Хорошо видно, что первыми тремя методами являются QueryIntreface, AddRef и Release соответственно. Эти три метода составляют незыблемую основу модели COM. Содержательные методы класса выделены красным цветом. Рассмотрим, например, первый из них - SendRequest. Метод не возвращает значения (VOID), и принимает один параметр с именем Req и типом BSTR. Тип BSTR это строка в кодировке UNICODE c предшествующим двухбайтовым дескриптором длины. Следующий метод GetResp возвращает значение типа BSTR и не требует параметров.

Если исследуемый класс имеет свойства, то для каждого из них в интерфейсе будет присутствовать одна или обе процедуры Property-Get/Property-Let. Интерфейс этих процедур описывается так же, как интерфейс методов (имя - возвращаемое значение - параметры). Если некоторое свойство представлено в интерфейсе класса обеими процедурами, значит это свойство допускает чтение и запись. Если присутствует только процедура Property-GET, значит свойство допускает только чтение (ReadOnly). Соответствеено, наличие только процедуры Property-LET означает, свойство допускает только запись (WriteOnly). В качестве примера класса со свойствами можно рассмотреть интерфейс класса полноэкранного календаря, который содержится в библиотеке Bservy.dll (ее можно скачать здесь). Из результата для краткости исключены стандартные методы COM и вывод для наглядности отформатирован:


(cominterface "Bservy.clsCal")

==> 

( ...
  (cshow         Method       VOID    NIL)
 
  (intDate       Property-GET BSTR    NIL) 
  (intDate       Property-LET BSTR    NIL)

  (singlDate     Property-GET BSTR    NIL) 
  (singlDate     Property-LET BSTR    NIL) 

  (FontSize      Property-GET REAL    NIL) 
  (FontSize      Property-LET REAL    NIL) 

  (MPrec         Property-GET INTEGER NIL) 
  (MPrec         Property-LET INTEGER NIL) 

  (SelectionMode Property-GET EMPTY   NIL) 
  (SelectionMode Property-LET EMPTY   NIL) 

  (ListDates     Property-GET BSTR    NIL) 
  (ListDates     Property-LET BSTR    NIL) 

  (MaxDates      Property-GET INTEGER NIL) 
  (MaxDates      Property-LET INTEGER NIL) 

  (colorUDay     Property-GET UI4     NIL) 
  (colorUDay     Property-LET UI4     NIL) 

  (colorHDay     Property-GET UI4     NIL) 
  (colorHDay     Property-LET UI4     NIL) 

  (colorSDay     Property-GET UI4     NIL) 
  (colorSDay     Property-LET UI4     NIL) 

  (colorIDay     Property-GET UI4     NIL) 
  (colorIDay     Property-LET UI4     NIL) 

  (colorMon      Property-GET UI4     NIL) 
  (colorMon      Property-LET UI4     NIL) 

  (colorBack     Property-GET UI4     NIL) 
  (colorBack     Property-LET UI4     NIL) 

  (About         Method       VOID    NIL) 

  (Accept        Property-GET BOOL    NIL) 

  (Title         Property-GET BSTR    NIL) 
  (Title         Property-LET BSTR    NIL)
)

Видно, что свойство Accept допускает только чтение (поскольку для этого свойства не задана процедура Property-LET.

Осталось сказать несколько слов про обозначения типов значений, требуемых или возвращаемых процедурами COM. Этих типов довольно много и полное их описание читатель может найти в любом руководстве по COM-модели. Здесь отметим, что кроме описанного выше типа BSTR (UNICODE-строка с дескриптором длины) встречаются типы INTEGER (двухбайтовое целое со знаком); LONG (четырехбайтовое целое со знаком); REAL и DOUBLE (соответственно четырех- и восьмибайтовое число с плавающей точкой); BOOL (четырехбайтовое логическое; 0 - False; не нуль - True). Типы UIn - это беззнаковые целые длиной n байтов.

COMMETHOD  

Функция COMMETHOD принимает два обязательных параметра: идентификатор COM-объекта и имя метода (типа STRING), а также переменное число параметров арифметического или строкового типа. Функция вызывает у объекта заданного идентификатором метод с именем, заданным вторым параметром. При этом методу передается список параметров, составленный из параметров функции COMMETHOD, начиная с третьего.

Функцию COMMETHOD следует вызывать в том случае, когда соответствующий метод не возвращает результата или возвращает результат, не являющийся ссылкой на COM-объект.

Все это иллюстрируется следующим примером:


(comCreateObject 'xlsApp "Excel.Application")

==> xlsApp

(comPropLet 'xlsApp "visible" -1)

==> T

(comMethod 'xlsApp "FindFile")

==> T

(comMethod 'xlsApp "CentimetersToPoints" 5)

==> 141.732283464567

(comMethod 'xlsApp "MethodaNet" 5)

Ошибка COM 438
Object doesn't support this property or method
==> ERRSTATE

(comMethod 'xlsApp "quit")

==> NIL

(comDestroyObject 'xlsApp)

==> T

На врезке видно, как создается приложение Microsoft Excel, делается видимым, и вызывается метод "FindFile" (этот метод не возвращает значения). Возврат T означает, что метод "FindFile" вызван успешно. Далее вызывается метод приложения, который преобразует сантиметры в пойнты. Этот метод возвращает значение. Попытка вызвать заведомо несуществующий метод вызывает ошибку. И, наконец, вызывается метод "quit" - приложение завершается.

COMMETHODO  

Функция COMMETHODO предназначена для вызова метода COM-объекта, возвращающего ссылку на другой COM-объект. Функция COMMETHODO принимает три обязательных параметра: идентификатор исходного COM-объекта, имя метода (типа STRING) и чистый атом, который станет идентификатором объекта, возвращаемого методом. Аргументы метода задаются четвертым и последующими параметрами.

Вот пример вызова функции COMMETHODO:


(comCreateObject 'wdApp "Word.Application")

==> wdApp

(comPropLet 'wdApp "Visible" -1)

==> T

(comPropGetO 'wdApp "Documents" 'docs)

==> docs

(comMethodO 'docs "ADD" 'doc)

==> doc

(proplist 'wdApp)

==> (COM Lib "Word.Application" COMHANDLE 1)

(proplist 'docs)

==> (COM Lib "Word.Application" COMHANDLE 2)

(proplist 'doc)

==> (COM Lib "Word.Application" COMHANDLE 3)

Здесь создается приложение Microsoft Word, делается видимым, а затем создается объект Documents. После чего вызывается метод Add для объекта Documents, а результат (объект Document) присваивается атому doc.

COMPROPGET  

Функция COMPROPGET принимает два параметра: идентификатор COM-объекта и имя свойства (арифметического, логического или строкового типа). При успешном завершении функция возвращает значение свойства. Если запрошенного свойства у объекта нет, то возбужается состояние ошибки.

Все это иллюстрируется следующим примером:


(comCreateObject 'wdApp "Word.Application")

==> wdApp

(comPropGet 'wdApp "Visible")

==> NIL

(comPropLet 'wdApp "Visible" -1)

==> T

(comPropGet 'wdApp "Visible")

==> T

(comPropGet 'wdApp "Name")

==> "Microsoft Word"

Здесь свойство Visible отображается в духе Лиспа: True - T и False - Nil.

COMPROPGETO  

Назначение функции COMPROPGETO заключается в получении объектного свойства какого-либо COM-объекта. Например, у COM-объекта "Word.Application" есть свойство-объект Documents. Функция COMPROPGETO принимает три обязательных параметра: идентификатор COM-объекта, имя свойства и чистый атом, который и станет возвращенным объектом. При успешном завершении функция возвращает значение свойства. Если запрошенного свойства у объекта нет, то возбужается состояние ошибки.

Все это иллюстрируется следующим примером:


(comCreateObject 'wdApp "Word.Application")

==> wdApp

(comPropLet 'wdApp "Visible" -1)

==> T

(comPropGetO 'wdApp "Documents" 'docs)

==> docs

(comPropGet 'docs "count")

==> 0

Атом docs становится идентификатором коллекции документов приложения Word.Application. Для только что созданного приложения коллекция документов пуста (свойство Count у объекта Documents равно нулю).

COMPROPLET  

Функция COMPROPLET обеспечивает присваивание свойству COM-объекта значение (арифметического, логического или строкового типа). Функция COMPROPLET принимает три обязательных параметра: идентификатор COM-объекта, имя свойства и значение, которое будет присвоено свойству, заданному вторым параметром. При успешном завершении функция возвращает T. Если запрошенного свойства у объекта нет, то возбужается состояние ошибки.

Вот пример:


(comCreateObject 'wdApp "Word.Application")

==> wdApp

(comPropLet 'wdApp "Visible" -1)

==> T

Создается приложение Microsoft Word и делается видимым, посредством присваиванию свойству Visible логического значения True (-1).