Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Учебное пособие 1381

.pdf
Скачиваний:
3
Добавлен:
30.04.2022
Размер:
1.08 Mб
Скачать

{

*pIFace = NULL;

return E_NOINTERFACE;

}

((IUnknown*)(*pIFace))->AddRef(); return S_OK;

}

З. Реализация методов пользовательских интерфейсов

Закончив с IUnknown, переходим к реализации пользовательских интерфейсов CоСаr. Добавьте в закрытый сектор следующие данные и инициализируйте их в конструкторе. Используйте корректные функции системных строк при работе с типом bstr:

// Закрытые (Private) данные класса СоСаr

BSTR m_petName; //Инициализация через SysAllocString(); // удаление — вызовом SysFreeString()

int

m_maxSpeed;

// Максимальная скорость объекта СоСаr

int

m_currSpeed;

// Текущая скорость СоСаr

// Конструктор и деструктор СоСаr

СоСаr::СоСаr() : m_refCount(0), m_currSpeed(0), m_maxSpeed(0)

{

m_petName = SysAllocString(L"Default Pet Name");

}

 

CoCar::~CoCar()

 

{

 

if (m_petName)

SysFreeString(m_petName);

}

 

Реализация IEngine проста. Speedup() увеличивает значение m_currspeed на 10 и возвращает S_OK. GetMaxSpeed()

позволяет получить значение переменной m_maxSpeed, a

GetCurrSpeed() — значение m_currSpeed:

// Реализация IEngine STDMETHODIMP CoCar::Speedup()

29

{

m_currSpeed += 10; return S_OK;

}

STDMETHODIMP CoCar::GetMaxSpeed(int* maxSpeed)

{

*maxSpeed = m_maxSpeed; return S_OK;

}

STDMETHODIMP CoCar::GetCurSpeed(int* curSpeed)

{

*curSpeed = m_currSpeed; return S_OK;

}

Интерфейс ICreateCar имеет два метода. SetPetName() принимает заданную клиентом bstr и помещает ее во внутренний буфер bstr, используя

SysReAllocstring(). SetMaxspeed() делает то же, что и раньше: устанавливает максимальную скорость, следя при этом, чтобы она не превышала константу max_speed (которую вы должны задать в заголовочном файле кокласса):

// Реализация ICreateCar

STDMETHODIMP CoCar::SetPetName(BSTR petName)

{

SysReAllocString(&m_petName, petName); return S_OK;

}

STDMETHODIMP CoCar::SetMaxSpeed(int maxSp)

{

if (maxSp < MAX_SPEED) m_maxSpeed = maxSp; return S_OK;

}

И, наконец, остается IStats. GetPetName() возвращает копию внутреннего буфера клиенту. Для этих целей сто-

ит использовать SysAllocString():

// Возвращает клиенту копию внутреннего буфера BSTR STDMETHODIMP CoCar::GetPetName(BSTR* petName)

30

{

*petName = SysAllocString(m_petName); return S_OK;

}

Метод DisplayStats() просто предоставляет два блока сообщений для отображения текущей скорости и имени машины. Единственная заслуживающая внимания деталь в нем

— преобразование Unicode в ANSI:

// Информация о СоСаr идёт в блоки сообщений

STDMETHODIMP CoCar::DisplayStats()

{

// Преобразовать BSTR в массив символов

char buff[MAX_LENGTH]; //Программная константа WideCharToMultiByte(CP_ACP, NULL, m_petName, -1, buff, MAX_LENGTH,

NULL, NULL);

MessageBox(NULL, buff, "Pet Name", MB_OK | MB_SETFOREGROUND); memset(buff, 0, sizeof(buff));

sprintf(buff, "%d", m_maxSpeed);

MessageBox(NULL, buff, "Max Speed", MB_OK| MB_SETFOREGROUND); return S_OK;

}

4.Создание функции начальной загрузки

Вреальной модели СОМ-объекты создаются с помощью библиотечных вызовов. Стандартный интерфейс COM

IClassFactory получается от сервера, и от него клиент может создать соответствующий кокласс.

Итак, создадим специальную функцию кoторая знает, как cделать CoCar, и возвращает ссылку на IUnknown. Поместите эту глобальную функцию, именуемую CarFactory(), в

