Написание приложений 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.

Вряд ли это станет новой библиотекой для мощных приложений, но поможет кому-то научиться новому.