CodeNet / Платформы / Windows / DLL
CodeNet / Платформы / Windows / MFC
Разработка DLL-модулей расширения MFC на языке Visual C++
Разработка DLL-модулей расширения MFC на языке Visual C++
Подготовка и компоновка DLL-модуля для некоторого образца расширенного класса.
Квалифицированные специалисты, занимающиеся подготовкой программ для Windows, постепенно отказываются от применения инструментального комплекта SDK Windows и переходят к современным средам разработки для этой ОС, которые обеспечивают более высокую степень абстрагирования от действующих на низком уровне встроенных механизмов этой системы. В результате таких перемен в выигрышной ситуации оказался язык Microsoft Visual C++, в котором основой для объектно-ориентированного представления Windows API служит библиотека базовых классов MFC (Microsoft Foundation Classes).
MFC в руках опытного разработчика - мощное, но далеко не совершенное средство. В действительности едва ли не все знакомые мне программисты, полагающиеся в работе на MFC, вынуждены были хоть раз в жизни разрабатывать собственный набор расширений на базе классов MFC, из которых порождались необходимые производные классы. Но это, в конце концов, относится к возможностям Си++. Если какой-либо класс не годится для ваших нужд (и если, по существу, ему изначально отводилась роль базового класса), вы можете вывести из него производный класс, а затем внести изменения в те его компоненты, которые вас не устраивают.
Предположим, что вы подготовили набор расширений для MFC, которыми, как предполагается, будут пользоваться и другие программисты вашей компании. Возникает вопрос, как скомпоновать эти расширения? Можно было бы распространить файлы с исходными текстами или раздать компоненты, совместимые с галереей Component Gallery Visual C++. Или, если вы предпочитаете MFC, скомпоновать подготовленные вами расширения в виде DLL-модулей. Из DLL-модулей расширения MFC классы экспортируются таким же образом, как из обычных DLL-модулей функции. Любая программа, динамически связанная с DLL-модулями MFC, может опять же динамически связываться с DLL-модулями расширения MFC. Все, что требуется от разработчика, - включить необходимые заголовочные файлы и библиотеку импорта DLL в список связей этой программы.
Насколько трудно подготовить DLL-модуль расширения MFC? Очень просто, если прибегнуть к средствам Visual C++ и MFC. В этой статье мы покажем, как создать DLL-модуль расширения MFC, который восполнил бы вопиющий недостаток MFC класса CToolTipCtrl. Кроме этого, мы расскажем, как составить программу, работающую с этим модулем. Когда вы поймете, насколько все это просто, у вас, возможно, возникнет желание самостоятельно заняться разработкой MFC-расширений.
Подготовка DLL-модуля расширения MFC
Теоретически вы без каких-либо трудностей назначите подсказки TollTips элементам управления диалогового окна или его произвольно выбранным областям с помощью имеющегося в MFC класса CToolTipCtrl. (Подсказки ToolTips - это миниатюрное окно со справочным текстом, которое всплывает, когда курсор мыши устанавливается на кнопке инструментальной панели или на другом объекте интерфейса.) Но есть одна проблема. Поскольку функция CToolTipCtrl::AddTool не выполняет автоматически деления на подклассы окна, которому назначается подсказка ToolTip, информацию о событиях, связанных с мышью, вам приходится передавать элементу управления ToolTip самостоятельно. Это обычно означает, что задача деления окна на подклассы возлагается на вас.
Элемент управления ToolTip, который служит основой объекта CToolTipCtrl, будет самостоятельно выполнять деление на подклассы, если вы позаботитесь о передаче ему соответствующих флажков, - функция, которую команда разработчиков MFC обошла своим вниманием. К счастью, исправить эту оплошность совсем нетрудно. Надо просто подготовить класс, производный от класса CToolTipCtrl, и заменить функцию CToolTipCtrl::AddTool аналогичной, содержащей флажок TTF_SUBCLASS в поле uFlags структуры TOOLINFO, используемой в сообщениях TTM_ADDTOOL. А еще лучше заменить ее двумя функциями - для добавления подсказок ToolTips к дочерним окнам и к прямоугольным областям окна. Функция AddTool предусматривает эти возможности, но синтаксически два этих метода очень отличаются друг от друга.
На лист. 1 и 2 приведен исходный текст для производного от CToolTipCtrl класса, носящего название CToolTipCtrlEx. Производному классу помимо унаследованных от CToolTipCtrl:AddWindow принадлежат две функции: для назначения подсказки ToolTip дочернему окну и AddRectangle, которая выполняет то же самое для прямоугольной области окна. Чтобы облегчить задачу назначения подсказки, в обеих функциях используется флажок TTF_SUBCLASS. Обозначим объект CToolTipCtrlEx как m_tooltipCtrl, тогда фрагмент для присвоения подсказки ToolTip кнопочному переключателю с идентификатором IDC_BUTTON выглядит довольно элементарно:
m_tooltipCtrl.AddWindow (GetDlgItem (IDC_BUTTON), "Введите сюда текст подсказки!");
Лист. 1. Заголовочный файл класса CToolTipCtrlEx.
// ToolTip.h: заголовочный файл // //////////////////////////////////////////////////////////// //Окно CToolTipCtrlEx class AFX_EXT_CLASS CToolTipCtrlEx : public CToolTipCtrl { // Конструктор public: BOOL AddRectangle (CWnd* pWnd, LPCTSTR pszText, LPCRECT pRect, UINT nIDTool); BOOL AddWindow (CWnd* pWnd, LPCTSTR pszText); CToolTipCtrlEx(); //Атрибуты public: //Операции public: //Переопределения // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CToolTipCtrlEx) //}}AFX_VIRTUAL //Реализация public: virtual ~CToolTipCtrlEx(); // Сгенерированные функции message map protected: //{{AFX_MSG(CToolTipCtrlEx) // Обратите внимание - "мастер" ClassWizard добавит // макрокоманды // remove member functions here, //}}AFX_MSG DECLARE_MESSAGE_MAP() }; -------------------------------------------------------------------------------- Лист. 2. cpp-файл для класса CToolTipCtrlEx. // ToolTip.cpp : файл реализации // #include "stdafx.h" #include "stdafx.h" #include "ToolTip.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //////////////////////////////////////////////////////////// // CToolTipCtrlEx CToolTipCtrlEx::CToolTipCtrlEx() { } CToolTipCtrlEx::~CToolTipCtrlEx() { } BEGIN_MESSAGE_MAP(CToolTipCtrlEx, CToolTipCtrl) //{{AFX_MSG_MAP(CToolTipCtrlEx) // NOTE - the ClassWizard will add remove mapping // macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() /////////////////////////////////////////////////////////// // Обработчики сообщений CToolTipCtrlEx BOOL CToolTipCtrlEx::AddWindow (CWnd* pWnd, LPCTSTR pszText) { TOOLINFO ti; ti, cbSize = sizeof (TOOLINFO); ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; ti.hwnd = pWnd->GetParent ()->GetSafeHwnd (); ti.uId = (UINT) pWnd->GetSafeHwnd (); ti.hinst = AfxGetInstanceHandle (); ti.lpszText = (LPSTR) pszText; return (BOOL) SendMessage (TTM_ADDTOOL, 0, (LPARAM) &ti); } BOOL CToolCtrlEx::AddRectangle (CWnd* pWnd, LPCTSTR pszText, LPCRECT pRect, UINT nIDTool) { TOOLINFO ti; ti.cbSize = sizeof (TOOLINFO); ti.uFlags = TTF_SUBCLASS; ti.hwnd = pWnd->GetSafeHwnd (); ti.uId = nIDTool; ti.hinst = AfxGetInstanceHandle (); ti.lpszText = (LPSTR) pszText; ::CopyRect (&ti.rect, pRect); return (BOOL) SendMessage (TTM_ADDTOOL, 0, (LPARAM) &ti); }
Как оформить класс CToolTipCtrlEx в виде DLL-модуля? Предлагаем пошаговые инструкции для Visual C++ версии 4.x:
Запуcтите Visual C++, создайте новый проект, выбрав пункт New (новый) в меню File, и щелкните дважды на команде Project Workspace (рабочая область проекта). В поле Name (имя) рабочей области нового проекта New Project Workspace наберите символы "MfcExt" (без кавычек). В окне Type (тип) выберите "мастера" MFC AppWizard(dll), затем щелкните на кнопке Create (создать).
В окне Step 1 "мастера" AppWizard в ответ на вопрос: "What type of DLL would you like to create?" ("Какого типа DLL-модуль вы хотите сформировать?") - выберите "MFC Extension DLL (using shared MFC DLL) [DLL-модуль расширения MFC (с использованием разделяемого модуля DLL MFC).] Щелкните на кнопке Finish (завершить) и на кнопке OK, чтобы сгенерировать исходный текст DLL-модуля. Обратите внимание на заключенную в скобки фразу "using shared MFC DLL", следующую за словами "MFC Extension DLL". Если вы составляете DLL-модуль расширения MFC, нет необходимости реализовывать возможность постоянной привязки к MFC. DLL-модуль должен быть связан с библиотеками MFC динамически.
Обратитесь к "мастеру" классов ClassWizard и щелкните на кнопке Add Class (добавить класс). Выберите New, чтобы открыть окно Create New Class (создать новый класс). В этом окне выберите CToolTipCtrl в панели "Base class" (базовый класс) и тип "CToolTipCtrlEx" в панели "Name". Щелкните на кнопке Change (изменить), чтобы изменить имена файлов Tooltip.h и Tooltip.cpp. (Особой надобности в подобных изменениях нет, но я все же выполняю этот шаг, чтобы избавиться от длинных имен файлов.) Отмените выбор в окне "Add to Component Gallery" ("Добавить к галерее компонентов"), чтобы сэкономить пространство на диске. Щелкните на команде Create, чтобы сообщить "мастеру" классов ClassWizard о необходимости породить новый класс, и на кнопке OK, чтобы завершить работу этого "мастера".
Добавьте к классу CToolTipCtrlEx принадлежащие функции AddWindow и AddRectangle. Позаботьтесь о том, чтобы обе функции были объявлены как public (общедоступные), поскольку обращения к ним будут происходить за пределами класса CToolTipCtrlEx. В Visual C++ есть простой способ добавления функции, принадлежащей классу, - щелкнуть правой клавишей мыши на имени класса в окне ClassView и выбрать в контекстном меню команду Add Function (добавить функцию).
В окне ClassView щелкните дважды на CToolTipCtrlEx, чтобы открыть заголовочный файл этого класса. Добавьте AFX_EXT_CLASS к описанию класса справа от ключевого слова class (см. лист. 1).
Создайте свой проект. В результате вы получите два очень важных файла: собственно DLL-модуль (Mfcext.dll) и библиотеку импорта DLL-модуля (Mfcext.lib). Библиотека импорта содержит в основном список имен экспортируемых функций или, как в нашем случае, перечень экспортируемых классов Cи++. Благодаря связи с библиотекой импорта программа может работать с классами, экспортируемыми из DLL-модулей расширения MFC, так, словно библиотека, содержащая эти классы, связана статически. Кроме этого, связь с библиотекой Mfcext.lib служит для Windows признаком того, что для выполнения конкретной программы необходим файл Mfcext.dll.
Применение DLL-модуля расширения MFC
Подготовка программы, работающей с модулем Mfcext.dll, не составляет особого труда. Просто включите заголовочный файл Tooltip.h в каждый класс, работающий с производным классом CToolTipCtrlEx, и включите файл Mfcext.lib в список связанных с вашим проектом библиотек. После чего следует рассматривать класс CToolTipCtrlEx как обычный класс MFC. Не забудьте, что нужно выбрать "As a shared DLL" ("Как разделяемый DLL-модуль"), когда наступит момент отвечать на вопросы "мастера" AppWizard, чтобы ваша программа могла связываться с MFC динамически. Вы можете добавить в список связей вашего проекта файл Mfcext.lib, выберите для этого пункт Settings (параметры) в меню Build языка Visual C++, щелкните на закладке Link (связь) и введите маршрут к файлу Mfcext.lib в окне "Object/library modules" (объектные/библиотечные модули).
Для примера мы приводим программу ToolTest, которая выводит единственную кнопку в диалоговом окне. Она динамически связывается с файлом Mfcext.dll и использует класс CToolTipCtrlEx. Программа работает с диалоговыми окнами, а классу диалогового окна принадлежит переменная CToolTipCtrlEx по имени m_tooltipCtrl. Следующий фрагмент исходного текста функции OnInitDialog диалогового окна создает элемент управления ToolTip и присваивает экранной кнопке этого окна подсказку ToolTip:
m_tooltipCtrl.Create (this); m_tooltipCtrl.AddWindowTool ( GetDlgItem (IDC_EXIT); "Чтобы закрыть, щелкните здесь");
Когда курсор устанавливается на этой кнопке, в окне ToolTip появляется текст "Чтобы закрыть, щелкните здесь". Щелчок на кнопке - и программа будет закрыта.
Программу ToolTest и файл Mfcext.dll можно загрузить вместе с исходными текстами из службы PC Magazine Online (www.pcmag.com). Выберите пункт Downloads в меню, расположенном с левой стороны базовой страницы, затем пункт PC Tech Archives, а после этого пункт V16n15.zip. Этот файл вы можете найти также в оперативной информационной службе CompuServe в форуме Utilities/Tips (GO ZNT:TIPS). Наши файлы входят в этот архив. В процессе их разархивирования (Mfcext.zip и Tooltest.zip) установите переключатель в PKUNZIP в положение -d, чтобы развернуть и каталоги, хранящиеся в ZIP-файлах. Прежде чем вы запустите ToolTest на выполнение, скопируйте файл Mfcext.dll в системный каталог Windows или в тот каталог, в котором находится исполнимый модуль Tooltest.exe. В противном случае Windows не сможет найти файл Mfcext.dll, когда начнется выполнение программы ToolTest. Если вы решите заняться подготовкой своей собственной версии модуля Tooltest.exe, внесите изменения в имя маршрута в поле "Object/library modules" диалогового окна Project Settings, чтобы информировать Visual C++ о том, в каком каталоге вашего ПК находится файл Mfcext.lib.
Заключение
Разработка DLL-модулей расширения MFC - задача простая, если, конечно, вы знаете, как это делается. Когда вы делитесь подобными расширениями с другими разработчиками, не забудьте кроме самих DLL-файлов передать им и заголовочные файлы, описывающие классы этих DLL-модулей, и соответствующие LIB-файлы. Тогда у ваших коллег будет все необходимое, чтобы воспользоваться подготовленными вами расширениями.
Джефф Просис - внештатный редактор журнала PC Magazine и автор книги Programming Windows 95 with MFC.