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;
// Создание и регистрация класса окна 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;
// Создание главного окна 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 | < Назад | Оглавление | Далее > |