Flying Cow
[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Форум » Создание игр посредством прямого программирования » Уроки по WinApi » Создание простейшего приложения Windows
Создание простейшего приложения Windows
NEWДата: Среда, 15.06.2011, 14:19 | Сообщение # 1
Сержант
Группа: Администраторы
Сообщений: 20
Репутация: 0
Статус: Offline
В этом уроке я научу вас как создать простейшее окно Windows средствами WinAPI.
Я составлял данную программу на Visual C++ 2008 Express Edition, поэтому там его работа гарантируется, если конечно руки совсем не кривые.
Для начала создайте проект и добавьте в него файл программного кода (как это делать я объяснять не буду, потому что надеюсь, что вы уже знаете об этом, а если нет, то вам сюда рано).
Для создания окна и работы с ним нам понадобятся некоторые функции.Чтобы получить к ним доступ достаточно просто подключить заголовочный файл windows.h к вашему проекту:
Code

// Заголовочные файлы
#include <windows.h>

Так же для самого окна нам понадобиться его дескриптор (переменная через которую мы сможем взаимодействовать с нашим окном). Объявим её:
Code

// Дескриптор окна
HWND hWnd = NULL;

Как вы знаете (наверное) в консольных приложениях точкой запуска проекта (запускаемая функция при запуске проекта) является функция main(). Тоже самое и здесь, только она называется WinMain(). У меня она выглядит следующим образом:
Code

//--------------------------------------------------
// С этой функции начинается выполнение программы
//--------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
   // Создание окон и элементов управления
   if ( !Init( hInstance, nCmdShow ) )
    return E_FAIL;

   // После их создания
   PostInit();

   // Цикл обработки сообщений
   MSG msg = {0};
   while( GetMessage( &msg, NULL, 0, 0 ) )
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }

   return (int) msg.wParam;
}

На первый взгляд она выглядит довольно сложно, но потом вы просто привыкните)). Рассмотрим что делает эта функция. Как вы видите она возвращает целочисленное значение (int) и принимает 4 аргумента (модификатор WINAPI просто сообщает компилятору в какой последовательности должны передаваться параметры функций через стек, а также некоторые другие вещи, но нас это не особо интересует, поэтому мы не будем останавливаться на этом). Рассмотрим что обозначают эти аргументы:
hInstance - дескриптор (или описатель, кому как нравится) приложения в системе.
hPrevInstance - описатель предыдущей запущенной копии приложения (в 32-ух разрядной версиях всегда равен NULL).
lpCmdLine - указатель на строку, которая содержит текст параметров командной строки (без имени исполняемого файла).
nCmdShow - определяет состояние окна приложения после запуска.
В теле этой функции вызывается функция инициализации приложения: Init() в которую передаются параметры hInstance и nCmdShow. Дело в том, что я специально создал эту вспомогательную функцию для того, что бы внутри неё прописывать создание окон. Если вы создаёте приложение с одним окном, то смысла в этой функции нет, но когда окон много, или просто присутствуют кнопки (причём не одна) или другие элементы управления, тогда гораздо удобнее вынести всю их инициализацию в отдельную функцию. Для создания окна необходимо задать, к какому именно приложению они относятся и в каком виде будут отображаться, поэтому в неё мы и передаем эти аргументы. Так же как вы видите она возвращает значение, которое после проверяется. Дело в том, что если программе не удалось создать окно, то она не будет дальше работать (ну или будет но нехорошо), да и вы в таком случае не сможете им воспользоваться, поэтому в случае провала создания окна нужно завершить работу приложения.
После идёт функция PostInit(). В ней будет удобно осуществлять какие-либо действия, которые будут происходить после инициализации приложения. Это будет удобно для таких вещей, как например инициализация графического интерфейса устройства. Но в данный момент нам ничего инициализировать не нужно, поэтому функция на самом деле пустая и её вызов можно за комментировать.
Рассмотрим что именно делает функция инициализации:
Code

//--------------------------------------------------
// Инициализация
//--------------------------------------------------
bool Init( HINSTANCE hInstance, int nCmdShow )
{
   // Создание основного окна
   if( FAILED( WindowCreate( &g_hWnd, hInstance, nCmdShow, WndProc ) ) )
    return false;

   //Отображаем окна на экране
   ShowWindow( g_hWnd, nCmdShow );
   UpdateWindow(g_hWnd);

   // Возврат успеха
   return true;
}

Внутри этой функции опять вызывается некая функция WindowCreate(). Это опять же созданная мной функция с помощью которой собственно и создаётся окно. Как вы видите вызов функции осуществляется из макроса FAILED(). Этот макрос возвращает TRUE, если в него передали какое-либо значение, свидетельствующее о неудачном завершении функции. В противном случае макрос возвращает значение TRUE. Получается если нам не удалось создать окно, то макрос вернёт значение TRUE, наша функция вернёт значение false и далее произойдёт выход из программы.
Далее идёт функция ShowWindow(), отображающая окно на экране, в которую передаётся дескриптор созданного окна и стиль его отображения. В нашем случае он определяется через переменную nCmdShow. Константы описывающие как будет отображаться окно начинаются с приставки SW_ (например SW_MINIMIZE - минимизировать (свернуть) окно, SW_NORMAL - отобразить в нормальном состоянии и другие). С помощью функции UpdateWindow() окно принудительно "обновиться" (перерисуется). Ей соответственно передаётся дескриптор нужного окна.
Рассмотрим как выглядит функция WindowCreate():
Code

//--------------------------------------------------
// Регистрация класса и создание окна
//--------------------------------------------------
HRESULT WindowCreate( HWND *hWnd, HINSTANCE hInstance, int nCmdShow, //основные аргументы
         LRESULT (WINAPI *WndProc)(HWND, UINT, WPARAM, LPARAM), //обработчик сообщений
         int left = CW_USEDEFAULT, int top = CW_USEDEFAULT, //положение окна
         int width = 640, int height = 480, //размер окна
         TCHAR name[] = L"Window", DWORD style = WS_OVERLAPPEDWINDOW, //заголовок и стиль отображения
         TCHAR classname[] = L"MainWindow", HWND hWndParent = NULL ) //название класса и дескриптор родительского окна
{
   // Регистрируем класс окна
   WNDCLASSEX wc;
   wc.cbSize = sizeof(WNDCLASSEX); // Размер структуры в байтах
   wc.style          = CS_HREDRAW | CS_VREDRAW; // Стиль окна
   wc.lpfnWndProc    = WndProc; // Указатель на оконную процедуру
   wc.cbClsExtra     = 0; // Резервирование памяти для   
   wc.cbWndExtra     = 0; //дополнительной информации
   wc.hInstance      = hInstance; // Дескриптор приложения
   wc.hIcon          = LoadIcon(NULL, IDI_APPLICATION); // Дескриптор основного значка
   wc.hCursor        = LoadCursor(NULL, IDC_ARROW); // Дескриптор курсора
   wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); // Кисть для фона
   wc.lpszMenuName   = NULL; // Имя ресурса меню
   wc.lpszClassName  = classname; // Имя класса
   wc.hIconSm        = LoadIcon(NULL, IDI_APPLICATION); // Дескриптор малого значка
   if( !RegisterClassEx(&wc) )
    return E_FAIL;

   // Создаем окно
   *hWnd = CreateWindow(
    classname, // Имя класса
    name, // Заголовок окна
    style, // Внешний вид (стиль) окна
    left, top, // Координаты положения окна на экране
    width, height, // Размеры окна
    hWndParent, // Дескриптор родительского окна
    NULL, // Дескриптор меню
    hInstance, // Дескриптор приложения
    NULL ); // Указатель на данные создания окна

   //Если не удалось создать окно - выходим из функции
   if( !(*hWnd) )
    return E_FAIL;

   // Возвращаем успех
   return S_OK;
}

