Застосування класів С + + спільно з WinApi при програмуванні для Windows (исходники), Різне, Програмування, статті

Операційна система Windows є об’єктно-орієнтованою. Всі елементи управління (об’єкти) є вікнами в тому чи іншому вигляді. Кожен такий елемент має свої параметри стану, вхідні і вихідні повідомлення. Традиційно при написанні програм з використанням чистого WinApi застосовуються методи структурного програмування. Ця стаття покаже як можна використовувати спільно WinApi і класи С + + на прикладі написання власної бібліотеки класів. У даній статті для перевірки та компіляції написаного коду буде використовуватися компілятор MS Visual C + +6.
Розглянемо принцип роботи найпростішого додатка Windows


#include <windows.h>
char szTitle[]=”Title”;
char szWindowclass[]=”Base”;
BOOL MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow)
{
    if (!MyRegisterClass (hInstance)) return FALSE;
    if (!InitInstance (hInstance, nCmdShow)) return FALSE;
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
BOOL MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASS wndclass;
    wndclass.style           = CS_HREDRAW / CS_VREDRAW;
    wndclass.lpfnWndProc     = (WNDPROC)WndProc;
    wndclass.cbClsExtra      = 0;
    wndclass.cbWndExtra      = 0;
    wndclass.hInstance       = hInstance;
    wndclass.hIcon           = LoadIcon(hInstance, IDI_APPLICATION);
    wndclass.hCursor         = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground   = (HBRUSH)(COLOR_WINDOW+1);
    wndclass.lpszMenuName    = 0;
    wndclass.lpszClassName   = szWindowclass;
    return RegisterClass(&wndclass);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd = CreateWindow(
            szWindowclass,
            szTitle,
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            NULL,
            NULL,
            hInstance,
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}


Як видно з лістингу, мінімальні вимоги для роботи програми наступні:
При запуску програми повинні відбуватися, щонайменше, дві події – має бути створено вікно і запущений цикл обробки повідомлень, з якого, з настанням якоїсь події, повинен бути здійснений вихід і робота програми повинна завершитися. Все це відбувається, як правило, до функцій WinMain (), яка є стандартною точкою входу в усі програми для Windows. Виходячи з цього, ми з’ясували, що нам потрібно клас, який буде запускати цикл обробки повідомлень.
Назвемо цей клас TApplication.
Далі в програмі йде реєстрація віконного класу і створення вікна на основі цього класу.

if (!MyRegisterClass (hInstance))
return FALSE;
if (!InitInstance (hInstance, nCmdShow))
return FALSE;

Перша з цих функцій реєструє віконні класи, друга створює саме вікно.
Покладемо на плечі класу TApplication реєстрацію віконних класів.
Реєстрацією займатиметься функція
BOOL Initialize( HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow).
Для цього нам знадобиться додати в клас внутрішній параметр HINSTANCE m_hInstance, він буде відповідати за дескриптор зразка додатки. Але у функції WinMain є ще два інших параметра.
Перший з них це командний рядок програми, другий – спосіб відображення вікна на екрані. Додамо ці параметри в наш клас.

	LPSTR	m_lpCmdLine;
int m_nCmdShow;

Так само нам потрібні функції доступу до цих змінним. Для цього введемо в наш клас функції які будуть давати можливість звертатися до цих змінним:

    inline LPSTR GetCmdLine() const { return m_lpCmdLine; }
inline int GetCmdShow() const { return m_nCmdShow; }
inline HINSTANCE GetInstanceHandle() const { return m_hInstance; }

В функціюрегістрірующую всі віконні класи, ми будемо передавати всі параметри з функції WinMain.

BOOL TApplication::Initialize( HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow)
{
HICON m_hIcon, m_hIconSmall;
m_lpCmdLine = lpCmdLine;
m_nCmdShow = nCmdShow;
m_hInstance = hInstance;
m_hIcon = m_hIconSmall = ::LoadIcon( NULL, IDI_APPLICATION );
WNDCLASSEX wClass;
wClass.cbSize = sizeof( wClass );
wClass.cbClsExtra = 0;
wClass.cbWndExtra = 0;
wClass.lpszMenuName = NULL;
wClass.hbrBackground= ::GetSysColorBrush( COLOR_WINDOW );
wClass.hCursor = ::LoadCursor( NULL, IDC_ARROW );
wClass.hIcon = wClass.hIconSm = m_hIcon;
wClass.hInstance = hInstance;
wClass.lpfnWndProc = TWindow::StaticWindowProc;
wClass.lpszClassName= MAINWINDOWCLASS;
wClass.style = CS_HREDRAW / CS_VREDRAW / CS_DBLCLKS;
if ( ! RegisterClassEx( &wClass ))
return FALSE;
return TRUE;
}

Розберемо детальніше що робить ця функція.
Змінні m_hIcon і m_hIconSmall нам знадобляться в подальшому, коли ми захочемо щоб наша програма мала іконку. А поки будемо задовольнятися тим, що нам може надати система.
m_hIcon = m_hIconSmall = ::LoadIcon( NULL, IDI_APPLICATION );
Далі ми присвоюємо всім внутрішнім змінним передані значення.
 m_lpCmdLine = lpCmdLine; m_nCmdShow = nCmdShow; m_hInstance = hInstance;
Після чого визначаємо структуру wClass містить інформацію про віконному класі який ми будемо реєструвати.
Заповнюємо цю структуру стандартними значеннями і намагаємося зареєструвати.
Тут MAINWINDOWCLASS це рядок визначена у файлі Globals.h:

/ / ——- Запобігання багаторазового включення заголовки ———
#ifndef GLOBALS_H
#define GLOBALS_H
//————————————————————————-
#include <windows.h>
#include <tchar.h>
#define MAINWINDOWCLASS “TControl”
//————————————————————————- / / DELETE_NULL видаляє об’єкт і встановлює вказівник на NULL / / Не завершуйте кінець цього макросу “;”
#define DELETE_NULL(ptr) { delete ptr; ptr = NULL; } / / Голбальная мінлива програми. / / Кожне нове додаток повинен починатися з рядка / / Application = new TApplication (); якщо цього не зробити то змінна буде / / Вказувати на NULL і додаток відразу некоректно завершиться.
class TApplication;
extern TApplication* Application;
//————————————————————————-
#endif

Якщо реєстрація класу пройшла успішно, функція повертає TRUE інакше FALSE.

wClass.lpfnWndProc =  TWindow::StaticWindowProc; 

StaticWindowProc-це основна функція, за допомогою якої, клас TWindow буде взаємодіяти з системою, а система з класом. Ця функція повинна бути обов’язково оголошена як CALLBACK і бути статичною. Інакше програма не буде працювати. Далі у функції WinMain йде функція створення вікна.

InitInstance (hInstance, nCmdShow))

Її ми поки пропустимо і перейдемо на пару рядків нижче. Якщо програма дісталася до цих рядків, це означає, що віконний клас був успішно зареєстрований і вікно створено. Після реєстрації класу і створення вікна програма входить в цикл обробки повідомлень.
Цим повинен займатися клас TApplication.
Для цього додамо в цей клас функцію Run ().

