Написание приложений Win32 с помощью одних классов C++ (часть 3)

ОГЛАВЛЕНИЕ

Данная статья рассматривает причины для развития кода от предыдущей до новой реализации. Хотя общие принципы те же, есть значительная разница.

• Скачать проект и демо - 97.7 Кб

Введение

Изначально это не задумывалось как третья часть главной статьи. Почему? Потому что при разработке стыковочного каркаса был сделан вывод, что при достижении определенной зрелости кода потребуется общая переделка.

Во-первых, чтобы исправить ряд ошибок и изъянов проектирования; во-вторых, для повышения удобства и гибкости кода.

Перед началом

Если вам интересна данная реализация, скачайте представленный в статье код. Если вам интересно все обсуждение, скачайте также код из предыдущей статьи: он устаревший, но служит базой для сравнения.

Данная статья предполагает знание вопросов, рассмотренных в части 2.

Исправленные ошибки

Ниже приведены исправленные ошибки из предыдущей версии.

"Сбой Нормана Бейтса"

Благодарим Нормана Бейтса за обнаружение ошибки, при анализе оказавшейся ошибкой проектирования. WrpBase::~WrpBase изначально вызывал pThis->Detach(). Это неверно по двум причинам:

1.pThis() приводит WrpBase к производному типу W, но внутри деструктора WrpBase W уже уничтожен!

2.Detach вызывает объект счетчика ссылок функции Relese, который может повторно вызвать объект W (уже уничтоженный).

От этого нельзя избавиться: автоматическое отцепление оберток является их мощью, но создает трудности. Решение - вызывать Detach() из каждого деструктора производного класса WrpBase. Чтобы не забыть об этом, ASSERT был помещен внутрь деструктора WrpBase: в тот момент не должно существовать никаких _pRC!

Это эквивалентно вызову "DestroyWindow" в производном деструкторе MFC CWnd. Другие неприятные ошибки, касающиеся безопасности типов, прятались в Detach и XRefCount_base. Но они были полностью устранены в переработанной новой конструкции. Но напоследок еще одна ошибка.
SShare<T> и SString

Вообще SString работает, но в его родителе есть дефект:

SShare::GetBuffer реализован через _OwnBuffer, но _OwnBuffer ошибочно был реализован через lstrcpyn. Это делало SShare подходящим только для TCHAR. Реализация была переделана на основе XBuffData::copyfrom, копирующего данные с помощью " ="и сравнивающего с помощью " ==". Сейчас она подходит для всех классов, являющихся присваиваемыми и сравниваемыми на равенство, и имеет "пустое значение".

Конечно, SString остается SShare<TCHAR, _T('\0')>. Но сейчас даже можно иметь SShare<double,0.>, or SSHrae<SSomeStruct, SSomestruct()> при необходимости.

Разделение библиотеки

С неприятными ошибками покончено, идем дальше. При работе над добавлением возможностей изучение Проводника решений и открытие вида класса по пространству имен NWin произвело плохое впечатление: они рисковали стать огромными.

Поэтому было решено увеличить число модулей, разделив библиотеку: NLib будет лишь "корневой библиотекой". Все возможности войдут в отдельные библиотеки, сделав корневую библиотеку (ядро) открытой для разных улучшений.

Корневая библиотека NLIB

Какие модули войдут в корневую библиотеку?

inside stdafx.h

 

определения GE_INLINE иGE_ONCE

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

Глобальные идентификаторы

определение ключевого слова interface (должно браться изwindows.h, но ...),crtdbg.h, определения ASSERT иVERIFY

коллекциииалгоритмыSTL

исключение,функциональный,утилита,вектор,список,набор,карта,двусторонняя очередь,стек,очередь,набор битов.

заголовки Windows и частые заголовки

windows.h, commctrl.h, olectrl.h, tchar.h

заголовкиядраNlib

Wrp.h, Wnd.h, WinString.h, MessageMap.h, ... и их загруженные заголовки:Misc.h,Coords.h windowlongptr.h) иEvt.h

outside stdafx.h

 

обертки GDI

GdiWrp.cpp иGdiWrp.h

Начинка цикла обработки сообщений

Msgloop.h иMsgloop.cpp

Загрузчик ресурсов

ResWrp.h

Обертки файловисериализация

Пока отсутствует в библиотеке (скоро выйдет).

Все модули в NLIB изменят свои интерфейсы в будущем только для добавления новых функций или членов. Никакие существующие прототипы функций больше не должны меняться.

Библиотека NGUI

Будет содержать все модули, использующие NLib для реализации других возможностей GUI, таких как прорисовываемое владельцем меню, стыковка, и т.д.

Фактически она содержит CmdUpdt.cpp, CmdImgs.cpp и связанное описание ресурсов, включаемых в описание ресурсов приложения (Ngui.rc и NGui_res.h), а также все развертывание этого этапа.

Соглашения о нумерации для описаний ресурсов

При использовании разных модулей должно быть установлено общее соглашение об использовании идентификаторов в файлах ресурсов.

Учитывая то, как Windows использует коды сообщений, коды команд и т.д., лучше избегать идентификаторов меньше 0x2000 (WM_USER + OCM__BASE), которые могут быть округлены до десятичного 8200.

Следовательно, NLIB_FIRST равен 8200, а NGUI_FIRST равен 8300. Такие числа подходят для контрольных идентификаторов. Для команд желательно оставаться в высшей части WORD (от 32768 и выше) во избежание перепутывания команд с контрольными уведомлениями. Значит, команды могут иметь то же предыдущее соглашение о нумерации, но с прибавлением смещения десятичного 40000.

Итак, 83xx будет ресурсами NGUI, а 483xx будет командами NGUI. 32768 (0x8000) также является значением по умолчанию, используемым EMenuUptator для принятия решения, отправлять или нет свои сообщения-запросы. Команды со значениями меньше 32768 не будут обновляться автоматически.