Как вы видите в неё передаётся достаточно большое количество аргументов. Рассмотрим их:
*hWnd - указатель на дескриптор окна, который мы создадим в этой функции.
hInstance и nCmdShow уже знакомы нам.
*WndProc - указатель на функцию, обработчик событий для создаваемого окна.
left, top - координаты (x и y соответственно) левого верхнего угла создаваемого окна. По умолчанию задаются значения CW_USEDEFAULT, что означает что операционная система сама выберет начальное положение (чуть ниже и правее предыдущего созданного окна).
width, height - размеры окна (ширина и высота соответственно). Как координаты левого верхнего угла, так и размеры задаются в пикселях.
name - заголовок окна.
style - стиль отображения окна. Константы, описывающие данные стили начинаются с приставки WS_. Например WS_OVERLAPPEDWINDOW означает, что создастся полноценное окно со всеми необходимыми элементами если посмотреть значение этой константы то можно увидить, что оно состоит из следующих констант: WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX.
classname - название ранее созданного класса. Если система не найдёт указанный вами класс, то окно не получится создать.
hWndParent - дескриптор родительского окна, если такового нету, то передаётся NULL.
Далее идёт регистрация класса для нашёго окна. Для этого сначала заполняется структура WNDCLASSEX и после с помощью функции RegisterClassEx(), принимающей указатель на заполненную структуру. Само окно создаётся с помощью функции CreateWindow(), возвращающей дескриптор созданного окна. Всё остальное описанно коментариями.
Дальше идёт проверка, удалось ли создать окно или нет. Если нет, то возвращается код ошибки E_FAIL. Иначе программа выполняется дальше и доходит до последней строчки в которой возвращается код успеха S_OK.
Вернёмся наконец-то к нашёй функции WinMain. Дальше в ней идёт цикл обработки сообщений:
Code

   // Цикл обработки сообщений
   MSG msg = {0};
   while( GetMessage( &msg, NULL, 0, 0 ) )
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }

   return (int) msg.wParam;

В ходе него происходить извлечение сообщения из очереди и отправка его на обработку. Перед самим циклом обьявляется переменная типа MSG и инициализируется нулевыми значениями. Далее идет цикл, который выполняется до тех пор, пока функция GetMessage() возвращает TRUE (тоесть пока не поступит сообщение об выходе - WM_QUIT). Эта функция извлекает первое сообщение из очереди, если оно там есть, иначе ждёт их появления. Соответственно первым аргументом является указатель на структуру сообщения. Второй аргумент служит для того, что бы определить из какого именно окна будут приниматься сообщения. Если передать значение NULL, то будут приниматься сообщения со всех окон, которые относятся к данному приложению. Последние два параметра позволяют организовать фильтрацию сообщений, извлекаемых функцией. Если код сообщения не принадлежит промежутку от 1-го параметра до 2-го, то оно будет просто игнорироваться. Если в качестве них передать пару нулей, то фильтрация не будет производиться. В самом цикле присутствует ещё две функции. Первая из них, TranslateMessage(), преобразует коды виртуальных клавиш к символам ASCII. Вторая же, DispatchMessage(), отправляет извлечённое сообщение на обработку в оконную процедуру.
Когда поступает сообщение на выход мы выходим из цикла и затем выходим из нашего приложения при этом возвращая код, который был в сообщении об выходе.
Нам осталось рассмотреть только обработчик событий и на этом можно считать наш урок законченным:
Code

//--------------------------------------------------
// Обработка сообщений
//--------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
   switch (message)   
   {
   case WM_DESTROY:
    PostQuitMessage(0);
    break;

   default:
    return DefWindowProc(hWnd, message, wParam, lParam);
   }

   return 0;
}

Эта функция получает в качестве аргументов дескриптор окна, которому поступило сообщение (hWnd), идентификатор сообщения (message) и параметры этого сообщения (wParam и lParam). Оконная процедура может либо сама обработать поступившее сообщение, либо если в ней нет обработчика для данного сообщения вызвать системную функцию DefWindowProc().
Пока что не будем подробно останавливаться на этой порцедуре, припишем только обработку одного сообщения, а именно закрытия окна так, что бы при закрытии окна закрывалось и наше приложение. Для этого нужно проверить какое поступило сообщение, и если его идентификатор равен константе WM_DESTROY, которая соответственно и обозначает закрытие окна, то вызовем функцию PostQuitMessage() и передадим ей 0. На этом можно считать наш первый урок законченным. Ниже приведён полный листинг нашей программы:
Code

// Заголовочные файлы
#include <windows.h>

//--------------------------------------------------
// Глобальные переменные
//--------------------------------------------------
// Дескриптор окна
HWND g_hWnd = NULL;

//--------------------------------------------------
// Прототипы функций
//--------------------------------------------------
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

//--------------------------------------------------
// Регистрация класса и создание окна
//--------------------------------------------------
HRESULT WindowCreate( HWND *hWnd, HINSTANCE hInstance, int nCmdShow, //основные аргументы
         LRESULT (WINAPI *WndProc)(HWND, UINT, WPARAM, LPARAM), //обработчик сообщений
         int left = CW_USEDEFAULT, int top = CW_USEDEFAULT, //положение окна
         int width = 640, int height = 480, //размер
         TCHAR name[] = L"Window", DWORD style = WS_OVERLAPPEDWINDOW, //заголовок и стиль отображения
         TCHAR classname[] = L"MainWindow", HWND hWndParent = NULL ) //название класса и дескриптор родительского окна
{
   // Регистрируем класс окна
   WNDCLASSEX wc;
   wc.cbSize = sizeof(WNDCLASSEX); // Размер структуры в байтах
   wc.style          = CS_HREDRAW | CS_VREDRAW; // Стиль окна
   wc.lpfnWndProc    = WndProc; // Указатель на оконную процедуру
   wc.cbClsExtra     = 0; // Резервирование памяти для   
   wc.cbWndExtra     = 0; //дополнительной информации
   wc.hInstance      = hInstance; // Дескриптор приложения
   wc.hIcon          = LoadIcon(NULL, IDI_APPLICATION); // Дескриптор основного значка
   wc.hCursor        = LoadCursor(NULL, IDC_ARROW); // Дескриптор курсора
   wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); // Кисть для фона
   wc.lpszMenuName   = NULL; // Имя ресурса меню
   wc.lpszClassName  = classname; // Имя класса
   wc.hIconSm        = LoadIcon(NULL, IDI_APPLICATION); // Дескриптор малого значка
   if( !RegisterClassEx(&wc) )
    return E_FAIL;

   // Создаем окно
   *hWnd = CreateWindow(
    classname, // Имя класса
    name, // Заголовок окна
    style, // Внешний вид (стиль) окна
    left, top, // Координаты положения окна на экране
    width, height, // Размеры окна
    hWndParent, // Дескриптор родительского окна
    NULL, // Дескриптор меню
    hInstance, // Дескриптор приложения
    NULL ); // Указатель на данные создания окна

   //Если не удалось создать окно - выходим из функции
   if( !(*hWnd) )
    return E_FAIL;

   // Возвращаем успех
   return S_OK;
}

//--------------------------------------------------
// Инициализация
//--------------------------------------------------
bool Init( HINSTANCE hInstance, int nCmdShow )
{
   // Создание основного окна
   if( FAILED( WindowCreate( &g_hWnd, hInstance, nCmdShow, WndProc ) ) )
    return false;

   //Отображаем окна на экране
   ShowWindow( g_hWnd, nCmdShow );
   UpdateWindow(g_hWnd);

   // Возврат успеха
   return true;
}

//--------------------------------------------------
// Пост-инициализация
//--------------------------------------------------
void PostInit()
{
}

//--------------------------------------------------
// Обработка сообщений
//--------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
   switch (message)   
   {
   case WM_DESTROY:
    PostQuitMessage(0);
    break;

   default:
    return DefWindowProc(hWnd, message, wParam, lParam);
   }

   return 0;
}

//--------------------------------------------------
// С этой функции начинается выполнение программы
//--------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
   // Создание окон и элементов управления
   if ( !Init( hInstance, nCmdShow ) )
    return 0;

   // После их создания
   PostInit();

   // Цикл обработки сообщений
   MSG msg = {0};
   while( GetMessage( &msg, NULL, 0, 0 ) )
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }

   return (int) msg.wParam;
}


Откомпилированный проект и код прилагается.
Прикрепления: Ex01SimpleWindo.rar (5.2 Kb)



 
Форум » Создание игр посредством прямого программирования » Уроки по WinApi » Создание простейшего приложения Windows
  • Страница 1 из 1
  • 1
Поиск:

Copyright Flying Cow © 2024Сайт создан в системе uCoz