int TApplication::Run()
{ / / Обробляється повідомлення
MSG msg; / / Отримуємо повідомлення
while ( GetMessage( &msg, NULL, 0, 0 ) > 0 )
{ / / Елси повідомлення не оброблено
if ( msg.hwnd == NULL)
continue; / / Транслюємо повідомлення
::TranslateMessage( &msg ); / / Відсилаємо повідомлення процедурам вікон
::DispatchMessage( &msg );
} / / Повертаємо код завершення програми
return ( LRESULT )msg.wParam;
}

Ця функція робить все те ж саме, що і звичайна програма без класів. Думаю коментарі тут зайві.

Повний лістинг класу TApplication: Заголовний файл

/ / ——- Запобігання багаторазового включення заголовки ———
#ifndef TAPPLICATION_H
#define TAPPLICATION_H
//————————————————————————–
#include “Globals.h”
//————————————————————————–
class TApplication
{
public:
int Run();
TApplication();
~TApplication();
BOOL Initialize( HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow);
inline LPSTR GetCmdLine() const { return m_lpCmdLine; }
inline int GetCmdShow() const { return m_nCmdShow; }
inline HINSTANCE GetInstanceHandle() const { return m_hInstance; }
protected:
HINSTANCE m_hInstance;
LPSTR m_lpCmdLine;
int m_nCmdShow;
};
//————————————————————————–
#endif

Файл реалізації

#include “TApplication.h”
#include “TWindow.h” / / Глобальний покажчик на об’єкт TApplication / / Існує в єдиному вигляді
TApplication *Application = NULL;
/*============================================================================== Функція:
TApplication() Параметри: Немає Повернення: Немає Призначення: Конструктор за умовчанням Примітки:
==============================================================================*/
TApplication::TApplication() {/ / Обнуляемвнутренніе члени
m_hInstance = NULL;
}
/*============================================================================== Функція:
~TApplication() Параметри: Немає Повернення: Немає Призначення: Віртуальний деструктор. Примітки:
==============================================================================*/
TApplication::~TApplication()
{
}
/*============================================================================== Функція:
BOOL Initialize(HINSTANCE hInstance,LPSTR lpCmdLine,int nCmdShow) Параметри: hInstance-дескриптор зразка програми lpCmdLine-командний рядок програми nCmdShow-параметр відображення програми Повернення: TRUE додаток ініціалізованої нормально. FALSE сталася помилка Призначення: Ініціалізувати внутрішні члени програми Примітки:
==============================================================================*/
BOOL TApplication::Initialize( HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow)
{ / / Заповнюємо внутрішні дані
HICON m_hIcon, m_hIconSmall;
m_lpCmdLine = lpCmdLine;
m_nCmdShow = nCmdShow;
m_hInstance = hInstance; / / Завантажуємо іконку з системи
m_hIcon = m_hIconSmall = ::LoadIcon( NULL, IDI_APPLICATION ); / / Реєструємо клас головного окона
WNDCLASSEX wClass;
wClass.cbSize = sizeof( wClass );
wClass.cbClsExtra = 0;
wClass.cbWndExtra = 0;
wClass.lpszMenuName = NULL;
wClass.hbrBackground = ::GetSysColorBrush( COLOR_WINDOW );
wClass.hCursor = ::LoadCursor( NULL, IDC_ARROW );
wClass.hIcon = wClass.hIconSm = m_hIcon;
wClass.hInstance = hInstance;
wClass.lpfnWndProc = TWindow::StaticWindowProc;
wClass.lpszClassName = MAINWINDOWCLASS;
wClass.style = CS_HREDRAW / CS_VREDRAW / CS_DBLCLKS; / / Якщо клас вікна не зарегістрірова виходимо
if ( ! RegisterClassEx( &wClass ))
return FALSE;
return TRUE;
}
/*============================================================================== Функція:
int Run() Параметри: Немає Повернення: Код завершення програми Призначення: Запустити цикл обробки повідомлень і повернути код завершення програми Примітки:
==============================================================================*/
int TApplication::Run()
{ / / Обробляється повідомлення
MSG msg; / / Отримуємо повідомлення
while ( GetMessage( &msg, NULL, 0, 0 ) > 0 )
{ / / Елси повідомлення не оброблено
if ( msg.hwnd == NULL)
continue; / / Транслюємо повідомлення
::TranslateMessage( &msg ); / / Відсилаємо повідомлення процедурам вікон
::DispatchMessage( &msg );
} / / Повертаємо код завершення програми
return ( LRESULT )msg.wParam;
}

З класом, який відповідає за ініціалізацію програми, реєстрацію віконних класів і запуск циклу обробки повідомлень розібралися. Займемося реалізацією класу відповідає за створення вікна і обробку повідомлень.
Для початку розберемося, що цей клас повинен з себе представляти.
Як мінімум він повинен мати процедуру обробки повідомлень і функцію створення вікна. Процедура, яка буде отримувати повідомлення від системи та їх обробляти, повинна бути оголошена як static і CALLBACK. Модифікатор CALLBACK вказує на те, що ця функція викликається операційною системою, і є функцією зворотного виклику.
Ця функція буде глобальною. Тобто для всіх класів успадкованих від TWindow ця функція буде однією і тією ж.
А як же бути з індивідуальними повідомленнями, що посилаються конкретного вікна?
Для цього нам знадобляться ще пара допоміжних класів і функцій:
TObjectManager– Зв’язаний список, в який ми будемо заносити покажчики на всі створені класи.
TObject – Який буде одночасно бути і вузлом списку TObjectManager і базовим класом для всіх об’єктів нашої бібліотеки.
Що стосується класу TWindow, Що є базовим класом для всіх віконних класів, то в нього нам буде потрібно додати кілька функцій. Але про них пізніше.
Повні листинги класів TObject і TObjectManager .
TObject.h

/ / ——- Запобігання багаторазового включення заголовки ———
#ifndef TOBJECT_H
#define TOBJECT_H
//—————————————————————————
#include “Globals.h”
//————————————————————————— / / TObject це базовий клас для всіх об’єктових є частиною пов’язаного / / Спіска.Просто визначаємо тривіальний клас який має покажчик на / / Наступний елемент тогоже самого або похідного типу і віртуальний деструктор
class TObject
{
public: / / Конструктор за замовчуванням
TObject()
{ m_pNextItem = NULL; m_pPrevItem=NULL; }
/ / Віртуальний деструктор.нічего не робить
virtual ~TObject()
{;} / / Це наступний елемент у зв’язаному спіске.Предназначен для того щоб уникнути / / Додаткових витрат на функції які можна викликати з базового класу.
TObject* m_pNextItem;
TObject* m_pPrevItem;
};
//—————————————————————————
#endif

TObjectManager.h

