Delphi, С++Builder и COM: вопросы и ответы

Наталия Елманова

После публикации осенью 1998 года цикла статей, посвященных C++Builder и COM-технологии, в адрес редакции поступило много вопросов, связанных с проблемами использования COM в приложениях Delphi и C++Builder. Данная статья посвящена ответам на некоторые наиболее часто встречающиеся из этих вопросов.

Уважаемый автор,

Я только начал изучать Delphi и не могу решить простые задачи, например, такие как инсталляция новых компонентов *.ocx (КомпьютерПресс, CD № 3’99). Не могли бы Вы помочь в решении этих вопросов?

Установка ActiveX (OCX) в палитру компонентов осуществляется просто: из меню среды разработки нужно выбрать опцию Component/Import ActiveX Control. Если в полученном списке нужного элемента не окажется, нужно перед этим зарегистрировать его из командной строки:

Regsvr32 <имя *.ocx-файла>

В документации к Delphi и C++Builder установка элементов управления ActiveX описана довольно подробно.

Уважаемый автор,

Не могли бы Вы подробнее осветить вопрос применения созданных компонентов ActiveX в оболочке Microsoft Word. Процесс создания объекта ActiveX не вызывает столько вопросов, сколько проблема его распространения. Как среда внедрения заманчиво выглядит Microsoft Word в связи с его большей распространенностью. Но созданные компоненты ActiveX недоступны во внедряемых объектах Word.

Буду Вам очень признателен, если Вы подскажете пути решения этой проблемы.

Если имеются в виду объекты, вставляемые с помощью меню Word Вставка|Объект(Insert|Object), то обычно таким образом вставляются OLE-объекты, поддерживаемые так называемыми серверами документов. Элементы ActiveX могут и не являться подобными серверами. Тем не менее их можно вставить непосредственно в документ Word 97, используя инструментальную панель элементов управления. На этой панели следует нажать кнопку для вывода списка дополнительных элементов и выбрать нужный элемент ActiveX. Он должен присутствовать в списке, если он зарегистрирован. Если же нет, его можно зарегистрировать, выбрав опцию регистрации элемента управления в конце списка.

Помимо этого практически любой ActiveX можно поместить на форму Visual Basic, вызываемую из документа Word. Однако следует заметить, что, если вы отдаете документ для использования на другом компьютере, следует отдать и библиотеку, содержащую элемент ActiveX, — документ Word содержит лишь ссылку на нее (и организовать ее регистрацию). Иными словами, в данном случае мы имеем дело с типичной задачей поставки приложения (или документа — в данном случае это неважно), содержащего библиотеку ActiveX. Ее можно решить, создав обычный дистрибутив (хотя бы с помощью InstallShield Express).

Однако более корректным представляется использование для этой цели Internet Explorer, так как он в отличие от Word предусматривает автоматическую регистрацию элементов управления ActiveX в составе Web-страницы.

После создания и переноса CAB-файла и Web-страницы на Web-сервер эта страница в браузере открывается, но на месте предполагаемой активной формы появляется только квадратик. Разъясните, в чем может заключаться проблема.

Причин такого поведения может быть несколько. Первая причина связана с тем, что далеко не все браузеры поддерживают отображение ActiveX с помощью элемента <OBJECT>. Для отображения ActiveX следует использовать Microsoft Internet Explorer версии 3.0 и выше (отметим, что в комплект поставки некоторых 32-разрядных версий Windows входит более ранняя версия этого браузера)1 или Netscape Communicator, оснащенный соответствующим модулем расширения (plug-in). Сам по себе Netscape Communicator не отображает элементов управления ActiveX, так как он позиционируется в качестве многоплатформного браузера, а ActiveX — это технология, специфичная для Windows.

