Глава 7

Java n СОМ в Visual J++

Модель компонентного объекта (Component Object Model — СОМ) фирмы Microsoft удивительно похожа на модель объектов Java. Сходство настолько велико, что фирма Microsoft добавила в виртуальную машину, разработан­ную для системы Windows, способность создавать и использовать в Java СОМ-объекты. Теперь СОМ-объекты, реализованные но других языках, можно применять и в Java-программах.

В частности, DAO API (Data Access Objects) и RDO API (Remote Data Objects), включенные в Visual J++, фактически реализованы кок СОМ-объекты. Эти объекты и их экземпляры могут быть использованы точно так же, кок если бы они были объектами Java. В настоящее время в системе Windows почти все СОМ-объекты можно включать в приложения и апплеты Java.

Раз эти модели так похожи и вводить СОМ-объекты в Java-программы так легко, нельзя ли использовать и объекты Java в СОМ-приложениях? Оказы­вается, можно! С помощью ВМ Java и ассоциируемых с нею программ вы можете создавать и использовать Java-объекты аналогично СОМ-объектам. Можно, например, интерфейс пользователя полностью реализовать но Java, а остальную часть приложения — на C++, Visual Basic или любом другом языке, использующем СОМ-объекты.

Читателей, знакомых с программированием собственно на Java, возможно, не впечатлит эта новая возможность. Поскольку СОМ является стандартом исполняемых двоичных файлов/ не становится ли использование СОМ-объектов просто другим способом создания так называемого платформно-зависимого Java-кода? Действительно, функционально использование СОМ эквивалент­но программированию с применением двоичных стандартов, таких как JRI-стондарт Netscape (недавно адаптированный для платформно-зависимого программирования но Java), но программировать с помощью СОМ настоль­ко легко, что это кажется просто чудом.

На платформах Windows стандарт СОМ очень популярен. Реализуя но его основе некоторые библиотеки, разработчики могут использовать содержа­щиеся в них классы в различных приложениях, удовлетворяющих стандарту СОМ. Конечно, самыми известными преимуществами Java-программ являются легкость распространения и переносимость. СОМ-объекты, в отличие от объектов Java, в действительности являются платформно-зависимыми модулями

211


(или DLL), значит, они выполняются намного быстрее, чем интерпретируе­мые классы байт-кода Java. Интеграция СОМ в Java наделяет приложения и апплеты Java множеством достоинств.

Предположим, вы реализовали в СОМ некоторый математический аппа­рат, создав СОМ-класс Mathematics, реализующий интерфейс IMathematics. Чтобы сделать этот класс частью приложения Java, следует написать (помимо некоторых предварительных действий, о которых будет рассказано в этой главе) приблизительно следующий код:

// Импорт пакета для СОМ-класса Mathematics. import math.*;

//' Создание СОМ-объекта Mathematics и получение ссылки на него. IMathematics math = (IMathematics)new Mathematics();

// Вызовы функций класса Mathematics. int result = math.someFunction(10, 100, 1000);

int product = math.multSomeNumbers(45, 67.8);

Данный код подобен обычному Java-коду. ВМ Java фирмы Microsoft позволяет осуществить полную взаимную интеграцию Java и СОМ. Таким же образом Java-объекты можно интегрировать в другие языки, удовлетво­ряющие стандарту СОМ, поскольку объекты Java могут быть представлены ВМ в качестве СОМ-объектов. Такая интеграция Java и СОМ — существен­ный вклад в обе технологии.

Моаелп Java n СОМ

В модели Java имеется класс объектов. В этом классе определен ряд переменных и методов, которые могут быть вызваны другими объектами Java. Каждый Java-класс может реализовать несколько различных интерфейсов Java, каждый из которых описывает некоторый ряд родственных методов (интерфейс — это набор объявлений методов). Схема реализации классом нескольких интерфейсов представлена на рис. 7.1.

Каждая стрелочка на этом рисунке соответствует вызываемому методу класса. Некоторые из этих методов объединены в родственные группы и реализуют определенный интерфейс. Остальные объекты теперь могут ссы­латься на экземпляр класса не по имени класса, а по имени интерфейса.

212                                                                                  Visual J++: основы программирования


Рис. 7.1. Реализация Java-классом нескольких интерфейсов

В СОМ также имеются классы. Их часто называют coclass, что является довольно странным сокращением от Component Object Class. В СОМ-классах нет методов и переменных. Давая ссылку на СОМ-объект, нельзя просто вызвать один из его методов. Вместо этого следует давать ссылку на объект как на экземпляр интерфейса. В определении интерфейса указано, какие методы объект реализует в действительности. Итак, если имеется ссылка на СОМ-объект, то можно вызвать его методы, определенные в этом интерфей­се. На рис. 7.2 показано, как один СОМ-объект реализует несколько различ­ных СОМ-интерфейсов.


ЙСОМ-класс


^Реализуемый! \ интерфейс 2 |


 


Рис. 7.2. Реализация СОМ-классом нескольких интерфейсов

Сходство между моделями, представленными на рис. 7.1 и 7.2, заключа­ется в том, что в обоих случаях класс реализует интерфейсы. Они почти взаимозаменяемы. Конечно, между реальными определениями объектов Java и СОМ-объектов имеется огромное различие, но их полные описания очень похожи, поскольку в обоих случаях объекты реализуют интерфейсы.

Благодаря этому ВМ для системы Windows позволяет легко создавать и вызывать СОМ-методы из Java-кода. СОМ-объект можно представить в Java-коде в виде объекта, который реализует один или несколько различных интерфейсов.

При создании СОМ-объекта в Java-коде вы немедленно получаете ссылку на объект в виде ссылки на интерфейс. Виртуальная машина Java отслеживает | тот факт, что используется ссылка не на действительный объект Java, а на | СОМ-объект. Но все эти детали скрыты от вас. Вам же необходимо лишь создать СОМ-объект и вызвать метод одного из его интерфейсов.