/ / ——- Запобігання багаторазового включення заголовки ———
#ifndef TOBJECTMANAGER_H
#define TOBJECTMANAGER_H
//————————————————————————–
#include “Globals.h”
#include “TObject.h”
//————————————————————————–
class TObjectManager
{
public:
virtual void Free();
virtual TObject * Find(TObject* pItem);
virtual void Empty();
virtual BOOL Delete(TObject *pItem,BOOL bDeleteItem =TRUE);
virtual void Add(TObject* pItem,BOOL bAddAtBeginningOfList =FALSE);
TObjectManager();
virtual ~TObjectManager(); / / Повертаємо число елементів у списку або об’єктів поміщених в список
UINT GetCount() { return m_nItemCount; } / / Покажчики на перший і останній об’єкт в списку
TObject* m_pFirstItem;
TObject* m_pLastItem; / / Число елементів у списку
UINT m_nItemCount;
};
//————————————————————————– / / Це приватний TObjectManager який ніколи не видаляє об’єкти з листа
class TWindowList : public TObjectManager
{
public: / / Конструктор за умовчанням. Нічого не робить
TWindowList()
{;} / / Очищаємо лист але не видаляємо об’єкти. Нехай вони самі себе знищують :).
~TWindowList()
{
Empty();
}
};
//————————————————————————–
#endif

TObjectManager.cpp

//————————————————————————–
#include “TObjectManager.h”
/*————————————————————————— Функція:
TObjectManager Параметри: Немає Повернення: Немає Призначення: Конструктор по умолчанію.Ініціалізірует внутрішні члени Примітки:
//————————————————————————-*/
TObjectManager::TObjectManager()
{
Empty();
}
/*————————————————————————— Функція:
~TObjectManager Параметри: Немає Повернення: Немає Призначення: Віртуальний деструктор. Викликає Free () для видалення об’єктів містяться у зв’язаному списку Примітки:
//————————————————————————-*/
TObjectManager::~TObjectManager()
{
Free();
}
/*————————————————————————— Функція:
Add Параметри: pItem-Покажчик на об’єкт котрий слід додати до списку
bAddAtBeginningOfList Повернення: Немає Призначення: Додає елемент в кінець списку якщо тільки bAddAtBeginningOfList дорівнює TRUE Примітки:
//————————————————————————-*/
void TObjectManager::Add(TObject *pItem, BOOL bAddAtBeginningOfList)
{
if ( !pItem )
return;
if ( m_pFirstItem )
{
if ( bAddAtBeginningOfList )
{
pItem->m_pNextItem = m_pFirstItem;
m_pFirstItem = pItem;
}
else
{
m_pLastItem->m_pNextItem = pItem;
m_pLastItem = pItem;
}
}
else
m_pFirstItem = m_pLastItem = pItem;
/ / Збільшуємо лічильник
m_nItemCount++;
}
/*————————————————————————— Функція:
Delete Параметри: pItem-Покажчик на об’єкт який слід видалити з пов’язаного списку bDeleteItem-видалити тільки зі списку або й об’єкт також Повернення: Вилучений чи об’єкт Призначення: Видаляє pItem із зв’язаного списку. Якщо bDeleteItem = TRUE буде вилучений і
pItem Примітки:
//————————————————————————-*/
BOOL TObjectManager::Delete(TObject *pItem, BOOL bDeleteItem)
{
if ( !pItem )
return FALSE;
/ / Шукаємо попередній елемент у списку
TObject* pThisItem = m_pFirstItem;
TObject* pPreviousItem = NULL;
BOOL bResult = FALSE;
while ( pThisItem )
{
if ( pThisItem == pItem )
{
bResult = TRUE;
/ / Це перший елемент?
if ( pItem == m_pFirstItem )
m_pFirstItem = pItem->m_pNextItem;
/ / Це останній елемент
if ( pItem == m_pLastItem )
m_pLastItem = pPreviousItem;
/ / Призначаємо попередній вузол вказувати на наступний
if ( pPreviousItem )
pPreviousItem->m_pNextItem = pItem->m_pNextItem;
/ / Зменшуємо лічильник
m_nItemCount–;
/ / Закінчили
break;
}
else
{ / / Зберігаємо попередній елемент і берм наступний елемент для циклу
pPreviousItem = pThisItem;
pThisItem = pThisItem->m_pNextItem;
}
}
/ / Видаляємо pItem?
if ( bDeleteItem )
DELETE_NULL(pItem)
return bResult;
}
/*————————————————————————— Функція:
Empty Параметри: Немає Повернення: Немає Призначення: Спустошений зміст пов’язаного списку не видаляючи самі об’єкти Примітки:
//————————————————————————-*/
void TObjectManager::Empty()
{
m_pFirstItem = m_pLastItem = NULL;
m_nItemCount = 0;
}
/*————————————————————————— Функція:
Find Параметри:
pItem Повернення:
TObject Призначення: Шукаємо елемент в списку і повертаємо його або повертаємо NULL якщо об’єкт не знайдено Примітки:
//————————————————————————-*/
TObject * TObjectManager::Find(TObject *pItem)
{
if ( pItem )
{
TObject* pThisItem = m_pFirstItem;
while ( pThisItem )
{
if ( pThisItem == pItem )
return pThisItem;
pThisItem = pThisItem->m_pNextItem;
}
}
return NULL;
}
/*————————————————————————— Функція:
Free Параметри: Немає Повернення: Немає Призначення: Звільняємо всі елементи в списку. Функція також видаляє самі об’єкти в списку. Примітки: Якщо потрібно лише скинути вміст варто викликати Empty ()
//————————————————————————-*/
void TObjectManager::Free()
{
TObject* pItem = m_pFirstItem;
TObject* pNextItem = NULL;
while ( pItem )
{
pNextItem = pItem->m_pNextItem;
DELETE_NULL(pItem)
pItem = pNextItem;
}
Empty();
}

Розберемося детальніше у вищенаведеному лістингу.
Що повинен уміти TObjectManager?
* Додавати об’єкти (вікна в список).
* Видаляти ці об’єкти зі списку, маючи можливість видалити не тільки елемент списку а й зруйнувати сам об’єкт.
* Очищати список від усіх об’єктів.
* Здійснювати пошук об’єкта в списку.
* Очищати список, видаляючи не тільки елементи списку, але й об’єкти також.
* Мати внутрішній лічильник об’єктів, збільшуючи або зменшуючи його при додаванні вузла в список або видаляючи вузол зі списку відповідно.
* Мати конструктор і деструктор.
У загальних рисах цей клас працює як звичайний зв’язаний список. У лістингу, грубо кажучи, наведено найпростіший зв’язаний список, адаптований для наших потреб.

Ми також додали клас TWindowList. Він призначений для того, щоб зберігати покажчики на віконні об’єкти. Ми не повинні використовувати TObjectManager для віконних об’єктів з наступних причин: При руйнуванні TObjectManager викликає функцію Free() Яка видаляє не тільки вузли списку а й самі об’єкти. На відміну від TWindowList, Який викликає функцію Empty() Не руйнує об’єкти а тільки очищає список від вузлів.

Клас TWindow


Цей клас найскладніший з усіх класів описаних вище.
Розберемося в тому, що повинен вміти цей клас.
Уявімо собі, що користувач постійно створює все нові й нові вікна. Всі ці вікна отримують повідомлення від системи за допомогою функції
LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Ця функція є спільною для всіх класів. Але нам то треба обробити події конкретного вікна. Як і в звичайному, структурному програмуванні, кожне вікно має свою функцію обробки повідомлень. Зазвичай вона називається WindowProc. В нашому випадку, кожне вікно також повинно мати функцію обробки повідомлень, крім головної функції, загальною для всіх окон.Для цих цілей додамо функцію
LRESULT WindowProc( UINT uMsg, WPARAM wParam, LPARAM lParam ).
Вона буде займатися обробкою повідомлень, посланих конкретного вікна. Але для того, щоб визначати якого вікна конкретно адресоване повідомлення, нам потрібен список всіх створених вікон. Цей список ми вже передбачили. Єдине чого не вистачає, так це функції для отримання дескриптора вікна m_hWnd. Для доступу до дескриптора вікна введемо в обіг функцію GetHandle ():

