Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STIP_Labs_2015_16.doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
6 Мб
Скачать
☆

Часть 2. Разработка клиентского приложения для локального сервера сом

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

Шаг 7. Создание каркаса приложения.

Запустите MVS-2010 и выполните тему меню FileNewProject. В появившемся окне выберите Project Types==Win32 и Templates=Win32 Console Application. Дадим проекту имя LocSrvClient. На вкладке Application Settings выберите Application type=Console application и отметьте флажок MFC.

Шаг 8. Импорт библиотеки типов. Скопируем в каталог текущего проекта файл LocSrv.tlb (из каталога проекта локального сервера LocSrv) и добавим директиву импорта в файл stdafx.h:

…

#include <afxext.h> // MFC extensions

#import "LocSrv.tlb" no_namespace

#include <afxdtctl.h> // MFC support for …

…

Шаг 9. Модификация главной функции. В главной функции опишем интеллектуальный указатель на интерфейс нашего сервера и опробуем его методы. Пример реализации главной функции приведен ниже.

Хочу еще раз обратить Ваше внимание на работу с символами кириллицы в консольном приложении, собранном с использованием кодировки Unicode. Для вывода символов кириллицы на монитор (в окно консольного приложения) с помощью объекта cout класса ostream их надо преобразовать в кодировку MS DOS с помощью функции CharToOemA(), параметры которой имеют тип char*. В данной программе введена функция-оболочка Rus() исключительно для удобства. Поток cout «умеет» выводить только строки символов char*, т.е. в кодировке ANSI. (Есть еще и поток wcout, который выводит символы Unicode, но он нам все равно в данном случае не помощник. См. подраздел «Русификация приложений» в конспекте лекций.) Другим способом кириллизации приложения является вызов функции setlocale(LC_ALL,”rus”), который достаточно сделать один раз в начале работы программы. Окрім того, в праці [4] ви знайдете функцію Ukr, що забезпечує можливість використання ураїнської мови у консольних застосуваннях.

Функция СОМ ErrorMessage() возвращает строку с сообщением об ошибке типа TCHAR*, т.е. в кодировке Unicode, и для преобразования этой строки в ANSI-строку кодировки MS DOS (Code Page 866) функция CharToOemA() или CharToOemW() не годится и приходится использовать функцию WideCharToMultiByte() с длинным списком параметров и неоднозначным именем. Если бы мы захотели показать сообщение об ошибке с помощью окна AfxMessageBox(), то это можно было бы сделать намного проще:

AfxMessageBox(_T("Элементарно, Ватсон!"));

AfxMessageBox(ex.ErrorMessage());

Здесь макрос _T обеспечивает приведение типа константы char * к типу Unicode (если это необходимо делать в соответствии с настройками проекта).

Попробуйте запустить данное клиентское приложение на выполнение. Если серверное приложение было собрано и хотя бы один раз запущено на выполнение, то оно выполнило саморегистрацию и должно проворно откликнуться на команду из клиентского приложения. Когда окно сервера появится на экране, попробуйте его свернуть и развернуть и понаблюдать, что будет с выведенным методом IPainter.PDraw() изображением.

Для того чтобы проверить, что обработка ошибок COM (блоки try и catch) «работает», добавьте оператор pPnt->PDraw(); после pPnt->Stop(); и проследите за выводом в окно клиентского приложения (вывод сообщения об ошибке будет сделан с некоторой задержкой по времени).

Шаг 10. Решение возможных проблем. Если при вызове методов сервера его окно не появится или же возникнет ошибка, значит надо искать ошибку, по всей видимости, на стороне серверного приложения.

Как можно выполнять отладку сервера, реализованного в виде самостоятельного приложения? Для этого необходимо:

  • загрузить проект сервера в ИС;

  • добавить в командную строку параметр /embedding (см. выше шаг 5);

  • если требуется, то поставить в нужных местах методов классов сервера контрольные точки;

  • запустить сервер в отладочном режиме (F5);

  • запустить клиентское приложение любым «внешним» способом, например, с помощью проводника.

Естественно, что до запуска клиентского приложения сервер должен быть зарегистрирован, т.е. запущен на выполнение без ключа /embedding. Сервер достаточно зарегистрировать один раз, т.е. при внесении изменений в его код, не касающихся добавлений новых интерфейсов или добавлений и изменений в заголовках методов, его не нужно повторно регистрировать. Естественно, если будет изменено место хранения сервера, т.е. он будет перемещен в другой каталог, то понадобится повторная регистрация сервера. В этом случае необходимо предварительно разрегистрировать сервер, чтобы из реестра была удалена уже недействительная информация о нем.

Шаг 11. Ура, самостоятельная работа!

