Часть 1: Дублирование интерфейсов
В вышеприведенном случае с Ричи Ричем мы видели, что DLL для аквариума и DLL для топливного бака не могли находиться на одном и том же компьютере, потому что ни клиентское приложение, ни две DLL не были COM компонентами. Какая бы DLL ни была скопирована на компьютер, только скопированная последней будет использоваться клиентским приложением. Как мы уже видели, использование некорректной DLL может привести к катастрофическим результатам: вертолет разбился. Мы предположили, что если бы разработчик программы использовал технологию COM, то он имел бы обе DLL на машине. Поскольку две DLL были бы различимы по их CLSID, они могли бы использоваться в пределах одного приложения. По технологии COM обе DLL должны задействовать идентичные методы через заменяемые интерфейсы.
Чтобы это доказать, мы собираемся создать единственное GUI-приложение, которое использует и показывает информацию, получаемую от двух серверов COM: GasTankLevelGetter DLL и FishTankLevelGetter DLL. Мы также создадим одно приложение, которое будет получать информацию от каждой COM DLL и отображать их. Опрос каждой DLL будет происходить попеременно по четырехсекундному таймеру. Чтобы подчеркнуть неизменность интерфейсов и что COM является двоичным стандартом, мы собираемся написать GUI-приложение FishTankLevelGetter COM DLL исключительно на основе информации о GasTankLevelGetter COM DLL. Однако мы не собираемся предоставлять вам исходный код GasTankLevelGetter COM DLL. Если вы переписали пример, вы найдете GasTankLevelGetter COM DLL в папке Binaries. Мы вам даже не скажем на чем написана GasTankLevelGetter: на Delphi, Visual C++, Java(tm), Cobol, Turbo Pascal или Visual Basic. Вам, однако, придется зарегистрировать GasTankLevelGetter DLL с помощью RegSvr32.
Как только вы зарегистрировали GasTankLevelGetter DLL с помощью RegSvr32, вы готовы начать, вооружившись OLE/COM Object Viewer. Если вы используете Visual C++ 5.0, OLE/COM Object Viewer находится в программной группе Visual C++ 5.0 при навигации через Start | Programs в Explorer.
Если у вас нет OLE/COM Object Viewer, спишите его из http://www.microsoft.com/oledev/ и запустите приложение.
Запустив OLE/COM Object Viewer, выберите режим View | Expert для просмотра Type Libraries. Пролистайте список и откройте папку под названием Type Libraries. Пролистайте папку пока не найдете GasTankLevelGetter 1.0 TypeLibrary (Ver 1.0). Выделите этот элемент списка и вы увидите на правой панели ID библиотеки типа и ее полный путь, как показано на рисунке.
Двойной щелчок на GasTankLevelGetter откроет окно, отображающее всю библиотеку типа. Эта информация берется их данных регистров, которые создаются при регистрации DLL. Данные по TypeLib хранятся в HKEY_CLASSES_ROOT \ TypeLib.
Раздел coclass содержит список поддерживаемых интерфейсов для компонентного объекта. Объект может иметь любое количество интерфейсов, перечисляемых в его теле и полностью описывающих тот набор интерфейсов, которые этот объект реализует, как входящих, так и исходящих. Ниже приведены CLSID и интерфейс, содержащиеся в coclass для данного COM объекта:
CLSID: 8A544DC6-F531-11D0-A980-0020182A7050
Interface Name: ILevelGetter
[ uuid(8A544DC6-F531-11D0-A980-0020182A7050), helpstring("LevelGetter Class") ] coclass LevelGetter { [default] interface ILevelGetter; }; Раскрывая далее информацию по интерфейсу наподобие coclass, мы можем определить:
- ID интерфейса 8A544DC5-F531-11D0-A9 80-0020182A7050.
- Интерфейс наследуется от IUnknown.
- Интерфейс поддерживает методы. Первые три метода возвращают значения типа long, а четвертый - указатель на BSTR.
Теперь, поскольку мы знаем, как построить интерфейс ILevelGetter, давайте создадим наш собственный компонент COM на основе этой информации. Если вы решили работать с существующим примером, все источники находятся в папке LevelViewer. Запустите Visual C++ 5.0 и создайте новый проект. Определите тип ATLComAppWizard как проект и "FishTankLevelGetter" как имя проекта. Мы полагаем, что вы создали новую папку проекта. Окно New Project Dialog должно выглядеть как это показано ниже.
В AppWizard для Server Type укажите Dynamic Link Library (DLL). Отметьте обе опции Allow merging of proxy/stub code и Support MFC.
Когда вы создали новый проект FishTankLevelGetter, выберите в меню Insert | New Class... для создания нового ATL класса. Вы можете выбрать любое имя класса, но убедитесь, что интерфейс называется IlevelGetter, а его тип - Custom, что указывает на наследование ILevelGetter от IUnknown. Если бы ILevelGetter в GasTankLevelGetter COM DLL наследовалась от IDispatch, нам пришлось бы выбрать тип интерфейса Dual, который указывал бы на то, что новый интерфейс будет производным от IDispatch. Если диалог New Class выглядит как показано ниже, нажмите OK, чтобы создать новый класс.
Следующий шаг заключается в редактировании FishTankLevelGetter.IDL. В IDL файле вам нужно иметь новый интерфейс ILevelGetter, наследуемый из IUnknown. Если вы работаете с примерами, вы увидите следующий код, который содержит четыре одинаковых неизменяемых метода интерфейса IlevelGetter, которые мы видели в интерфейсе ILevelGetter GasTankLevelGetter.
Наиболее просто добавить код с помощью " copy and paste" непосредственно из окна ITypeLib Viewer. Ваш код должен выглядеть точно также, как в примере, за исключением ID интерфейса.
Откройте LevelGetter.H и объявите методы в классе. В вашем классе объявление методов должно выглядеть как это показано ниже: