Делаем свое GUI приложение средствами WinAPI

в 19:08, , рубрики: c++, GUI, WinAPI, интерфейсы, метки: , ,

Здравствуй уважаемыйин!

Это мой дебют на Хабре!

Я достаточно долгое время читаю статьи с хабра мне они все нравятся. Но вот в чем загвоздка, я сам захотел попробовать что то свое сделать, так сказать сделать свой вклад в развитие данного проекта, хотя мой вклад будет достаточно мал и ничтожный.

Предусловие

Я хотел бы показать, рассказать более простым языком новичкам о GUI-приложениях на WinAPI.
GUI — (Graphic User Interfrace) графический пользовательский интерфрейс.
Для новичков это будет может более понятно, тем листать мануалы по WinAPI. Для более опытных программистов С++, моя статья
не содержит никакой полезной и интересной информации, так что смело можете давить Alt + Left, если нет тогда читаем до конца
Чтобы писать GUI-приложения нужно иметь хоть малейшее представление что такое GUI.

Что такое GUI ?

Смотрим всем известную Wikipedia — GUI!
GUI, грубо говоря это — окна. Рассмотрим пример окна блокнота в котором я писал эту статью
Noteepad++

Основные:

1 — Кнопка системного меню
2 — Системные кнопки (сворачивание/максимализация/закрытие)
3 — Меню
4 — Панель инструментов
5 — Полосы просмотра
6 — Строка состояния
7 — Рабочая область
Этот демонстрируется в качестве примера.

Знакомимся с WinAPI:

WinAPI это — Windows_API
Еще не долго и мы сделаем свое первое приложение, но как на примере у нас оно точно не получится
Для разработчика окно — это большое количество элементов, функционирующих под управлением приложенния и операционной системы.
А для программирование, окна — это переменные, над которыми выполняют некоторые действия.
Для создания окна операционной системе нужно указать к какому классу оно принадлежитф.

Немножко теории:

Набор используемых ресурсов класса задают в структуре типа WNDCLASS. Она имеет следующий вид:

typedef struct 
{ UINT		style;
  WNDPRO	lpfnWndProc;
  Int 	        cbClsExtra;
  Int	        cbWndExtra;
  HANDLE        hInstance;
  HICON	        hIcon;
  HCURSOR 	hCursor;
  HBRUSH        hbrBackground;
  LPCTSTR       lpszMenuName;
  LPCTSTR	lpszClassName; 
} WNDCLASS;

Небольшой разбор:

— style — стиль окна,
— lpfnWndProc — указывает на функцию окна,
— hInstance — указывает на дескриптор текущего приложения,
— hIcon — указывает на иконку,
— hCursor — указывает курсор,
— hbrBackground — задает дескриптор кисти закрашивания фона окна,
— lpszMenuName — указывает на имя ресурса главного меню окон этого класса,
— lpszClassName — указывает на текстовою строку, содержащую имя регистрируемого класса.
Думаю теории достаточно, если что-то конкретное будет интересовать, то поисковики никто не отменял.

Приступаем к практике:

И так друзья, сейчас будет самое интересное, сейчас мы будем делать свое первое GUI-приложение
Для начала объявим заголовочный файл «windows.h»

#include <windows.h>

В нем содержаться все необходимые WinAPI функции.
Теперь нужно объявить функции которые мы будем использовать.


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM RegMyWindowClass(HINSTANCE, LPCTSTR);

Сейчас будет самая важная функция, вообще, всей программы!
Всем программистам на С++ известна функция

int main ()

Для приложений на WinAPI наша функция называется WinMain(!)
Модификатор WINAPI указывает на то, что функция должна сама откорректировать расположение аргументов в стеке.


int WINAPI WinMain(HINSTANCE	hInstance,
                   HINSTANCE	hPrevInstance,
                   LPSTR	lpCmdLine,
                   int	        nCmdShow)

Едем дальше!
Давайте назовем свой будущий класс


  LPCTSTR lpzClass = ("My Window Class!");

