netlib.narod.ru< Назад | Оглавление | Далее >

Построение каркаса приложения

Уверен, вы согласитесь, что необходимость постоянно перепечатывать один и тот же код — код для создания окна, рисования графики, воспроизведения звука и т.д., — каждый раз, когда вы начинаете новый проект очень утомляет. Почему бы не составить библиотеку из тех функций, которые присутствуют в каждом приложении, что избавит вас от рутины и оставит больше времени на разработку самого приложения?

Именно эта идея лежит в основе каркаса приложения (application framework). На базовом уровне каркас должен содержать код для инициализации окна приложения и различных подсистем (графики, ввода, сети и звука), обработки результатов инициализации, выполняемых в каждом кадре процедур и функций для завершения работы приложения. Использование технологии модульного программирования также помогает, поскольку основные компоненты, такие как упомянутые подсистемы, содержатся в отдельных объектах.

На данном этапе цель заключается в разработке простого проекта, который может использоваться в качестве фундамента для остальных ваших приложений. Создайте новый проект и назовите его framework (или как-нибудь иначе, но чтобы название описывало его назначение). В этом проекте создайте файл с именем WinMain.cpp, который будет предсталять точку входа вашего приложения.

Исходный код WinMain.cpp должен быть минимальным и содержать только код, необходимый для инициализации окна. Взгляните на исходный код, находящийся в файле WinMain.cpp, который я обычно использую в каркасе для своих проектов:

// Включаемые файлы
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>

// Экземпляр главного приложения
HINSTANCE g_hInst; // Глобальный дескриптор экземпляра
HWND      g_hWnd;  // Глобальный дескриптор окна

// Размеры окна приложения, его тип, класс и заголовок
#define WNDWIDTH  400
#define WNDHEIGHT 400
#define WNDTYPE   WS_OVERLAPPEDWINDOW

const char g_szClass[] = "FrameClass";
const char g_szCaption[] = "FrameCaption";

// Главные прототипы функци приложения

// Точка входа
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev,
                   LPSTR szCmdLine, int nCmdShow);

// Функция для отображения сообщений об ошибках
void AppError(BOOL Fatal, char *Text, ...);

// Процедура обработки ссобщений
long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg,
                           WPARAM wParam, LPARAM lParam);

// Функции для регистрации и отмены регистрации классов
BOOL RegisterWindowClasses(HINSTANCE hInst);
BOOL UnregisterWindowClasses(HINSTANCE hInst);

// Функция для создания окна приложения
HWND CreateMainWindow(HINSTANCE hInst);

// функции для инициализации, завершения работы и обработки кадров
BOOL DoInit();
BOOL DoShutdown();
BOOL DoPreFrame();
BOOL DoFrame();
BOOL DoPostFrame();

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev,
                   LPSTR szCmdLine, int nCmdShow)
{
    MSG Msg;

    // сохраняем экземпляр приложения
    g_hInst = hInst;

    // Регистрация класса окна
    // Возвращаем управление, если не удалась
    if(RegisterWindowClasses(hInst) == FALSE)
        return FALSE;

    // Создание окна
    // Возвращаем управление, если не удалось
    if((g_hWnd = CreateMainWindow(hInst)) == NULL)
        return FALSE;

    // Инициализация приложения
    // Возвращаемся в случае ошибки
    if(DoInit() == TRUE) {
        // Вход в цикл обработки сообщений
        ZeroMemory(&Msg, sizeof(MSG));
        while(Msg.message != WM_QUIT) {
            // Обработка сообщений Windows (если они есть)
            if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
            } else {
                // Предкадровая обработка,
                // прекращаем обработку кадра,
                // если вернули FALSE
                if(DoPreFrame() == FALSE)
                    break;

                // Обработка кадра,
                // прекращаем обработку,
                // если вернули FALSE
                if(DoFrame() == FALSE)
                    break;

                // Послекадровая обработка,
                // прекращаем обработку кадра,
                // если вернули FALSE
                if(DoPostFrame() == FALSE)
                    break;
            }
        }
    }
    // Функции завершения работы
    DoShutdown();

    // Отменяем регистрацию класса окна
    UnregisterWindowClasses(hInst);

    return TRUE;
}

