ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Categories:

Состояние программы в глобальной переменной

Размышляя над смыслом первых двух абзацев статьи «Managing Application State», решил дописать текст простейшей программы с оконным интерфейсом в Windows.

В первом абзаце вышеуказанной статьи процедура окна обозначается прилагательным «stateless» (дословно — «без состояния»). (Про состояние программы я немного писал в одном из предыдущих постов). Это означает, что при каждом вызове процедура окна не помнит, что происходило при предыдущем вызове, и вообще не знает, чем, собственно, занимается данная программа в целом. Во входных параметрах у этой процедуры есть лишь информация о сообщении, по которому ее вызвала операционная система. Если свои процедуры мы вызываем сами и можем передать в них нужные входные параметры, то процедуру окна вызывает операционная система со входными параметрами, которые мы не можем изменить.

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

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

Так как у нас есть окно, то информацию будем выводить в него. Для этого используем функцию TextOut (строго говоря, это не функция, а макрос, который при сборке программы разворачивается в конкретную реализацию функции — TextOutA или TextOutW). Подробнее о выводе текста в окно можно почитать тут.

Пример:
...
// в обработке сообщения WM_PAINT

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

// вставляем вывод строки "Илья" в окно
TextOut(hdc, 0, 0, L"Илья", 4);

EndPaint(hwnd, &ps);
...

Так как процедура окна, как говорилось выше, является «stateless», для хранения числа перерисовок клиентской области окна введем глобальную переменную n.

#include <windows.h>

int n = 0; // счетчик перерисовок (глобальная переменная)

...

// в обработке сообщения WM_PAINT

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

// счетчик перерисовок
n = n + 1;
// вывод значения счетчика перерисовок в наше окно
TextOut(hdc, 0, 0, n, 4);

EndPaint(hwnd, &ps);
...

В таком виде программа не заработает — при сборке компилятор выдаст ошибку — функции TextOut в качестве четвертого входного параметра можно задавать строку, а не число. Преобразуем целое число в строку (об этом я писал в предыдущем посте).

#include <windows.h>
#include <string> // подключаем для работы со строковым классом

int n = 0; // счетчик перерисовок (глобальная переменная)

...

// в обработке сообщения WM_PAINT

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

// счетчик перерисовок
n = n + 1;
// преобразование числа в строку
std::wstring str = std::to_wstring(n);
// вывод значения счетчика перерисовок в наше окно
TextOut(hdc, 0, 0, str.c_str(), str.length());

EndPaint(hwnd, &ps);
...

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

Дело в том, что счетчик-то считает каждый раз верно, а вот отображение его нового значения в окне происходит не всегда. По умолчанию в область перерисовки не всегда включается вся клиентская область окна целиком. Например, в случае увеличения размера окна в область перерисовки по умолчанию включается только та часть клиентской области, на которую увеличилось окно. Это делается из соображений оптимизации. А так как наше значение счетчика при увеличении окна отображается в левом верхнем углу, который не попадает в этом случае в область перерисовки, то новое значение счетчика и не отображается.

Чтобы наша программа перерисовывала каждый раз всю клиентскую область окна целиком, перед началом отрисовки вызовем функцию InvalidateRect со значением NULL во втором параметре. Вообще, данная функция добавляет определенную программистом во втором параметре область к области перерисовки. Но если передать в этом параметре значение NULL, то в область перерисовки будет добавлена вся клиентская область окна целиком.

Итак, окончательный текст программы:
#include <windows.h>
#include <string> // подключаем для работы со строковым классом

int n = 0; // счетчик перерисовок (глобальная переменная)

...

// в обработке сообщения WM_PAINT

// указываем, что перерисовать нужно всю клиентскую область целиком
InvalidateRect(hwnd, NULL, FALSE);

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

// счетчик перерисовок
n = n + 1;
// преобразование числа в строку
std::wstring str = std::to_wstring(n);
// вывод значения счетчика перерисовок в наше окно
TextOut(hdc, 0, 0, str.c_str(), str.length());

EndPaint(hwnd, &ps);
...

Про отрисовку (перерисовку) окна на экране можно почитать тут:
https://textpub.neocities.org/m/microsoft/winapp/PaintingWindow.html
Tags: Образование, Программирование, Статьи
Subscribe

  • Банкир, Дик Фрэнсис, роман

    Прекрасный роман («Банкир», 1982) одного из моих любимых писателей, Дика Фрэнсиса. Дик Фрэнсис писал (он умер в 2010 году) детективы. Его романы…

  • Сценарий фильма, который никогда не снимут

    Залпом прочитал набросок сценария художественного фильма, который в 2015-2016 годах написал в своем ЖЖ Григорий Циденков в десяти постах: 1.…

  • Marcus Nimbler закрыл свой канал на YouTube

    Любопытный персонаж из Германии по имени Marcus Nimbler вел свой канал на «YouTube» с 25 февраля 2016 г. За это время у него накопилось 131 тыс.…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments