Домашнее задание №2 для подготовки к занятиям по разработке сетевого приложения для Windows на С++
Цель задания Подготовиться к занятиям по разработке сетевого при-
ложения для Windows на языке С++. Изучить применение методов интерфейса IUnknown.
Для освоения работы с GUID, HRESULT, счетчиком ссылок, строками СОМ и логику IUnknown разработаем программу CoCarApp.exe. Она пока не является стандартным СОМ клиент/серверным приложением: объект СОМ создается и исполняется внутри одиночного исполняемого файла.
Кокласс CоСаr, построенный здесь, будет использоваться на следующем занятии, где мы поместим класс СОМ в исполняемый "домик" DLL, который затем будет доступен для СОМ-клиента.
Пример проекта данной работы содержится в каталоге
… \Ch3\CoCarApp3-1.
1. Проектирование интерфейса
Для проекта консольного приложения Win32, выберите Empty application. Разработаем класс, поддерживающий три пользовательских интерфейса. В предыдущем домашнем заданиия класс CCаr предоставлял все функциональные возможности автомобиля через единственный открытый интерфейс. Этот интерфейс можно разбить на логические группы (интерфейсы). Создадим новый заголовочный файл с именем interfaces.h и определим три новых интерфейса, которые моделируют поведение автомобиля:
/* IStats используется для получения имени автомобиля,а также для отображения параметров состояния реализуемого объекта */ DECLARE_INTERFACE_(IStats,IUnknown)
24
{
STDMETHOD(DisplayStats)() PURE; STDMETHOD(GetPetName)(BSTR* petName) PURE;
};
// IEngine моделирует поведение двигателя. //Можем ускорять его, получать значения //максимальной и текущей скорости
DECLARE_INTERFACE_(IEngine, IUnknown)
{
STDMETHOD(SpeedUp)() PURE; STDMETHOD(GetMaxSpeed)(int* maxSpeed) PURE; STDMETHOD(GetCurSpeed)(int* curSpeed) PURE;
};
/* ICreateCar используется для присвоения имени реализуемому объекту и задания максимальной скорости */
DECLARE_INTERFACE_(ICreateCar, IUnknown)
{
STDMETHOD(SetPetName)(BSTR petName) PURE; STDMETHOD(SetMaxSpeed)(int maxSp) PURE;
};
Все пользовательские интерфейсы СОМ должны быть произведены от IUnknown, так что не забудьте добавить
#include <windows.h> в файл. interfaces.h. Также добавьте к определению интерфейса директивы препроцессора
#ifndef /#define/ #endif, чтобы предотвратить ошиб-
ки повторного определения.
Присвойте GUID каждому пользовательскому интерфейсу. Создайте другой заголовочный файл с именем iid.h, который будет содержать описания макросов define_guid. Используя guidgen.exe, создайте три новых GUID с соответствующими константами (с префиксом IID_). Вот примеры GUID для каждого интерфейса (у вас будут, конечно же, другие):
// {A533DA30-D372-11d2-B8CF-0020781238D4}
25
DEFINE_GUID(IID_IEngine,
Oxa533da30, Oxd372, Ox11d2, Oxb8, Oxcf, 0x0, 0x20, 0x78, 0x12, 0x38, Oxd4);
//{A533DA31-D372-11d2-B8CF-0020781238D4} DEFINE_GUID(IID_IStats,
. . .
//{A533fiA32-D372-11d2-B8CF-0020781238D4} DEFINE_GUID(IID_ICreateCar,
. . .
Создайте еще один новый файл с именем iid.cpp. Он должен содержать только следующие ниже #include. He забудьте вставить его в рабочее пространство проекта (исполь-
зуйте меню Project | Add To Project | Files).
#include <windows.h> |
// Для всего, относящегося к СОМ |
#include <initguid.h> |
// Для DEFINE_GUID |
#include "iid.h" |
// IID |
Иметь один централизованный файл для всех GUID ваших приложений очень полезно, если вам потребуется включать определения в другие проекты. Полезно иметь и соответствующий файл *.срр для ID ваших интерфейсов.
2. Реализация IUnknown
Вставьте новый класс C++ с именем СоCаr, содержащий интерфейсы IEngine, IStats и ICreateCar. Понадобит-
ся реализовать не только методы каждого пользовательского интерфейса, но и три метода IUnknown. Вот начальный заголовочный файл для СоCаr:
// СоСаr реализует четыре интерфейса СОМ
#include "interfaces.h"
class CoCar : public IEngine, public ICreateCar, public IStats
{
public:
CoCar();
virtual ~CoCar(); // IUnknown
26
STDMETHODIMP QueryInterface(REFIID riid, void** pIFace); STDMETHODIMP_(ULONG)AddRef(); STDMETHODIMP_(ULONG)Release();
// IEngine
STDMETHODIMP SpeedUp();
STDMETHODIMP GetMaxSpeed(int* maxSpeed); STDMETHODIMP GetCurSpeed(int* curSpeed); // IStats
STDMETHODIMP DisplayStats(); STDMETHODIMP GetPetName(BSTR* petName); // ICreateCar
STDMETHODIMP SetPetName(BSTR petName); STDMETHODIMP SetMaxSpeed(int maxSp) ;
};
Мы не будем использовать cout в реализации методов интерфейсов Car. На этот раз возьмем функцию MessageBox() Win32 API. Это позволит нам перенести класс СоСаr в исполняемую DLL в следующей работе, не привязываясь к консольному клиенту Win32.
Сначала реализуем счетчик ссылок. Добавьте закрытый (private) счетчик ссылок с именем m_refcount (типа ULONG) к классу СоСаr и обнулите его в конструкторе. Функция AddRef() будет добавлять к счетчику единицы, a Release() — отнимать единицы и проверять на окончательный сброс, после которого объект сам себя выгрузит из памяти с помощью команды delete this.
// Схема счетчика ссылок объекта
STDMETHODIMP_(ULONG) CoCar::AddRef()
{
return ++m_refCount;
}
STDMETHODIMP_(ULONG) CoCar::Release()
{
if (--m_refCount == 0)
27
{
delete this; return 0;
}
else
return m_refCount;
}
Теперь реализуем Queryinterface(), чтобы возвращать указатели интерфейса для IUnknown, IStats, ICreateCar и IEngine. He забудьте вызвать AddRef(),
если вы предоставляете указатель интерфейса, и вернуть e_nointerface в случае отсутствия поддержки интерфейса:
//QueryInterfасе() отвечает за видимость
//указателя "this" для данного клиента
STDMETHODIMP CoCar::QueryInterface(REFIID riid, void** pIFace)
{
// Что конкретно от меня хотят? if(riid == IID_IUnknown)
{
*pIFace = (IUnknown*) (IEngine*)this;
}
else if (riid == IID_IEngine)
{
*pIFace = (IEngine*)this;
}
else if (riid = IID_IStats)
{
*pIFace = (IStats*) this;
}
else if (rud == IID_ICreateCar)
{
*pIFace = (ICreateCar*)this;
}
else
28