HWND TWindow::GetHandle()
{
if ( m_hWnd && ::IsWindow( m_hWnd ))
return m_hWnd;
return NULL;
}

Що вона робить? Якщо об’єкт має дескриптор вікна і вікно з цим дескриптором існує, то ми повертаємо цей дескриптор, в іншому випадку, повертаємо нульове значення. При виконанні функції StaticWindowProc ми будемо шукати це вікно в списку вікон, і передавати повідомлення саме йому.
Пошуком вікон займеться процедура

TWindow * TWindow::FindObjectByHandle(HWND hWnd)
{ / / Дескриптор існує?
if ( hWnd )
{ / / Раптом наш об’єкт знаходиться першим у списку
TWindow* pWindow = (TWindow*)GLOBAL_WINDOW_LIST.m_pFirstItem; / / Ні. Шукаємо об’єкт в голобальном списку по дескриптору
while ( pWindow )
{ / / Дескриптори збігаються?
if ( pWindow->m_hWnd == hWnd ) / / Так. Повертаємо об’єкт
return pWindow; / / Проходимо по всьому списку в пошуках об’єкта
pWindow = (TWindow*)pWindow->m_pNextItem;
}
} / / Не знайшли. Повертаємо NULL
return NULL;
}

А що якщо існує вікно зі своїм дескриптором, але не є класом TWindow або його спадкоємцем, а нам так хотілося б їм керувати, так само як і свої класом. Вихід є, напишемо функцію, що прикріплюють дескриптор існуючого вікна до нашого класу. Функцію назвемо Attach. У неї ми будемо передавати дескриптор існуючого вікна і параметр який визначатиме руйнувати нам дескриптор вікна при його знищенні чи ні, а повертати вона буде TRUE якщо дескриптор прикріплений до об’єкта або FALSE якщо нет.По замовчуванням нам не потрібно руйнувати дескриптор.
Для цього прапора введемо ще одну змінну:

BOOL m_bDestroyHandle;

Декларація функції Attach:
BOOL Attach(HWND hWnd,BOOL bDestroy = FALSE );
Також наш об’єкт може бути діалогом або дочірнім вікном багатодокументного програми. Додамо і ці змінні.

BOOL m_bIsMDIFrame;
BOOL m_bIsDialog;

Реалізуємо функцію визначальну чи є наш об’єкт діалогом:

inline BOOL IsDialog()
{
return m_bIsDialog;
}

Нам потрібно ще одна змінна в якій ми будемо зберігати адресу старої віконної процедури WNDPROC m_lpfnOldWndProc;

