Привіт усім. В даній статті ми поговоримо про створення вікон і віконних елементів в ОС Віндовс, не використовуючи будь-яку візуальну оболонку на подобі 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 (індикатор порядку байтів). Після компілювання і запуску даної програми ми можемо отримати наступний результат: simple_windows_window

Створюємо віконні елементи

Тепер спробуємо створити які-небудь елементи і додати їх до вікна - одне вікно без дочірніх елементів виглядає красиво і гордо, але і сумно. Отже давайте за допомогою викликів функцій 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) ;
   
}
Після компілювання і виконання даної програми, ми отримаємо наступний результат: windows_window_with_elements
Категорії: