    Глава 7 

    Java п СОМ в 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), значит, они выполняются намного быстрее, чем интерпретируемые 
^ассы da^f^Koro'Java: Интеграция СОМ в Java наделяет приложения и апплеты Java 
множеством достоинств. 
    Предположим, вы реализовали в СОМ некоторый математический аппарат, создав 
СОМ-класс Mathematics, реализующий,интерфейс [Mathematics. Чтобы сделать этот 
класс частью приложения 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-класс может реализовать несколько различных интерфейсов Jaya, 
каждый из которых описывает некоторый ряд родственных методов (интерфейс — это 
набор объявлений методов). Схема реализации классом нескольких интерфейсов 
представлена на рис. 7.1. 
    Каждая стрелочка на этом рисунке соответствует вызываемому методу класса. 
Некоторые из этих методов объединены в родственные группы и реализуют 
определенный интерфейс. Остальные объекты теперь могут ссылаться на экземпляр 
класса не по имени класса, а по имени интег>фейса.. 

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


    Родительский     )• 
    
    
    ----->-  Интерфейс 1 
    I	Ч*..^....^/.^,,,^,,^:,^/ 
    Класс, 
    реализующий       __w   Интерфейс 2 интерфейсы   .. I         {	• 
    i i i 
    -->•: Интерфейс 3 

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

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

    __ < Реализуемый ; ^ интерфейс 1  I 
    СОМ-класс    __0 Реализуемый ——U интерфейс 2  ; 
    ___ Реализуемый , s интерфейс 3 j 

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

    Сходство между моделями, представленными на рис. 7.1 и 7.2, заключается в 
том, что в обоих случаях класс реализует интегзфейсы.^Они почти 
взаимозаменяемы. Конечно, между^реальными определениями объектов Java и 
СОМ-объектов имеется огромное различиеТно их полные описания очень похожи, 
поскольку в обоих случаях объекты реализуют интерфейсы. 
    Благодаря этому ВМ для системы Windojys позволяет легко создавать и 
вызывать СОМ-методы из Java-кода. СОМ-объект можно представить в Java-коде в 
виде объекта, который реализует один или несколько различных интерфей%в§4 
    При создании СОМ-объекта в Java-коде вы немедленно получаете ссылку на 
объект в виде ссылки на интерфейс. Виртуальная машина Java отслеживает тот 
факт, что используется ссылка не на действительный объект Java, а на 
СОМ-объект. Но все эти детали скрыты от вас. Вам же необходимо лишь создать 
СОМ-объект и вызвать метод одного из его интерфейсов. 

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


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

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

    СОМ-интерфейс 
    Вызывает метод СОМ-объекта 
    "ВМ"" 
    Метод 
    СОМ-объекта возвращает значение 
    Вызов метода _^4   СОМ-объекта 
    •4V 
    Возврат значения, переведенного на, 

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

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

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


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

    СОМ определяет двоичный стандарт, в котором синтаксис методов интерфейса 
известен на этапе компиляции. В гипотетической программе (написанной не на 
языке Java), которая использует СОМ-объекты упомянутого ранее класса 
Mathematics, необходимо откомпилировать программу с 
определен2те^^нт?]^ге^.а,.^е^^^	корректно *вызв*алъ методы СОМ-объекта. 
    ^ОднакАдля класса или интерфейса. имеетоя„с.пособ сообщить "внешнему 
мир^_о_том, что представляю.! србои_егр \гетоды. и параметры этих Методов. 
Этот способi в_cи?тeJv^Windows_?eaлизyeтcя с; помошыо библиотеки типов 
(гуреТШгагу). Она представляет собой файл специального формата, в котором 
описаны все реализованные здесь методы и СОМ-классы. 

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

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

    " Java Type Libiary Wizard 
    Select'items to be conversed: 
    jB.ee.Qe r.lfi.TlrP.6.. ЦЬ.гаги.............................................. 
    '.'] H f M L i nline M ultirnedia 
    _llnternet Explorer Scripting Object Model 
    EjlvAuto 1.0 Type Library 
    J Marquee Control Library 
    ..JMarquee OLE Control module 
    ...jMicrosoft .Access 3.0 Object Library 
    '""Microsoft ActiveX Plugin 
    3Microsoft DAO 2.5/3.5 Compatibility Library 
    UMicrosoft DAO 3.5 Object Library 
    "JMicrosoft Excel 8.0 Object Library 
    .,..]Microsoft Forms 2.0 Object Library 
    ]Microsoft HTML Intrinsic Controls 
    jMicrosoft Internet Controls I)Microsoft Office 8.0 Object Library 
