Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТП лекции Раздел 4.doc
Скачиваний:
16
Добавлен:
28.09.2019
Размер:
2.56 Mб
Скачать

4.13.2. Определение пользовательского интерфейса.

Для определения нашего пользовательского интерфейса нужно создать новый текстовый файл и назвать его FirstSrv.idl, как показано в лис­тинге 2.2. Этот файл используется для на­писания нашего определения интерфейса на языке IDL. Мы хотим определить компонент FirstComponent, который реализует пользовательский интерфейс IFirstInterfасе. Интерфейс будет иметь единственный метод DoSomething() .

Листинг 2.2. Файл FirstSrv содержит описание интерфейса и библиотеки типов (FirstSrv. idl) на языке IDL

// FirstSrv.idl

import "oaidl.idl";

[

object,

uuid(Поместите сюда GUID1)

]

interface IFirstInterface : IUnknown

{

HRESULT DoSomething();

};

[

uuid(Поместите сюда GUID2),

version(1.0)

]

library FirstTypeLib

{

importlib("stdole32.tlb") ;

[

uuid(Поместите сюда GUID3)

]

coclass FirstComponent

{

[default] interface IFirstInterface;

};

};

Введите программный код из листинга 2.2 в созданный текстовый файл. По­требуется сгенерировать три значения GUID: по одному для идентификатора ID интерфейса (ПD), ID (LIBID) библиотеки типов и ID (CLSID) для класса компонента CoClass. Чтобы сгенерировать значения GUID, можно запустить программу GUIDGEN.EXE (один из инструментов пакета Visual Studio) и скопировать GUID в Registry Format (Формат системного реестра

Сохраните файл и откройте командную строку. Смените каталог на тот, в котором вы хотите сохранить файл, и запустите следующую команду компиляции файла IDL (лучше включить IDL в проект):

midl FirstSrv.idl

Программа MIDL.EXE является компилятором MIDL. Этот компилятор чи­тает файл IDL и генерирует следующие ключевые файлы.

• Прокси-файл интерфейса (interface proxy file) firstsrv_p.с, содержащий программный код маршализации для интерфейса IFirstInterfасе, определенного в файле IDL.

• Файл заголовка firstsrv.h, содержащий интерфейс и определения типов C++. Он также объявляет символьные константы для идентификаторов ин­терфейса ID (IID_IFirstInterfасе) и класса компонентов CLSID (CLSID_FirstComponent) класса компонента.

• Файл firstsrv_i.c идентификаторов UUID интерфейса, содержащего определения GUID для идентификаторов IID, CLSID и LIBID, объявленных в файле заголовка.

• Библиотеку типов firstsrv.tlb, являющуюся бинарной версией файла IDL.

4.13.3. Реализация пользовательского интерфейса.

Имея определение нашего интерфейса, можно приступать к реализации тре­буемого интерфейса IUnknown. Запустите приложение Visual Studio и создайте новый проект Win32 DLL - FirstSrvDll. Выберите опцию создания пустого проекта DLL, и щелкните на кнопке Finish (Готово). Добавьте в проект новый файл заголовка C++, и назовите его FirstComponent.h. В этот файл мы поместим определение и реализацию нашего компонента.

Листинг 2.3. Класс CFirstComponent, реализующий интерфейс

IFirstInterface (FirstComponent.h)

#include <windows.h>

#include "firstsrv.h"

class CFirstcomponent : public IFirstInterface

{

public:

CFirstcomponent() : m_ulRefCnt(0)

{ }

~CFirstComponent()

{ }

// Методы интерфейса IUnknown

STDMETHOD (QueryInterface)(REFIID riid, void** ppv)

{

if (riid==IID_IUnknown)

*ppv=static_cast<IUnknown *>(this);

else if (riid==IID_IFirstInterface)

*ppv=static_cast<IFirstInterface *>(this);

else

*ppv=NULL;

if(*ppv!=NULL)

{

static_cast<IUnknown *>(*ppv)->AddRef();

return S_OK;

}

else

return E_NOINTERFACE;

}

STDMETHOD_ (ULONG, AddRef)()

{

InterlockedIncrement(reinterpret_cast<LPLONG>(&m_ulRefCnt));

return m_ulRefCnt;

}

STDMETHOD_ (ULONG, Release)()

{

if(!InterlockedDecrement(

reinterpret_cast<LPLONG>(&m_ulRefCrit)))

{

delete this;

return 0;

}

return m_ulRefCnt;

}

// Методы IFirstInterface (здесь только один метод)

STDMETHOD (DoSomething)()

{

MessageBox(NULL, "We did it! (Мы сделали это!) ",

"First COM Server (Первый сервер COM) ",MB_OK +

MB_ICONINFORMATION);

}

private:

ULONG m_ulRefCnt;

}

Примечание

В отличие от большинства типов данных в VC++, начинающихся с символа Н, тип hresalt ничего не обрабатывает. Большинство вызовов методов и функций СОМ библиотеки API возвращают тип данных hresult, указывающий на успешное или ошибочное завершение вызова, предоставляя информацию о причине ошибки. При работе со значением hresult вы не должны самостоятельно интерпретировать результаты; вместо этого используйте макросы обработки ошибок СОМ. Обычно это макрос failed, возвращающий true, если функция завершилась с ошибкой, и дополняющий его макрос succeeded, который возвращает true, если вызванная функция завершилась успешно.

Исследуем полученный программный код и посмотрим, что он выполняет. Во-первых, мы включили в него файл windows.h, который содержит объявле­ния некоторых функций API, которые понадобятся нам в дальнейшем. Кроме того, мы включили в проект файл firtsrv.h, сгенерированный компилято­ром IDL. Этот файл содержит определение нашего интерфейса. Объявлен класс CFirstComponent, который порождается из IFirstInterface (опреде­ление и нтерфейса взято из firstsrv.h).

Конструктор класса просто инициализирует переменную-элемент класса m_ulRefCnt значением 0. После объявления деструктора класса мы приступаем к реализации методов для интерфейса IUnknown. Макросы STDMETHOD и STDMETHOD_ используются для описания типа возвращаемого результата и соглашения о вызовах функций. Макрос STDMETHOD подразумевает, что воз­вращаемый функцией тип данных является HRESULT, который пригоден почти для всех методов интерфейса СОМ. Макрос STDMETHOD_ позволяет опреде­лить возвращаемый функцией тип данных в своем первом параметре. Поскольку методы AddRef и Release возвращают счетчик ссылок (reference count), для их определения использован метод STDMETHOD_. Метод QueryInterfасе определяется с помощью макроса STDMETHOD, возвращающего HRESULT. Можно было объявить методы более просто, выполнив это следующим образом.

virtual HRESULT _stdcall QueryInterfасе (REFIID riid, void **ppv);

virtual ULONG _stdcall AddRef();

virtual ULONG _stdcall Release();

Хотя такое объявление методов совершенно корректно, оно сильно зависит от систем Win32. Использование же макросов позволяет компилировать наш код на различных системах, к примеру Macintosh (и, может быть, даже Win64!). Другой макрос, STDMETHODIMP, подобен макросу STDMETHOD, но не определяет метод как виртуальный.

Метод QueryInterface проверяет запрашиваемый интерфейс и определяет, поддерживается ли он данным компонентом. Для этого он сравнивает пере­менную riid с идентификаторами интерфейсов IUnknown (IID_IUnknown) и HD_FirstInterfасе, а затем возвращает соответствующий указатель, приводя указатель this к типу соответствующего базового класса. Заметьте: перед возвратом указателя запрашиваемого интерфейса мы должны с помощью AddRef добавить эту ссылку. Это правило СОМ, гарантирующее, что компо­нент внезапно не исчезнет после использования его интерфейса клиентом.

Функции AddRef и Release имеют подобные функциональные возмож­ности. Они обе используют средства Win32 API для получения монопольного доступа к переменной m_ulRefCnt с целью ее уменьшения или увеличения. Мы сделали это для обеспечения "дружественности к потокам" (thread-friendly). Если бы для добавления ссылок использовалось что-то наподобие операции m_ulRefCnt ++, то при исполнении множества потоков могли бы возникнуть проблемы, вызванные их конкуренцией. Кроме уменьшения счетчика ссылок, Release удаляет экземпляр объекта, если содержимое счетчика ссылок равно нулю. Подразумевается, что объект создан с помощью оператора new; поэтому мы удаляем его с помощью оператора delete.

Теперь мы разобрались с интерфейсом lUnknown. и можем реализовать IFirstInterfасе. Метод DoSomething — единственный метод этого интер­фейса. Его реализация отображает окно с сообщением о нашем успехе.