Взаимодействие классов
Функция showError демонстрирует, как в условиях СОМ можно обработать исключительную ситуацию. Если мы хотим выявить причину ошибки, спрятанную в HRESULT, то следует воспользоваться методом GetDescription интерфейса lErrorinfо. Сначала мы получаем указатель на него с помощью объекта класса ccomPtr. Этот класс, так же как и CGomQiPtr, автоматизирует работу с методами главного интерфейса lUnknown, за исключением метода Queryinterface:
void CPropDlg::ShowError() { USES_CONVERSION; //====== Создаем инерфейсный указатель CComPtr<IErrorInfo> pError; //====== Класс для работы с Unicode-строками CComBSTR sError; //====== Выясняем причину отказа GetErrorlnfo (0, &pError); pError › GetDescription(SsError); // Преобразуем тип строкового объекта для вывода в окно MessageBox(OLE2T(sError),_T("Error"),MB_ICONEXCLAMATION); }
Если вы построите сервер в таком виде, то вас встретит неприятное сообщение о том, что ни один из явных или неявных родителей CPropDlg не имеет в своем составе функции OninitDialog. Обращаясь за справкой к документации (по классу CDialogimpl), мы убеждаемся, что это действительно так. Значит, инструмент Studio .NET, который создал заготовку функции обработки, не прав. Но как же будет вызвана наша функция OninitDialog, если она не является виртуальной функцией одного из базовых классов? Ответ на этот вопрос, как и на большинство других, можно получить в режиме отладки.
Закомментируйте строку вызова родительской версии, которая производится с помощью многообещающего ключевого слова _super (это и есть лекарство), поставьте точку останова на строке, следующей за ней, и нажмите F5. Если вы не допустили еще одной, весьма вероятной, ошибки, то тестовый контейнер сообщит, что он не помощник в процессе отладки, так как не содержит отладочной информации. Согласитесь с очевидным фактом, но не делайте поспешного вывода о том, что невозможно отлаживать все СОМ-серверы. В тот момент, когда вы инициируете новую страницу свойств, отладчик возьмет управление в свои руки и остановится на нужной строке программы. Теперь вызовите одно из самых полезных окон отладчика по имени Call stack, в нем вы увидите историю вызова функции OninitDialog, то есть цепочку вызовов функций. Для этого:
- Дайте команду Debug › Windows › Call Stack (или ALT + 7).
- Внедрите это окно, если необходимо, в блок окон отладчика (внизу экрана).
- Убедитесь, что вызов произошел из функции DialogРгос одного из базовых классов, точнее шаблонов классов, CDialoglmplBaseT.
Этот опыт иллюстрирует тот факт, что все необычно в мире ATL. Этот мир устроен совсем не так, как MFC. Шаблоны классов дают удивительную гибкость всей конструкции, способность приспосабливаться и подстраиваться. Теперь рассмотрим вторую, весьма вероятную, ошибку. Секцию protected в классе CPropDlg следует правильно разместить (странно, не правда ли?). Лучше это сделать так, чтобы сразу за ней шло объявление какой-либо из существующих секций public. Если поместить ее, например, перед макросом:
DECLARE_REGISTRY_RESOURCEID(IDR__PROPDLG)
То макрос окажется безоружным против такой атаки, хотя по идее он должен сопротивляться и даже не замечать наскоков подобного рода. Возможно, этот феномен исчезнет в окончательной версии Studio .NET.