[Глава 7. Java n СОМ в Visual J++                                                                                              213


С точки зрения СОМ

С точки зрения СОМ, всякий раз, когда объект Java создает СОМ-объект, используя оператор new, ВМ системы Windows создает СОМ-объект и увеличивает на единицу значение счетчика ссылок для каждой переменной Java, в которой содержится ссылка на СОМ-объект. Когда в Java-коде объект преобразуется в один из своих интерфейсов, ВМ в действительности осуще­ствляет вызов метода QueryInterfaceQ для получения новой ссылки.

Большую часть этой вспомогательной работы выполняет ВМ Java. Когда Java-программа вызывает один из методов СОМ-объекта, ВМ преобразует этот вызов и его параметры в вызов соответствующего СОМ-интерфейса. Однако в самой Java-программе СОМ-объект "выглядит" так же, как и объект Java. Как именно происходит вызов метода СОМ-объекта, показано на рис. 7.3.

Рис. 7.3. Вызов СОМ-метода в Java-коде

При каждой ссылке на СОМ-объект ВМ вызывает метод AddRefQ его интерфейса lUnknown. При удалении ссылки (т.е. после того как ссылаю­щаяся переменная переназначается или выходит из области видимости) вызывается метод ReleaseQ. В этом случае жизненный цикл СОМ-объекта протекает во многом так же, как и жизненный цикл объекта Java: объект сохраняется в памяти до тех пор, пока существует хотя бы одна ссылка на него. Когда же все ссылки удаляются, он автоматически выгружается. В модели СОМ-объектов, когда количество ссылок на объект становится равным нулю, объект сам решает, удалить себя или нет. В отличие от данной модели в Java решение о том, какой объект надо удалить из-за отсутствия на него ссылок, принимает сборщик мусора.

214                                                                                  Visual J++: основы программпрованпя


Библиотеки типов и переходные классы Java/COM

СОМ определяет двоичный стандарт, в котором синтаксис методов интерфейса известен на этапе компиляции. В гипотетической программе (написанной не на языке Java), которая использует СОМ-объекты упо­мянутого ранее класса Mathematics, необходимо откомпилировать программу с определением интерфейса. Без этого определения невозможно корректно вызвать методы СОМ-объекта.

Однако для класса или интерфейса имеется способ сообщить "внешнему миру" о том, что представляют собой его методы и параметры этих методов. Этот способ в системе Windows реализуется с помощью библиотеки типов (type library). Она представляет собой файл специального формата, в котором описаны все реализованные здесь методы и СОМ-классы.

Создание переходного класса Java/COM

При создании переходных классов Java/COM для СОМ-библиотеки используется утилита Java Type Library Wizard. Для ее вызова следует выбрать команду Java Type Library Wizard меню Tools. На следующем рисунке показано появляющееся при этом диалоговое окно Java Type Library Wizard.

В этом диалоговом окне представлены все СОМ-библиотеки, зарегист­рированные в реестре Windows и имеющие собственные библиотеки типов. Java Type Library Wizard может создать переходный класс Java/COM для любой из этих библиотек. Если при инсталляции Visual J++ была установ­лена поддержка DAO, то в этом списке будет содержаться компонент

Глава 7. Java n СОМ в Visual J++                                                                                               215


"Microsoft DAO 3.0 Object Library". В диалоговом окне Java Type Library Wizard, кроме указанных (см. предыдущий рисунок), представлено еще несколько библиотек.

Чтобы создать переходный класс для любой из этих библиотек (или для них всех), следует установить опцию, находящуюся слева от соответствую­щего элемента списка, и нажать кнопку ОК. Java Type Library Wizard займется созданием переходного класса. Во вкладке Java Type Library Wizard окна Output будет отображаться процесс его создания. На следующем рисунке показана подобная вкладка, появляющаяся при создании переходного класса библиотеки DAO.

Как видим, в первой строке имеется оператор import Java. Его необходимо добавлять в начало любой Java-программы, использующей СОМ-библиоте-ку. Оператор import указывает компилятору, где искать переходные классы. В частности, оператор import, показанный на предыдущем рисунке, сообщает компилятору, что в программе используются классы пакета dao3032, который автоматически создается мастером.

Класс-файлы, которые создал мастер, можно найти в каталоге \Win-dows\Java\Trustlib\<packagename>. После создания переходных классов биб­лиотеки DAO в каталоге Trustlib создается каталог dao3032, в котором размещаются класс-файлы для различных СОМ-классов библиотеки DAO. Поскольку каталог Trustlib является одним из тех, в котором ВМ осуществ­ляет поиск класс-файлов, то переходные классы библиотеки DAO становятся доступными любой Java-программе, к файлам которой корректно добавлен оператор import.

Получение информации о СОМ-интерфепсах

Утилита OLE Object View в Visual J++ позволяет просмотреть описания СОМ-классов и интерфейсов. Не будь такого инструмента, пользователям пришлось бы заранее выяснять, являются ли методы и значения констант частями СОМ-интерфейсов. Так, если не знать, что в интерфейсе IBeeper имеется метод Веер(), то объект Веер, созданный в Java-программе, окажется бесполезным. Чтобы применять этот объект, необходимо знать объявленные методы и интерфейсы. Для проверки методов и констант СОМ-интерфейсов служит OLE Object View.

Для запуска утилиты OLE Object View следует выбрать команду OLE Object View меню Tools. На рис. 7.4 показано, как выглядит окно OLE Object View. В нем представлены все зарегистрированные СОМ-библиотеки, имеющие

216                                                                                 Visual J++: основы программирования


библиотеки типов. (Библиотека типов typelib содержит двоичное представ­ление интерфейсов, СОМ-объектов, типов данных и т. д.) Чтобы ознако­миться с классами и интерфейсами, описанными в библиотеке, следует дважды щелкнуть мышью на имени библиотеки в левой панели.

Рис. 7.4. Утилита OLE Object View, предназначенная для просмотра библиотеки Beeper

Для просмотра классов и интерфейсов библиотеки предназначена утили­та ITypelib Viewer. На рис. 7.5 представлена отображаемая ею информация об интерфейсе IBeeper библиотеки Beeper 1.0. Это окно удобно использовать для поиска методов, реализованных в конкретном интерфейсе, с тем чтобы применять их в Java-программе.

Следует отметить, что при использовании Java Type Library Wizard для создания переходных классов мастер создает еще и файл с определениями класса и интерфейса. Этот файл расположен в том же каталоге, что и созданный класс-файл (в каталоге \Windows\Java\Trustlib). Он представляет собой простой текстовый файл с именем "summary, txt". Например, при создании переходных классов библиотеки DAO 3.0 с помощью Java Type Library Wizard в каталоге \Windows\Java\Trustlib\dao3032 появится файл summary.txt. В этом текстовом файле содержится информация обо всех классах и интерфейсах, определенных в соответствующей СОМ-библиотеке. Возможно, для получения такой информации легче использовать утилиту

Глава 7. Java n СОМ в Visual J++                                                                                               217


OLE Object View, а не текстовый файл, поскольку она предоставляет более наглядный способ ориентации во всех методах и интерфейсах.

Рис. 7.5. Просмотрщик ITypelib с СОМ-интерфейсом [Beeper

Использование СОМ-обьектов в Java

После создания переходных классов СОМ-классы и интерфейсы можно использовать в приложениях и annneraxJava. Однако применение СОМ-объектов в апплетах сопряжено с некоторыми ограничениями, о которых будет рассказано далее в этой главе.

Для создания СОМ-объекта достаточно вызвать оператор new с именем СОМ-класса. Не забывайте при этом использовать оператор import, чтобы иметь возможность указать краткое имя СОМ-класса. В противном случае вам пришлось бы предпослать СОМ-классу имя пакета:

IMyCOMObj m = new thepackage.MeCOMObj();

У конструктора СОМ-класса никогда не бывает аргументов. Необходимо немедленно преобразовать объект, возвращаемый оператором new, в один из СОМ-интерфейсов СОМ-объекта. Это нужно сделать обязательно, в против­ном случае во время выполнения возникнет ошибка. Вот почему следующий код не будет работать:

MyCOMObj = new MyCOMObjO;

218                                                                                  Visual J++: основы программпрованпя


А первый пример верен, поскольку экземпляр MyCOMObj сразу же после создания был преобразован в IMyCOMObj (являющийся интерфейсом).

Теперь в Java-программе имеется ссылка на СОМ-объект, и можно вызывать любой из методов его интерфейса. Далее представлен СОМ-код для приложения javabeep, включенного в Visual J++. Все вызовы методов СОМ-объекта сосредоточены в обработчике событий mouseDownQ, кото­рый, разумеется, вызывается при щелчке мыши.

public boolean mouseDown(Event evt, int x, int y)

{

String BeeperString = new String();

// Проверка существования объекта Beeper и, при // необходимости, его создание. if(m Beeper==null)

т Beeper = (IBeeper) new Beeper();

// Вызов метода ВеерО объекта Beeper.

// При шестом вызове метода ВеерО объект Веер

// возвращает ошибку OLE, которая выдает исключение Java.

//В этом примере исключение перехватывается, объект Beeper

// уничтожается, создается новый объект Beeper,

// выполнение программы продолжается.

try

{

m Beeper. ВеерО;

}

catch(corn.ms.corn.ComException e)

{

// Уничтожение объекта Beeper путем задания

// m Beeper=null.

m Beeper=null;

// Создание нового объекта Beeper.

m Beeper == (IBeeper) new Beeper();

m Beeper. ВеерО;

// Вызов методов getCount() и getltem() объекта Beeper.

for (int i=l; i<=m Beeper.getCount(); i++)

{

// Создание строки сообщения из строки, // возвращенной методом getltem(). BeeperString+==m_Beeper.getItem(i) +" ";

// Отображение строки. m Graphics.drawString(BeeperString,x,у);

return true;

Глава 7. Java n СОМ в Visual J++                                                                                              219


В первую очередь обратите внимание на то, как создается объект Beeper и как ссылка на него хранится во время выполнения программы. После того как объект Beeper будет создан, он немедленно будет преобразован в объект интерфейса IBeeper (это СОМ-интерфейс, определенный в библиотеке Beeper). Когда это сделано, можно вызывать любой метод интерфейса IBeeper, что в действительности приводит к вызову метода СОМ-объекта:

m Beeper = (IBeeper) new Beeper(); // m Beeper объявляется // ссылкой на метод // интерфейса IBeeper.

m_Beeper.Beep();                    // Вызов метода

// интерфейса IBeeper.

Перехват СОМ-искпюченпп

В ВМ фирмы Microsoft добавлен особый класс исключений, которые отражают проблемы, возникающие в СОМ-объектах. Исключение com.ms.com.COMExcep-tion выдается СОМ-объектами, если при выполнении СОМ-метода про­исходят какие-либо ошибки.

Выдача СОМ-исключения осуществляется всякий раз, когда СОМ-метод возвращает значение HRESULT, которое показывает, что при выполнении метода произошла ошибка. Возможно, СОМ-серверу не хватает памяти или СОМ-объект по какой-либо причине сам себя удалил. В любом случае необходимо заключить вызов подобных методов в блок try/catch, который осуществит перехват исключения com.ms.com.COMException.

В приложении javabeep в блок try/catch заключен вызов метода Веер() объекта Beeper.

Свойства СОМ-объектов

В апплете javacallingcom, включенном в Visual J++, показан пример СОМ-объекта, предоставляющего свои свойства "внешнему миру". С помо­щью "двунаправленного интерфейса" (СОМ-термин, не имеющий отноше­ния к пониманию этого примера) СОМ-объекты предоставляют свои свойства другим объектам. Это означает, что СОМ-объект может создавать список своих свойств и методов на этапе выполнения, поэтому для исполь­зования объекта необязательно на этапе компиляции знать о нем все. Эти свойства могут быть доступными только для чтения или для чтения и записи. В примере javacallingcom представлен СОМ-сервер с двумя свойствами:

одно — только для чтения, второе — для чтения и записи.

СОМ-сервер реализует СОМ-класс, который очень похож на класс Beeper апплета javabeep. СОМ-класс ССОМВеерег способен сделать больше, чем класс Beeper (в частности, он может по требованию проигрывать различные стандартные аудиофайлы системы). Апплет javacallingcom предоставляет пользователю небольшое окно, позволяющее воспроизводить все стандарт­ные аудиофайлы WWW-броузера.

220                                                                                  Visual J++: основы программирования


После копирования примера javacallingcom на диск локальной машины необходимо сначала зарегистрировать СОМ-сервер и до того, как выполнять апплет, создать его переходные классы. В приведенном ниже примере динамическая библиотека СОМ-сервера называется comserver.dll. Для того чтобы зарегистрировать эту библиотеку в системе как СОМ-сервер, следует воспользоваться прикладной программой RegSrv32 системы Windows. Про­грамма RegSrv32 находится в каталоге \Windows\System и является частью операционной системы, доступной на любой платформе Win32. Для того чтобы зарегистрировать сервер comserver.dll, перейдите в каталог javacalling­com и введите в командную строку следующее:

\windows\system\regsrv32 comserver.dll

При этом DLL будет автоматически зарегистрирована в качестве сервера для СОМ-класса ССОМВеерег.

Далее необходимо создать переходные классы для сервера. Как уже говорилось ранее, команда Java Type Library Tool меню Tools позволяет создать переходные классы. После регистрации сервера в диалоговом окне Java Type Library Wizard появится элемент СОМ Server 1.0. Выберите этот элемент для создания переходных классов сервера comserver.dll, а затем откомпилируйте проект и запустите его.

На рис. 7.6 показано окно OLE Object View с классами и интерфейсами сервера СОМ Server 1.0. В интерфейсе ICOMBeeper (интерфейс класса ССОМВеерег, который используется в апплете) имеются три метода: две перегруженные версии метода SoundQ и метод SoundNameQ. Свойство Sound класса ССОМВеерег указывает, какой звук должен быть воспроизведен при следующем вызове метода Р1ау(). Одна перегруженная версия метода Sound() принимает описание звука, а другая возвращает описание звука, установлен­ного в данный момент для объекта ССОМВеерег. Свойство SoundName представляет собой строку, описывающую текущий звук, заданный для объекта ССОМВеерег. Это свойство доступно только для чтения.

В Java все СОМ-свойства представлены методами getXXX() и setYXZQ, которые используются для доступа к значению свойства или его модифика­ции. Например, для интерфейса ICOMBeeper в Java предусмотрены методы getSoundQ, setSound() и getSoundNameQ. (Метода setSoundNameQ не суще­ствует, поскольку свойство SoundName доступно только для чтения.) Методы getSoundQ и setSoundQ используются для получения и задания свойства Sound объекта ССОМВеерег. Метод getSoundNameQ предназначен для за­полнения списка, перечисляющего различные стандартные аудиофайлы.

Если в окне OLE Object View встречаются перегруженные методы, это означает, что они являются методами работы со свойствами, и в Java для доступа к значению соответствующего свойства и его модификации необхо­димо использовать методы gettAZQ и setAXYQ. Их имена состоят из слов "get" или "set" с добавлением имени свойства. Итак, чтобы получить номер текущего аудиофайла, надо вызвать метод getSoundQ, чтобы изменить теку­щий аудиофайл, следует использовать метод setSoundQ. Для получения строки с описанием текущего аудиофайла следует вызвать метод getSound-NameQ.

Глава 7. Java н СОМ в Visual J++                                                                                              221


Рис. 7.6. Окно утилиты OLE Object View, просматривающей СОМ-интерфейс ICOMBeeper

Перечнспнмые СОМ-типы в Java

СОМ-объект может представить список значений в виде переменной перечислимого типа. Эти переменные по существу представляют собой список чисел, аналогичный списку типа enum языка С. В основном они используются в качестве набора констант при вызовах СОМ-методов. Ска­жем, в примере javacallingcom используется переменная перечислимого типа, представляющая действующие значения свойства Sound для класса ССОМ-Веерег.

Значения перечислимого типа из примера javacallingcom можно увидеть в окне OLE Object View. На рис. 7.7 показано окно OLE Object View с интерфейсом Beeper-Constants сервера СОМ Server 1.0. В списке Vari­ables/Data Members в правой части окна представлены различные значения свойства Sound класса ССОМВеерег. Каждому значению присвоено имя, которое можно использовать вместо самого значения. Так, значение Веерег-Constants.Asterisk является константой и равно 64.

222                                                                                 Visual J++: основы программирования


Рис. 7.7. Интерфейс Beeper Constants в окне OLE Object View

В примере javacallingcom эти константы используются для облегчения программирования класса ССОМВеерег и облегчения чтения его кода. Следующая строка взята из апплета:

m_beeper.putSound(BeeperConstants.Asterisk) ;

Ее гораздо легче понять, чем строку

m__beeper.putSound(64);

В Java такие наборы констант реализованы в виде интерфейсов Java, содержащих только наборы числовых значений. ВМ создает из интерфейса BeeperConstants интерфейс Java, эквивалентный перечислимому типу. Код для интерфейса может выглядеть следующим образом:

public interface BeeperConstants { public final int Default = 0;

public final int Asterisk = 64;

public final int Exclamation = 48;

public final int Hand = 16;

public final int Question = 32;

public final int StandardBeep = -1;

Глава 7. Java n СОМ в Visual J++                                                                                             223


Использование Java-обьектов в СОМ

Можно использовать не только СОМ-объекты в Java-программах, но и, наоборот, объекты Java в СОМ. У ВМ Java для системы Windows имеется внутризадачный сервер, называющийся MSJAVA.DLL, который выполняет функцию моста между СОМ и Java. На рис. 7.8 показано, как СОМ вызывает методы объектов Java посредством MSJAVA.DLL.

С помощью аналогичного механизма можно выполнять удаленный вызов объектов Java посредством DCOM. Программа JavaReg (включенная в Visual J++) может действовать как proxy-сервер DCOM, позволяя программам на удаленных машинах создавать и вызывать объекты Java через Internet (О подобном механизме, называемом Remote Method Invocation (RMI), гово­рится в главе 12. С его помощью можно вызвать методы удаленных объектов Java. Средства RMI содержатся в базовых библиотеках Java I.I.)

Интерфейс Java

Рис. 7.8. Вызов методов Java с помощью сервера MSJAVA.DLL

Для того чтобы сделать объекты Java видимыми в СОМ, не требуется писать специальный код. СОМ может "понимать" и регистрировать объекты Java в реестре системы Windows.

Чтобы объекты Java можно было вызывать из СОМ и DCOM, необходимо выполнить следующие операции:

1. Описать интерфейсы и классы с использованием языка Object Description Language (ODL) и создать библиотеку типов классов и интерфейсов.

2. Создать класс Java.

3. Зарегистрировать класс Java в реестре Windows как СОМ-объект. (Только для представления в виде СОМ-объектов.)

224                                                                                 Visual J++: основы программирования


4. Зарегистрировать объект Java в реестре системы Windows как DCOM-объект. (Только для представления в виде DCOM-объектов.)

Для пояснения перечисленных выше операций ниже будут приведены примеры Java-классов и интерфейсов, представленных как СОМ- и DCOM-классы. Класс Observable и связанные с ним интерфейсы основываются на концепции Observable/Observer, широко используемой в базовом пакете java.util, который описан в главе 11.

Описание интерфейсов Java с точки зрения СОМ

Вызовы СОМ-объектов происходят через СОМ-интерфейсы. Как и в Java, интерфейс является набором объявлений методов. СОМ-объект реали­зует интерфейс, так что другой СОМ-код может вызвать его функции.

Чтобы объекты Java можно было вызывать из СОМ, следует определить интерфейсы Java так, чтобы СОМ-объекты могли "понимать" их. Это дела-| ется при помощи языка описания объектов (ODL). Данный язык предназна­чен для такого описания объектов и интерфейсов, которое не зависит от их реализации. С помощью ODL можно определить объекты и интерфейсы, а | затем реализовать их, используя любой язык — C++, Java, Visual Basic, Pascal.

В рассматриваемом случае необходимо сначала определить объекты и интерфейсы Java в ODL, а затем реализовать их в Java. Если объект описан 1 на ODL, то особый компилятор создает библиотеку типов, которая описы-I вает объекты и интерфейсы Java в СОМ. Как вы помните, для создания ! переходных классов Java/COM с использованием Java Type Library Wizard I требуется библиотека типов, описывающая СОМ-классы и интерфейсы для i Java. Здесь же все наоборот -— необходима библиотека типов, описывающая классы и интерфейсы Java для СОМ. Описание объектов и интерфейсов осуществляется с помощью ODL, а программа MkTypLib, включенная в каталог \MSDEV\BIN, используется для компиляции ODL-файлов в биб­лиотеки типов (файлы с расширением TLB).

Ниже приведен пример ODL-файла, описывающего класс Observable, который во многом похож на класс Observable пакета java.util, рассмотренный I в главе 11. (Полное описание ODL включено в раздел InfoViewer электронной I документации по Visual J++. Более кратко о нем будет рассказано ниже.)

j //Observable, adi

I [ uuid (27E955CO-13C6-lldO-AE38-444553540000) ]

I library LObservable

I ( j   import lib("stdole32.tlb");

[ odi, uuid(27E955C2-13C6-lldO-AE38-444553540000) ] interface lObserver

Глава 7. Java n СОМ в Visual J++                                                                                             225


HRESULT update() ;

[uuid (27E955Cl-13C6-lldO-AE38-444553540000) ]

interface lObservable

{

HRESULT add0bserver([in] lObserver* obs));

HRESULT addObservers([out, retval] long* 1) ;

HRESULT countObserver([in] lObserver* obs);

HRESULT delete0bservers() ;

HRESULT hasChanged([out, retval] boolean* b) ;

HRESULT notifyObservers() ;

[ uuid (27E955C3-13C6-lldO-AE38-444553540000) ]

coclass Observable

{

interface lObservable;

}

};

В этом файле описаны два интерфейса и один класс: lObserver и lOb­servable -- это интерфейсы, a Observable — класс. На первый взгляд все это может показаться несколько запутанным. Однако синтаксис станет вполне понятен читателю после знакомства с данным параграфом. Класс Observable объявлен реализующим интерфейс lObservable. У интерфейса lObservable есть пять методов: add0bserver(), count0bserver(), deleteObserverQ, deleteObser-vers() и hasChangedQ. Назначение объекта Observable состоит в том, что он может уведомить "наблюдателей" о том, когда произошли изменения в его состоянии. Например, можно создать объект Observable, который будет следить за количеством пользователей, зарегистрированных в системе. Когда названное число изменится, объект Observable сообщит об этом "наблюдателям".

В интерфейсе lObserver описан только один метод, который необходимо реализовать в классе Observer: updateQ. Этот метод вызывается объектом Observable, когда происходят изменения состояния последнего. Если интер­фейс lObserver доступен Java-программе в качестве СОМ-интерфейса, то его можно использовать для связи с объектом Observable. Объекту Observable передается список объектов, реализующих интерфейс lObserver. Неважно, реализованы эти объекты в СОМ или на Java. Но если они реализуют интерфейс lObserver, описанный в ODL-файле, то объект Observable может вызывать метод updateQ всякий раз, когда меняется его состояние.

Далее приведен Java-эквивалент описания интерфейсов lObserver и lOb­servable. Но вам никогда не придется его использовать, поскольку все, что нужно для описания интерфейсов Java, —- это ODL-файл. Из данного файла имеющаяся в Visual J++ прикладная программа JavaTLB создает класс-файлы.

226                                                                                 Visual J++: основы программпровання


// Интерфейс I Observer объявлен в Observable, odi. package observable;

import corn.ms.corn.lUnknown;

public interface lObserver extends lUnknown {

public abstract void update();

// Интерфейс lObservable объявлен в Observable.odi. package observable;

import corn.ms.corn.lUnknown;

public interface lObservable extends lUnknown {

public abstract int countObservers();

public abstract void deleteObservers();

public abstract void notifyObservers();

public abstract void addObserver(lObserver iObserver);

public abstract boolean hasChanged() ;

public abstract void deleteObserver(lObserver iObserver);

}

Для того чтобы создать переходные классы Java/COM, которые описаны в файле Observable.odi, следует использовать программу MkTypLib, находя­щуюся в каталоге \MSDEV\BIN. Перейдите в каталог, где хранится ODL-файл, и введите в командной строке следующее:

C:>\msdev\bin\mktyplib /nocpp Observable.odi

При выполнении этой программы создается файл с именем Observable.tlb. Файл с расширением TLB является библиотекой типов для интерфейсов и классов, описанных в ODL-файле. Следует заметить, что опция /nocpp требуется только в том случае, когда в системе не инсталлирована среда разработки Visual C++. Если она установлена, опцию /nocpp указывать не надо.

Ранее в этой главе рассказывалось об использовании утилиты Java Type Library Wizard (вызываемой из главного меню) при создании классов Java/COM для зарегистрированных СОМ-библиотек. Библиотека типов, созданная только что с помощью программы MkTypLib, на самом деле описывает классы и интерфейсы, еще не зарегистрированные в Windows. Поэтому Java Type Library Wizard не смогла бы создать переходный класс Java/COM для класса Observable и связанных с ним интерфейсов. (В дейст­вительности Java Type Library Wizard является просто графическим пользо­вательским интерфейсом программы JavaTLB.) Java Type Library Wizard может создавать переходные классы только из библиотек, которые уже зарегистрированы в реестре Windows. Для программы JavaTLB такого огра­ничения не существует.

Глава 7. Java и СОМ в Visual J++                                                                                            227


Чтобы создать переходные классы Java/COM для библиотеки типов Observable с помощью программы JavaTLB, следует перейти в каталог TLB-файла и ввести в командной строке следующее:

C:>\msdev\bin\javatlb Observable.tib

В результате будет выведено сообщение о том, что программа JavaTLB успешно создала переходные классы Java/COM для данной библиотеки и они находятся в каталоге \Windows\Java\Trustlib\Observable. Будут созданы следующие класс-файлы: Observable.class, lObservable.class и lObserver.class.

В реализации класса Observable должен присутствовать оператор

import observable.*;

Он указывает, что к исходному коду подключаются классы и интерфейсы пакета observable. Этот оператор должен присутствовать в начале каждого исходного файла, в котором имеется ссылка на данный пакет.

Пакет observable создается из библиотеки типов подобно тому, как создаются пакеты для переходных классов Java/COM. (Не точно таким же образом, но результат тот же.)

Язык описания объектов (ODL)

Как показано в примере Observable/Observer, ODL является форматом, созданным специально для определения классов и интерфейсов способом, который полностью игнорирует все аспекты реализации. При определении компонентов, использующихся в СОМ, действительно не важно, на каком языке они созданы. Это самое большое достоинство СОМ: компоненты, написанные на разных языках, выполняющиеся на различных платформах с использованием несопоставимых операционных систем, могут использовать друг друга и создавать экземпляры друг друга. Написанный на C++ сервер базы данных, представленный в виде СОМ-объекта, может использоваться приложением или апплетом Java, запущенным в совершенно ином адресном пространстве, возможно, даже на другом компьютере. Способ осуществления этого не имеет значения, можно принимать все это за магические действия.

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

Классы и интерфейсы описываются на ODL. Единственная часть интер­фейса Java, которую вам надо описать,— это объявления методов, а единст­венная часть классов Java, которую вы должны описать,— это реализация

228                                                                                 Visual J++: основы программирования


интерфейсов этого класса. Это все, что нужно сделать на ODL: описать классы и интерфейсы так, чтобы можно было создать библиотеку типов. Можно также определить список констант и других элементов, которые облегчат использование интерфейсов и классов Java в СОМ. Однако рас­смотрение таких, более сложных, элементов выходит за пределы данной главы, в которой даются только элементарные сведения о представлении классов Java в виде СОМ-объектов. В электронной версии документации, включенной в Visual J++, имеется полное описание синтаксиса ODL. Чтение подобного документа трудно назвать увлекательным занятием, но потрачен­ное на это время окупится приобретенным опытом. Представление объектов Java в виде СОМ-объектов делает их более удобными для использования в программах.

Универсальные идентификаторы

В отличие от Java, в СОМ механизм "именования" уникальных интер­фейсов, классов, библиотек, перечислений и прочих элементов очень строг. Это достигается за счет использования универсально уникальных идентифи­каторов (UUID). (Аббревиатуры, соответствующие типу представляемых элементов,— GUID, IID и прочие — являются названиями различных типов UUID.)

UUID занимает 128 битов. Каждый UUID является уникальным числом, идентифицирующим интерфейс, класс, библиотеку или какой-либо другой элемент. Если элементу присвоили UUID, то изменить такой элемент уже нельзя. Если интерфейсу lObserver выделен UUID {27E955C2-13C6-11DO-АЕ38-444553540000}, то нельзя будет менять синтаксис его методов. При­менение UUID гарантирует, что его значение всегда буДет использоваться с определенным интерфейсом и этот интерфейс нельзя будет изменить.

Ближайшим эквивалентом UUID в Java служит полностью определенное имя класса. Имя класса используется для ссылки на конкретный класс Java. Однако, в отличие от UUID, класс можно менять. Это может привести к некоторым трудностям. Рассмотрим, например, код двух следующих классов:

// Файл Foo.Java package Moa;

public class Foo { public Foo() {}

// Файл Bar.Java package Moa;

public class Bar { private Foo foo;

public Bar() {

foo = new Foo () }

Глава 7. Java n СОМ в Visual J++                                                                                          229


В какой-то момент времени решено было изменить класс Foo, объявив его конструктор как private:

// Файл Foo.Java (версия 2) package Moa;

public class Foo {

private Foo() {} }

Файл Foo.java откомпилировали заново, но старая копия файла Bar.class сохранилась. Если попробовать создать объект Bar, то будет выдано исклю­чение Security Exception. Файл Bar.class определяет вполне корректный класс, но он ссылается на старую версию класса Foo, в которой имеется конструк­тор, объявленный как public. Подобные несоответствия неминуемы, и избе­жать их можно, только тщательно проверяя создаваемые классы.

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

В СОМ строка UUID перед каждым элементом идентифицирует данный элемент. Например, перед интерфейсом lObserver в файле Observer.odi стоит такой заголовок:

[ odi, uuid(27E955C2-13C6-lldO~AE38-444553540000) ] interface lObserver

UUID объявляет, каким будет универсальный идентификатор для дан­ного интерфейса.

В Visual J++ имеется инструмент, который пользователь может приме­нять для создания собственных UUID. Алгоритм их генерации фактически гарантирует при каждом его использовании предоставление уникального числа. Даже если все люди начнут заниматься только генерацией UUID на различных машинах в течение ближайшего миллиона лет, шансы на то, что значение каких-либо UUID совпадут, будут близки к нулю. Таким образом, можно создавать любое количество UUID, используя их, например, для нумерации носков или любых других целей.

Команда вызова программы генерации UUID расположена в меню Tools и имеет название Create GUID. Ее диалоговое окно показано на следующем рисунке. Для создания нового GUID следует нажать кнопку New GUID, Чтобы скопировать UUID в буфер обмена, нужно нажать кнопку Copy. Опции поля GUID Format представляют четыре способа форматирования UUID. Для ODL-файлов почти всегда используется четвертая опция, ос­тальные три предназначены для программ, написанных в среде Visual C++.

230                                                                                  Visual J++: основы программирования


Библиотеки ODL

В ODL объекты и интерфейсы описываются как принадлежащие библио­теке. Например, в файле Observer.odi имеется такая строка:

[ uuid(27E955CO-13C6-lldO-AE38-444553540000) ] library LObservable {

}

Для идентификации библиотеки используется UUID. Имя библиотеки LObservable обозначает "Library Observable". Привыкайте, что перед именем любой библиотеки должна стоять буква L.

Имя библиотеки ODL соответствует имени пакета, в котором находятся классы и интерфейсы Java. Например, переходные классы Java/COM для ODL-файла Observable помещаются в пакет с именем observable.

Первая строка определения библиотеки имеет такой вид:

importlib("stdole32.tlb") ;

Это означает, что все интерфейсы и классы, определенные в библиотеке типов StdOLE32, будут импортированы в ODL-файл. В названной библио­теке объявлены интерфейсы lUnknown, IDispatch и все заранее определен­ные стандартные интерфейсы OLE. Их необходимо импортировать во все другие библиотеки.

Оставшаяся часть определения библиотеки состоит из содержащихся в ней объявлений интерфейсов и классов.

Глава 7. Java n СОМ в Visual J++                                                                                             231


Классы ODL

В библиотеке можно определить один или несколько классов. Как и самой библиотеке, классам приписываются UUID. Ниже приведен пример полного объявления класса Observable, взятого из файла Observable.odi:

[ uuid(27E955C3-13C6-lldO-AE38-444553540000) ]

coclass Observable

{

interface lObservable;

}

Ключевое слово coclass идентифицирует соответствующий элемент как описание класса. (В СОМ термин "coclass''является аббревиатурой от выра­жения "Component Object Class".) В объявлении класса идентифицируются интерфейсы, которые реализует класс. В частности, класс Observable реали­зует интерфейс lObservable, объявленный в той же библиотеке. Вот, собст­венно, и все, что имеется в объявлении класса: реализованные в нем интерфейсы.

Интерфейсы ODL

Интерфейсы, которые реализуются в классах Java, доступных из СОМ-объектов, определены в ODL-файлах. Это очень важно, поскольку единст­венный способ для других объектов вызвать методы класса — это вызвать их через интерфейс, определенный в ODL-файле. Более того, хотя вы и обес­печиваете фактическую реализацию классов Java в класс-файле, вы не создаете никакого Java-кода для этих интерфейсов. Вместо этого программа JavaTLB создает для них класс-файлы.

Объявление интерфейса снабжено заголовком:

[ odi, uuid(27E955Cl-13C6-lldO-AE38-444553540000) ] interface lObservable {

... // Объявления методов lObservable. }

Как и объявление класса или библиотеки, объявление интерфейса начи­нается с указания его UUID. Обратите внимание на ключевое слово odi, находящееся в квадратных скобках: "[ odi, uuid(...) ]". Здесь должны присут­ствовать оба ключевых слова — и "odi", и "uuid".

Далее следует назвать методы и перечислить их параметры. Список параметров должен иметь определенный формат. Для каждого параметра необходимо задать имя, тип и список атрибутов. Атрибуты указывают СОМ-объекту, как использовать параметр.

В ODL каждый параметр метода того или иного интерфейса относится к входным ("in") или возвращаемым ("out") параметрам. Входной параметр является аргументом метода, а возвращаемый — информацией, которую возвращает метод. Пример входного параметра приведен в объявлении метода add0bserver() интерфейса lObservable:

HRESULT addObserver([in] lObserver* obs) ;

232                                                                                Visual J++: основы программирования


Аргументом этого метода является указатель на lObserver. Им может выступать как СОМ-объект, так и объект Java, реализующий интерфейс lObserver. При передаче интерфейса методу после его имени следует ставить звездочку.

Через возвращаемый параметр метод Java может передавать данные. В C++ возвращаемый параметр содержит указатель на область памяти ("вызов по ссылке"). В противоположность ему входной параметр должен помещать значение в стек ("вызов по значению"). В Java подобных указателей нет. Но в ODL-методах все же могут быть использованы возвращаемые параметры. Предположим, например, что вы реализуете метод MinMax из СОМ-интер-фейса, который, получая два числа, возвращает также два: меньшее и большее. В ODL этот метод можно объявить следующим образом:

HRESULT MinMax([in] int a, [in] int b, [out] int* lowest, [out] int* highest) ;

При этом в класс-файле переходного класса Java/COM будет создано такое объявление:

public void MinMax(int a, int b, int ashLowest[], int ashHighest[]) throws corn.ms.corn.ComException;

Возвращаемые параметры преобразуются в интерфейсах Java в массивы того же типа. При вызове метода Java в каждом из этих массивов будет содержаться только один элемент. Если изменить нулевой элемент такого массива, то при возврате из метода соответствующее значение в вызвавшем его СОМ-объекте также изменится. Ниже приведен пример реализации в Java метода MinMax:

import MinMax.*; // Или имя имеющейся библиотеки. import com.ms.com.*;

public class Mathematics implements MinMaxInterface { public Mathematics() {}

// Метод MinMax() интерфейса MinMax.

public void MinMax(short a, short b, short ashLowest[], short ashHighest[]) throws corn.ms.corn.ComException { if(a < b) {

ashLowest[0] = a;

ashHighest[0] == b;

j        } else { I            ashLowest[0] == b;

I           ashHighest[0] = a;

I ;           } I       } }

Глава 7. Java н СОМ в Visual J++                                                                                              233


При объявлении параметров (как входных, так и возвращаемых) с ис­пользованием встроенных типов необходимо проделать некоторое преобра­зование. Дело в том, что в Java встроенные типы не совсем совпадают с ODL-типами. Наибольшее отличие между ними проявляется в ODL-типе long, который соответствует типу int в Java (32-битовое целое).

Для определения возвращаемого значения используется комбинация атрибутов возвращаемого параметра "out" и "retval". Ниже приведено объяв­ление метода count0bservers() интерфейса lObservable, который возвращает число наблюдателей, зарегистрированных в объекте, который реализует данный интерфейс:

HRESULT countObservers([out, retval] long* 1) ;

Это соответствует такому объявлению в Java:

public int countObservers();

Параметр, имеющий атрибут retval, определяет возвращаемое значение метода, принадлежащего классу Java. Очевидно, что только у одного пара­метра может быть атрибут retval, поскольку метод может возвращать только одно значение. Правда, возвращаемых параметров может быть сколько угодно.

Запись классов Java

Создание классов Java является простым делом, если имеется ODL-файл с определением интерфейсов и классов Java. Оставшаяся часть книги в основном посвящена созданию классов Java. Фактически единственное различие между обычным Java-кодом и Java-кодом, написанным для СОМ, заключается в способе выдачи исключений.

Объявления некоторых ODL-методов очень похожи на объявления ме­тодов Java, отличаясь от последних лишь тем, что все они должны возвращать значение типа HRESULT, как показано в приведенном ниже объявлении метода delete0bservers():

HRESULT deleteObservers() ;

Это связано со способом, используемым для передачи исключений между объектами Java и СОМ-объектами. Если в методе Java, вызванном из СОМ-объекта, выдается исключение, то оно должно быть одного из двух типов: com.ms.com.ComFailedException или com.ms.comComSuccessException. Транслятор Java/COM (внутризадачный сервер MSJAVA.DLL) фактически преобразует такое исключение в возвращаемое значение для вызывающего СОМ-объекта. Это возвращаемое значение имеет тип HRESULT. Когда возврат из метода Java, вызванного СОМ-объектом, происходит без выдачи исключения, сервер MSJAVA.DLL создает и возвращает значение типа HRESULT. Это означает, что вызывающий СОМ-объект получит значение S_OK. Чтобы получить какое-то другое возвращаемое значение, необходимо осуществить выдачу исключения com.ms.com.ComFailedException или

234                                                                                 Visual J++: основы программпрованпя


com.ms.comComSuccessException. Исключение ComFailedException возвра­щает одно из значений Е_ХХХХ типа HRESULT, а исключение ComSuc-cessException — значение 8_ХХХХтит HRESULT.

Вот почему все ODL-методы должны возвращать значение типа HRE­SULT. Этот тип служит индикатором успешного или ошибочного выполне­ния метода и может включать дополнительную информацию о причинах неудачи. Во втором случае фактическое возвращаемое значение может быть не столь важным, как эта информация. Например, при запросе базы данных СОМ-объекта о числе записей в определенной таблице при наличии каких-либо катастрофических ошибок метод вернет одно из значений Е_ЛЖКУтипа HRESULT (т.е. старший бит значения HRESULT установлен), указывающее, что по какой-то причине метод не может быть завершен. Если метод был выполнен корректно, то возвращается одно из значений S_XXXX типа HRESULT (т.е. старший бит значения HRESULT не установлен), указываю­щее, что возвращаемые параметры содержат необходимую информацию.

Далее приведена реализация класса Observable, описанного в файле Observable, odi. Оператор "import observable.*" импортирует интерфейсы I0b-servable и lObserver, созданные программой JavaTLB.

import java.util.*;

import com.ms.com.*;

import observable.*;

public class Observable implements lObservable { private boolean m fChanged == false;

private Vector m_vect0bservers;

public synchronized void addObserver(lObserver obs)

throws ComException { try {

if (m_vect0bservers ==null)

m vectObservers = new Vector();

if (!m_veсtObservers.contains(obs)) m vectObservers.addElement(obs);

} catch (OutOfMemoryError me) {

throw new ComFailException(Ox8007000E); // EJDUTOFMEMORY. } }

public synchronized void deleteObserver(lObserver obs)

throws ComException { m vectObservers.removeElement(obs);

}

public void notifyObservers() throws ComException { if (!hasChanged() | | (null === m vectObservers)) return;

int ii = m vectObservers.size();

Глава 7. Java n СОМ в Visual J++                                                                                             235


while (--ii >= 0) {

lObserver obs = (Iobserver)m__vect0bservers.elementAt(ii);

obs.update() ;

}

clearChanged();

}

public synchronized void deleteObservers() { m_vect0bservers = null;

}

protected synchronized void setChanged() { m_fChanged = true;

}

protected synchronized void clearChanged() { m_fChanged == false;

}

public synchronized boolean hasChanged()

throws ComException { return m_fChanged;

}

public synchronized int countObservers()

throws ComException { if(m vectObservers != null)

return m_veсtObservers.size() ;

else

return 0;

} }

Регистрация класса Java в качестве СОМ-обьекта

Последнее, что надо сделать для того, чтобы класс Java можно было вызывать из других СОМ-объектов,-— это зарегистрировать его. В реестре Windows зарегистрированы все СОМ-классы. Это значит, что реестр содер­жит записи, индексируемые по UUID СОМ-класса, которые указывают, какие DLL- или ЕХЕ-файлы могут вызывать или создавать экземпляры класса. Для всех классов Java используется один и тот же внутризадачный DLL-сервер — сервер MSJAVA.DLL, содержащийся на компакт-диске Visual J++.

Для того чтобы вручную зарегистрировать класс Java в реестре Windows, необходимо с помощью прикладной программы RegEdit (доступной в среде Win32) или с помощью собственной программы инсталляции создать в реестре несколько записей:

236                                                                                 Visual J++: основы программпрованпя


как и другие внутризадачные СОМ-серверы. Но по отношению к удаленному DCOM-клиенту программа JavaReg действует как DCOM-сервер.

Можно дать указание программе JavaReg функционировать в качестве DCOM-сервера объектов Java, добавив один аргумент в командную строку при регистрации класса Java в качестве СОМ-объекта. Вместо приведенной выше командной строки необходима такая:

javareg /register /class:MyPerformer

/cisid:{42E5AFC6-DBAA-llcf-BAFD-OOAA0057B223} /surrogate

Аргумент surrogate в командной строке заставляет программу JavaReg создавать записьв реестре, которая в случае класса Observable будет выглядеть следующим образом:

HKEY_CLASSES_ROOT CLSID

{27E955C3-13C6-lldO-AE38-444553540000} InprocServer32 = msjava.dll

Javaclass == Observable LocalServer32 = javareg

/cisid:{42E5AFC6-DBAA-llcf-BAFD-OOAA0057B223} /surrogate

Хостинг от uCoz