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

Прога лабы 5 семестр

.pdf
Скачиваний:
20
Добавлен:
11.02.2015
Размер:
728.33 Кб
Скачать

41

Рис. 31. Выбор конфигурации проекта

Отметьте пункт Automation (чтобы узнать для чего это нужно нажмите правой кнопкой слово Automation и выберете пункт меню “Что это такое?”). Нажмите Finish.

Добавим в проект класс производный от CСmdTarget (для этого необходимо на вкладке ClassView щёлкнуть правой кнопкой на первой строчке и затем выбрать пункт New Class).

42

Рис. 32. Добавление класса производного от CСmdTarget

В качестве имени класса выберете любое имя, а также отметьте пункт Automation. Оставим на время этот класс и создадим пустой заголовочный файл (файл с расширением .h), в котором будет находиться определение нашего интерфейса. Для этого выберем пункт меню Project / Add to Project / New… Появится окно, изображённое на

рис.33.

43

Рис. 33. Добавление заголовочного файла к проекту

Не забудьте отметить галочкой пункт Add to Project. Нажмите OK.

Добавим в него пользовательский интерфейс имеющий одну пользовательскую функцию, которая будет считать сумму двух чисел:

class ICalc : public IUnknown

{

public:

// пользовательская функция нашего интерфейса

STDMETHOD_(int, Sum) (int first, int second)=0;

protected:

// Additional Protected Declarations

private:

// Additional Private Declarations };

Интерфейс объявляется как класс производный от IUnknown. Все функции определяются определённым образом в виде чисто

виртуальных функций (=0).

Теперь необходимо сгенерировать для каждого интерфейса уникальный идентификатор. Для этого воспользуемся утилитой guidgen.exe. С её помощью сгенерируем GUID 3-го типа (см. рис.34) и вставим его в созданный заголовочный файл.

44

Рис. 34. Диалог программы guidgen.exe

Теперь заголовочный файл должен выглядеть следующим образом (за исключением уникального идентификатора – на его месте будет ваше число):

// {45BDA284-6188-11d5-92D5-94C5C6E8C950} static const IID IID_ICalc =

{ 0x45bda284, 0x6188, 0x11d5, { 0x92, 0xd5, 0x94, 0xc5, 0xc6, 0xe8, 0xc9, 0x50 } };

class ICalc : public IUnknown

{

public:

STDMETHOD_(int, Sum) (int first, int second)=0; protected:

//Additional Protected Declarations private:

//Additional Private Declarations

};

Вернёмся к созданному нами классу. Для начала подключим к файлу определения класса (.h) только что созданный нами заголовочный файл с помощью директивы препроцессора include (например, #include "Yur_interface.h").

Теперь добавим в определение класса следующие макросы и функции (в его protected-

секцию):

DECLARE_OLECREATE(имя класса) – класс работает с OLE; BEGIN_INTERFACE_PART(имя вложенного класса, имя пользовательского интерфейса)

// здесь должен располагаться список пользовательских функций

45

// интерфейса

END_INTERFACE_PART(имя вложенного класса)

Пара последних макросов создаёт вложенный класс, который наследуется от пользовательского интерфейса. В панели Workspace вложенные классы отличаются от остальных наличием латинской буквы X, которая предшествует имени вложенного класса.

После добавления выше перечисленных макросов protected-секция должна выглядеть

так:

virtual ~CYur();

// Generated message map functions //{{AFX_MSG(CYur)

// NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG

DECLARE_MESSAGE_MAP()

DECLARE_OLECREATE(CYur)

BEGIN_INTERFACE_PART(Calc, ICalc)

STDMETHOD_(int, Sum)(int first, int second);

END_INTERFACE_PART(Calc)

// Generated OLE dispatch map functions //{{AFX_DISPATCH(CYur)

// NOTE - the ClassWizard will add and remove member functions here. //}}AFX_DISPATCH

DECLARE_DISPATCH_MAP()

DECLARE_INTERFACE_MAP()

Теперь рассмотрим реализацию класса (производного от CCmdTarget). Сначала добавим в карту интерфейсов (макросы BEGIN_INTERFACE_MAP и END_INTERFACE_MAP)

наш пользовательский интерфейс IСalc с помощью макроса INTERFACE_PART:

BEGIN_INTERFACE_MAP(CYur, CCmdTarget)

INTERFACE_PART(CYur, IID_ IYur, Dispatch)

INTERFACE_PART(CYur, IID_ICalc, Calc)

END_INTERFACE_MAP()

Затем сгенерируем ещё один GUID для нашего класса (в данном случае CYur) и вставим его перед конструктором класса наследуемого от CCmdTarget. Это будет GUID 1-го типа:

IMPLEMENT_OLECREATE(CYur, "YUR",

0x45bda286, 0x6188, 0x11d5, 0x92, 0xd5, 0x94, 0xc5, 0xc6, 0xe8, 0xc9, 0x50);

Осталось реализовать все функции нашего интерфейса: 3 стандартные и 1 пользовательская. Для нашего примера это будет выглядеть следующим образом:

// CYur message handlers

46

STDMETHODIMP_(ULONG) CYur::XCalc::AddRef()

{

TRACE("CYur::XCalc::AddRef\n");

METHOD_PROLOGUE(CYur, Calc) return pThis->ExternalAddRef();

}

STDMETHODIMP_(ULONG) CYur::XCalc::Release()

{

TRACE("CYur::XCalc::Release\n");

METHOD_PROLOGUE(CYur, Calc) return pThis->ExternalRelease();

}

STDMETHODIMP CYur::XCalc::QueryInterface(REFIID iid, LPVOID* ppvObj)

{

TRACE("CYur::XCalc::QueryInterface\n"); METHOD_PROLOGUE(CYur, Calc)

return pThis->ExternalQueryInterface(&iid, ppvObj);

}

STDMETHODIMP_(int) CYur::XCalc::Sum(int first, int second)

{

return (first + second);

}

Соберите проект (меню Build и затем пункт Build или F7) и зарегистрируйте получившийся dll файл с помощью утилиты regsvr32. Это можно сделать прямо из Visual C++,

из меню Tools / Register Controls.

47

Создание COM-клиента

Создадим новый проект. Тип проекта – MFC AppWizard(exe). При выборе конфигурации проекта выберем – Dialog based. Как и для сервера отметим пункт Automation.

К файлу определения класса диалога (*.h-файл) подключаем файлы с определениями всех интерфейсов, которые будем использовать (те же файлы, что подключали при создании dll) .

Добавляем к классу диалога следующие переменные:

LPUNKNOWN имя_переменной (например m_pIUnknown) – указатель на

IUnknown.

Указатели на все используемые интерфейсы.

Для нашего примера эта часть будет выглядеть так:

public:

LPUNKNOWN m_pIUnknown;

ICalc* m_pICalc;

В конструкторе класса диалога необходимо обнулить указатели на все интерфейсы:

m_pICalc = NULL;

m_pIUnknown = NULL;

В обработчике создания диалога (OnInitDialog()) получим указатели на интерфейсы:

HRESULT hResult; CLSID clsidSrv;

/* Получаем идентификатор библиотеки */

hResult = ::CLSIDFromProgID(L"YUR", &clsidSrv); if (FAILED(hResult))

{

/* Если возникла ошибка - выводим окно сообщения */ ::MessageBox(NULL, "CLSIDFromProgID error", "Error",MB_OK);

return -1;

}

/* Загружаем библиотеку и получаем указатель на IUnknown */ hResult = ::CoCreateInstance( clsidSrv, GetControllingUnknown(),

CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pIUnknown);

if (FAILED(hResult))

{

/* Если возникла ошибка - обнуляем указатель на IUnknown и выводим окно сообщения */

m_pIUnknown = NULL;

::MessageBox(NULL, "CoCreateInstance error", "Error",MB_OK); return -1;

}

/* получаем указатель на необходимый интерфейс */ hResult = m_pIUnknown->QueryInterface(IID_ICalc,

48

(void**)&m_pICalc); if (FAILED(hResult))

{

/* Если возникла ошибка - обнуляем указатель на интерфейс и выводим окно сообщения */

m_pICalc = NULL;

::MessageBox(NULL,"QueryInterface error", "Error",MB_OK); return -1;

}

В качестве первого параметра функции CLSIDFromProgID необходимо использовать то имя, которое вы ввели при назначении уникального идентификатора для класса сервера

(IMPLEMENT_OLECREATE(CYur, "YUR",

0x45bda286, 0x6188, 0x11d5, 0x92, 0xd5, 0x94, 0xc5, 0xc6, 0xe8, 0xc9, 0x50);). В данном случае это имя YUR.

Теперь разработаем наш диалог. Для этого откроем его из вкладки ресурсов проекта. С помощью стандартных элементов управления создайте диалог, изображённый на рис.35.

Рис. 35. Разработка диалогового окна

Свяжите окна редактирования с переменными m_first, m_second, m_yursum типа int соответственно (это можно сделать так: выделив нужный EditBox нажать Ctrl и два раза щёлкнуть на нём). Также создайте функцию-обработчик нажатия кнопки “Считай” (два раза щёлкните на кнопке).

Теперь напишем код для функции-обработчика. Например такой:

void CYur_clientDlg::OnSum()

49

{

UpdateData();

m_yursum = m_pICalc->Sum(m_first,m_second); UpdateData(FALSE);

}

В первой строчке осуществляется передача значений из окон редактирования в переменные m_first и m_second. Во второй строчке мы вызываем функцию Sum нашего интерфейса, которая возвращает значение для переменной m_yursum. В третьей строчке значение переменной m_yursum выводится в соответствующее окно редактирования.

Теперь можно собрать проект и протестировать приложение.

50

ИНДИВИДУАЛЬНЫЕ ЗАДАНИЯ

10. Подправьте код так, чтобы при нажатии кнопки “Считай” выполнялась операция вычитания.

11. Подправьте код так, чтобы при нажатии кнопки “Считай” выполнялась операция умножения.

12. Подправьте код так, чтобы при нажатии кнопки “Считай” выполнялась операция деления.

13. Сделайте так, чтобы конечная сумма выводилась в первое окно редактирования.

14. Сделайте так, чтобы конечная сумма выводилась во второе окно редактирования.

15. Добавьте ещё одну пользовательскую функцию серверу (например, реализующую вычитание) и вызовете её из кода клиента.

16. Объявите ещё один пользовательский интерфейс с двумя методами в нём (любыми).

17. Объявите ещё один пользовательский интерфейс имеющий один метод с 3-мя параметрами (любыми).

КОНТРОЛЬНЫЕ ВОПРОСЫ

1.Что такое интерфейс?

2.Чем отличается вложенный класс от обычного?

3.ДлячегонеобходимапарамакросовBEGIN_INTERFACE_PART иEND_INTERFACE_PART?

4.Какие методы имеет каждый интерфейс?

5.Какой интерфейс является базовым для всех пользовательских интерфейсов?

6.На какие три этапа условно можно разбить процесс создания COM-сервера?

7.Для чего необходима функция UpdateData() в функции OnSum()?

8.Для чего необходима функция ::CoCreateInstance()? Для чего нужны два двоеточия перед именем функции?

9.Для чего необходима функция ::CLSIDFromProgID()?

10.Что обозначает префикс “Х” перед именем класса?

11.В виде каких файлов реализованы готовые COM-сервер и COM-клиент?

12.Каким образом создаётся вложенный класс наследуемый от пользовательского интерфейса?

13.Зачем необходимо регистрировать COM-сервер?