Вторая причина может быть связана с настройкой уровня безопасности браузера. Пользователь, желающий выполнять элемент управления ActiveX под управлением браузера, должен в общем случае дать разрешение на это — ведь ActiveX содержит исполняемый код, и нет никакой гарантии, что он безопасен в использовании. Поэтому, если элемент управления ActiveX не имеет электронной подписи (а в России сейчас получить ее довольно сложно), при использовании настроек браузера по умолчанию он выполняться не будет, а некоторые версии Internet Explorer при этом еще и не сообщают пользователю о том, что элемент ActiveX не был выполнен. Чтобы осуществить выполнение неподписанного элемента управления ActiveX, в настройках параметров безопасности браузера нужно явным образом указать, что пользователь разрешает выполнять код в элементах управления ActiveX, полученных либо с конкретного Web-сервера, либо с любого сервера в Internet.

Есть и третья возможная причина — операционная система настроена так, что пользователю запрещено изменять реестр, и в этом случае ActiveX в нем, естественно, не зарегистрируется.

И, наконец, еще одна возможная причина может заключаться в том, что приложение может быть разбито на «пакеты» (packages), которые ошибочно не включены в комплект поставки. В этом случае рекомендуется проверить опции проекта и, если необходимо, добавить недостающие файлы.

С удовольствием прочитал Вашу статью: «Создание контроллеров автоматизации....», но по ходу дела у меня возник вопрос: как заставить управляемое приложение остаться активным после завершения контроллера? Пример: некая программа генерирует отчет, управляя текстовым процессором Word. Требуется, чтобы после создания отчета окно Word не закрывалось. Я добился этого методом вызова функции AddRef применительно к управляемому объекту, но подозреваю, что этот способ некорректен.

В принципе можно использовать вызов функции AddRef. Но наиболее традиционный способ (если используются вариантные переменные, как в примерах из статьи «Создание контроллеров автоматизации...») — поместить вариантную переменную в глобальную область видимости приложения-контроллера. В этом случае данные, содержащиеся в этой переменной, могут существовать, пока запущено приложение-контроллер, а вместе с ними остается активным и сам COM-сервер (если, конечно, из приложения-контроллера не был вызван метод, приводящий к его закрытию, и если этой вариантной переменной не было присвоено другое значение).

Я не понял, как формируется имя сервера, занесенное в реестр (Project1.MyAuto3)? Строку MyAuto3 мы задаем явно, а откуда берется Project1?

Project1 в данном случае — имя исполняемого файла COM-сервера. MyAuto3 — это имя COM-объекта, реализованного в данном сервере (COM-объектов в одном сервере может быть и несколько).

Мой вопрос связан с автоматизацией Excel из приложений Delphi. Вот пример нормально работающего кода, написанного на Delphi:

var
   v:variant;
begin
      v := CreateOleObject(‘Excel.Application.8’);
      v.Visible := True;
      v.WorkBooks.Add;
      v.Range(‘A2’) := 12;
      v.ActiveCell.FormulaR1C1 := ‘=RAND()’;
      v.ActiveCell.Font.Bold := True;
end;

Если же использовать команду

   v.Range(‘A2’).Select;

она не выполняется, и появляется сообщение об ошибке: Member not found.

Точно так же не выполняются и многие другие команды, которые можно найти в макросах Excel. Я экспериментировал с Visual FoxPro 5.0, и все команды (макросы) из Excel можно переносить в него практически без изменений, подставляя впереди имя переменной, например v.Range(‘A2’).Select

На самом деле в Delphi подобная команда должна выглядеть так:

v.Range[‘A2’].Select;.

Иными словами, если вы пользуетесь справочной системой Visual Basic for Applications, нужно заменять в ее примерах не только кавычки, но и скобки. Дело в том, что получающийся код должен удовлетворять требованиям синтаксиса языка программирования того средства разработки, на котором пишется контроллер Excel. Хотя синтаксис Pascal и позволяет создавать видимость того, что мы вызываем методы вариантной переменной (не все языки программирования позволяют это делать, например, в C++ подобным образом с вариантными переменными обращаться нельзя), из этого не следует, что в него можно включать без изменений синтаксические конструкции из Visual Basic. Что касается FoxPro — синтаксис используемого в этом средстве разработки языка с этой точки зрения (я имею в виду именно употребление скобок и кавычек в описании методов переменных, содержащих ссылки на COM-объекты), видимо, более близок к Visual Basic, чем синтаксис Pascal.