BOOL RegisterWindowClasses(HINSTANCE hInst)
{
    WNDCLASSEX wcex;

 

ПРИМЕЧАНИЕ
Вы используете функцию RegisterWindowClasses для регистрации класса окна (путем вызова функции RegisterClassEx). При вызове функции RegisterWindowClasses указывайте в ее единственном параметре значение HINSTANCE приложения (то, которое было передано вам в функцию WinMain).

 

    // Создание и регистрация класса окна
    wcex.cbSize        = sizeof(wcex);
    wcex.style         = CS_CLASSDC;
    wcex.lpfnWndProc   = WindowProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = hInst;
    wcex.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = NULL;
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = g_szClass;
    wcex.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wcex))
        return FALSE;

    return TRUE;
}

BOOL UnregisterWindowClasses(HINSTANCE hInst)
{
    // Отмена регистрации класса окна
    UnregisterClass(g_szClass, hInst);

    return TRUE;
}

HWND CreateMainWindow(HINSTANCE hInst)
{
    HWND hWnd;

 

ПРИМЕЧАНИЕ
Вы используете функцию CreateMainWindow для создания и отображения окна заданного размера и типа (указанных макроопределениями WNDWIDTH, WNDHEIGHT и WNDTYPE соответственно). Просто укажите в единственном параметре CreateMainWindow значение HINSTANCE вашего приложения.

 

    // Создание главного окна
    hWnd = CreateWindow(g_szClass, g_szCaption,
                        WNDTYPE, 0, 0, WNDWIDTH, WNDHEIGHT,
                        NULL, NULL, hInst, NULL);
    if(!hWnd)
        return NULL;

    // Отображение и обновление окна
    ShowWindow(hWnd, SW_NORMAL);
    UpdateWindow(hWnd);

    // Возвращаем дескриптор окна
    return hWnd;
}

void AppError(BOOL Fatal, char *Text, ...)
{
    char CaptionText[12];
    char ErrorText[2048];
    va_list valist;

    // Создаем заголовок окна сообщения
    // на основе флага Fatal
    if(Fatal == FALSE)
        strcpy(CaptionText, "Error");
    else
        strcpy(CaptionText, "Fatal Error");

    // Создаем текст сообщения
    va_start(valist, Text);
    vsprintf(ErrorText, Text, valist);
    va_end(valist);

    // Отображаем окно сообщений
    MessageBox(NULL, ErrorText, CaptionText,
               MB_OK | MB_ICONEXCLAMATION);

    // Отправляем сообщение о завершении работы приложения,
    // если ошибка является фатальной
    if(Fatal == TRUE)
        PostQuitMessage(0);
}

// Процедура обработки сообщений - обрабатывает только
// сообщение о завершении работы приложения
long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg,
                           WPARAM wParam, LPARAM lParam)
{
    switch(uMsg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

BOOL DoInit()
{
    // Здесь выполняются функции инициализации приложения,
    // такие как инициализация графической системы, сети, звука и т.д.
    // Возвращает TRUE в случае успешной инициализации и FALSE при ошибке.

    return TRUE;
}

BOOL DoShutdown()
{
    // Здесь выполняются действия, завершающие работу приложения,
    // такие как отключение график, звука и т.д. Возвращает TRUE
    // в случае успешного завершения работы и FALSE при ошибке.

    return TRUE;
}

BOOL DoPreFrame()
{
    // Выполняет подготовку к формированию кадра, например,
    // установку таймера. Возвращает TRUE в случае успешной
    // подготовки и FALSE при ошибке.

    return TRUE;
}

BOOL DoFrame()
{
    // Выполняет операции формирования кадра, такие как визуализация.
    // Возвращает TRUE при успехе и FALSE при ошибке.

    return TRUE;
}

    BOOL DoPostFrame()
{
    // Выполняет посткадровую обработку, например,
    // синхронизацию со временем и т.д.
    // Возвращает TRUE при успехе и FALSE при ошибке.

    return TRUE;
}

Представленный выше код каркаса инициализирует окно и запускает цикл обработки сообщений, ожидающий, пока не придет сигнал о завершении программы. Все представленные функции обрабатывают различные моменты инициализации или завершения работы приложения. Обратите внимание, что создаваемое окно приложения не является фоновым — это необходимо для работы с DirectX Graphics, о чем вы узнаете в главе 2 «Рисование с DirectX Graphics».

Чтобы изменить другие параметры окна, такие как высоту, ширину или тип, вы можете отредактировать находящиеся в начале кода определения констант. То же самое справедливо и для класса окна и его заголовка, которые объявлены в двух константах в начале кода.

Обратите внимание, что я добавил функцию, которая нигде не вызывается. Это функция AppError, которую я использую для отображения пользовательских сообщений об ошибках. Если ей в параметре Fatal передать значение TRUE, то работа приложения будет завершена, в то время как значение FALSE позволяет программе продолжать работу.

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


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz