{
*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