файл СoСаrАрр.срр:

//CarFactory() — глобальная функция, а не часть СоСаr.

//Если присоединим этот метод к СоСаr, то

//мы должны будем создать машину, чтобы создать машину...

31

HRESULT CarFactory(void** pIFace)

{

HRESULT hr;

LPUNKNOWN pUnk = NULL; // typedef для IUnknown

//Динамически отводим память под новый объект СоСаr

СоСаr *pCar = new СоСаr();

//Получаем указатель IUnknown из СоСаr

hr = pCar->QueryInterface(IID_IUnknown, (void**)&pUnk); if(SUCCEEDED(hr))

{

*pIFace = pUnk; return S_OK;

}

else

{

delete pCar; return E_FAIL;

}

}

Теперь динамически можем создавать любое количество объектов СоСаr. Остается написать код клиента.

5. Разработка кода стороны клиента

Нужно добавить код в функцию main () для выполнения следующих операций.

1. С помощью вызова CarFactory() создать объект СоСаr. Для этого необходимо определить переменную IUnknown*, чтобы передать ее в функцию.

// Получаем IUnknown из нового объекта СоСаr

IUnknown* pUnk; HRESULT hr;

hr = CarFactory((void**) SpUnk);

2. Из полученного указателя IUnknown пытаемся получить IID_ICreateCar. Если это проходит успешно, выполняем

32

методы интерфейса:

//Создаем СоСаr ICreateCar *pICreateCar;

pUnk->QUeryInterface(IID_ICreateCar, (void**)&pICreateCar); pICreateCar -> SetMaxSpeed(30);

BSTR name = SysAllocString(OLESTR("Bertha")); pICreateCar -> SetPetName (name); SysFreeString(name);

3.Получаем интерфейс IStats от CoCar и вызываем

DisplayStats().

4.Теперь запрашиваем IEngine. Учитывая логику IUnknown, вы имеете право запросить IEngine из любого допустимого указателя, следовательно, можно вызвать

QueryInterface() из указателей IUnknown или ICreateCar. Используйте IStats::GetPetName() для получения имени кокласса, которое мы можем преобразовать в строку ANSI с помощью WideCharToMultiByte() или wcstombs(), чтобы проинформировать пользователя о выходе за допустимые пределы параметров.

//Ускорение!

int curSp = 0; int maxSp = 0;

pUnk->QueryInterface(IID_IEngine, (void**)&pIEngine); pIEngine -> GetMaxSpeed(&maxSp);

do

{

pIEngine -> SpeedUp();

pIEngine -> GetCurSpeed(&curSp); cout << "Speed is: " << curSp << endl;

} while(curSp <= maxSp);

// Преобразуем в масссив char char buff[MAX_NAME_LENGTH]; BSTR bstr;

pStats -> GetPetName(&bstr);

33

wcstombs(buff, bstr, MAX_NAME_LENGTH); cout << buff << " взорвалась! " << endl << endl; SysFreeString(bstr);

// Освобождаем все полученные интерфейсы, чтобы удалить СоСаr if(pUnk) pUnk->Release();

if(pIEngine) pIEngine->Release(); if(pStats) pStats->Release(); if(pICreateCar) pICreateCar->Release();

СоСаr пока еще не является бинарным (исполняемым) объектом, годным для повторного использования. Чтобы достичь этой цели, нам необходимо познакомиться с фабриками класса и хранилищами объектов.

Контрольные вопросы

1.Каково назначение методов интерфейса IUnknown?

2.Назовите правила применения этих методов.

3.Что значит «создание объекта»?

4.Как создаются объекты в модели COM?

5.Для чего применяется GUID?

34

Практическое занятие № 5 Разработка (внутрипроцессного) сервера распределённого

приложения для Windows на С++

Цель практического занятия Изучить и освоить разработку внутрипроцессного сер-

вера распределённого приложения для Windows, получить практические навыки по разработке сетевых программ на языках С, С++.

Теоретические сведения:

Будет разработан первый настоящий СОМ-сервер. Возьмем класс СоСаr, разработанный дома, и создадим фабрику класса для активизации его по запросу клиента. Также поместим новые объекты в Win32 DLL, реализуем экспорт

(DllGetciassObject() и DllCanUnloadNow()) и соз-

дадим регистрационный файл для внесения CОM-информации в реестр системы. Пример проекта находится в каталоге … \Сh3\CarInProcServ3-2

1. Подготовка рабочего пространства проекта и перенос

СоСаr

Начнем с создания нового пустого рабочего пространства проекта Win32 DLL с именем СarInProcServer (рис. 3).

Добавьте новый файл с именем CarInProcServer.cpp. Объявите две глобальных переменных типа ulong для счетчика блокировок и счетчика объектов. Убедитесь, что новый файл вставлен в рабочее пространство проекта:

#include <windows.h>

//Количество блокировок сервера

ULONG g_lockCount =0;

//Количество "живых" объектов в сервере

ULONG g_objCount =0;

35

Рис. 3. Новый проект Win32 DLL

Кокласс CоСаr и соответствующие интерфейсы, определенные в предыдущей работе, легко переносятся и в этот проект. Скопируйте следующие файлы из каталога прошлой работы в текущий каталог данной работы:

СoСar.h и СoCar.cpp - кокласс Car; iid.h и iid.cpp - файлы GUIP;

interfaces.h - пользовательские интерфейсы. Вставьте файлы *,срр в пространство проекта (с помо-

щью Project | Add To Project | Files...) и откомпилируйте. За-

головочные файлы вручную можно не включать, т. к. они вставляются автоматически.

Поскольку данная версия CоCаr будет размещена в двоичном файле, нам потребуется CLSID для уникальной идентификации CоCаr в среде СОМ. Сгенерируйте новый GUID (с помощью guidgen.exe), кoторый станет CLSID для CоCаr.Определите константу CLSID_CoCar в качестве первого параметра макроса define_guid. Добавьте этот GUID в ваш существующий файл iid.h.

36

Чтобы закончить перенос исходного кода, нужно реализовать увеличение и уменьшение глобального счетчика объектов в конструкторе и деструкторе класса СоСаr. Мы должны информировать DLL-сервер о том, сколько объектов на данный момент «живут» в нем. Используйте ключевое слово extern для обращения к глобальной переменной g_objCount внутри класса СоСаr:

// CоCаr.срр

extern ULONG g_objCount;

СоСаr::СоСаr() : m_refCount(0), m_currSpeed(0), m_maxSpeed(0) { ++g_objCount; // Прочий код...

}

СоСаг::~CoCar()

{

--g_objCount; // Прочий код...

}

IDE поместил все файлы *.h в каталог External Dependencies окна FileView и определения интерфейсов не показываются в ClassView. Можно перенести их в каталог Header Files, после чего интерфейсы станут видимыми из

ClassView (рис. 4).

Рис. 4. Исправленный ClassView.

37

2. Разработка фабрики класса для СоСаr

Для создания СоСаr понадобится фабрика класса. Вставьте новый класс с именем coCarClassFactory, производный от IClassFactory. Вспомните, что объект класса обеспечивает языково-независимый способ создания объектов СОМ. Вот начальное определение объекта класса:

#include <windows.h>

class CoCarClassFactory:public IClassFactory

{

public:

CoCarClassFactory();

virtual ~CoCarClassFactory(); // IUnknown

STDMETHODIMP QueryInterface(REFIID riid, void** piFace); STDMETHODIMP_(ULONG)AddRef(); STDMETHODIMP_(ULONG)Release();

// IClassFactory

STDMETHODIMP LockServer(BOOL fLock); STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppv);

private:

ULONG m_refCount; // Устанавливается в нуль в конструкторе!

};

Начнем c реализации методов IUnknown, AddRef(), Release() и QueryInterfасе().

Код почти полностью совпадает с реализацией IUnknown для СоСаr, за исключением того, что на этот раз QueryInterfасе о проверяет только IID_IUnknown и IID_IClassFactory:

// Реализация IUnknown в фабрике класса

STDMETHODIMP_(ULONG) CoCarClassFactory::AddRef()

{

return ++m_refCount;

38