Проверим регистрацию этого класса


  if (!RegMyWindowClass(hInstance, lpzClass))
    return 1;

Небольшая, но удобная мелочь. Вычисляем координаты центра экрана

RECT screen_rect;
  GetWindowRect(GetDesktopWindow(),&screen_rect); // разрешение экрана
  int x = screen_rect.right / 2 - 300;
  int y = screen_rect.bottom / 2 - 75;

Создание окна, с помощью CreateWindow, она имеет следующую структуру:


HWND		CreateWindow( 
LPCTST		lpClassName,        //Указатель на имя класса
LPCTST		lpWindowName,       //Указатель на имя окна
DWORD	        dwStyle,            //Стиль окна 
int 	        x,                  //Координата левого края окна 
int		y,                  //Координата верхнего края окна 
int             nWidth,             //Ширина окна 
int             nHeight,            //Высота окна 
HWND            hWndParent,         //Дескриптор окна-родителя или окна-владельца 
HMENU           hMenu,              //Дескриптор меню или идентификатор дочернего окна 
HANDLE          hInstance,          //Дескриптор приложения 
LPVOID          lpParam             //Указатель на данные окна 
)  

Ну, мы делаем тоже самое!


  HWND hWnd = CreateWindow(lpzClass, TEXT("Hello Habr!"),
    WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 600, 300, NULL, NULL,
    hInstance, NULL);

Если окно не создано, описатель будет равен 0.


  if(!hWnd) return 2;
/* Теперь делаем цикл обработки сообщений
 Именно здесь происходит «вызов» функции окна */
   MSG msg = {0};    // структура сообщения
  int iGetOk = 0;   // переменная состояния
  while ((iGetOk = GetMessage(&msg, NULL, 0, 0 )) != 0) // цикл сообщений
  {
    if (iGetOk == -1) return 3;  // если GetMessage вернул ошибку - выход
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;  // возвращаем код завершения программы
}

Теперь нам еще нужно зарегистрировать класс окна
Что мы сейчас и сделаем!


ATOM RegMyWindowClass(HINSTANCE hInst, LPCTSTR lpzClassName)
{
  WNDCLASS wcWindowClass = {0};
  // функция обработки сообщений
  wcWindowClass.lpfnWndProc = (WNDPROC)WndProc;
  // стиль окна
  wcWindowClass.style = CS_HREDRAW|CS_VREDRAW;
  // дескриптор экземпляра приложения
  wcWindowClass.hInstance = hInst;
  // название класса
  wcWindowClass.lpszClassName = lpzClassName;
  // загрузка курсора
  wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  // загрузка цвета окон
  wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
  return RegisterClass(&wcWindowClass); // регистрация класса
}

В каждом приложении нужна функция обработки сообщения, это в своем роде некоторое место где происходит обработка
взаимодействия между пользователем и самим приложением.


LRESULT CALLBACK WndProc(
  HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  // выборка и обработка сообщений
  switch (message)
  {
  case WM_LBUTTONUP:
     // реакция на сообщение
    MessageBox(hWnd, ("У нас все получилось!"), ("My GUI Program"), 0);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);  // реакция на сообщение
    break;
  default:
    // все сообщения не обработанные Вами обработает сама Windows
    return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

И это все!!! Надеюсь всем было интересно!
Все достаточно ясно. Никаких сложностей возникнуть не должно.
Да, от себя я добавил маленькую обработку событий, а именно реакцию на нажатие левой кнопки мышки.
И так, подобьем итоги:
— готовая первая программа на WinAPI,
— набрались немножко опыта(новички),
— я также набрался опыта.

Спасибо всем кто дочитал до конца, строго не судите и не ругайте за ошибки(я их по ходу написания много исправил ),
может изложил мысль не так как хотелось бы но все же, как мне кажется на нормальном языке и понятном языке. Если будет нужно, то и возможно продолжение.

Всем удачи, всем спасибо!

P.S Если возникли проблемы с кодом, вот выложил исходники + статья(html) (писал в CodeBlocks)

Автор: GraF_DOS

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js