TlMicrosoft Remote Data Object 2.0 
    'Location: 
    J 
    ^ 

    В этом диалоговом окне представлены все СОМ-библиотеки, зарегистрированные 
в реестре Windows и имеющие собственные библиотеки типов. Java Type Library 
Wizard может создать переходный класс Java/COM для любой из этих библиотек. 
Если при инсталляции VjajaJ_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. 

    ЩMicrosoft (R) Visual J++ Java Typelib Conversion Utility Version 1.01 
7048 Copyright (C) Microsoft Corp 1996  All rights reserved 
    import  dao303?.«; 
    C:^?IN95\java\trustlib\dao303?\sumi«ary   txt(l)•   Class summary   
information  crested 

    Tool returned code: 0 

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

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

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

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

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

    'ffz OLE 2 Object Vie» IHin321 
    R!e    Objects    Interfaces   View   Help 
    ^Application Performance Explorer Server Maris_*J ^Application Performance 
Explorer Service (Ver ^Application Performance Explorer Worker (Ver: 
^Application Performance Explorer Worker Provl' I Jf*Application Performance 
Explorer, Standard Ini 
    У* Beeper 1.0 Type Library (Ver 1.0) 
    ^'Bitmap Image 
    f* Borland standardVCL type library (Ver 1.0] 
    (fa Calendar Control 3.0 
    («I Chart PX 
    ft ChartFX 2.0 OLE Custom Control fVer 2.0]       ' 
    |* CO M S erver 1.0 Type Library IVer 1.0) 
    ЦЙ Crystal R eport Control	?4i 
    f* Crystal R eport Control IVer 1.0)	'.: у 
    f*Crystal Report Control 4.6 [Ver 4.6]	; 
    ^* Data Access Form Designer Add-In fen VB4 IV i, ,;: 
    I* D ata Access Form D esigner Add-l n for VB 4 (Vf|jjj 
    f^DBGrid Control	|й 
    У Delaware Animated Button Control (Ver 1.0) 
    i* D esaware Animated В utton Control IVer 1.01   JJ 
    Ready 
    OtS2 Beeper 1.0 Type Library (Ver 1.0) 
    ^e   {6364D582-OFDB-11 CF-e700-OQiA0053006D. 
    This is a Type Libraiy Registration Database Entries: 
    \TypeLib46384D582-OFDB-11CF-8700-OOAA0053C 1.0 = Beeper 1.0 Type Library 0 = 
    Win32 = D:VJAVA6EEPU>eeper.dll FUGS = 0 HELPDIR-D;VIAVABEEP 
    J 

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

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

    Глава 7. Java n COM в Visual J++ 217 

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


    1   *• Hvpelib (Beepef 1.0 Type Library (V« 1.0))                          
                                                           0 1 
    Browsing a lypeLibrary.                    Name DocSi Number of Typelnfos: 
  2                  Help С lypelnfo:                                      
HelpF		= IBeeper                                                               
           *j , : ing = IBeeper Interface                                      
              «3 ontext ID = 0X00000000                                        
       ^,,'i le = n/a                                                          
             '.'.ii :.'••', itainingTypeLib returned valid pTypelib (0X001 91 
2AC, 1 ) -^Ш id = disointerface                                                
      jf|  i: Variables/Data Members:                                , ————— . 
    F '.. 1  '	U'Deki Functions          
    MewEnum                                
AddR.eL...............................................__!giJl Count            
                           ill i|| GellDsdfNames                        Щ Щ 
GetTypelnfo                              — • Т I GetTypelnfoCount              
       IV j | Invoke                                             \     ' Itom  
                                                                    Л]    ! 
-unction Prototype:                                  F	To File.    | UNCDESC 
for Beep:   ,,,,;й,:;5!.:> 
    VOID Beeplj                                _J  : •f  1     !	'Jarne = Beep 
                                                                    * 
DocString = Play the current sound H elp Content ID = 0X00000000               
                          » & Help File = n/a                                  
                                fi Я rnemid = 0X60020000                       
                                4i B; unckind = DISPATCH       '               
                            Й i nvkind = FUN С                                 
                           * :' ::allconv = STDCALL                            
                         '''J cParams = 0                                      
                          ? i :PararnsOpt = 0                                  
                              il|l:i Л/ft = 28                                 
                                         SB; A'FuncFlags = 0                   
                                             gfe sUmH-; г-Fi in ,- Me--,- •./! 
г УШИ . .. ,,._._......,.........ЯШЙ 

    A/c. 7.5. Просмотрщик ITypelib с СОМ-интерфепсом IBeeper 


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

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

    IMyCOMObj m = new thepackage.MeCOMObj(); 

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

    MyCOMObj = new MyCOMObj(); 


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

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

    public boolean mouseDown(Event evt, int x, int y) { 
    String BeeperString = new StringO; 
    // Проверка существования объекта Beeper и, при // необходимости, его 
создание, if(m_Beeper==null) 
    m_Beeper = (IBeeper) new Beeper(); 
    // Вызов метода Веер О объекта Beeper. 
    // При шестом вызове метода ВеерО объект Веер 
    // возвращает ошибку OLE, которая выдает исключение Java. 
    // В этом примере исключение перехватывается, объект Beeper 
    // уничтожается, создается новый объект Beeper, 
    // выполнение программы продолжается. 
    try 
    { 
    m_Beeper. ВеерО; } 
    catch(com.ms.com.ComException e) { 
    // Уничтожение объекта Beeper путем задания 
    // m_Beeper=null. 
    m_Beeper=null ; 
    // Создание нового объекта Beeper. 
    m_Beeper = (IBeeper) new Beeper 0; 
    m_Beeper. Beep 0 ; } 
    // Вызов методов getCountО и getltemO объекта Beeper. 
    for (int i=l; i<=m_Beeper.getCount(); i + + ) 
    { 
    // Создание строки сообщения из строки, 
    // возвращенной методом getltem(). 
    BeeperS t r ing+=m__Beeper .getltem(i) +" "; } 
    // Отображение строки. 
    m_Graphics.drawstring(BeeperString,x, у); 
    return true; } 


    Глава 7. Java n COM в Visual J++	219 

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

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


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

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

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

    В апплете javacallingcom, включенном в Visual J++, показан пример 
СОМ-объекта, предоставляющего свои свойства "внешнему миру". С помощью 
''дзу^шравленного интерфейса" (СОМ-термин, не имеющий отношения к пониманию 
~5то76" примера) СОМ-объекты предоставляют свои свойства другим объектам. Это 
означает, что СОМ-объект может создавать список своих свойств и методов на 
этапе выполнения, поэтому для использования объекта необязательно На этапе 
компиляции знать о нем все. Эти свойства могут быть доступными только для 
чтения или для чтения и записи. В примере javacallingcom представлен 
СОМ-сервер с двумя свойствами: одно — только для чтения, второе — для чтения и записи. 
    СОМ-сервер реализует СОМ-класс, который очень похож на класс Beeper 
апплЪч? javabeep. СОМ-класс ССОМВеерег способен сделать больше, чем класс 
Beeper (в частности, он может по требованию проигрывать различные стандартные 
аудиофайлы системы). Апплет javacallingcom предоставляет пользователю 
небольшое окно, позволяющее воспроизводить все стандартные аудиофайлы WWW-броузера. 

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

    После копирования примера javacal/ingcom на диск локальной машины 
необходимо сначала зарегистрировать СОМ-сервер и до того, как выполнять 
апплет, создать его переходные классы. В приведенном ниже примере динамическая 
б^иШиотека~СОМ'-'сё^вёра называется comserver.dll. Для того чтобы 
зарегистрировать эту библиотеку в системе как СОМ-сервер, следует 
воспользоваться прикладной программой RegSrv~32 системы Windows. Программа 
RegSrv32 находится в каталоге \Windows\System и является частью операционной 
системы, доступной на любой платформе Win32. Для того чтобы зарегистрировать 
сервер comserver.dll, перейдите в каталог javacalling-com и 
Ев^лт^в^команщую„строку следующее: 

    \windows\system\regsrv32 comserver.dll 

    При этом DLL будет автоматически зарегистрирована в качестве сервера для 
СОМ-класса ССОМВеерег. 
    Далее необходимо создать переходные классы для сервера. Как уже говорилось 
ранее, команда Java Type Library Tool меню Tools позволяет создать переходные 
классы. После регистрации сервера в диалоговом окне Java Type Library Wizard 
появится элемент COM Server 1.0. Выберите этот элемент для создания переходных 
классов сервера comserver.dll, а затем откомпилируйте проект и запустите его. 
    На рис. 7.6 показано окно OLE Object View с классами и интерфейсами 
сервера COM Server 1.0. В интерфейсе ТСОМВеерег (интерфейс класса ССОМВеерег, 
который используется в апплете) имеются три метода: две перегруженные версии 
метода SoundQ и метод SoundNameQ. Свойство Sound класса ССОМВеерег указывает, 
какой звук должен быть воспроизведен при следующем вызове метода PlayQ. Одна 
перегруженная версия метода SoundQ принимает описание звука, а другая 
возвращает описание звука, установленного в данный момент для объекта 
ССОМВеерег. Свойство SoundName представляет собой строку, описывающую текущий 
звук, заданный для объекта ССОМВеерег. Это свойство доступно только для чтения. 
    В Java все СОМ-свойства представлены методами getXXXQ и selXXXQ, которые 
используются для доступа к значению свойства или его модификации. Например, 
для интерфейса ТСОМВеерег в Java предусмотрены методы getSoundQ, setSoundQ и 
getSoundName(). (Метода setSoundName() не существует, поскольку свойство 
SoundName доступно только для чтения.) Методы getSoundQ и setSoundQ 
используются для получения и задания свойства Sound объект^ ССОМВеерег. Метод 
getSoundNameQ предназначен для заполнения списка, перечисляющего различные 
стандартные аудиофайлы. 
    Если в окн* OLE Object View встречаются перегруженные методы, это 
означает, что они являются методами работы со свойствами, и в Java для доступа 
к%начению соответствующего свойства и его модификации необходимо использовать 
методы getXXXQ и selXXXQ. Их имена состоят из слов "get" или "set" с 
добавлением имени свойства. Итак, чтобы получить номер текущего аудиофайла, 
надо вызвать метод getSoundQ, чтобы изменить текущий аудиофайл, следует 
использовать метод setSoundQ. Для получения строки с описанием текущего 
аудиофайла следует вызвать метод getSound-NameQ. 

    Глава 7. Java n COM в Visual J++	221 


    |,^^^^K-"sn»«I.ibCCOMSefVell.ei№»Lib<a<y(We.1.0H                           
   :• .  ~             •               ,.              ':H 
    yFile    Object   g jiiil e N 
    owsmg a 1 уреЬшагу.                   Name = ICOMBeeper                    
                                          -*j DocStiing = ICOMBeeper Interface 
                                        Д umber of Typelnfoi:   4              
    Help Context ID = ОЮООООООО 
    ^ Application    X .^Application    | ^Application $1 Application ' 
;	jipelnfo:                                        HelpF	ile = n/a 
ntainingTypeLib returned valid pTypelib (ОЮ01 91 2AC. 3) •   , id = 
disointerface ...                                                 Zj 
Variabl*s/t)ataMeriiberi:         •   -..•••... y_ ————— 
    COMB^pHlJi                      jj    [Hoei-i FuriCtiOt15         •        
                                            : 
    ^Application    \	GetTypelnfoCount                       •*•!     -	:  I 
.mi .Г i   ill 
    • У Веера 1.0 Л> rj=^ Bitmap ImaC- '	Invoke                                
      """"  ! : Play                                           ' '   [ 1 
Querylnterface                                  \ •	:     To File... [ 
    Release                                           I i 
    («p Calendar Cc":	Sound                                      . .     !• 1 
    ||к| Chart FX     Ё	.Sound _____________     -|:;| 
    J j^ChaitFX2.0 
    |в Crystal Rep. i #* Crystal Rep.' 4?* Crystal Rep.   ! 
    String SoundNarrieQ                    ^ j i	•Jams = SoundName             
                                        :>,: )ocString = Get the description 
of the output sound        ™ ^ Help Context ID = 0X00000000                    
                 У:. ,:? 
    I^DataAcces   •		Help File = n/'a                                          
                   ф f
    I^DataAcce? (HjDBGrid Cor		mernid = 0X60020002                             
                      :S   ' unckind = DISPATCH                                
                  iM nvkind = PROPERTYGET                                      
      S' 
    ^Desaware.^   •		callconv = STDCA,LL             '                         
              'E 
    ^Desaware A'   ;		cParams = 0                                              
                   ч . . 
    ^ Delaware .^   i		cPararnsOpt - 0                                         
                   .', .:'•-••'• 
    oVft-36                                                                    
  '•'"•' 
    Ready	Jj	wFuncFlags = 0                                                    
            i: p|prnHc.^rF.jnr Ы^^г vt = '^Irin.T                              
                                   —— ! 
    v^^ 

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


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

    СОМ-объект л^ожет представить список значений в виде переменной 
перечислимого типа. Эти переменные по существу представляют~~о)бЪй список 
чисел, аналогичный списку типа enum языка С. В основном они используются в 
качестве набора констант при вызовах СОМ-методов. Скажем, в примере 
javacallingcom используется переменная перечислимого типа, представляющая 
действующие значения свойства Sound для класса ССОМ-Веерег. 
    Значения перечислимого типа из примера javacallingcom можно увидеть в окне 
OLE Object View. На рис. 7.7 показано окно OLE Object View с интерфейсом 
BeeperConstants сервера COM Server 1.0. В списке Vari-ables/Oata Members в 
правой части окна представлены различные значения свойства Sound класса 
ССОМВеерег. Каждому значению присвоено имя, которое^можно использовать вместо 
самого значения. Так, значение Beeper-Constants.Asterisk является константой и 
равно 64. 


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


    RypeLib (COMSeiuei 10 Тур* Lib<«iy (Vei 1.0J) 
    Wrowsing a,TjipeLibiaty. 
    NumBer of Typelnfos:   <! JjipeWo: 
    3 
    rfunctions
    Name - BeeperConstants DocStiing = MessageBeep Sound Constants Help 
Context ID = 0X00000000 Help File = n/a 
    G etContainingTypeLib returned valid pTypelib (0X001912AC, 1) , typekind = 
enurn	JLJ Yanables/Data Members: 
    ''"""'    "''"''   "     ' '    dote 
    d 
    Asterisk (int) = 64 Ixclamation (int)« 48 Hand (int) = 16 Question (int)» 
32 StandaidBeep (int) = -1 
    jVaigES 
    Narne = Default 
    DocStnng = n/a 
    Help Context ID = 0X00000000 
    Help File = n/a 
    memid - 0X40000000 
    «VarFlags = NOT READONLY 
    varkind = CONST 
    elerndescVar.tdesc.vt = int 
    elemdescVar.idldesc.wlDLFIags = 
    elemdescVar idldesc.dwResetved • 0X00000000 
    IpvarValue =• Long = 0 

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

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

    m__beeper.putSound(ВеерегConstants.Asterisk); 
    Ее гораздо легче понять, чем строку 
    m_beeper.putSound(64) ; 

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

    public interface BeeperConstants { 
    public fin"al int Default = 0; 
    public final int Asterisk = 64; 
    publi%,final int Exclamation = 48; 
    public I^nal int Hand = 16; 
    public final int Question = 32; 
    public final int StandardBeep = -1; ) 


    Глава 7. Java n COM в 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 1.1.) 

    Интерфейс Java 
    Внешняя программа вызывает метод объекта Java 
    Возвращаемое значение после трансляции передается вызывающему процессу 
    СОМ-сервер MSJAVA DLL ВМ 
    Транслятор 
    Объект Java, реализующий интерфейс Java 
    Возвращаемое1 значение 

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

    Для того чтобы сделать объекты Java видимыми в СОМ, не требуется писать 
специальный код. СОМ может "понимать" и регистрировать объекты Java в реестре 
системы Windows. 
    Чтобы объекты Java можно было вызывать из СОМ и DCOM, необходимо выполнись 
следующие операции: 
    1.   Описать интерфейсы и классы с использованием языка Object Description 
Katiguage (ODL) и создать библиотеку типов классов и интерфейсов. 
    2.   Создать класс Java. 
    3.   Зарегистрировать класс Java в реестре Windows как СОМ-объект. (Только 
для представления в виде СОМ-объектов.) 

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

    ? 4.   Зарегистрировать объект Java в реестре системы Windows как 
DCOM-объект. (Только для представления в виде DCOM-объектов.) 
    Для пояснения перечисленных выше операций ниже будут приведены примеры 
Java-классов и интерфейсов, представленных как СОМ- и DCOM-классы. Класс 
Observable и связанные с ним интерфейсы основываются на концепции 
Observable/Observer, широко используемой в базовом пакете ja^j5OTnf6Topbra 
описан в главе 11. 

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

    г	Вызовы СОМ-объектов происходят через СОМ-интерфейсы. Как и в Java, 
интерфейс является набором объявлений методов. СОМ-объект реализует интерфейс, 
так что другой СОМ-код может вызвать его функции. 
    Чтобы объекты Java можно было вызывать из СОМ, следует определить 
интерфейсы Java так,. чтрбы.СОМ-объекты могли "понимать" их. Это делается при 
помощи языка описания объектов (ODL). Данный язык предназначен для такого 
описания объектов и интерфейсов, которое не зависит от их реализации. С 
помощью ODL можно определить объекты и интерфейсы, а затем реализовать их, 
используя любой язык — C++, Java, Visual Basic, Pascal. 
    В рассматриваемом случае необходимо сначала определить объекты и 
интерфейсы Java в ODL, а затем реализовать их в Java. Если объект описан на 
ODL, то особый^компилятор создает библиотеку типов, которая описывает объекты 
и интерфейсы Java в СОМ. Как вы помните, для создания переходных классов 
Java/COM с использованием Java Type Library Wizard требуется библиотека типов, 
описывающая СОМ-классы и интерфейсы для Java. Здесь же все наоборот — 
необходима библиотека типов, описывающая классы и интерфейсы Java для СОМ. 
Описание объектов и интерфейсов осуществляется с помощью ODL, а программа 
MkTygLib, включенная в каталог \MSDEV\BIN, используется для компиляции 
ODL-файлов в библиотеки типов (файлы с расширением TLB).	"""* " ....... 
    Ниже приведен пример ODL-файла, описывающего класс Observable, который во 
многом похож на класс Observable пакета java.util, рассмотренный в главе 11. 
(Полное описание ODL включено в раздел Info Viewer электронной документации по 
Visual J++. Более кратко о нем будет рассказано ниже.) 

    * 
    //Observable.adl 
    [   uuid   (27E955O)-13C6-lldO-AE38-444553540000)    ] 
    library LObservable 
    { 
    impor*KUb("stdole32.tlb") ; 
    [ odl, uuid(27E955C2-13C6-lldO-AE38-444553540000) ] 
    interface lObserver 
    { 


    Глава 7. Java n COM в Visual J++	225 


    HRESULT update(); 
    [uuid (27E955Cl-13C6-lldO-AE38-444553540000) ] 
    interface lObservable 
    { 
    HRESULT addObserver([in] lObserver* obs)); 
    HRESULT addObservers([out, retval] long* 1) ; 
    HRESULT countObserver ( [in] lObserver* obs); 
    HRESULT deleteObservers(); 
    HRESULT hasChangedf[out, retval] boolean* b); 
    HRESULT notifyObservers(); 
    [ uuid (27E955C3-13C6-lldO-AE38-444553540000) ] 
    coclass Observable 
    { 
    interface lObservable; 
    } 

    В этом файле описаны два интерфейса и один класс: lObserver и lObservable 
— это интерфейсы, a Observable — класс. На первый взгляд все это может 
показаться несколько запутанным. Однако синтаксис станет вполне понятен 
читателю после знакомства с данным параграфом. Класс Observable объявлен 
реализующим интерфейс lObservable. У интерфейса lObservable есть пять методов: 
addObserverQ, countObserverQ, deleteObserverQ, deleteObser-versQ и 
hasChangedQ. Назначение объекта Observable состоит в том, что он может 
уведомить "наблюдателей" о том, когда произошли изменения в его состоянии. 
Например, можно создать объект Observable, который будет следить за 
количеством пользователей, зарегистрированных в системе. Когда названное число 
изменится, объект Observable сообщит об этом "наблюдателям". 
    В интерфейсе lObserver описан только один метод, который необходимо 
реализовать в классе Observer: updateQ. Этот метод вызывается объектом 
Observable, когда происходят изменения состояния последнего. Если интерфейс 
lObserver доступен Java-программе в качестве СОМ-интерфейса, то его можно 
использовать для связи с объектом Observable. Объекту Observable передается 
список объектов, реализующих интерфейс lObserver. Неважно, реализованы эти 
объекты в СОМ или на Java. Но если они реализуют интерфейс lObserver, 
описанный в ODL-файле, то объект Observable может вызывать метод updateQ 
всякий раз, когда меняется его состояние. 
    ^Лалее приведен Java-эквивалент описания интерфейсов lObserver и 
ЮЬ-servaBte. Но вам никогда не придется его использовать, поскольку все, что 
нужно для описания интерфейсов Java, — это ODL-файл. Из данного файла 
имеющаяся в Visual J++ прикладная программа JavaTLB создает класс-файлы. 

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


    // Интерфейс lObserver объявлен в Observable.odl. package observable; 
    import com.ms.com.lUnknown; 
    public interface lObserver extends lUnknown { 
    public abstract void update(); } 
    // Интерфейс lObservable объявлен в Observable.odl. package observable; 
    import com.ms.com.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.odl, следует использовать программу MkTypLib, находящуюся в 
каталоге \MSDEV\BIN. Перейдите в каталог, где хранится ODL-файл, и введите в 
командной строке следующее: 

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

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

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

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

    С:>\msdev\bin\javatlb Observable.tlb 

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

    import  observable.*; 

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

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

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

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

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

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

    В отличие от Java, в СОМ механизм "именования" уникальных интерфейсов, 
классов, библиотек, перечислений и прочих элементов очень строг. Это 
достигается за счет использования универсально уникальных идентификаторов 
(UUID). (Аббревиатуры, соответствующие типу представляемых элементов,— GUID, 
IID и прочие — являются названиями различных типов UUID.) 
    UUID занимает 128 битов. Каждый UUID является уникальным числом, 
идентифицирующим интерфейс, класс, библиотеку или какой-либо другой элемент. 
Если элементу присвоили UUID, то изменить такой элемент уже нельзя. Если 
интерфейсу lObserver выделен UUID {27E955C2-13C6-11DO-АЕ38-444553 540000}, то 
нельзя будет менять синтаксис его методов. Применение 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 COM в Visual J++	229 

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


    // Файл Foo.java (версия 2) package Moa; public class Foo { private Foo() {) 
    } 

    Файл Foo.java откомпилировали заново, но старая копия файла Bar.class 
сохранилась. Если попробовать создать объект Ваг, то будет выдано исключение 
SecurityException. Файл Bar.class определяет вполне корректный класс, но он 
ссылается на старую версию класса Foo, в которой имеется конструктор, 
объявленный как public. Подобные несоответствия неминуемы, и избежать их 
можно, только тщательно проверяя создаваемые классы. 
    Однако в СОМ такие проблемы никогда не возникают, потому что UUID 
гарантирует представление всегда одного и того же интерфейса. Реализации 
методов могут меняться, но их синтаксис остается прежним. Для того чтобы 
изменить интерфейс (скажем, добавить в него новый метод или изменить параметры 
уже существующего), необходимо создать новый интерфейс и выделить для него 
новый UUID, хотя можно использовать код, реализующий первый интерфейс, если 
новый интерфейс такой же. 
    В СОМ строка UUID перед каждым элементом идентифицирует данный элемент. 
Например, перед интерфейсом [Observer в файле Observer.odl стоит такой заголовок: 

    [ odl, uuid(27E955C2-13C6-lldO-AE38-444553540000) ] interface lObserver 
    UUID объявляет, каким будет универсальный идентификатор для данного интерфейса. 

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

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


    Библиотеки ODL 

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

    [ 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 и СОМ в Visual J++ 231 


    Классы ODL 

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

    [ uuid(27E955C3-13C6-lldO-AE38-444553540000) ] 
    coclass Observable 
    { 
    interface lObservable; 
    } 

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

    Интерфейсы ODL 

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

    [ odl, uuid(27E955Cl-13C6-lldO-AE38-444553540000) ] interface lObservable { 
    ... // Объявления методов lObservable. } 

    Как и объявление класса или библиотеки, объявление интерфейса начинается с 
указания его UUID. Обратите внимание на ключевое слово odl, находящееся в 
квадратных скобках: "[ odl, uuid(...) ]". Здесь должны присутствовать оба 
ключевых слова — и "odl", и "uuid". 
    Далее следует назвать методы и перечислить их параметры. Список параметров 
должен иметь определенный формат. Для каждого параметра необходимо задать имя, 
тип и список атрибутов. Атрибуты указывают СОМ-объекту, как использовать параметр. 
    В ODL каждый параметр метода того или иного интерфейса относится к входным 
("in") или возвращаемым ("out") параметрам. Входной параметр является 
аргументом метода, а возвращаемый — информацией, которую возвращает метод. 
Пример входного параметра приведен в объявлении метода addObserverQ интерфейса lObservable: 

    HRESULT addObserver([in] lObserver* obs); 


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

    Аргументом этого метода является указатель на lObserver. Им может 
выступать как СОМ-объект, так и объект Java, реализующий интерфейс lObserver. 
При передаче интерфейса методу после его имени следует ставить звездочку. 
    Через возвращаемый параметр метод Java может передавать данные. В C++ 
возвращаемый параметр содержит указатель на область памяти ("вызов по 
ссылке"). В противоположность ему входной параметр должен помещать значение в 
стек ("вызов по значению"). В Java подобных указателей нет. Но 
JL-S2JklM?Io?^x,Jce Ж5..МЙОСижгъ использованы возвращаемые параметры. 
Предположим, например, что вы реализуете метод 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   com.ms.com.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 com.ms.com.ComException { if(a < b) { 
    ashLowest[0] = a; ashHighest[0] = b; } else ( 
    ashLowest[0] = b; ashHighest[0] = a; i 
    } I 

    Глава 7. Java n COM в Visual i++	233 

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

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

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

    public int countObservers(}; 

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

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

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

    HRESULT deleteObserversO ; 

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

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

    com.ms.comComSuccessException. Исключение ComFailedException возвращает 
одно из значений Е_ХХХХ типа HRESULT, а исключение ComSuc-cessException — 
значение S_XXXX типа HRESULT. 
    Вот почему все ОРЬ-методы должнь^ возвращать, значение типа HRESULT. Этот 
тип служит индикатором успешного или ошибочного выполнения" метода и может 
включать дополнительную информацию о причинах неудачи. Во втором случае 
фактическое возвращаемое значение может быть не столь важным, как эта 
информация. Например, при запросе базы данных СОМ-объекта о числе записей в 
определенной таблице при наличии каких-либо катастрофических ошибок метод 
вернет одно из значений Е_ХХХХтипа HRESULT (т.е. старший бит значения HRESULT 
установлен), указывающее, что по какой-то причине метод не может быть 
завершен. Если метод был выполнен корректно, то возвращается одно из значений 
S_XXXX типа HRESULT (т.е. старший бит значения HRESULT не установлен), 
указывающее, что возвращаемые параметры содержат необходимую информацию. 
    Далее приведена реализация класса Observable, описанного в файле 
Observable.odl. Оператор "import observable.*" импортирует интерфейсы 
ЮЬ-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_vectObservers; 
    public synchronized void addObserver(lObserver obs) 
    throws ComException { try { 
    if (m_vectObservers ==null) 
    m_vectObservers = new Vector(); 
    if (!m_vectObservers.contains(obs)) 
    m_vectObservers.addElement(obs); } catch (OutOfMemoryError me) { 
    throw new ComFailException(Ox8007000E); // E_OUTOFMEMORY. 
    ) 
    i 
    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 и COM в Visual J++	235 


    while (—ii >= 0) { 
    lObserver obs = (Iobserver)m_vectObservers.elementAt(ii); obs.update () ; 
    } 
    clearChanged(); 
    public synchronized void deleteObservers () ( 
    m_vectObservers = null; } 
    protected synchronized void setChangedO { m_fChanged = true; 
    } 
    protected synchronized void clearChanged() ( 
    m_fChanged = false; } 
    public synchronized boolean hasChangedO 
    throws ComException { return m_fChanged; 
    public synchronized int countObservers() 
    throws ComException if(m_vectObservers != null) 
    ' return m__vectObservers . size () ; else 
    return 0; 


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

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

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


    HKEY_CLASSES_ROOT CLSID 
    (uuid класса  Java,   такое же,   как из  ODL-файла} InprocServer32  = msjava.dll 
    Javaclass  = имя СОМ-класса  из  ODL-файла 

    В частночти, запись для класса Observable, определенного в файле 
Observable, odl, выглядит так: 

    HKEY_CLASSES_ROOT CLSID 
    {27E955C3-13C6-lldO-AE38-444553540000} InprocServer32 = msjava.dll 
Javaclass = Observable 

    Для удобства в Visual J++ включена прикладная программа, позволяющая 
создавать эти записи. Программа JavaReg, находящаяся в каталоге \MSDEV\BIN, 
создает соответствующие записи в реестре Windows. 
    Для того чтобы программа JavaReg могла создавать записи реестра, ей 
необходимо передать три параметра командной строки. Например, приведенная 
далее командная строка (при запуске из каталога, в котором компилировался файл 
Observable.java) регистрирует класс Observable, описанный в файле Observable.odl: 

    \msdev\bin\javareg  /register  /class:Observable 
    /clsid:{27E955C3-13C6-lldO-AE38-444553540000} 


    Регистрация класса Java в качестве DCOM-объекта 

    Давно обещанная реализация DCOM (Distributed COM) осуществлена на 
платформе Window NT 4.0, Используя DCOM-объекты, можно вызвать их любые 
методы, которые имеются где-то в сети, даже через Internet. Что касается 
программирования на Java, то различий между созданием СОМ- и DCOM-объектов 
практически не существует. 
    Различия появляются в программе-сервере, используемой для вызова объекта 
Java из сети. Дело в том, что для обслуживания объекта Java, который должен 
быть доступен через DCOM, на машине сервера должна выполняться специальная 
локальная программа-сервер. 
    Чтобы объект Java был доступен просто через СОМ, для него нужно 
зарегистрировать внутризадачный сервер. Все объекты Java используют один и тот 
же СОМ-сервер MSJAVA.DLL. К сожалению, обслуживать DCOM-объекты с помощью 
внут^изадаанргр сервера невозможно. Нужен локальный сервер (программа с 
расширениемЕХЕ). Тем, кто не знаком с СОМ и DCOM, не "стоит беспокоиться по 
поводу указанных различий, поскольку решение этой проблемы достаточно просто. 
    В качестве "суррогата" внутризадачного сервера MSJAVA.DLL можно 
использовать саму программу JavaReg. Существует возможность "приказать" 
программе JavaReg загрузить сервер MSJAVA.DLL в свое адресное пространство и 
передавать ему все вызовы DCOM. Этот сервер вызывается так же, 

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

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

    javareg /register /class:MyPerformer 
    /clsid:{<J2E5AFC6-DBAA-llcf-BAFD-OOAA0057B223} 
    /surrogate 

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

    HKEY_CLASSES_ROOT CLSID 
    {27E955C3-13C6-lldO-AE38-444553540000} InprocServer32 = msjava.dll 
    Javaclass = Observable LocalServer32 = javareg 
    "-""/clsid:{42E5AFC6-DBAA-llcf-BAFD-OOAA0057B223} 
    /surrogate 

