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

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

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

}

STDMETHODIMP_(ULONG) CoCarClassFactory::Release () { if(--m_refCount == 0)

{

delete this; return 0;

}

return m_refCount;

}

//Фабрики класса не реализуют интерфейсы кокласса, который они создают!

STDMETHODIMP CoCarClassFactory::QueryInterface(REFIID riid, void** ppv)

{

//Какой интерфейс желаете?

if(riid == IID_IUnknown)

{

*ppv = (lUnknown*)this;

}

else if(riid == IID_IClassFactory)

{

*ppv = (IClassFactory*)this;

}

else

{

*ppv = NULL; return E_NOINTERFACE;

}

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

}

Теперь реализуем IClassFactory:: CreateInstance(). В этом методе создадим объект СоСаr и запросим нужный интерфейс. Вернем HRESULT, который клиент сможет проанализировать. Если в методе CreateInstance() что-то не сработает, выделенную под кокласс память надо освободить:

// Создаем СоСаr!

39

STDMETHODIMP CoCarClassFactory:: CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppv)

{

// В этом объекте класса агрегация не поддерживается if(pUnkOuter != NULL)

return CLASS_E_NOAGGREGATION; СоСаr* pCarObj = NULL;

HRESULT hr;

//Создаем автомобиль pCarObj = new CoCar;

//Запрашиваем интерфейс

hr = pCarObj -> QueryInterface(riid, ppv);

// Проблема? Освобождаем занятую память if (FAILED(hr))

delete pCarObj;

return hr;

}

Реализуем IClassFactory: :LockServer()

для работы со счетчиком блокировок. В конструкторе и деструкторе объекта класса добавьте соответствующий код для этого счетчика. Для обращения к глобальным переменным, определенным в CarInProcServer.cpp, используйте ключевое, слово extern в файле СРР вашего объекта класса:

//Завершаем построение фабрики класса extern ULONG g_lockCount;

extern ULONG g_objCount;

//Конструктор

CoCarClassFactory::CoCarClassFactory()

{

m_refCount = 0; g_objCount++;

}

//Деструктор

CoCarClassFactory::~CoCarClassFactory() { g_objCount--; }

40

// Блокировки сервера

STDMETHODIMP CoCarClassFactory::LockServer(BOOL fLock)

{

if (fLock)

++g_lockCount;

else

--g_lockCount;

return S_OK;

}

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

3. Реализация хранилища компонентов DLL

Реализуйте в файле CarlnProcServer.cpp метод

DllGetClassObject():

//Этот экспортируемый метод делает фабрики класса видимыми для

//менеджера управления сервисом (Service Control Manager)

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) { /*

1.Проверьте, совпадают ли CLSID и СоСаr, если нет — верните код ошибки

2.Создайте объект CoCarClassFactory

3.Запросите параметр REFIID у нового объекта класса

4.Установите HRESULT для клиента

*/

}

Последняя экспортируемая функция DLL — это

DllCanUnloadNow():

// Метод определяет, можно ли выгрузить DLL из памяти

STDAPI DllCanUnloadNow()

{

//Если g_objCount и g_lockCount оба равны нулю, возвращаем S_OK,

//в противном случае — S_FALSE

}

41

Ниже приводится возможный код для каждой из экспортируемых функций:

// Экспортируемые функции DLL

STDAPI DllCanUnloadNow ();

{

if (g_lockCount == 0 && g_objCount == 0)

return S_OK;

// Выгрузи меня

else

 

return S_FALSE;

// Оставь в памяти

}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)

{

HRESULT hr;

CoCarClassFactory *pCFact = NULL;

//Знаем, как в этом сервере создавать машины if(rclsid != CLSID_CoCar)

return CLASS_E_CLASSNOTAVAILABLE;

//Запрашивается CoCarClassFactory

pCFact = new CoCarClassFactory;

// Получаем интерфейс из CoCarClassFactory hr = pCFact -> QueryInterface(riid, ppv) ; if(FAILED(hr))

delete pCFact; return hr;

}

Создайте и вставьте в проект файл carinprocserver.def, чтобы сделать эти две функции видимыми окружающему миру. Имя в операторе library должно совпадать с именем проекта, который вы компонуете:

LIBRARY "CARINPROCSERVER" EXPORTS

DllGetClassObject @l PRIVATE DllCanUnloadNow @2 PRIVATE

42

4. Внесение информации в реестр

Чтобы завершить наш DLL-сервер, нужно создать еще REG-файл для внесения минимальной и полной информации о сервере в реестр системы. Создайте ProgID для вашего кокласса с подключом CLSID. Введите его прямо под HKCR. Затем введите CLSID для вашего кокласса с подключом InprocServer32, указывающим на физический путь к DLL.

Вот пример файла carinprocserver.reg:

REGEDIT

HKEY_CLASSES_ROOT\CarInProcServer.CoCar\CLSID = {7AD2D539-EE35-lld2-B8DE- 0020781238D4}

HKEY_CLASSES_ROOT\CLSID\{7AD2D539-EE35-lld2-B8DE-0020781238D4} = CarlnProcServer.CoCar

HKEY_CLASSES_ROOT\CLSID\{7AD2D539-EE35-lld2-B8DE-0020781238D4} \InprocServer32 = E:\ATL\Labs\Chapter03\CarInProcServer \Debug\CarInProcServer.dll

После того как сформируете REG-файл, просто щелкните по нему дважды в Проводнике Windows, чтобы записать информацию в реестр. Используйте regedit.exe для проверки правильности занесения информации.

Итак, создан СОМ-сервер на обычном C++ и без всяких библиотек. Позже построим клиент СОМ на C++, обращающийся к этому серверу.

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

1.Для чего служит фабрика класса?

2.Какая информация записывается в реестр системы о распределённых COM-программах?

