Перед тем как мы начнем работать с окном
СОМ-объекта, вводя в него реакции на управляющие
воздействия, покажем, как добавить страницу
свойств (property page) в уже существующий блок
страниц объекта, который активизируется с
помощью контекстного меню. Страница свойств
является отдельным элементом управления,
называемым Property Page, интерфейсы которого
должны быть реализованы в рамках отдельного
ко-класса. Такая структура позволяет нескольким
ко-классам одновременно пользоваться страницами
свойств, размещенными в общем СОМ DLL-сервере.
Новый класс для поддержки страницы свойств
помещается в сервер с помощью той же процедуры,
которую мы использовали при вставке класса
COpenGL, но при этом следует выбрать другой тип
элемента управления. Вновь воспользуемся
услугами мастера Studio.Net ATL Add Class.
-
Установите
фокус на элемент ATLGL в дереве Solution
Explorer и в контекстном меню выберите
команду Add > Add Class, при этом важно,
чтобы фокус стоял на имени проекта ATLGL
-
В окне диалога Add Class выберите категорию
ATL, шаблон ATL Property Page и нажмите
кнопку Open.
-
В окне мастера ATL Property Page выберите
вкладку Names и в поле Short Name введите
PropDlg.
-
Перейдите на вкладку Attributes и
просмотрите допустимые установки, ничего в
них не меняя.
-
Перейдите на вкладку Strings и в поле Title
введите имя страницы Light, которое будет
обозначено на вкладке (page tab). В поле Doc
String введите строку Graphics Properties.
-
Нажмите кнопку Finish.
Просмотрите результаты. Прежде всего убедитесь,
что в проекте появился новый класс CPropDlg,
который поддерживает функциональность страницы
свойств и окна диалога. Однако, запустив сервер
и вызвав из контекстного меню его свойства, вы
не увидите новой страницы. Там будут только те
две страницы, которые были и до момента, как вы
подключили поддержку страницы свойств. Для того
чтобы новая страница действительно попала в блок
страниц элемента, надо ввести новый элемент в
карту свойств разрабатываемого элемента COpenGL.
Откройте файл OpenGL.h и найдите в нем карту
свойств. Она начинается строкой:
BEGIN_PROP_MAP(COpenGL)
Введите в нее новый элемент:
PROP_ENTRY("Свет",
1, CLSID_PropDlg)
который привязывает (binds) новую страницу к
существующему блоку страниц свойств. Как видите,
страница создается и связывается с объектом
COpenGL по правилам СОМ, то есть с помощью
уникального идентификатора ко-класса
CLSlD_PropDlg. Единица определяет индекс DISPID
(dispatch identifier) — 32-битный идентификатор,
который используется упоминавшейся выше функцией
invoke для идентификации методов, свойств и
аргументов. Карта свойств теперь должна
выглядеть следующим образом:
BEGIN_PROP_MAP(COpenGL)
PROP_DATA_ENTRY("_cx", m_sizeExtent.ex, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
PROP_ENTRY("FillColor", DISPID_FILLCOLOR,
CLSID_StockColorPage)
PROP_ENTRY("CBeT",
1, CLSID_PropDlg) END_PROP_MAP()
Здесь важно уяснить, что каждая строка типа
PROP_ENTRY соответствует какой-то
функциональности, скрытой в каркасе сервера.
Например, стандартное свойство Fill Color
реализовано с помощью одной переменной
m_clrFillColor и пары функций FillColor,
упоминания о которых вы видели в IDL-файле. Тела
этих функций остались за кулисами. То же
справедливо относительно страницы свойств.
Важным моментом является появление нового
ко-класса в составе библиотеки типов,
генерируемой DLL-сервером. В коде, приведенном
ниже, отметьте появление строк, связанных с
ко-классом PropDlg и, конечно, не обращайте
внимание на идентификаторы CLSID, которые могут
не совпадать даже с предыдущей версией в этой
книге, так как в процессе разработки сервера мне
приходится неоднократно повторять заново процесс
создания ко-классов:
Примечание
Каждый раз при этом идентификаторы CLSID
обновляются, и ваш реестр распухает еще больше.
Хорошим правилом для запоминания в этом случае
является следующее. Убирайте регистрацию всего
сервера каждый раз, когда вы целиком убираете
какой-либо неудачный ко-класс. Это, как мы
отмечали, делается с помощью команды Start > Run
> regsvr32 -u "C:\My Projects\ATLGL\ Debug\ATLGL.dll.".
Перед тем как нажать кнопку ОК, внимательно
проверьте правильность файлового пути к вашему
серверу.
library
ATLGLLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb")
;
[
uuid(6DEBB446-C43A-4AB5-BEEl-110510C7AC89)
helpstring("_IOpenGLEvents Interface")
]
dispinterface
_IOpenGLEvents
{
properties:
methods:
};
[
uuid(5B3EF182-CD91-426F-9309-2E4869C353DB),
helpstringC'OpenGL Class")
]
coclass
COpenGL
{
[default] interface
IQpenGL;
[default, source] dispinterface _IOpenGLEvents;
};
//======
Новые элементы в библиотеке типов сервера
[
uuid(3AE16CD6-4558-460F-8A7E-5AB83D40DE9A),
helpstring("_IGraphPropEvents
Interface")
]
dispinterface
_IGraphPropEvents
{
properties:
methods:
};
[
uuid(lAOC756A-DA17-4630-91BO-72722950B8F7) ,
helpstring("GraphProp Class")
]
coclass
PropDlg
{
interface
lUnknown;
[default, source] dispinterface
_IGraphPropEvents;
};
Убедитесь, что в составе проекта появились новые
файлы (PropDlg. h, PropDlg. cpp и PropDlg. rgs).
Откройте первый файл описаний и отметьте, что
класс CPropDlg происходит от четырех родителей
(классов ATL и одного интерфейса). Два из них (ccomObjectRootEx
и CGomCoClass) мы уже встречали ранее, а два
других (iPropertyPagelmpl и CDialoglmpl), как
нетрудно догадаться, поддерживают
функциональность диалоговой вкладки (страницы),
размещаемой в блоке страниц (property sheet), и
самого диалога, то есть механизм обмена данными.
Оба родителя являются шаблонами, которые уже
настроены на наш конкретный класс CPropDlg.
Конструктор класса:
CPropDlg()
{
m_dwTitleID =
IDSJTITLEPropDlg;
m_dwHelpFileID =
IDS_HELPFILEPropDlg;
m_dwDocStringID =
IDS_DOCSTRINGPropDlg;
}
устанавливает унаследованные переменные
m_dwTitleio и идентификаторы строковых ресурсов
в те значения, которые им присвоил мастер
Studio.Net. Сами строки вы можете увидеть в
ресурсах, если откроете узел дерева String Table.
В классе изначально присутствует реакция на
кнопку Apply, которая, как вы знаете, всегда
сопровождает блок диалоговых вкладок (property
sheet):
//====== Реакция на
нажатие кнопки Apply
STDMETHOD(Apply)(void)
{
ATLTRACE(_T("CPropDlg::Apply\n"));
for
(UINT i = 0; i < m_nObjects; i++)
{
// Do something interesting here
// ICircCtl* pCirc;
//m_ppUnk[i]->QueryInterface(IID_ICircCtl,
(void**)SpCirc)
// pCirc->put_Caption(CComBSTR("smth special"));
// pCirc->Release();
}
m_bDirty = FALSE;
return S__OK;
}
В комментарий мастер поместил подсказку, которая
дает намек о том, как следует пользоваться новым
классом. Как вы видите, общение между двумя
классами нашего сервера (copenGL и CPropDlg)
должно происходить по правилам СОМ, то есть с
помощью указателя на интерфейс. Этот факт
производит впечатление излишней усложненности.
Если оба класса расположены в рамках одной DLL,
они могли бы общаться друг с другом с помощью
прямого указателя, несмотря на то, что сама DLL
загружается в пространство чужого процесса.
Примечание
Имя ICircCtl, которое присутствует в подсказке,
не имеет отношения к нашему проекту. Оно связано
с учебным примером по созданию элементов
управления с помощью библиотеки ATL. Вы можете
увидеть этот пример в
MSDN (Visual C++ Tutorials > Creating the Circle
Control).
Переменная m_bDirty используется каркасом в
качестве флага доступности кнопки Apply. Если
m_bDirt у == FALSE; то кнопка недоступна. Она
тотчас же должна стать доступной, если
пользователь страницы диалога свойств введет
изменения в органы управления на лице диалога.
Конечно, этим состоянием управляет разработчик,
то есть мы с вами. |