Здесь, наконец-то, вы сможете отказаться от навязываемого вам сценария и в полной мере проявить свои творческие и другие выдающиеся способности и качества. Предлагается сделать из сервера конфетку, такой продукт, от которого шпионов из Microsoft приходилось бы отгонять битами или танками как назойливых мух. Например, клиентское приложение спрашивает у пользователя: «Свет мой юзер, ты скажи, что тебе нарисовать?». А пользователь отвечает: «Я тебе подсказывать не буду, догадайся сама, но чтобы мне понравилось. Но это должно быть … цвета, на … фоне, размером …х… и т.д. Зрозумiло? Do you understand me?». После этого клиентское приложение вызывает соответствующие методы сервера (вы их элементарно добавите) и … пользователь вне себя от счастья, а преподаватель за такую Лабу ставит не обычные 5 баллов, а целых (точнее, нецелых) 5.1! Среди методов сервера должен найтись хотя бы один, который способен очистить окно сервера.

Наверное, вам захочется проверить, что будет, если после запуска клиентского приложения и загрузки сервера взять да и закрыть сервер, а после этого вызвать какой-нибудь его метод?

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

И уже после того, как преподаватель примет у вас работу, вы придумаете способ, чтобы сервер перерисовывал свое окно самостоятельно, если тупой юзер вдруг случайно (или, с него станется, преднамеренно!) перекроет окно сервера и тем самым испортит изображение.

Шаг 12.Использование строк в качестве параметров методов СОМ объекта. Как надо написать метод сервера, который в качестве параметра должен получить (или возратить) строку символов? Невзирая на то, что типом параметра метода можно объявить char *, делать этого не стоит. Для передачи строк методам сервера надо использовать тип BSTR или тождественный ему LPCTSTR. Эти типы описаны в файлах WinNT.h и WTypes.h:

typedef wchar_t WCHAR; // wc, 16-bit UNICODE character

typedef WCHAR OLECHAR;

typedef /* [wire_marshal] */ OLECHAR *BSTR;

typedef __nullterminated CONST WCHAR *LPCWSTR, *PCWSTR;

typedef LPCWSTR PCTSTR, LPCTSTR;

Когда вы будете выбирать тип параметра с помощью мастера добавления метода к интерфейсу, то будете указывать тип BSTR и будете видеть этот тип в описании метода в файле .idl, а вот в файле реализации метода мастер укажет тип параметра LPCTSTR. Вот пример реализации метода:

void CPainter::WriteXY(LONG X, LONG Y, LPCTSTR Str)

{

AFX_MANAGE_STATE(AfxGetAppModuleState());

CDC *pDC;

CWnd * pWnd=AfxGetApp()->GetMainWnd();

pDC=glView->GetDC();

pDC->TextOut(X,Y,Str);

}

В клиентском приложении вызов этого метода может выглядеть так:

LONG x=10,y=20;

BSTR bStr=_T("Тестируем WriteXY2");

pPnt->WriteXY(x,y,bStr);

Шаг 13. Если вдруг возникли проблемы …

Корректная работа программы – это всего один случай, а ошибочных может быть сколько угодно.

Если вы получите сообщение об ошибке (код ошибки 0x800401F3)

«Недопустимая строка с указанием класса»

то, значит, Ваш сервер не зарегистрирован в реестре (вы его не запускали на выполнение) или вы переместили его в корзину после регистрации или вы ошиблись в записи его имени и т.д. и т.п. Для проверки того, что сервер зарегистрирован в реестре, поищите в реестре с помощью RegEdit.exe строку LocSrv.Painter. Она должна отыскаться в ветви реестра HKEY_CLASSES_ROOT и записи соответствующего раздела должны быть подобны следующим:

HKEY_CLASSES_ROOT\CLSID\{AF8DFEB4-A9ED-425F-9C5C-67FED676873E}

LocSrv.Painter

InprocHandler32 = ole32.dll

LocalServer32 = d:\MyLab4\LocSrv\debug\LocSrv.exe

ProgID = LocSrv.Painter

Сообщение об ошибке (код ошибки 0x80020003)

Член группы не найден

свидетельствует о том, что не удается вызвать на выполнение запрошенный метод сервера, но при этом метод «виден», т.е. присутствует в файле LocSrv.tlh. Такой ошибки можно добиться (это доказано!), если метод сервера добавлялся вручную (а не так как было рекомендовано на шаге 6) и это было проделано некорректно. Например, если при добавлении метода «вручную» вы забыли добавить его в файл реализации методов СОМ объекта Painter.cpp, а именно в карту методов, доступных посредством интерфейса IDispatch:

BEGIN_DISPATCH_MAP(CPainter, CCmdTarget)

DISP_FUNCTION_ID(CPainter, "PDraw", dispidPDraw, PDraw,

VT_EMPTY,VTS_UI4,VTS_UI4,VTS_UI4,VTS_UI4, VTS_UI4, VTS_UI4)

DISP_FUNCTION_ID(CPainter, "Stop", dispidStop, Stop, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

Такая запись в карте для метода PDraw() будет сгенерирована мастером в том случае, если метод будет иметь 6 параметров типа int (обратите внимание на идентификаторы VTS_UI4 в карте методов интерфейса).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]