Написание приложений Win32 с помощью одних классов C++ (часть 2) - Тестовый проект
ОГЛАВЛЕНИЕ
Тестовый проект
Было создано несколько проектов для тестирования примеров применения.
Исполняемый файл – чистый проект Win32, использующий NLib как статическую библиотеку.
Файл stdafx.h включает NLib/StdAfx.h, и – для неотладочных версий – определяет символ GE_FORCEINLINE: это приводит к включению файлов NLib CPP в конец их соответствующих файлов .h с объявлением всех функций как "inline".
Поскольку решение содержит два проекта, и поскольку W3 зависит от NLib, приходится подключать Nlib.lib, даже если весь код встроенный. Это достигается путем определения в исходнике emptylib.cpp локального скрытого символа (бессмысленного, чтобы что-то компилировалось, или никакой библиотеки не генерируется), и путем видоизменения конфигурации отладки (где все файлы, кроме emptylib.cpp, включены в процесс генерации) и выпускаемой версии (где правило генерации изменено на обратное). emptylib.cpp также сконфигурирован на неиспользование предварительно скомпилированных заголовков.
Функция WinMain очень простая:
int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
NUtil::STrace::_Filter() = 2;
NUtil::SWinMain winmainparams(hInstance);
NWin::SMsgLoop loop(true); //создать экземпляр цикла
CMainWnd wnd;
wnd.Create(hInstance);
loop.Loop();
return 0;
}
Создается CMainWnd, и выполняется цикл обработки сообщений. CMainWnd объявляется так:
class CMainWnd: public NWin::EWnd
{
protected:
NWin::EMenuUpdator _mnuupdt;
NWin::EMenuImagerIDE _mnuimg;
public:
CMainWnd() {}
bool Create(HINSTANCE hInstance);
protected:
LRESULT OnFileExit(WORD wNotifyCode,
WORD wID, HWND hWndCtl, bool& bHandled);
LRESULT OnExecSomeCommand(WORD wNotifyCode,
WORD wID, HWND hWndCtl, bool& bHandled);
LRESULT OnCreate(LPCREATESTRUCT lpcs, bool& bHandled);
LRESULT CMainWnd::OnPaint(bool& bHandled);
BEGIN_MSG_MAP(CMainWnd)
ONMSG_WM_CREATE(OnCreate)
ONMSG_WM_PAINT(OnPaint)
COMMAND_ID_HANDLER_U(ID_FILE_EXIT, OnFileExit)
COMMAND_ID_HANDLER_U(ID_HELP_ABOUT, OnExecSomeCommand)
COMMAND_ID_HANDLER_U(ID_FILE_NEW, OnExecSomeCommand)
COMMAND_ID_HANDLER_U(ID_FILE_OPEN, OnExecSomeCommand)
COMMAND_ID_HANDLER_U(ID_FILE_SAVE, OnExecSomeCommand)
END_MSG_MAP()
};
Обратите внимание на макрос COMMAND_ID_HANDLER_U в карте сообщений, и заметьте, что некоторые команды были реализованы посредством той же функции-заполнителя OnExecSomeCommand. Функция Create является сокращением для обработки регистрации WNDCLASSEX и созданием настоящего окна через EWnd::CreateWnd(...).
Функция OnCreate (обработчик для WM_CREATE) инициализирует обертки других членов и прикрепляет их к тому же самому CWnd. Те обертки не обязаны быть членами, но заключение их в один и тот же класс упорядочивает структуру данных.
Ниже приведено два тела:
bool CMainWnd::Create(HINSTANCE hInstance)
{
LPCTSTR wcname = _T("W3MainWnd");
NUtil::SResID resWapp(IDR_WApp);
static NWin::SWndClassExReg clsrg(true, hInstance, wcname, resWapp);
//instantiate a window class
if(!CreateWnd(hInstance, wcname, _T("W3 test"),
WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
NWin::SRect(NWin::SPoint(CW_USEDEFAULT,CW_USEDEFAULT),
NWin::SSize(600,400)), NULL,
LoadMenu(hInstance, resWapp), NULL))
return false;
return true;
}
LRESULT CMainWnd::OnCreate(LPCREATESTRUCT lpcs, bool& bHandled)
{
Default();
NUtil::SResID resWapp(IDR_WApp);
_mnuupdt.LoadAccelerators(HInstance(), resWapp);
_mnuupdt.Attach(*this);
_mnuimg.GetCmdImgs().LoadCmdImages(HInstance(), resWapp);
_mnuimg.Attach(*this);
return 0;
}
В момент прикрепления дополнительной обертки окно уже существует. В конце OnCreate на окно ссылаются три обертки. Отцепление и связывание осуществляется в карте сообщений: это автоматически делается процессами прикрепления EWnd::Attach и отцепления Detach.
Команды меню Файл/Новый, Файл/Открыть, Файл/Сохранить и Помощь/О были реализованы с помощью пустой функции.
Команды Меню/Автоматически обновить и Меню/Изображения являются переключателями. Они выбраны, если прикреплены _mnuupdt и _mnuimg соответственно. Команды реализованы путем поочередного прикрепления/отцепления внутренних оберток.
При отцеплении _mnuupdt меню перестают обновлять свое собственное состояние. Если отцепление производится сразу после запуска приложения, без отображения других меню, меню появятся полностью активными и без описаний быстрых клавиш. Если отцепление происходит позже – меню сохраняют последнее установленное состояние.
Подробность отладки
В самом начале WinMain есть строка "NUtil::STrace::_Filter() = 2;". Она сокращает вывод отладочной информации, генерируемой STrace и STRACE.
В коде извлечение всех сообщений установлено на "уровень 0", а отправка сообщений / выделение памяти установлены на "уровень 1". Если нужен более подробный вывод отладочной информации, установите значение в 1 или в 0. Неудобство в том, что управление меню замедляется из-за операции обертывания / распаковки объектов GDI и из-за всех сообщений, генерируемых операцией.
Заключение
Возможно, заново изобретается колесо, но, как ни странно, улучшать описанные возможности было приятней, чем писать код с помощью WTL.
Вряд ли это станет новой библиотекой для мощных приложений, но поможет кому-то научиться новому.