Привіт усім.
В даній статті ми поговоримо про створення вікон і віконних елементів в ОС Віндовс, не використовуючи будь-яку візуальну оболонку на подобі Visual Studio чи DevC++. Будемо розглядати випадок суцільного використання коду С++.
Створення примірників і структура програми
Для того, щоб створити примірник вікна чи його дочірній елемент використовують дві функції: CreateWindow і CreateWindowEx. Їхнє оголошення виглядає наступним чином:
HWND WINAPI CreateWindow (LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam) ;
HWND WINAPI CreateWindowEx (DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam) ;
Як бачимо повертають дані функції, у разі успішного виконання, тип HWND, який являється вказівником на певну структуру вікна, або його дочірнього елемента. У разі неуспішного виконання функції, повернене значення буде прирівнюватися до NULL.
Параметри.
- lpClassName — ім'я класу елемента в якості рядка символів (LPCTSTR або LPWSTR). Дані класи, як ми побачимо з прикладу наведеного нижче, можуть бути як визначені і зареєстровані програмістом, так і класами за умовчанням, які визначені системою. Системні класи можуть приймати наступні значення:
- “BUTTON” для кнопки;
- “COMBOBOX” для поля з випадаючим списком;
- “EDIT” - для поля для введення;
- “LISTBOX” для елемента табличного представлення;
- “MDICLIENT” для програми, яка має декілька вікон;
- “RichEdit” - даний клас створює елемент, який відповідає за перегляд тексту з форматуванням символів і параграфів, і також може містити об'єкти COM (Component Object Model);
- “RICHEDIT_CLASS” - те ж саме що попередній, але версії 2.0; “SCROLL_BAR” - для елементу, який являє собою смугу прокручування;
- “STATIC” - для мітки з статичним текстом.
- lpWindowName — ім'я (або заголовок) вікна в якості рядка символів (LPCTSTR або LPWSTR). Для елементів керування вікна, це значення буде використовуватись в якості статичної мітки на елементі (для кнопки — напис на кнопці).
- dwStyle — стиль створюваного вікна. Може приймати значення: WS_TABSTOP, WS_VISIBLE, WS_CHILD, BS_DEFPUSHBUTTON і інші.
- x, y — ліва верхня координата початку площини вікна в якості цілих беззнакових.
- nWidth, nHeight — розміри вікна в пікселях для ширини і висоти відповідно.
- hWndParent — вказівник на примірник батьківського вікна.
- hMenu — вказівник на меню, або дочірнє вікно в залежності від типу вікна.
- hInstance — вказівник на примірник модуля, з яким буде асоціюватися вікно.
- lpParam — вказівник на параметр, який буде передаватися обробнику подій вікна через структуру CREATESTRUCT (lpCreateParams), на яку буде вказувати параметр lParam повідомлення WM_CREATE.
- dwExStyle (для CreateWindowEx) — розширені параметри стилю вікна, яке створюється.
Для того, щоб ваша програма скомпілювалася і показала вікно вам будуть необхідні дві функції: WinMain в якості головної функції вікна (аналог main), і WindowProcedure в якості функції обробки повідомлень, які приходять до вікна (події вікна: натискання кнопок, клавіш). Оголошуватися вони повинні наступним чином:
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil) ;
LRESULT CALLBACK WindowProcedure (HWND,
UINT,
WPARAM,
LPARAM) ;
Параметри для WinMain
- hInstance — структура-обробник для поточного примірника програми.
- hPrevInstance — вказівник на попередній примірник програми. Цей параметр завжди має значення NULL. Якщо вам необхідно визначити чи програма була запущена повторно, створіть м'ютекс з унікальним ім'ям.
- lpCmdLine — рядок командного інтерпретатора окрім ім'я програми.
- nCmdShow — цей параметр контролює, як саме програма повинна бути показаною (SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE і т.д.).
Параметри для WindowProcedure: перший параметр типу HWND — це вказівник на структуру поточного вікна (на яке прийшла подія); другий UINT — це тип повідомлення (WM_CREATE, WM_DESTROY, WM_SIZE і інші); наступні два параметри залежать від типу повідомлення, яке надійшло до програми.
Програма
Проста програма, яка демонструє вікно без будь-яких елементів може виглядати наступним чином (Ви можете використовувати її як каркас програми):
#include <windows.h>
#include <commctrl.h>
#include <Commdlg.h>
#include <Wingdi.h>
#include <commctrl.h>
using namespace std ;
/* головна функція програми (аналог main) */
int WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil) ;
/* функція обробки повідомлень */
LRESULT CALLBACK WindowProcedure (HWND,
UINT,
WPARAM,
LPARAM) ;
/* ім'я класу вікна */
wchar_t window_class_name [] = L"WindowInWindowsClass" ;
/* реалізація головної функції програми */
int WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
/* ініціація бібліотеки */
InitCommonControls () ;
/* об'єкт, в якому будемо містити наше вікно */
HWND window ;
/* Змінна, в якій зберігаються повідомлення до програми */
MSG messages ;
/* структура даних для класу вікна */
WNDCLASSEX wincl ;
/* заповнюємо структуру класу вікна */
wincl.hInstance = hThisInstance ;
wincl.lpszClassName = window_class_name ;
wincl.lpfnWndProc = WindowProcedure ;
wincl.style = CS_DBLCLKS ;
wincl.cbSize = sizeof (WNDCLASSEX) ;
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
wincl.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wincl.lpszMenuName = NULL ;
wincl.cbClsExtra = 0 ;
wincl.cbWndExtra = 0 ;
/* Створюємо об'єкт, який буде заповняти кольором полотно вікна */
wincl.hbrBackground = (HBRUSH) CreateSolidBrush (RGB (232, 246, 255)) ;
/* реєструємо клас і перевіряємо повернене */
/* значення на предмет помилки */
if (!RegisterClassEx (&wincl))
{
MessageBox (GetDesktopWindow (),
L"Не можу зареєструвати клас вікна",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* створюємо примірник вікна */
window = CreateWindowEx (0, /* Додаткові можливості */
window_class_name, /* ім'я класу вікна */
L"Просте Вікно у Вікнах", /* заголовок */
WS_OVERLAPPEDWINDOW, /* звичайне вікно */
1, /* координата лівого верхнього кута по осі x */
1, /* координата лівого верхнього кута по осі у */
1000, /* ширина програми в пікселях */
600, /* висота програми в пікселях */
GetDesktopWindow (), /* вікно буде дочірнім до робочого столу */
NULL, /* немає меню */
hThisInstance, /* примірник обробника програми */
NULL) ; /* дані створення вікна (сигнал WM_CREATE) */
/* перевіряємо повернене значення на предмет помилки */
if (window==NULL)
{
MessageBox (GetDesktopWindow (),
L"Помилка при створенні примірника вікна.",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* Показуємо вікно на весь екран */
ShowWindow (window, SW_MAXIMIZE) ;
/* Приймаємо повідомлення, поки не зустрінемо 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Перетворюємо віртуальні клавіші до символьних */
TranslateMessage (&messages) ;
/* Посилаємо повідомлення до WindowProcedure */
DispatchMessage (&messages) ;
}
/* Повертаємо значення, яке нам дала функція PostQuitMessage() */
return messages.wParam ;
}
/* Дана функція викликається системною функцією DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND phwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* обробляємо повідомлення */
switch (message)
{
case WM_DESTROY:
/* посилаємо повідомлення WM_QUIT */
PostQuitMessage (0) ;
break;
case WM_SIZE:
/* обробляємо зміну розмірів вікна */
case WM_COMMAND:
/* обробляємо події вікна (натискання кнопок і т.д.) */
default:
/* запускаємо обробник за умовчанням (якщо
** не враховано повідомлення) */
return DefWindowProc (phwnd, message, wParam, lParam) ;
}
return 0;
}
Для компілювання даного коду програми необхідно скористатися командою (у випадку портованого на ОС Віндовс компілятора GCC):
g++ windows_windows_simple.cxx -o file.exe -D UNICODE -D _UNICODE -luser32 -lcomdlg32 -lgdi32 -lcomctl32
Для компілятора, який використовується в Windows SDK (cl.exe), яку ви можете безплатно завантажити з офіційного сайту Майкрософт, команда набуде вигляду:
cl.exe -FAu windows_windows_simple.cxx -D UNICODE -D _UNICODE -l user32.lib comdlg32.lib gdi32.lib comctl32.lib
Для коректного відображення символів української (а також інших не-ASCII символів) слід використовувати два рази параметр компілятора “-D” з значеннями “UNICODE” і “_UNICODE”, що зробить Вашу програму сумісною з різними кодуваннями. З використанням параметра “-D”, перед відкриванням лапок кожного рядка символів слід ставити букву L (наприклад L“привіт, Світ!”). Також слід пам'ятати, що вихідний код програми повинен бути збережений з кодуванням “UTF-8” без символа BOM (індикатор порядку байтів).
Після компілювання і запуску даної програми ми можемо отримати наступний результат:
Створюємо віконні елементи
Тепер спробуємо створити які-небудь елементи і додати їх до вікна - одне вікно без дочірніх елементів виглядає красиво і гордо, але і сумно.
Отже давайте за допомогою викликів функцій CreateWindow створимо кнопку і поле для введення, а також оголосимо і реалізуємо обробник події натискання кнопки — виведемо вміст поля введення на екран у повідомленні (функція MessageBox).
Програма прийме приблизно наступний вигляд:
#include <windows.h>
#include <commctrl.h>
#include <Commdlg.h>
#include <Wingdi.h>
#include <commctrl.h>
#include <string>
using namespace std ;
HWND window ; /* об'єкт, в якому будемо містити вікно */
HWND button ; /* об'єкт, в якому будемо містити кнопку */
HWND edit ; /* об'єкт, в якому будемо містити поле для введення */
/* головна функція програми (аналог main) */
int WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil) ;
/* функція обробки повідомлень */
LRESULT CALLBACK WindowProcedure (HWND,
UINT,
WPARAM,
LPARAM) ;
/* обробник події кнопки */
void button_handler () ;
/* ім'я класу вікна */
wchar_t window_class_name [] = L"WindowInWindowsClass" ;
/* реалізація головної функції програми */
int WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
/* ініціація бібліотеки */
InitCommonControls () ;
/* Змінна, в якій зберігаються повідомлення до програми */
MSG messages ;
/* структура даних для класу вікна */
WNDCLASSEX wincl ;
/* заповнюємо структуру класу вікна */
wincl.hInstance = hThisInstance ;
wincl.lpszClassName = window_class_name ;
wincl.lpfnWndProc = WindowProcedure ;
wincl.style = CS_DBLCLKS ;
wincl.cbSize = sizeof (WNDCLASSEX) ;
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
wincl.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wincl.lpszMenuName = NULL ;
wincl.cbClsExtra = 0 ;
wincl.cbWndExtra = 0 ;
/* Створюємо об'єкт, який буде заповняти кольором полотно вікна */
wincl.hbrBackground = (HBRUSH) CreateSolidBrush (RGB (232, 246, 255)) ;
/* реєструємо клас і перевіряємо повернене */
/* значення на предмет помилки */
if (!RegisterClassEx (&wincl))
{
MessageBox (GetDesktopWindow (),
L"Не можу зареєструвати клас вікна",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* створюємо примірник вікна */
window = CreateWindowEx (0, /* Додаткові можливості */
window_class_name, /* ім'я класу вікна */
L"Просте Вікно у Вікнах", /* заголовок */
WS_OVERLAPPEDWINDOW, /* звичайне вікно */
1, /* координата лівого верхнього кута по осі x */
1, /* координата лівого верхнього кута по осі у */
600, /* ширина програми в пікселях */
300, /* висота програми в пікселях */
GetDesktopWindow (), /* вікно буде дочірнім до робочого столу */
NULL, /* немає меню */
hThisInstance, /* примірник обробника програми */
NULL) ; /* дані створення вікна (сигнал WM_CREATE) */
/* перевіряємо повернене значення на предмет помилки */
if (window==NULL)
{
MessageBox (GetDesktopWindow (),
L"Помилка при створенні примірника вікна.",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* створюємо примірник вікна */
button = CreateWindow (L"BUTTON", /* ім'я класу кнопки */
L"Натисни на мене", /* заголовок */
WS_TABSTOP |
WS_VISIBLE |
WS_CHILD |
BS_DEFPUSHBUTTON, /* властивості кнопки */
3, /* координата лівого верхнього кута по осі x */
3, /* координата лівого верхнього кута по осі у */
200, /* ширина кнопки в пікселях */
25, /* висота кнопки в пікселях */
window, /* кнопка буде дочірньою до вікна */
(HMENU)1, /* WindowProc wParam */
hThisInstance, /* примірник обробника програми */
NULL) ; /* дані створення кнопки (сигнал WM_CREATE) */
/* перевіряємо повернене значення на предмет помилки */
if (button==NULL)
{
MessageBox (GetDesktopWindow (),
L"Помилка при створенні примірника кнопки.",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* створюємо примірник вікна */
edit = CreateWindow (L"EDIT", /* ім'я класу поля */
L"", /* немає заголовку */
WS_CHILD |
WS_VISIBLE |
BS_TEXT, /* властивості поля */
3+3+200, /* координата лівого верхнього кута по осі x */
3, /* координата лівого верхнього кута по осі у */
300, /* ширина поля в пікселях */
25, /* висота поля в пікселях */
window, /* поле буде дочірнім до вікна */
NULL, /* немає меню */
hThisInstance, /* примірник обробника програми */
NULL) ; /* дані створення поля (сигнал WM_CREATE) */
/* перевіряємо повернене значення на предмет помилки */
if (edit==NULL)
{
MessageBox (GetDesktopWindow (),
L"Помилка при створенні примірника поля для введення.",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* Показуємо вікно на весь екран */
ShowWindow (window, SW_SHOW) ;
/* Приймаємо повідомлення, поки не зустрінемо 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Перетворюємо віртуальні клавіші до символьних */
TranslateMessage (&messages) ;
/* Посилаємо повідомлення до WindowProcedure */
DispatchMessage (&messages) ;
}
/* Повертаємо значення, яке нам дала функція PostQuitMessage() */
return messages.wParam ;
}
/* Дана функція викликається системною функцією DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND phwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* обробляємо повідомлення */
switch (message)
{
case WM_DESTROY:
/* посилаємо повідомлення WM_QUIT */
PostQuitMessage (0) ;
break;
case WM_SIZE:
/* обробляємо зміну розмірів вікна */
case WM_COMMAND:
/* обробляємо події кнопки */
if (wParam==1)
{
button_handler () ;
}
default:
/* запускаємо обробник за умовчанням (якщо
** не враховано повідомлення) */
return DefWindowProc (phwnd, message, wParam, lParam) ;
}
return 0;
}
/* обробник події кнопки */
void button_handler ()
{
/* змінна, яка буде містити значення поля для введення */
wstring msg = L"Ви ввели наступний текст: " ;
/* визначаємо необхідний розмір буфера */
int length = GetWindowTextLength (edit) ;
/* виділяємо необхідний буфер у пам'яті */
wchar_t* buff = new wchar_t [length+1] ;
/* обнуляємо буфер */
memset ((void*)buff, 0, (length+1)*sizeof(wchar_t)) ;
/* отримуємо текст поля у виділений буфер */
GetWindowText (edit, (LPWSTR)buff, length) ;
/* копіюємо значення */
msg += buff ;
/* повертаємо пам'ять системі */
delete[] buff ;
/* виводимо повідомлення на екран */
MessageBox (window, msg.c_str (), L"Важливе повідомлення", MB_OK | MB_ICONEXCLAMATION) ;
}
Після компілювання і виконання даної програми, ми отримаємо наступний результат: