textpub.neocities.org . [ список статей ]

Мой перевод англоязычной статьи «Writing the Window Procedure» с сайта компании Microsoft (ссылка на оригинал: новая, старая), автор статьи там не указан, дата публикации оригинала: 05.10.2010 г.

Написание процедуры окна

Функция DispatchMessage вызывает процедуру окна, соответствующую окну, которое является получателем сообщения. Процедура окна имеет следующий заголовок:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

У этой функции четыре параметра:

LRESULT — значение целочисленного типа, которое наша программа возвращает операционной системе. Оно содержит ответ нашей программы на конкретное сообщение. Содержание этого значения зависит от кода сообщения. CALLBACK — соглашение о вызове нашей функции.

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

switch (uMsg)
{
case WM_SIZE: // Обработка изменения размеров окна
    
// и так далее

}

Дополнительные данные к сообщению содержатся в параметрах lParam и wParam. Оба эти параметра — целочисленные значения размером с разрядность указателя (32 бита или 64 бита). Значение каждого из них зависит от кода сообщения (uMsg). Для каждого сообщения нужно по коду сообщения находить информацию по нему в MSDN и приводить параметры к правильному типу данных в соответствии с найденной информацией. Обычно дополнительные данные представляют собой либо числовое значение, либо указатель на структуру. У некоторых сообщений дополнительные данные отсутствуют.

Например, в документации по сообщению WM_SIZE сказано, что:

Типичная процедура окна обрабатывает десятки сообщений, поэтому она может стать довольно длинной. Одним из способов сделать наш код более модульным является выделение обработки каждого сообщения в отдельную функцию. В процедуре окна приведем параметры wParam и lParam к правильному типу данных и передадим эти значения в отдельную функцию. Например, для обработки сообщения WM_SIZE процедура окна будет выглядеть примерно так:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_SIZE:
        {
            // Макрос для получения младшего слова
            int width = LOWORD(lParam);
            // Макрос для получения старшего слова
            int height = HIWORD(lParam);
            // Реакция на сообщение
            OnSize(hwnd, (UINT)wParam, width, height);
        }
        break;
    }
}

void OnSize(HWND hwnd, UINT flag, int width, int height);
{
    // Реакция на изменение размеров окна
}

Макросы LOWORD и HIWORD получают 16-битные значения из параметра lParam, являющиеся новыми шириной и высотой окна. (Вы можете отыскать детальную информацию по этому параметру в документации MSDN для каждого кода сообщения.) Процедура окна извлекает эти ширину и высоту, а затем передает их в отдельную функцию OnSize.

Стандартная обработка сообщения (по умолчанию)

Если для какого-либо сообщения в нашей процедуре окна не предусмотрено обработки, следует передать параметры сообщения прямо в функцию DefWindowProc. Эта функция выполняет предусмотренную операционной системой стандартную обработку этого сообщения, эта обработка зависит от типа сообщения.

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

Как избежать узких мест в процедуре окна

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

Чтобы решить эту проблему, в описанном случае нужно переместить действия, занимающие много времени, в другой поток, использовав одну из возможностей многозадачности, встроенных в Windows:

textpub.neocities.org . [ список статей ]