Чем с точки зрения синтаксиса может при автоматизации Excel помочь импорт библиотеки типов?

Если рассматривать только проблемы синтаксиса, импорт библиотеки типов полезен, скорее, в случае С++, а не Pascal.

Если мы не импортируем библиотеку типов, то синтаксис С++ будет совсем другим, нежели синтаксис Pascal. Рассмотрим простейший пример:

1) Form1.Show — этот оператор использует настоящий метод класса TForm (аналог на C++ — Form1->Show());

2) Var V:variant;
   V:=CreateOleObject(‘Excel.Application’); 
   .....
   V.Visible:=True; - 

Здесь используется «метод» для установки «свойства» варианта, но в действительности это инициирование вызова удаленных процедур в Excel (и его внутренних методов). В данном случае внутренний метод Excel, предназначенный для показа его окна, может называться как угодно, и Show есть лишь внешнее опубликованное имя этого внутреннего метода.

Аналог на C++ :

 Variant V;
 V=CreateOleObject(“Excel.Application”);
 V.OlePropertySet(“Visible”,true);    //! 

Итак, мы видим, что на самом деле Visible с точки зрения C++ — это просто строка. Иными словами, и Visual Basic, и Delphi, и Visual FoxPro просто совершают некоторые манипуляции со строками, позволяя помещать их в исходный текст без явного указания на то, что это строки, заставляя тем самым думать, что вызываются методы вариантной переменной. C++ подобных вещей делать не позволяет. Зато его код иллюстрирует, что происходит на самом деле при автоматизации Excel в Visual Basic или Delphi. Фактически Visible — просто строка, передаваемая в Excel из приложения-контроллера.

Импорт же библиотеки типов позволяет создать объекты в адресном пространстве контроллера автоматизации, имеющие те же методы, что и объекты в адресном пространстве Excel. Соответственно, можно вызывать настоящие методы этого «своего» объекта, а их реализация на самом деле будет заключаться в вызове удаленных процедур, обращенных к Excel (даже если это локальный Excel), которые инициируют манипуляции уже с внутренними объектами Excel. Соответственно, после этого синтаксис на любом языке будет похож (с точностью до скобок, кавычек и указателей) на синтаксис Visual Basic.То есть код С++

 V.Visible=true

становится верным. Правда, при использовании импорта библиотеки типов можно уже на этапе компиляции проверить, нет ли синтаксических ошибок в названии самих методов, и при этом само приложение-контроллер будет работать быстрее (за счет несколько иного механизма создания таблицы виртуальных методов). Но если при этом используется раннее связывание, то, скорее всего, приложение-контроллер окажется полностью несовместимым со старыми или будущими версиями Excel, так как даже при наличии тех же самых методов они окажутся в других местах таблицы виртуальных методов.

Есть ли возможность с помощью Delphi или C++Builder создать контроллер автоматизации одновременно для Excel 7.0 и Excel 97?

Если нужно создать приложение-контроллер, совместимое и с текущей, и с прежней версиями Excel, лучше или не импортировать библиотеку типов, или импортировать, но использовать позднее связывание по причинам, изложенным в предыдущем ответе (в документации к Delphi подробно описано, как это сделать), и не использовать вызовы методов, отсутствующих в старой версии Excel. Совместимость со старыми версиями COM-серверов является требованием спецификации COM, и, надо полагать, Microsoft следует своей собственной спецификации.

Однако не стоит полагаться на это, не произведя соответствующего тестирования с теми версиями COM-сервера, с которыми предполагается использовать данный контроллер. Дело в том, что в разных языковых версиях Office 95 (немецкой, французской, английской) некоторые методы назывались по-разному. С Delphi 2 поставлялся пример, который это иллюстрирует. Поэтому стоит уточнить (или проверить экспериментально), как именно с точки зрения именования методов Excel как COM-сервера локализована версия, которую планируется использовать.

КомпьютерПресс 7'1999