BOOL TWindow::Attach( HWND hWnd, BOOL bDestroy /* = FALSE */ )
{ / / Повинні руйнувати вікно
if ( bDestroy )
{ / / Шукаємо об’єкт по дескриптору
if ( FindObjectByHandle(hWnd ) )
{ / / Знайшли. Чи не приєднуємо дескріптор.Он вже приєднаний
return FALSE;
}
} / / Буфер для імені класу
TCHAR szClassName[ 10 ]; / / Отримуємо ім’я класу
if ( ::GetClassName( hWnd, szClassName, 10 ))
{ / / Якщо # 32770 це ім’я класу то це вікно вікно діалогу / / # 32770 системна змінна визначає діалог
m_bIsDialog = ( BOOL )( _tcscmp( _T( “#32770” ), szClassName ) == 0 ); / / Прирівнюємо дескриптор вікна до переденному дескриптору
m_hWnd = hWnd; / / Прапор руйнування вікна дорівнює переданому
m_bDestroyHandle = bDestroy; / / Призначаємо віконну процедуру
m_lpfnOldWndProc = ( WNDPROC )::GetWindowLong( hWnd, m_bIsDialog ?
DWL_DLGPROC : GWL_WNDPROC ); / / Якщо віконна процедура не дорівнює статичної процедурі вікна
if ( m_lpfnOldWndProc && m_lpfnOldWndProc !=
( WNDPROC )TWindow::StaticWindowProc )
{ / / Призначаємо процедуру вікна
::SetWindowLong( hWnd, m_bIsDialog ? DWL_DLGPROC : GWL_WNDPROC,
( LONG )TWindow::StaticWindowProc );
} / / Приєднали. Повертаємо TRUE
return TRUE;
} / / Не отримано ім’я класу / / Обнуляемс процедуру вікна
m_lpfnOldWndProc = NULL; / / Обнуляємо дескриптор вікна
m_hWnd = NULL; / / Не приєднали дескриптор. повертаємо FALSE
return FALSE;
}

Принцип роботи цієї функції такий:
Оперделяем ім’я класу вікна з переданого дескриптора.
Якщо ім’я класу з’ясувати не вдалося, робимо висновки, що дескриптор липовий і нічого не прикріплюючи, виходимо з функції.
Якщо ім’я класу вдалося з’ясувати, то визначаємо, чи не є переданий дескриптор дескриптором діалогу.
В системі набір символів # 32770 говорить про те, що цей клас – діалог.
Якщо є, присвоюємо змінної m_bIsDialog статус, за яким будемо надалі визначати що наш об’єкт – діалог.
Далі просто прирівнюємо дескриптор класу до переданого дескриптору і прапор руйнування до переданого прапору. Після чого отримуємо покажчик на стару віконну процедуру і зберігаємо її в змінній m_lpfnOldWndProc.
Проробивши Еге операції з’ясовуємо, чи існує стара процедура і чи не є вона нашої поточної статичної процедурою. Якщо не існує і не є, то призначаємо віконному дескриптору нову віконну процедуру, яка за сумісництвом і наша статична процедура.
Виходимо з функції, повертаючи параметр TRUE який красномовно говорить про те, що нам вдалося прикріпити переданий дескриптор до нашого об’єкту.

HWND TWindow::Detach()
{ / / Чи є віконна процедура статичної процедурою
if ( m_lpfnOldWndProc && m_lpfnOldWndProc != ( WNDPROC )TWindow::StaticWindowProc )
{ / / Встановлюємо нову процедуру вікна
::SetWindowLong( m_hWnd, m_bIsDialog ? DWL_DLGPROC : GWL_WNDPROC, ( LONG )m_lpfnOldWndProc ); / / Прирівнюємо дескриптор до тимчасового
HWND hWnd = m_hWnd; / / Ми не діалог
m_bIsDialog = FALSE; / / Обнуляємо дескриптор вікна
m_hWnd = NULL; / / Видаляємо віконну процедуру
m_lpfnOldWndProc = NULL; / / Повертаємо дескриптор
return hWnd;
} / / Нічого від’єднувати
return NULL;
}

Функція Detach від’єднує дескриптор вікна від нашого класу. У цій функції ми спочатку визначаємо чи існує стара процедура і чи не є вона нашої поточної статичної процедурою. Якщо так воно і є, то встановлюємо дескриптору вікна стару віконну процедуру. Створюємо тимчасовий дескриптор вікна, прирівнюючи його до поточного.
Говоримо, що наш об’єкт більше не є діалогом.
Видаляємо поточний дескриптор вікна.
Видаляємо стару віконну процедуру і повертаємо від’єднаний дескриптор.
Якщо ж умова не виконана, то повертаємо нульовий покажчик.
Ось ми і дісталися до місця, де пора б додати функцію створення самого вікна.
Назвемо її Create ();

BOOL TWindow::Create(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, 
DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu)
{ / / Якщо вже є дескриптор
if ( GetHandle() ) / / Повертаємо FALSE. Вікно вже створено
return FALSE; / / Заповнюємо структуру створення вікна переданими параметрами
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hwndParent;
cs.hMenu = nIDorHMenu;
cs.lpCreateParams = 0L; / / Попереднє створення вікна. / / Якщо ия класу не передано заповнює його ім’ям класу за замовчуванням
if ( PreCreateWindow( &cs ))
{ / / Створюємо вікно
m_hWnd = ::CreateWindowEx( cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
Application->GetInstanceHandle(),
( LPVOID )this ); / / Якщо створили вікно
if ( m_hWnd )
{ / / Вимагаємо зруйнувати вікно при від’єднанні дескриптора
m_bDestroyHandle = TRUE; / / Заповнюємо стару процедуру вікна
m_lpfnOldWndProc = ( WNDPROC )::GetWindowLong( m_hWnd, m_bIsDialog
? DWL_DLGPROC : GWL_WNDPROC ); / / Якщо стара процедура не статична процедура цього класу
if ( m_lpfnOldWndProc && m_lpfnOldWndProc !=
( WNDPROC )TWindow::StaticWindowProc ) {/ / Встановлюємо процедуру вікна
if (::SetWindowLong(m_hWnd, m_bIsDialog?DWL_DLGPROC:GWL_WNDPROC,
( LONG )TWindow::StaticWindowProc )) / / Вікно створено успішно. Повертаємо TRUE
return TRUE;
} / / Процедура вже призначено
else / / Вікно створено успішно. Повертаємо TRUE
return TRUE;
} / / Повертаємо TRUE якщо є робочий дескриптор
return ( BOOL )( m_hWnd );
} / / Створення вікна не відбулося. або воно вже було створено раніше
return FALSE;
}

Всі параметри взяті з стандартної функції WinApi CreateWindowEx().
На самому початку варто перевірити, чи не намагаємося ми створити вікно, яке вже було раніше створено.
Якщо намагаємося, кидаємо цю затію і виходимо. Якщо не намагаємося, заповнюємо структуру створення вікна CREATESTRUCT переданими парамтрамі. Функція PreCreateWindow, раніше не описувана, перевіряє, передано ім’я класу створюваного вікна чи ні. Якщо не передано, то заповнює ім’я значенням за замовчуванням узятим з файлу Globals.h

BOOL TWindow::PreCreateWindow(LPCREATESTRUCT pCS)
{
if ( pCS->lpszClass == NULL )
pCS->lpszClass = MAINWINDOWCLASS;
return TRUE;
}

Ця функція віртуальна і в успадкованих класах її можна буде перевизначити. Наприклад її можна буде використовувати при створенні свого класу, не успадкованого від класу TWindow.

Після виклику цієї функції намагаємося створити вікно на основі заповненої вище структури.
У додаткові дані вікна записуємо покажчик на наш об’єкт. Він нам буде потрібно для ідентифікації нашого класу в функцие StaticWindowProc.
Якщо вікно створено міняємо прапор руйнування об’єкта на те, що при знищенні об’єкта дескриптор повинен бути знищений.
Отримуємо стару процедуру вікна.
Визначаємо, чи існує стара процедура і чи не є вона нашої поточної статичної процедурою.
Якщо всі умови дотримані, встановлюємо нову процедуру вікна і виходимо з функції.
Залишилося зробити останній штрих і пробувати нашу бібліотеку в действіі.Нам треба заповнити процедуру обробки повідомлень від системи.

LRESULT CALLBACK TWindow::StaticWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam )
{ / / Це нам згодиться для обробки повідомлень
TWindow *pWindow = NULL; / / Вікно початок створюватися / / Нам треба прикріпити дескриптор вікна до цього об’єкта?
if ( uMsg == WM_NCCREATE ) {/ / Покажчик на об’єкт цього вікна повинен бути переданий використовуючи поле / / LParam структури CREATESTRUCT.
pWindow = ( TWindow * )(( LPCREATESTRUCT )lParam )->lpCreateParams; / / Прикріплюємо до об’єкта
if ( pWindow ) pWindow->Attach( hWnd, TRUE );
}
else if ( uMsg == WM_INITDIALOG )
{ / / Спершу перевіряємо чи є lParam об’єктом в нашому списку вікон
pWindow = (TWindow*)GLOBAL_WINDOW_LIST.Find((TObject*)lParam) ; / / Якщо об’єкт не був знайдений в списку, він повинен бути дороговказом в поле / / PROPSHEETPAGE.В цьому випадку об’єкт повинен бути в полі lParam цієї / / Структури.
if ( pWindow == NULL )
pWindow = ( TWindow * )(( PROPSHEETPAGE * )lParam )->lParam; / / Прикріплюємо до об’єкта
pWindow->Attach( hWnd, TRUE );
}
else
{ / / Якщо ми досягли цього місця то дескриптор вже прикріплений до об’єкта
pWindow = FindObjectByHandle( hWnd );
} / / Ми маємо дійсний покажчик
if ( pWindow != NULL && pWindow->GetHandle() )
{ / / Заздалегідь встановлюємо результат обробки повідомлення
LRESULT lResult = -1; / / Отримуємо тип повідомлення
switch ( uMsg )
{
case WM_CREATE: / / Викликаємо обробник повідомлення
lResult = pWindow->OnCreate(( LPCREATESTRUCT )lParam );
break;
case WM_CLOSE: / / Викликаємо обробник повідомлення
lResult = pWindow->OnClose();
break;
case WM_DESTROY: / / Викликаємо обробник повідомлення
lResult = pWindow->OnDestroy();
break;
case WM_COMMAND:
{ / / Викликаємо обробник повідомлення
lResult = pWindow->OnCommand(( UINT )HIWORD( wParam ),
( UINT )LOWORD( wParam ), ( HWND )lParam );
break;
}
} / / Якщо повідомлення не було оброблено (lResult = -1) і дескриптор вікна все / / Ще є дійсним ми викликаємо процедуру вікна
if ((( lResult == -1 && uMsg != WM_CREATE ) //
( lResult == -2 && uMsg == WM_CREATE ))
&& pWindow->GetHandle())
lResult = pWindow->WindowProc( uMsg, wParam, lParam ); / / Ми починаємо руйнувати вікно
if ( uMsg == WM_NCDESTROY )
{ / / Дескриптор дійсний
if ( pWindow->GetHandle())
{ / / / Від’єднуємо дескриптор
pWindow->Detach(); / / Видаляємо дескриптор
pWindow->m_hWnd = NULL; / / Видаляємо віконну процедуру
pWindow->m_lpfnOldWndProc = NULL;
}
} / / Повертаємо результат обробки повідомлення
return lResult;
} / / Ми не змогли знайти дескриптор в нашому глобальному списку вікон. Це може / / Означати що дескриптор не був прикріплений до об’єкта / / В цьому випадку ми просто залишаємо це повідомлення віконному дескриптору
TCHAR szClassName[ 10 ]; / / Отримуємо ім’я класу вікна
if ( ::GetClassName( hWnd, szClassName, 10 ))
{ / / Це діалогове вікно? Якщо так то повертаємо 0 і не передаємо повідомлення / / Процедурі вікна
if ( _tcscmp( _T( “#32770” ), szClassName ) == 0 )
return 0;
} / / Передаємо необроблене повідомлення процедурі вікна за замовчуванням і повертаємо / / Результат його обробки
return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
}

Ця функція є найважливішою. Якщо ви не зрозумієте як працює ця функція ви не зрозумієте як працює бібліотека.


А працює ця функція досить просто.
На початку операції створення вікна система посилає програмі повідомлення WM_NCCREATE, що говорить про те, що незабаром буде створена не клієнтська частина вікна. У другому параметрі повідомлення передається покажчик на структуру CREATESTRUCT.
Пам’ятаєте, ми при створенні вікна, у функції Create (), передали в додатковий параметр вказівник на наш об’єкт.
Нам потрібно виділити його з переданої структури і прирівняти його до нашого тимчасового об’єкту.

pWindow =    ( TWindow * )((LPCREATESTRUCT)lParam)->lpCreateParams;

Наводячи до типу TWindow переданий параметр, ми прирівнюємо раніше створений об’єкт до нашого тимчасового об’єкту.
Після того, як ми отримали покажчик на наш об’єкт, нам треба прикріпити до нього переданий дескриптор hWnd.

 if ( pWindow)pWindow->Attach( hWnd, TRUE );

Але наше вікно може бути і діалогом. В цьому випадку замість обробки повідомлення WM_NCCREATE нам слід обробляти повідомлення WM_INITDIALOG. Це повідомлення надсилається системою, до того як діалог буде відображений. Якби ми крім класів вікон ми могли створювати ще й класи діалогів, то при обробці цього повідомлення логічно було б викликати функцію, яка налаштувала б діалог до його відображення на екрані. (Про це в наступних статтях)
При відправці цього повідомлення, у другій параметр записується структура PROPSHEETPAGE, з якої ми можемо отримати покажчик на наш об’єкт.

pWindow =   ( TWindow * )(( PROPSHEETPAGE *)lParam)->lParam;

При отриманні покажчика потрібно прикріпити переданий дескриптор до нашого об’єкту. Але до цього варто перевірити, чи немає такого об’єкта в нашому списку вікон.

pWindow =  (TWindow*)GLOBAL_WINDOW_LIST.Find((TObject*)lParam);

Якщо ні, прикріплюємо дескриптор до об’єкту:

pWindow->Attach( hWnd, TRUE );

Система викликає цю функцію не тільки при створенні вікна, але і при відправці повідомлення. По цьому, якщо повідомлення WM_NCCREATE і WM_INITDIALOG не оброблені, ми просто шукаємо об’єкт в нашому списку вікон за переданим дескриптору.

pWindow = FindObjectByHandle( hWnd );

Далі ми перевіряємо чи існує об’єкт або ми так і не отримали покажчик на нього

if ( pWindow !=   NULL&&pWindow->GetHandle() )

Якщо існує починаємо обробляти надіслані повідомлення.

Обробка повідомлень.


Так як клас TWindow є базовим класом, то всі функції які ми будемо обробляти повинні бути віртуальними. Це потрібно для того, щоб класи спадкоємці могли їх перевизначити і обробити по-своєму. Функції базового класу, при обробці повідомлень повертають значення -1. Це говорить системі що повідомлення не оброблено. Спадкоємці при обробці цих повідомлень мають повертати значення більше або рівну нулю.

Попрошу звернути увагу, що при обробці повідомлення WM_CREATE функція базового класу повертає не -1 а -2. Бо якщо ми повернемо -1 система розцінить це повернення як невдачу при створенні вікна і завершить роботу програми.

LRESULT TWindow::OnCreate( LPCREATESTRUCT /*pCS*/ )
{
return -2;
}

У цю функцію передається структура створення вікна. Після обробки повідомлень ми дивимося на код повернення функції. Якщо повідомлення не було оброблено (lResult = -1) і дескриптор вікна все ще є дійсним, ми викликаємо процедуру вікна за замовчуванням.

lResult = pWindow->WindowProc( uMsg, wParam, lParam );

Якщо оброблюваний повідомлення WM_NCDESTROY. То ми руйнуємо вікно, від’єднуємо дескриптор від об’єкта і видаляємо віконну процедуру. Якщо повідомлення не оброблено ні в статичній функції ні в функції за умовчанням ми передаємо необроблене повідомлення назад в систему.

return ::DefWindowProc( hWnd, uMsg, wParam, lParam );

Лістинг класу TWindow TWindow.h

// TWindow.h: interface for the TWindow class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_TWINDOW_H__9CF24F11_A481_48F8_8AD5_CA7D9DCC1B83__INCLUDED_)
#define AFX_TWINDOW_H__9CF24F11_A481_48F8_8AD5_CA7D9DCC1B83__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include “TObject.h”
class TWindow : public TObject
{
public:
static TWindow * FindObjectByHandle(HWND hWnd );
BOOL Create( DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu );
HWND GetHandle();
static LRESULT CALLBACK StaticWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
TWindow();
virtual ~TWindow();
inline BOOL IsDialog() { return m_bIsDialog; }
protected:
virtual LRESULT OnCommand( UINT nNotifyCode, UINT nCtrlID, HWND hWndCtrl );
virtual LRESULT OnDestroy();
virtual LRESULT OnClose();
virtual LRESULT OnCreate( LPCREATESTRUCT pCS );
virtual LRESULT WindowProc( UINT uMsg, WPARAM wParam, LPARAM lParam );
virtual void Destroy();
virtual BOOL PreCreateWindow( LPCREATESTRUCT pCS );
HWND Detach();
BOOL Attach(HWND hWnd,BOOL bDestroy = FALSE );
HWND m_hWnd;
BOOL m_bIsMDIFrame;
BOOL m_bIsDialog;
BOOL m_bDestroyHandle;
WNDPROC m_lpfnOldWndProc;
};
#endif // !defined(AFX_TWINDOW_H__9CF24F11_A481_48F8_8AD5_CA7D9DCC1B83__INCLUDED_)

TWindow.cpp

// TWindow.cpp: implementation of the TWindow class.
//
//////////////////////////////////////////////////////////////////////
#include “TApplication.h”
#include “TWindow.h”
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#include “TObjectManager.h”
TWindowList GLOBAL_WINDOW_LIST;
TWindow::TWindow()
{
m_bIsDialog = FALSE;
m_hWnd = NULL;
m_lpfnOldWndProc = NULL;
m_bDestroyHandle = TRUE;
GLOBAL_WINDOW_LIST.Add( this , FALSE);
}
TWindow::~TWindow()
{
Destroy();
GLOBAL_WINDOW_LIST.Delete( this, FALSE );
return;
}
/*============================================================================== Функція:
LRESULT CALLBACK StaticWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,
LPARAM lParam ) Параметри: hWnd-вікно послали повідомлення uMsg-обробляється повідомлення wParam-перший параметр повідомлення lParam-другий параметр повідомлення Повернення: результат обробки повідомлення Призначення: Віконна процедура окна.Обрабативает надходять повідомлення
==============================================================================*/
LRESULT CALLBACK TWindow::StaticWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,
LPARAM lParam )
{ / / Це нам згодиться для обробки повідомлень
TWindow *pWindow = NULL; / / Вікно початок створюватися / / Нам треба прикріпити дескриптор вікна до цього об’єкта?
if ( uMsg == WM_NCCREATE ) {/ / Покажчик на об’єкт цього вікна повинен бути переданий використовуючи поле / / LParam структури CREATESTRUCT.
pWindow = ( TWindow * )(( LPCREATESTRUCT )lParam )->lpCreateParams; / / Прикріплюємо до об’єкта
if ( pWindow ) pWindow->Attach( hWnd, TRUE );
}
else if ( uMsg == WM_INITDIALOG )
{ / / Спершу перевіряємо чи є lParam об’єктом в нашому списку вікон
pWindow = (TWindow*)GLOBAL_WINDOW_LIST.Find((TObject*)lParam) ; / / Якщо об’єкт не був знайдений в списку, він повинен бути дороговказом в поле / / PROPSHEETPAGE.В цьому випадку об’єкт повинен бути в полі lParam цієї / / Структури.
if ( pWindow == NULL )
pWindow = ( TWindow * )(( PROPSHEETPAGE * )lParam )->lParam; / / Прикріплюємо до об’єкта
pWindow->Attach( hWnd, TRUE );
}
else
{ / / Якщо ми досягли цього місця то дескриптор вже прикріплений до об’єкта
pWindow = FindObjectByHandle( hWnd );
} / / Ми маємо дійсний покажчик
if ( pWindow != NULL && pWindow->GetHandle() )
{ / / Заздалегідь встановлюємо результат обробки повідомлення
LRESULT lResult = -1; / / Отримуємо тип повідомлення
switch ( uMsg )
{
case WM_CREATE: / / Викликаємо обробник повідомлення
lResult = pWindow->OnCreate(( LPCREATESTRUCT )lParam );
break;
case WM_CLOSE: / / Викликаємо обробник повідомлення
lResult = pWindow->OnClose();
break;
case WM_DESTROY: / / Викликаємо обробник повідомлення
lResult = pWindow->OnDestroy();
break;
case WM_COMMAND:
{ / / Викликаємо обробник повідомлення
lResult = pWindow->OnCommand(( UINT )HIWORD( wParam ),
( UINT )LOWORD( wParam ), ( HWND )lParam );
break;
}
} / / Якщо повідомлення не було оброблено (lResult = -1) і дескриптор вікна все / / Ще є дійсним ми викликаємо процедуру вікна
if ((( lResult == -1 && uMsg != WM_CREATE ) //
( lResult == -2 && uMsg == WM_CREATE ))
&& pWindow->GetHandle())
lResult = pWindow->WindowProc( uMsg, wParam, lParam ); / / Ми починаємо руйнувати вікно
if ( uMsg == WM_NCDESTROY )
{ / / Дескриптор дійсний
if ( pWindow->GetHandle())
{ / / / Від’єднуємо дескриптор
pWindow->Detach(); / / Видаляємо дескриптор
pWindow->m_hWnd = NULL; / / Видаляємо віконну процедуру
pWindow->m_lpfnOldWndProc = NULL;
}
} / / Повертаємо результат обробки повідомлення
return lResult;
} / / Ми не змогли знайти дескриптор в нашому глобальному списку вікон. Це може / / Означати що дескриптор не був прикріплений до об’єкта / / В цьому випадку ми просто залишаємо це повідомлення віконному дескриптору
TCHAR szClassName[ 10 ]; / / Отримуємо ім’я класу вікна
if ( ::GetClassName( hWnd, szClassName, 10 ))
{ / / Це діалогове вікно? Якщо так то повертаємо 0 і не передаємо повідомлення / / Процедурі вікна
if ( _tcscmp( _T( “#32770” ), szClassName ) == 0 )
return 0;
} / / Передаємо необроблене повідомлення процедурі вікна за замовчуванням і повертаємо / / Результат його обробки
return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
}
/*============================================================================== Функція:
HWND GetHandle() Параметри: Немає Повернення: Отримати дескриптор вікна пов’язаного з об’єктом Призначення: Повертає дескриптор вікна Примітки:
==============================================================================*/
HWND TWindow::GetHandle()
{
if ( m_hWnd && ::IsWindow( m_hWnd ))
return m_hWnd;
return NULL;
}
/*============================================================================== Функція:
BOOL Attach( HWND hWnd, BOOL bDestroy = FALSE ) Параметри: hWnd-дескриптор вікна bDestroy-чи повинні зруйнувати вікно при від’єднанні дескриптора Повернення: TRUE – приєднати дескриптор FALSE-не приєднали Призначення: Прикріплюємо дескриптор вікна до цього об’єкта. Це буде працювати тільки в тому випадку якщо об’єкт ще не має дескриптора вікна. Примітки:
==============================================================================*/
BOOL TWindow::Attach( HWND hWnd, BOOL bDestroy /* = FALSE */ )
{ / / Повинні руйнувати вікно
if ( bDestroy )
{ / / Шукаємо об’єкт по дескриптору
if ( FindObjectByHandle(hWnd ) )
{ / / Знайшли. Чи не приєднуємо дескріптор.Он вже приєднаний
return FALSE;
}
} / / Буфер для імені класу
TCHAR szClassName[ 10 ]; / / Отримуємо ім’я класу
if ( ::GetClassName( hWnd, szClassName, 10 ))
{ / / Якщо # 32770 це ім’я класу то це вікно вікно діалогу / / # 32770 системна змінна визначає діалог
m_bIsDialog = ( BOOL )( _tcscmp( _T( “#32770” ), szClassName ) == 0 ); / / Прирівнюємо дескриптор вікна до переденному дескриптору
m_hWnd = hWnd; / / Прапор руйнування вікна дорівнює переданому
m_bDestroyHandle = bDestroy; / / Призначаємо віконну процедуру
m_lpfnOldWndProc = ( WNDPROC )::GetWindowLong( hWnd, m_bIsDialog ?
DWL_DLGPROC : GWL_WNDPROC ); / / Якщо віконна процедура не дорівнює статичної процедурі вікна
if ( m_lpfnOldWndProc && m_lpfnOldWndProc !=
( WNDPROC )TWindow::StaticWindowProc )
{ / / Призначаємо процедуру вікна
::SetWindowLong( hWnd, m_bIsDialog ? DWL_DLGPROC : GWL_WNDPROC,
( LONG )TWindow::StaticWindowProc );
} / / Приєднали. Повертаємо TRUE
return TRUE;
} / / Не отримано ім’я класу / / Обнуляемс процедуру вікна
m_lpfnOldWndProc = NULL; / / Обнуляємо дескриптор вікна
m_hWnd = NULL; / / Не приєднали дескриптор. повертаємо FALSE
return FALSE;
}
/*============================================================================== Функція:
HWND Detach() Параметри: Немає Повернення: Від’єднаний дескриптор Призначення: Від’єднує дескриптор вікна від об’єкта не видаляючи сам дескриптор. Так вікно може далі посилати повідомлення, але не бути об’єктом
==============================================================================*/
HWND TWindow::Detach()
{ / / Чи є віконна процедура статичної процедурою
if ( m_lpfnOldWndProc && m_lpfnOldWndProc != ( WNDPROC )TWindow::StaticWindowProc )
{ / / Встановлюємо нову процедуру вікна
::SetWindowLong( m_hWnd, m_bIsDialog ? DWL_DLGPROC : GWL_WNDPROC, ( LONG )m_lpfnOldWndProc ); / / Прирівнюємо дескриптор до тимчасового
HWND hWnd = m_hWnd; / / Ми не діалог
m_bIsDialog = FALSE; / / Обнуляємо дескриптор вікна
m_hWnd = NULL; / / Видаляємо віконну процедуру
m_lpfnOldWndProc = NULL; / / Повертаємо дескриптор
return hWnd;
} / / Нічого від’єднувати
return NULL;
}
BOOL TWindow::Create(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu)
{ / / Якщо вже є дескриптор
if ( GetHandle() ) / / Повертаємо FALSE. Вікно вже створено
return FALSE; / / Заповнюємо структуру створення вікна переданими параметрами
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hwndParent;
cs.hMenu = nIDorHMenu;
cs.lpCreateParams = 0L; / / Попереднє створення вікна. / / Якщо ия класу не передано заповнює його ім’ям класу за замовчуванням
if ( PreCreateWindow( &cs ))
{ / / Створюємо вікно
m_hWnd = ::CreateWindowEx( cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
Application->GetInstanceHandle(),
( LPVOID )this ); / / Якщо створили вікно
if ( m_hWnd )
{ / / Вимагаємо зруйнувати вікно при від’єднанні дескриптора
m_bDestroyHandle = TRUE; / / Заповнюємо стару процедуру вікна
m_lpfnOldWndProc = ( WNDPROC )::GetWindowLong( m_hWnd, m_bIsDialog
? DWL_DLGPROC : GWL_WNDPROC ); / / Якщо стара процедура не статична процедура цього класу
if ( m_lpfnOldWndProc && m_lpfnOldWndProc !=
( WNDPROC )TWindow::StaticWindowProc ) {/ / Встановлюємо процедуру вікна
if (::SetWindowLong(m_hWnd, m_bIsDialog?DWL_DLGPROC:GWL_WNDPROC,
( LONG )TWindow::StaticWindowProc )) / / Вікно створено успішно. Повертаємо TRUE
return TRUE;
} / / Процедура вже призначено
else / / Вікно створено успішно. Повертаємо TRUE
return TRUE;
} / / Повертаємо TRUE якщо є робочий дескриптор
return ( BOOL )( m_hWnd );
} / / Створення вікна не відбулося. або воно вже було створено раніше
return FALSE;
}
BOOL TWindow::PreCreateWindow(LPCREATESTRUCT pCS)
{
if ( pCS->lpszClass == NULL )
pCS->lpszClass = MAINWINDOWCLASS;
return TRUE;
}
/*============================================================================== Функція:
TWindow *FindObjectByHandle(HWND hWnd ) Параметри: hWnd-дескриптор вікна за яким шукається об’єкт Повернення: Покажчик на TWindow або NULL якщо об’єкт із таким дескриптором не знайдено Призначення: Отримати покажчик на об’єкт по дескриптору вікна Примітки: Може терпіти невдачі якщо потрібне вікно з дескриптором hWnd не є вікном бібліотеки, або для об’єкта був викликаний метод Detach
==============================================================================*/
TWindow * TWindow::FindObjectByHandle(HWND hWnd)
{ / / Дескриптор існує?
if ( hWnd )
{ / / Раптом наш об’єкт знаходиться першим у списку
TWindow* pWindow = (TWindow*)GLOBAL_WINDOW_LIST.m_pFirstItem; / / Ні. Шукаємо об’єкт в голобальном списку по дескриптору
while ( pWindow )
{ / / Дескриптори збігаються?
if ( pWindow->m_hWnd == hWnd ) / / Так. Повертаємо об’єкт
return pWindow; / / Проходимо по всьому списку в пошуках об’єкта
pWindow = (TWindow*)pWindow->m_pNextItem;
}
} / / Не знайшли. Повертаємо NULL
return NULL;
}
LRESULT TWindow::OnCommand( UINT /*nNotifyCode*/, UINT /*nCtrlID*/, HWND /*hWndCtrl*/ )
{
return -1;
}
LRESULT TWindow::OnDestroy()
{
return -1;
}
LRESULT TWindow::OnClose()
{
return -1;
}
LRESULT TWindow::OnCreate( LPCREATESTRUCT /*pCS*/ )
{
return -2;
}
LRESULT TWindow::WindowProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
LRESULT lResult = 0;
if ( m_lpfnOldWndProc && m_lpfnOldWndProc != ( WNDPROC )TWindow::StaticWindowProc )
#if __BORLANDC__==0x0550
lResult = ::CallWindowProc((FARPROC) m_lpfnOldWndProc, GetHandle(), uMsg, wParam, lParam );
#else
lResult = ::CallWindowProc( m_lpfnOldWndProc, GetHandle(), uMsg, wParam, lParam );
#endif
else if ( m_bIsDialog == FALSE )
{
lResult = ::DefWindowProc( GetHandle(), uMsg, wParam, lParam );
}
return lResult;
}
void TWindow::Destroy()
{
if ( GetHandle())
{
if ( m_bDestroyHandle )
{
HWND hWnd = m_hWnd;
Detach();
::DestroyWindow( hWnd );
}
else
Detach();
}
}

Тестування бібліотеки.


Для того щоб протестувати то що ми написали проробимо наступне. Створимо проект з одним єдиним файлом. Нехай він буде називатися TetsWindow.cpp
Напишемо в ньому таке:

#include <globals.h>
#include <TApplication.h>
#include <TWindow.h>
class TTestWindow :public TWindow
{
protected:
virtual LRESULT OnClose()
{
PostQuitMessage( 0 );
return 0;
}
};
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
int ret=0;
Application= new TApplication;
Application->Initialize(hInstance,lpCmdLine,nCmdShow);
TTestWindow *Wnd= new TTestWindow; Wnd-> Create (0L, NULL, “Приклад Вікна”, WS_OVERLAPPEDWINDOW / WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL);
ret= Application->Run();
delete Application;
delete Wnd;
return ret;
}

Ну і як?. Обсяг написання програми скоротився в рази?
Що ж ми тут робимо?
1. Створюємо новий об’єкт TApplication

Application=new TApplication;

2. Створюємо об’єкт вікна.

TTestWindow *Wnd= new TTestWindow;

3. Створюємо саме вікно.

Wnd-> Create (0L, NULL, “Приклад вікна”, WS_OVERLAPPEDWINDOW / WS_VISIBLE, CW_USEDEFAULT, 
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,NULL);

4. Входимо в цикл обробки повідомлень

Application->Run();

Все. Бібліотекою можна починати користуватися. У наступних статтях я розповім про те, як підключати меню до додатка, додавання багатодокументний функціональності і багато іншого.


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*