43

Практическое занятие № 6 Разработка клиента распределённого приложения для

Windows на С++

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

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

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

В этой работе спроектируем СОМ-клиента, который будет использовать созданный на предыдущем практическом занятии СОМ-сервер СоCаr. Вы осуществите запрос SCM с помощью библиотеки СОМ, будете оперировать с указателями интерфейса, работать с bstr и создавать фабрики класса. Завершится процесс преобразования CoCаr. Вот его этапы:

структура с набором глобальных функций; простой объект C++;

объект C++ с множественными интерфейсами; исполняемая DLL на базе СОМ.

Готовый проект находится в каталоге … \Ch3\CoCarClient3-3.

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

Создадим новое консольное приложение Win32 с именем CoCarClient, на этот раз выберите simple project (простой проект). Прежде чем использовать CarInProcServer.dll, необходимо внести в рабочее пространство следующие файлы:

-iid.h и iid.cpp. Нужны константы GUID. Вставьте файл СРР в проект;

-interfaces.h. Нужны имена интерфейсов, чтобы определить переменные указателей к ним;

-включите также заголовочные файлы в начало файла

cocarclient.cpp.

44

После копирования этих файлов в подкаталог проекта, можно приступать к созданию кода клиента

#include "interfaces.h" #include "iid.h" #include <iostream.h> int main()

{

return 0;

}

2. Реализация функции main()

Реализуйте в головном блоке следующие функциональные возможности.

// Код клиента на C++ int main(void)

{

//Инициализация подсистемы СОМ

//Получение указателя IClassFactory из CoCarClassFactory

//Вызов Createlnstance() из pICF, запрос IID_ICreateCar из СоСаr

//Выполнение ICreateCar.

//Запрос IID_IStats из указателя ICreateCar или IUnknown

//Вызов DisplayStats() из pIStats QI для IEngine

//Разгоняем машину

// Используя pIStats->GetPetName(), выводим BSTR на консоль,

//информируя пользователя о "кончине" машины

//Освобождаем все полученные указатели интерфейсов

//Завершаем подсистему СОМ

return 0;

}

Большая часть кода клиента совпадает с приложением CоСаrАрр (см. домашнее задание №2). Вот полный листинг:

// Программа СОМ-клиента

//

45

int main()

{

CoInitialize(NULL); HRESULT hr; IClassFactory* pCF = NULL;

ICreateCar* pICreateCar = NULL; IStats* pStats = NULL;

IEngine* pEngine = NULL;

cout << "**********************" << endl; cout << "The Amazing CoCar Client" << endl; cout << "**********************" << endl;

// Получить указатель фабрики класса CoCar.

hr = CoGetClassObject(CLSID_CoCar, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void**)&pCF);

// Создать CoCar & получить ICreateCar.

hr = pCF->CreateInstance(NULL, IID_ICreateCar,(void**)&pICreateCar); pCF ->Release();

if (SUCCEEDED (hr) )

{

pICreateCar->SetMaxSpeed(30) ;

BSTR petName = SysAllocString(L"Shazzam! "); pICreateCar->SetPetName (petName) ; SysFreeString (petName) ;

// Теперь получаем IStats

hr = pICreateCar->QueryInterface(IID_IStats, (void**) SpStats) ; pICreateCar->Release( ) ;

}

if (SUCCEEDED(hr) )

{

//Указываем данные машины и получаем IEngine pStats->DisplayStats( ) ;

hr = pStats->QueryInterface(IID_IEngine, (void**) &pEngine) ;

}

// Разгоняемся!

46

if (SUCCEEDED(hr) ) { int curSp = 0; int maxSp = 0;

pEngine->GetMaxSpeed(&maxSp) ; do{

pEngine->SpeedUp ( ) ; pEngine-X3etCurSpeed(&curSp) ; cout << "Speed is: " << curSp << endl;

}while(curSp <= maxSp) ;

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

BSTR bstr; pStats->GetPetName(Sbstr) ;

WideCharToMultiByte (CP_ACP, NULL, bstr, -1, buff, 80, NULL, NULL); cout << buff << " has blown up! Lead Foot!" << endl << endl;

//Убираем за собой

SysFreeString(bstr) ; if (pEngine) pEngine->Release( ) ; if (pStats) pStats->Release( ) ;

}

CoUninitialize(); return 0;

}

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

1.Каким образом клиентская программа отыскивает соответствующую серверную программу?

2.Как формируется значение CLSID?

3.Для чего служат переменные типа HRESULT?

47

Практическое занятие № 7 Особенности разработки распределённых приложений для

Windows с применением различных языков

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

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

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

Эта работа позволяет поэкспериментировать с языковой независимостью модели СОМ. В ней расширяется разработанный ранее сервер CarInProcServer.dll до поддержки информации о типе и применяются файлы, сгенерированные компилятором MIDL. После обновления сервера СоСаr появляется возможность построить клиентские приложения на Visual Basic и Java, а также клиента C++ с использованием директив компилятора СОМ.

Готовые решения находятся в каталогах:

… \Ch4\CarServerTypeInfo: СоСаг с библиотекой типов;

… \Ch4\CarServerTypeInfo\VB Client: клиент на Visual Basic;

… \Ch4\ CarServerTypeInfo\VJ Client: клиент на J++;

… \Ch4\ CarServerTypeInfo\CPP Import Client: клиент

C++ с библиотекой типов.

В этой работе расширяется сервер CarInProcServer.dll (см. практическое занятие №6) для поддержки информации о типе. Сначала откройте рабочее пространство CarInprocServer (или сделайте копию проекта) и вставьте в проект новый файл (через меню File | New...) с именем CarServerTypeInfo.idl. В этом файле будут находиться все определения интерфейсов, коклассов и библиотек сервера.

48