Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
LEKCII.doc
Скачиваний:
9
Добавлен:
17.09.2019
Размер:
1.12 Mб
Скачать

Сообщения.

Большинство сообщений создают драйверы устройств ввода/вывода, таких, как клавиатура, мышь или таймер. Драйверы создают сообщения при поступлении аппаратных прерываний. Например, когда вы нажимаете и затем отпускаете клавишу, драйвер обрабатывает прерывания от клавиатуры и создает несколько сообщений. Аналогично сообщения создаются при перемещении мыши или в том случае, когда вы нажимаете кнопки на корпусе мыши. Можно сказать, что драйверы устройств ввода/вывода транслируют аппаратные прерывания в сообщения. В операционной системе Windows обычные приложения никогда не обращаются напрямую к аппаратуре и не работают с системными областями памяти, такими, как таблицы дескрипторов прерываний или видеопамять. Все это делают драйверы и виртуальные драйверы. Поэтому ограничения, накладываемые на обычные приложения Windows, никак не сказываются на функциональных возможностях самого приложения.

Прежде всего, сообщения попадают в системную очередь сообщений Windows. Системная очередь сообщений одна. Далее из нее сообщения распределяются в очереди сообщений приложений. Для каждого приложения создается своя очередь сообщений.

Очередь сообщения приложений может пополняться не только из системной очереди. Любое приложение может послать сообщение любому другому сообщению, в том числе и само себе.

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

Сообщение, в основном, состоит из полей wParam и lParam. В сообщении, формируемом при нажатии любой кнопки мыши, младшее слово lParam содержит X – координату курсора, а старшее – Y – координату. Семь младших бит поля wParam содержит состояние кнопок мыши и клавиш Shift и Ctrl. Если кнопка или клавиша нажата, то соответствующий бит находится в состоянии 1.

Создание окна в Windows

Структура программы в Windows:

Функция WinMain в цикле обработки сообщений с помощью функции GetMessage выбирает сообщения из очереди сообщений приложения и распределяет их функциям окон, вызывая функцию DispatchMessage.

Функция GetMessage предназначена для выборки сообщения из очереди приложения. Сообщение выбирается из очереди и записывается в область данных, принадлежащую приложению.

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

Окна

#include <windows.h>

#include <stdlib.h>

const char szClassName[] = "WinLab_01";

const char szWindowName[] = "Term II. Windows Lab 01.";

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);

#pragma argsused

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

HWND hWnd;

// Описание класса окна

WNDCLASS wndclass;

wndclass.style = CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc = WndProc;

wndclass.hInstance = hInstance;

wndclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

wndclass.hbrBackground = 5;

wndclass.lpszMenuName = NULL;

wndclass.lpszClassName = szClassName;

if (!RegisterClass(&wndclass)) // Регистрируем класс окна

exit(FALSE);

// Создаем окно

hWnd=CreateWindow(szClassName,szWindowName, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0,CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd) exit(FALSE);

// Показываем окно

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

// Цикл сообщений

MSG msg;

while(GetMessage(&msg, NULL, 0, 0))

{TranslateMessage(&msg);

DispatchMessage(&msg);}

return msg.wParam;}

/* Обработчик сообщений */

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{switch (iMessage)

{case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, iMessage, wParam, lParam); }

return 0;}

Сообщение с кодом WM_CREATE передается функции окна в момент создания окна.

В процессе работы приложения функция окна может получать сообщения с различными кодами как через очередь сообщений приложения, так и непосредственно, в обход очереди сообщений. Обработчики этих сообщений, определенные в функции окна, являются методами для работы с окном как с объектом.

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

Модификатор Pascal требует компилировать функцию в стиле языка Паскаля – с очисткой стека от параметров функции. Если указать другой модификатор (cdecl, WINAPI) то вызов функции будет компилироваться в стиле С, стек будет очищен вызывающей программой.

Параметр hInstance идентифицирует данное приложение. Он содержит адрес памяти, выделенной для работы. Этот адрес является виртуальным и по умолчанию настроен таким образом, что равен 0x40000.

Второй параметр hPrevInstance определяет, по какому адресу будет загружено приложение, запущенное повторно. Это позволяло дублировать при повторных запусках не весь код. В настоящее время не используется и сохраняется для обеспечения совместимости с предыдущими версиями.

Поле lpszCmdLine позволяет передавать запускаемому приложению командную строку. Если на рабочем столе для запуска создан ярлык, то щелчок правой кнопкой вызывает появление окна свойств. Если программа запускается из среды разработки, то командная строка может быть задана в свойствах проекта Project - Property-Debugging - CommandArguments. В командной строке (в отличие от argc и argv) отсутствует первый параметр, определяющий путь к исполняемому файлу. Получит командную строку с первым параметром можно с помощью функции GetCommandLine(). Порядок записи операндов в стек не зависит от модификатора. Первый параметр передаётся последним и будет в вершине стека.

В области стека функции WinMain созданы две переменные с именами msg и hwnd:

MSG msg; // структура для работы с сообщениями

HWND hwnd; // идентификатор главного окна приложения

Переменная msg представляет собой структуру типа MSG, описанную в файле windows.h следующим образом:

typedef struct tagMSG

{

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

} MSG;

Эта переменная предназначена для временного хранения сообщений и используется в цикле обработки сообщений.

Если регистрация класса произошла успешно, функция RegisterClass возвращает атом с ненулевым значением.

Структура WNDCLASS используется для регистрации класса окна. Эта структура определена в файле windows.h:

typedef struct tagWNDCLASS

{

UINT style;

WNDPROC lpfnWndProc;

int cbClsExtra;

int cbWndExtra;

HINSTANCE hInstance;

HICON hIcon;

HCURSOR hCursor;

HBRUSH hbrBackground;

LPCSTR lpszMenuName;

LPCSTR lpszClassName;

} WNDCLASS;

Перед регистрацией вам необходимо заполнить все поля в этой структуре.

Поле style определяет стиль класса и задается в виде констант (описанных, как всегда, в файле windows.h), имя которых начинается с префикса CS_, например CS_HREDRAW, CS_VREDRAW. Стиль задает реакцию окна на изменение его размера, на выполнение в окне операции двойного щелчка мышью (double click), а также позволяет определить другие характеристики окна, создаваемого на базе данного класса. Например, если для стиля задать значение CS_HREDRAW | CS_VREDRAW, при изменении вертикального или горизонтального размера окна приложение должно его перерисовать, то есть нарисовать заново все или часть того, что было изображено в окне до изменения размера.

Если стиль класса не используется, то для него задается нулевое значение:

wc.style = 0;

В поле lpfnWndProc необходимо записать адрес функции окна, которая будет выполнять обработку сообщений, поступающих во все окна, созданные на базе данного класса. Имя функции окна можно выбрать любое. В нашем случае используется имя WndProc, хотя вы можете использовать другое имя. Запись адреса функции окна должна выполняться следующим образом:

wc.lpfnWndProc = (WNDPROC) WndProc;

Поле lpfnWndProc имеет тип WNDPROC (дальний указатель на функцию), который мы рассмотрим чуть позже, при описании функции окна. Для того чтобы избежать получения от компилятора предупреждающего сообщения о несоответствии типов, вы должны использовать явное преобразование типа.

Поле cbClsExtra используется для резервирования дополнительной памяти, общей и доступной для всех окон, создаваемых на базе данного класса.

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

Если приложение не создает в описании класса никаких дополнительных областей, поэтому для заполнения поля cbClsExtra используется нулевое значение:

wc.cbClsExtra = 0;

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

wc.cbWndExtra = 0;

Поле hInstance перед регистрацией класса окна должно содержать идентификатор приложения, создающего класс окна. В качестве этого идентификатора следует использовать значение, полученное функцией WinMain в параметре hInstance:

wc.hInstance = hInstance;

Следующее поле имеет имя hIcon и тип HICON. Это идентификатор пиктограммы, в которую превращается окно при уменьшении его размеров до предела (при минимизации окна).

Для загрузки пиктограммы в приложении вызывается функция программного интерфейса Windows с именем LoadIcon:

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

Прототип функции LoadIcon:

HICON WINAPI LoadIcon(HINSTANCE hinst, LPCSTR pszicon);

Первый параметр функции (hinst) содержит идентификатор приложения, второй (pszicon) - имя ресурса-пиктограммы.

Позже мы научим вас определять для окон собственные пиктограммы, нарисованные с помощью приложения Resource Workshop, входящего в комплект поставки Borland C++ for Windows.

В поле hCursor (имеющем тип HCURSOR) вы можете задать вид курсора мыши при его прохождении над окном. Вы знаете, что курсор мыши меняет свою форму при перемещении над различными окнами приложений Windows. При регистрации класса окна вы можете указать форму курсора, для чего и используется поле hCursor.

В нашем приложении мы задаем курсор в виде стандартной стрелки, для чего вызываем функцию LoadCursor и указываем в качестве второго параметра константу IDC_ARROW:

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

Прототип функции LoadCursor:

HCURSOR WINAPI LoadCursor(HINSTANCE hinst, LPCSTR pszCursor);

Вы можете определить для окна свой курсор, нарисовав его аналогично пиктограмме при помощи такого приложения, как Resource Workshop или Microsoft SDKPaint. Однако пока для простоты мы будем использовать стандартный курсор.

Далее нам необходимо заполнить поле hbrBackground, имеющее тип HBRUSH. Это поле позволяет определить кисть (brush), которая будет использована для закрашивания фона окна. В качестве кисти можно использовать "чистые" цвета или небольшую пиктограмму размером 8 х 8 точек.

В нашем приложении мы использовали системный цвет, который Windows использует для закрашивания фона окон:

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

Системный цвет можно изменять при помощи приложения Control Panel. Позже мы научим вас задавать для фона окна другие цвета и раскрашивать окно при помощи пиктограмм.

Поле lpszMenuName (указатель на строку типа LPCSTR) определяет меню, располагающееся в верхней части окна. Если меню не используется, при заполнении этого поля необходимо использовать значение NULL:

wc.lpszMenuName = (LPSTR)NULL;

Тип LPCSTR определяется как константный дальний указатель на строку символов:

typedef const char FAR* LPCSTR;

Очень важно поле lpszClassName. В это поле необходимо записать указатель на текстовую строку, содержащую имя регистрируемого класса окон:

wc.lpszClassName = (LPSTR)szClassName;

На этом подготовку структуры wc к регистрации класса окна можно считать законченной. Можно вызывать функцию RegisterClass.

Создание главного окна приложения

Далее приложение вызывает функцию CreateWindow для того, чтобы создать главное окно приложения:

hwnd = CreateWindow(

szClassName, // имя класса окна

szWindowTitle, // заголовок окна

WS_OVERLAPPEDWINDOW, // стиль окна

CW_USEDEFAULT, // задаем размеры и расположение

CW_USEDEFAULT, // окна, принятые по умолчанию

CW_USEDEFAULT,

CW_USEDEFAULT,

0, // идентификатор родительского окна

0, // идентификатор меню

hInstance, // идентификатор приложения

NULL); // указатель на дополнительные параметры

В случае успеха функция CreateWindow возвращает идентификатор окна (типа HWND). Если окно создать не удалось, функция возвращает нулевое значение.

Приведем прототип функции CreateWindow:

HWND CreateWindow(LPCSTR lpszClassName, LPCSTR lpszWindowName, DWORD dwStyle,int x, int y, int nWidth, int nHeight,HWND hwndParent, HMENU hmenu, HINSTANCE hinst,void FAR* lpvParam);

Многочисленные параметры функции CreateWindow дополняют описание окна, сделанное при создании класса окна.

Первый параметр функции (lpszClassName) - указатель на строку, содержащую имя класса, на базе которого создается окно. В процессе инициализации приложения мы зарегистрировали класс с именем "WindowAppClass" (переменная szClassName).

Второй параметр функции (lpszWindowName) - указатель на строку, содержащую заголовок окна (Title Bar). В нашем случае окно будет иметь заголовок "Window Application" (переменная szWindowTitle).

Третий параметр (dwStyle) - стиль создаваемого окна. Этот параметр задается как логическая комбинация отдельных битов. Константа WS_OVERLAPPEDWINDOW соответствует окну, которое может перекрывать другие окна, имеет заголовок, системное меню, кнопки для минимизации и максимизации окна, а также рамку вокруг окна, с помощью которой можно изменять размер окна. Операционная система Windows позволяет задавать различные стили для создаваемых окон. Мы их рассмотрим в дальнейшем.

Четвертый и пятый параметры функции CreateWindow для окна данного стиля определяют горизонтальную (x) и вертикальную (y) координату относительно верхнего левого угла экрана видеомонитора.

Шестой и седьмой параметры определяют ширину (nWidth) и высоту (nHeight) создаваемого окна.

Наше приложение в качестве координат окна и его размеров использует константу CW_USEDEFAULT. При этом операционная система Windows сама определяет положение и размеры создаваемого окна.

Восьмой параметр (hwndParent) определяет индекс родительского окна. Для нашего приложения в качестве значения используется нуль, так как в приложении создается только одно окно.

Девятый параметр (hmenu) - идентификатор меню или идентификатор порожденного (child) окна. В нашем случае никакого меню или порожденного окна нет, поэтому в качестве значения используется нуль.

Десятый параметр (hinst) - идентификатор приложения, которое создает окно. Необходимо использовать значение, передаваемое функции WinMain через параметр hInstance.

Одиннадцатый, последний параметр функции (lpvParam) представляет собой дальний указатель на область данных, определяемых приложением. Этот параметр передается в функцию окна вместе с сообщением WM_CREATE при создании окна. Наше приложение не пользуется этим параметром.

Отображение окна на экране

Окно создано, но на экране оно еще не появилось, в чем вы можете убедиться, запустив приложение под управлением отладчика. Поэтому, проверив, что создание окна выполнено успешно (функция CreateWindow вернула ненулевое значение), необходимо сделать окно видимым (нарисовать его на экране). Это можно сделать с помощью функции ShowWindow:

ShowWindow(hwnd, nCmdShow);

Прототип функции:

BOOL ShowWindow(HWND hwnd, int nCmdShow);

Функция отображает окно, идентификатор которого задан первым параметром (hwnd), в нормальном, максимально увеличенном или уменьшенном до пиктограммы виде, в зависимости от значения второго параметра (nCmdShow). После отображения окна в нормальном или максимально увеличенном виде внутренняя поверхность окна закрашивается кистью, определенной при регистрации класса.

Внешний вид окна, создаваемого нашим приложением, показан на рис.

Сразу после функции ShowWindow в приложении вызывается функция UpdateWindow.

UpdateWindow(hwnd);

Прототип функции:

void UpdateWindow(HWND hwnd);

Функция UpdateWindow вызывает функцию окна, заданного идентификатором, передаваемым в качестве параметра hwnd, и передает ей сообщение WM_PAINT. Получив это сообщение, функция окна должна перерисовать все окно или его часть. Наше приложение не обрабатывает это сообщение, передавая его функции DefWindowProc.

Цикл обработки сообщений

После отображения окна функция WinMain запускает цикл обработки сообщений:

while(GetMessage(&msg, 0, 0, 0))

{

DispatchMessage(&msg);}

Функция GetMessage предназначена для выборки сообщений из очереди приложения и имеет следующий прототип:

BOOL GetMessage(LPMSG lpmsg, HWND hwnd,WORD uMsgFilterMin, WORD uMsgFilterMax);

Первый параметр функции (lpmsg) является дальним указателем на структуру типа MSG, в которую будет записано выбранное из очереди сообщение. Тип LPMSG определен в файле windows.h следующим образом:

typedef MSG FAR* LPMSG;

Второй параметр (hwnd) является идентификатором окна, для которого необходимо выбрать сообщение из очереди приложения.

Очередь приложения содержит сообщения, предназначенные для всех окон, созданных приложением. Если в качестве второго параметра функции GetMessage указать нуль, будет выполняться выборка всех сообщений, предназначенных для всех окон приложения.

Третий (uMsgFilterMin) и четвертый (uMsgFilterMax) параметры функции GetMessage позволяют определить диапазон сообщений, выбираемых из очереди приложения, задавая соответственно минимальное и максимальное значение кодов выбираемых сообщений. Если для этих параметров указать нулевые значения (как это сделано в нашем приложении), из очереди приложения будут выбираться все сообщения.

Как работает функция GetMessage?

Эта функция может выбирать сообщения только из очереди того приложения, которое ее вызывает. Если очередь сообщений приложения пуста или содержит только сообщения с низким приоритетом, функция GetMessage передает управление другим работающим приложениям, обеспечивая невытесняющую мультизадачность. Таким образом, проверяя очередь сообщений, приложение может передать управление другим приложениям. Эти приложения, в свою очередь, тоже вызывают функцию GetMessage. Таким образом, приложения распределяют между собой процессорное время.

Выбранное функцией GetMessage сообщение удаляется из очереди сообщений приложения и записывается в структуру, адрес которой задан первым параметром функции.

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

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

DWORD DispatchMessage(LPMSG lpmsg);

Единственный параметр функции (lpmsg) является указателем на структуру, содержащую сообщение. Функция DispatchMessage возвращает значение, полученное при возврате из функции окна. Обычно это значение игнорируется приложением.

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

С помощью специальных функций, таких, как SendMessage или CallWindowProc, вы все же можете при необходимости вызвать функцию окна. Однако в цикле обработки сообщений следует использовать именно функцию DispatchMessage, так как она для каждого сообщения вызывает именно ту функцию окна, которой это сообщение предназначено.

Завершение работы приложения

Приложение обычно завершает свою работу, когда вы нажимаете комбинацию клавиш <Alt+F4> или выбираете строку "Close" в системном меню главного окна приложения. При этом в его функцию окна попадает сообщение WM_DESTROY. В ответ на это сообщение функция окна помещает в очередь сообщение WM_QUIT, вызывая для этого функцию PostQuitMessage. Как вы уже знаете, выборка этого сообщения приводит к завершению цикла обработки сообщений и, следовательно, к завершению работы приложения.

Если операционная система Windows завершает свою работу, функциям окна каждого работающего приложения передается сообщение WM_QUERYENDSESSION. Обрабатывая это сообщение соответствующим образом, приложение может завершить свою работу, предварительно сохранив все необходимые данные.

Стиль класса окна

Стиль класса окна определяется при регистрации класса окна. Во всех предыдущих примерах приложений мы не задавали стиль окна, определяя содержимое соответствующего поля в структуре WNDCLASS следующим образом:

wc.style = 0;

Стиль класса окна задается в виде отдельных битов, для которых в файле windows.h определены символические константы с префиксом CS_:

Стиль

Описание

CS_BYTEALIGNCLIENT

Внутренняя область окна (client area) должна быть выравнена по границе байта видеопамяти. Иногда используется для ускорения процесса вывода изображения

CS_BYTEALIGNWINDOW

Все окно (не только внутренняя область окна) должно быть выравнено по границе байта видеопамяти

CS_CLASSDC

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

CS_DBLCLKS

Функция окна будет получать сообщения при двойном щелчке клавишей мыши (double click)

CS_GLOBALCLASS

Данный класс является глобальным и доступным другим приложениям. Другие приложения могут создавать окна на базе этого класса

CS_HREDRAW

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

CS_NOCLOSE

В системном меню окна необходимо запретить выбор функции закрытия окна (строка Close будет отображаться серым цветом, и ее нельзя выбрать)

CS_OWNDC

Для каждого окна, определяемого на базе данного класса, будет создаваться отдельный контекст отображения

CS_PARENTDC

Окно будет пользоваться родительским контекстом отображения, а не своим собственным. Родительский контекст - это контекст окна, создавшего другое окно (см. дальше)

CS_SAVEBITS

Для данного окна Windows должна сохранять изображение в виде битового образа (bitmap). Если такое окно будет перекрыто другим окном, то после уничтожения перекрывшего окна изображение первого окна будет восстановлено Windows на основании сохраненного ранее образа

CS_VREDRAW

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

Чаще всего используются стили CS_HREDRAW и CS_VREDRAW:

wc.style = CS_HREDRAW | CS_VREDRAW;

Если для класса заданы стили CS_HREDRAW и CS_VREDRAW, при изменении размеров окна функция окна может получить сообщение WM_PAINT. В этом случае функция окна должна перерисовать часть окна или все окно.

Стиль CS_DBLCLKS используется при необходимости отслеживать двойные щелчки мышью. При этом в функцию окна посылаются сообщения WM_LBUTTONDBLCLK и WM_RBUTTONDBLCLK. Если этот стиль не будет задан, как бы быстро вы ни щелкали мышью, функция окна получит только идущие парами сообщения о том, что вы нажимаете и отпускаете левую или правую клавишу мыши.

Стиль окна

Определенный в классе окна стиль класса окна используется при создании на базе этого класса всех окон. Для дальнейшего уточнения внешнего вида и поведения окна используется другая характеристика - стиль окна. Стиль окна указывается при создании окна функцией CreateWindow. В наших примерах основное окно приложения не имело стиля класса окна, но для него был определен стиль окна WS_OVERLAPPEDWINDOW:

Для определения стиля окна используются символические константы с префиксом WS_, определенные в файле windows.h. С помощью этих констант можно определить примерно два десятка стилей окна, однако чаще всего используются несколько основных стилей.

Мы рассмотрим три основных стиля окон - перекрывающиеся окна (overlapped window), временные окна (pop-up window) и дочерние окна (child window).

Перекрывающиеся (overlapped) окна

Перекрывающиеся окна обычно используются в качестве главного окна приложения. Такие окна имеют заголовок (title bar), рамку и, разумеется, внутреннюю часть окна (client region). Дополнительно перекрывающиеся окна могут иметь (а могут и не иметь) системное меню, кнопки для максимального увеличения размера окна и для сворачивания окна в пиктограмму, вертикальную и горизонтальную полосу просмотра (scroll bar) и меню.

В первых версиях операционной системы Windows (версии 1.х) окна располагались рядом и назывались tiled window (tile - черепица). Сейчас такие окна не используются, вместо них появились перекрывающиеся окна, способные перекрывать окна других приложений. Перекрывающиеся окна называются также окнами верхнего уровня (top-level window).

Файл windows.h содержит следующее определение стиля перекрывающегося окна:

#define WS_OVERLAPPED 0x00000000L

В нашем приложении для определения стиля перекрывающегося окна мы использовали символическую константу WS_OVERLAPPEDWINDOW, определенную как логическое ИЛИ нескольких констант:

#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | \

WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | \

WS_MAXIMIZEBOX)

Константа WS_OVERLAPPED определяет базовый стиль окна - перекрывающееся окно. Стиль WS_OVERLAPPEDWINDOW в добавление к базовому указывает, что окно должно иметь заголовок (константа WS_CAPTION), системное меню (WS_SYSMENU), толстую рамку для изменения размера окна (WS_THICKFRAME), кнопку минимизации размера окна (WS_MINIMIZEBOX) и кнопку для максимального увеличения размера окна (WS_MAXIMIZEBOX). Если окно имеет заголовок, вы можете его перемещать по экрану.

Вы можете попробовать в предыдущем примере изменить стиль окна, создав свой собственный с использованием приведенных выше констант.

Приложение Windows может создавать несколько окон, связанных между собой "узами родства" и "отношениями собственности". В частности, при создании перекрывающегося окна при помощи функции CreateWindow в качестве восьмого параметра функции вы можете указать так называемый идентификатор окна - владельца. Окно - владелец уже должно существовать на момент создания второго окна, имеющего владельца.

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

Если окно - хозяин сворачивается в пиктограмму, все окна, которыми оно владеет, становятся невидимыми. Если вы сначала свернули в пиктограмму окно, которым владеет другое окно, а затем и окно - хозяин, пиктограмма первого (подчиненного) окна исчезает.

Если вы уничтожили окно, автоматически уничтожаются и все принадлежащие ему окна.

Обычное перекрывающееся окно, не имеющее окна - владельца, может располагаться в любом месте экрана и принимать любые размеры. Подчиненные окна располагаются всегда над поверхностью окна-владельца, загораживая его.

Координаты создаваемых функцией CreateWindow перекрывающихся окон указываются по отношению ко всему экрану. Таким образом, если вы создаете перекрывающееся окно с координатами (0, 0), оно будет расположено в верхнем левом углу экрана.

Временные (pop-up) окна

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

Временные окна, в отличие от перекрывающихся, могут не иметь заголовок (title bar). Если для временного окна определен заголовок, оно может иметь и системное меню. Часто для создания временных окон, имеющих рамку, используется стиль WS_POPUPWINDOW, определенный в файле windows.h следующим образом:

#define WS_POPUPWINDOW (WS_POPUP | WS_BORDER | WS_SYSMENU)

Если надо добавить к временному окну системное меню и заголовок, стиль WS_POPUPWINDOW следует использовать в комбинации со стилем WS_CAPTION, добавляющим заголовок.

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

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

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

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

Дочерние окна

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

По аналогии с другими базовыми стилями в файле windows.h определена константа WS_CHILDWINDOW, которая полностью эквивалентна константе WS_CHLD:

#define WS_CHILDWINDOW (WS_CHILD)

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

Перечислим особенности дочерних окон.

Само собой разумеется, что дочерние окна должны иметь окно-родителя (окон-сирот не бывает!). Только дочерние окна могут иметь родителей, перекрывающие и временные окна могут иметь окно-хозяина, но не родителя. У дочерних окон могут быть "братья" (или "сестры", кому что больше нравится).

Дочерние окна всегда располагаются на поверхности окна-родителя. При создании дочернего окна начало системы координат расположено в левом верхнем углу внутренней поверхности окна - родителя (но не в верхнем углу экрана, как это было для перекрывающихся и временных окон).

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

При создании дочернего окна в качестве девятого параметра (вместо идентификатора меню, которого не может быть у дочернего окна) функции CreateWindow необходимо указать созданный вами идентификатор дочернего окна. Дочернее окно как бы "прилипает" к поверхности родительского окна и перемещается вместе с ним. Оно никогда не может выйти за пределы родительского окна. Все дочерние окна скрываются при сворачивании окна-родителя в пиктограмму и появляются вновь при восстановлении родительского окна.

При изменении размеров родительского окна дочерние окна, на которых отразилось такое изменение (которые вышли за границу окна и появились вновь), получают сообщение WM_PAINT. При изменении размеров родительского окна дочерние окна не получают сообщение WM_SIZE. Это сообщение попадает только в родительское окно.

Список стилей окна

Приведем полный список возможных стилей окна, определенных в виде символических констант в файле windows.h. Некоторые из приведенных здесь стилей будут подробно рассмотрены в соответствующих главах этого тома или в следующих томах "Библиотеки системного программиста".

Имя константы

Описание стиля

WS_BORDER

Окно с рамкой

WS_CAPTION

Окно с заголовком. Этот стиль несовместим со стилем WS_DLGFRAME. При использовании стиля WS_CAPTION подразумевается использование стиля WS_BORDER

WS_CHILD

Дочернее окно. Несовместим со стилем WS_POPUP

WS_CHILDWINDOW

То же самое, что и WS_CHILD

WS_CLIPCHILDREN

Этот стиль используется при создании родительского окна. При его использовании родительское окно не перерисовывает свои внутренние области, занятые дочерними окнами

WS_CLIPSIBLINGS

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

WS_DISABLED

Вновь созданное окно сразу становится заблокированным (не получает сообщения от мыши и клавиатуры)

WS_DLGFRAME

Окно с двойной рамкой без заголовка. Несовместим со стилем WS_CAPTION

WS_GROUP

Определяет первый орган управления в группе органов управления. Используется только в диалоговых панелях

WS_HSCROLL

В окне создается горизонтальная полоса просмотра

WS_ICONIC

То же самое, что и WS_MINIMIZE

WS_MAXIMIZE

Создается окно максимально возможного размера

WS_MAXIMIZEBOX

Окно содержит кнопку для увеличения его размера до максимально возможного. Этот стиль необходимо использовать вместе со стилями WS_OVERLAPPED или WS_CAPTION, в противном случае указанная кнопка не появится

WS_MINIMIZE

Создается окно, уменьшенное до предела (свернутое в пиктограмму). Этот стиль необходимо использовать вместе со стилем WS_OVERLAPPED

WS_MINIMIZEBOX

Окно содержит кнопку для сворачивания окна в пиктограмму (минимизации размеров окна). Этот стиль необходимо использовать вместе со стилем WS_OVERLAPPED или WS_CAPTION, в противном случае указанная кнопка не появится

WS_OVERLAPPED

Создается перекрывающееся окно, имеющее заголовок и рамку

WS_OVERLAPPEDWINDOW

Создается перекрывающееся окно, имеющее заголовок, рамку для изменения размера окна, системное меню, кнопки для изменения размеров окна. Этот стиль является комбинацией стилей WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX

WS_POPUP

Создается временное (pop-up) окно

WS_POPUPWINDOW

Комбинация стилей WS_POPUP, WS_BORDER и WS_SYSMENU. Для того чтобы сделать системное меню доступным, необходимо дополнительно использовать стиль WS_CAPTION

WS_SYSMENU

Окно должно иметь системное меню

WS_TABSTOP

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

WS_THICKFRAME

Окно должно иметь толстую рамку для изменения размера окна

WS_VISIBLE

Создается окно, которое сразу становится видимым. По умолчанию окна создаются невидимыми, и для их отображения требуется вызывать функцию ShowWindow

WS_VSCROLL

В окне создается вертикальная полоса просмотра

WS_TILED

Устаревший стиль, аналогичен WS_OVERLAPPED

WS_SIZEBOX

Устаревший стиль, аналогичен WS_THICKFRAME

WS_TILEDWINDOW

Устаревший стиль, аналогичен WS_OVERLAPPEDWINDOW

MDIS_ALLCHILDSTYLES

Этот стиль используется при создании дочерних MDI-окон и определяет окна, которые могут иметь любые комбинации стилей. По умолчанию дочерние MDI-окна имеют стили WS_MINIMIZE, WS_MAXIMIZE, WS_VSCROLL, WS_HSCROLL

Меню.

Включение ресурсов

Для включения ресурсов в загрузочный модуль приложения вам надо создать текстовый файл описания ресурсов. Этот файл может быть создан либо текстовым редактором (например, встроенным в Borland C++ for Windows), либо при помощи приложения Resource Workshop.

При создании окна в приложении Windows вы можете указать, что окно должно иметь меню. Обычно меню создается в главном окне приложения. Такое меню мы будем называть меню приложения.

Для активизации строки меню вам надо установить на нее курсор и сделать щелчок левой клавишей мыши, либо нажать клавишу <Alt> и затем клавишу, соответствующую подчеркнутой букве. Например, для активизации строки "File" следует использовать клавиши <Alt> и <F>. Если нажать, а затем отпустить клавишу <Alt>, нужную строку в меню приложения можно будет выбрать клавишами перемещения курсора по горизонтали <Left> и <Right>. Для активизации строки в последнем случае после выбора следует нажать клавишу <Enter>. Для отказа от выбора можно воспользоваться клавишей <Esc>.

Строки меню могут быть использованы либо для выбора команд, либо для активизации дополнительных временных меню (pop - up menu ). Как правило, строки меню приложения используются только для активизации временных меню, но не для выполнения команд. Некоторые строки меню могут отображаться серым цветом. Это заблокированные строки, которые не могут быть выбраны.

Временное меню содержит строки, расположенные в столбец. Для выбора строки из временного меню вы можете воспользоваться мышью или клавишами перемещения курсора по вертикали <Up> и <Down>. В последнем случае для завершения выбора следует нажать клавишу <Enter>. Можно также воспользоваться клавишей <Alt> и клавишей, соответствующей подчеркнутой букве в нужной строке.

Если выбор строки меню приводит к выполнению команды (например, команды создания документа, завершения работы приложения, копирования фрагмента документа в универсальный буфер обмена Clipboard и т. д.), строка меню содержит краткое название выполняемой команды, например, "New", "Copy", и т. д. Если же при выборе строки на экране появляется диалоговая панель, к слову справа добавляется многоточие.

Вы можете создавать многоуровневые меню Можно использовать многократную вложенность меню. Однако ими трудно пользоваться.

Windows имеет системное меню, которое можно вызвать щелчком левой клавиши мыши по окну активизации системного меню, расположенному слева от заголовка окна либо при помощи комбинации клавиши <Alt> и клавиши пробела.

Приложение может создать меню в любом месте экрана. Такое меню называют плавающим (floating menu).

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

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

Меню не обязательно должно содержать только текстовые строки. Вы можете создать меню из графических изображений или из комбинации графических изображений и текста.

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

Функции для работы с меню

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

Создание меню

Даже если в файле описания ресурсов нет определения шаблона меню, приложение может создать меню "с нуля" для любого своего перекрывающегося или временного окна (но не для дочернего). Для создания пустого меню можно воспользоваться функцией CreateMenu :

HMENU WINAPI CreateMenu(void);

Функция возвращает идентификатор созданного меню или NULL при ошибке.

Как правило, в меню верхнего уровня (в меню приложения) создаются временные меню. Для создания временного меню воспользуйтесь функцией CreatePopupMenu:

HMENU WINAPI CreatePopupMenu (void);

В дальнейшем вы можете добавить в меню верхнего уровня созданные функцией CreatePopupMenu временные меню или отдельные строки, вызвав функцию AppendMenu.

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

Для подключения к окну с идентификатором hwnd меню с идентификатором hmenu вы можете воспользоваться функцией SetMenu:

BOOL WINAPI SetMenu (HWND hwnd, HMENU hmenu);

Перед вызовом этой функции вы должны загрузить меню и получить его идентификатор, например, при помощи функции LoadMenu.

Функция SetMenu возвращает TRUE при успешном завершении и FALSE при ошибке.

Добавление строк

Для добавления строк в созданные функциями CreateMenu и CreatePopupMenu пустые меню можно воспользоваться функцией AppendMenu :

BOOL WINAPI AppendMenu(HMENU hmenu, UINT fuFlags, UINT idNewItem, LPCSTR lpszNewItem);

Параметр hmenu указывает идентификатор меню, к которому добавляется строка или временное меню. Вы должны использовать значение, полученное от функций CreateMenu и CreatePopupMenu.

Параметр fuFlags определяет атрибуты создаваемого элемента меню. Можно указывать следующие значения (соответствующие символические константы описаны в файле windows.h):

Константа

Описание

MF_BITMAP

Для изображения строки меню используется графическое изображение bitmap. Если указан этот параметр, младшее слово параметра lpszNewItem содержит идентификатор изображения

MF_CHECKED

При выводе меню на экран строка меню отмечается галочкой " "

MF_DISABLED

Строка меню отображается в нормальном виде (не серым цветом), но находится в неактивном состоянии

MF_ENABLED

Строка меню разблокирована и отображается в нормальном виде

MF_GRAYED

Строка меню отображается серым цветом и находится в неактивном состоянии. Такую строку нельзя выбрать

MF_MENUBREAK

Если описывается меню верхнего уровня, элемент меню выводится с новой строки. Если описывается временное меню, элемент меню выводится в новом столбце

MF_MENUBARBREAK

Аналогично MF_MENUBREAK, но дополнительно новый столбец отделяется вертикальной линией (используется при создании временных меню)

MF_OWNERDRAW

Строка меню рисуется окном, создавшем меню. Когда меню отображается в первый раз, функция этого окна получает сообщение WM_MEASUREITEM, в ответ на которое функция окна должна сообщить размеры области, занимаемой изображением строки меню. Рисовать изображение строки надо тогда, когда в функцию окна придет сообщение WM_DRAWITEM. Флаг MF_OWNERDRAW можно указывать только для временных меню

MF_POPUP

С данным элементом меню связывается временное меню. Если используется этот флаг, параметр idNewItem должен содержать идентификатор временного меню, связанного с данным элементом

MF_SEPARATOR

Используется для создания горизонтальной разделительной линии во временных меню. Если указан этот флаг, параметры lpszNewItem и idNewItem не используются

MF_STRING

Элемент меню является строкой символов. Параметр lpszNewItem должен указывать на строку символов, закрытую двоичным нулем

MF_UNCHECKED

При выводе меню на экран строка не отмечается галочкой " "

Можно указывать сразу несколько флагов, объединив их операцией логического ИЛИ, однако следует иметь в виду, что существует четыре группы взаимно несовместимых флагов:

MF_DISABLED, MF_ENABLED, MF_GRAYED

MF_BITMAP, MF_OWNERDRAW, MF_STRING

MF_MENUBREAK, MF_MENUBARBREAK

MF_CHECKED, MF_UNCHECKED

Назначение параметра idNewItem зависит от параметра fuFlags. Если значение параметра fuFlags не равно MF_POPUP, через idNewItem вы должны передать идентификатор создаваемой строки меню. Этот идентификатор будет записан в параметр wParam сообщения WM_COMMAND при выборе данной строки. Если же значение параметра fuFlags равно MF_POPUP, через параметр idNewItem вы должны передать функции AppendMenu идентификатор временного меню.

Назначение параметра lpszNewItem также зависит от параметра fuFlags. Если этот параметр равен MF_STRING, параметр lpszNewItem должен указывать на строку символов, закрытую двоичным нулем, если MF_BITMAP - младшее слово параметра lpszNewItem содержит идентификатор изображения, а если параметр fuFlags равен MF_OWNERDRAW, приложение должно передать через параметр lpszNewItem 32-битовое значение, идентифицирующее строку меню.

Еще одна функция, предназначенная для добавления элементов в меню, называется InsertMenu . Эта функция может добавить элемент в середину меню, сдвинув вниз уже существующие элементы.

Приведем прототип функции InsertMenu:

BOOL WINAPI InsertMenu(HMENU hmenu, UINT idItem, UINT fuFlags, UINT idNewItem, LPCSTR lpszNewItem);

Параметры этой функции аналогичны параметрам функции AppendMenu, за исключением параметров idItem и fuFlags.

Параметр idItem определяет элемент меню, перед которым должен быть вставлен новый элемент. Интерпретация этого параметра зависит от значения параметра fuFlags.

В дополнение к возможным значениям параметра fuFlags, описанным нами для функции AppendMenu, вместе с функцией InsertMenu вы можете использовать еще два - MF_BYCOMMAND и MF_BYPOSITION.

Если указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор элемента меню, перед которым будет вставлен новый элемент.

Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер элемента меню, перед которым будет вставлен новый элемент. Для того чтобы добавить элемент в конец меню, для параметра idItem можно указать значение -1.

После внесения всех изменений в меню приложение должно вызвать функцию DrawMenuBar :

void WINAPI DrawMenuBar(HWND hwnd);

Эта функция перерисовывает полосу меню для указанного параметром hwnd окна. В качестве параметра функции следует передать идентификатор окна, создавшего меню.

Изменение строк

Для изменения строк (элементов) существующего меню вы можете воспользоваться функцией ModifyMenu :

BOOL WINAPI ModifyMenu(HMENU hmenu, UINT idItem, UINT fuFlags, UINT idNewItem, LPCSTR lpszNewItem);

Параметры этой функции идентичны параметрам функции InsertMenu.

Функция ModifyMenu заменяет указанный элемент меню на новый. При замещении временного меню оно уничтожается и все связанные с ним ресурсы освобождаются.

После того как вы изменили меню, не забудьте вызывать функцию DrawMenuBar, описанную выше.

В программном интерфейсе Windows существует следующие пять функций:

Функция

Описание

AppendMenu

Добавление элемента в меню

DeleteMenu

Удаление элемента из меню

InsertMenu

Вставка элемента в меню

ModifyMenu

Изменение элемента меню

RemoveMenu

Удаление элемента меню без освобождения ресурсов, занимаемых этим элементом

Удаление строк

Для удаления элементов меню, таких, как строки и временные меню, предназначена функция DeleteMenu :

BOOL WINAPI DeleteMenu(HMENU hmenu, UINT idItem, UINT fuFlags);

Параметр hmenu определяет меню, из которого будет удален элемент.

Параметр idItem определяет удаляемый элемент, причем его интерпретация зависит от значения параметра fuFlags.

Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор удаляемого элемента меню. Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер удаляемого элемента меню.

При удалении временного меню все связанные с ним ресурсы освобождаются.

Для отображения результата удаления меню следует вызвать функцию DrawMenuBar.

В программном интерфейсе Windows определена функция RemoveMenu , имеющая параметры, аналогичные параметрам функции DeleteMenu:

BOOL WINAPI RemoveMenu(HMENU hmenu, UINT idItem, UINT fuFlags);

Эта функция удаляет указанный ей элемент из меню, но не уничтожает связанные с ним ресурсы, поэтому вы можете вновь воспользоваться удаленным элементом меню (если знаете его идентификатор, о том как получить идентификатор временного меню мы расскажем немного позже).

Напомним, что для уничтожения меню используется функция DestroyMenu :

BOOL WINAPI DestroyMenu(HMENU hmenu);

В качестве параметра функции передается идентификатор уничтожаемого меню. Функция освобождает все ресурсы, связанные с уничтоженным меню.

Активизация и блокирование строк меню

Для изменения состояния элемента меню удобно использовать функцию EnableMenuItem :

BOOL WINAPI EnableMenuItem(HMENU hmenu, UINT idItem, UINT uEnable);

Параметр hmenu указывает идентификатор меню, над элементом которого будет выполняться операция активизации или блокирования.

Параметр idItem определяет элемент меню, над которым выполняется операция. Интерпретация этого параметра зависит от значения параметра uEnable.

Параметр uEnable может принимать значения MF_DISABLED, MF_ENABLED или MF_GRAYED в комбинации с одним из значений: MF_BYCOMMAND или MF_BYPOSITION.

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

Для активизации заблокированного ранее элемента меню укажите значение MF_ENABLED.

Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор элемента меню, состояние которого будет изменено. Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер элемента меню, состояние которого будет изменено.

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

Отметка строк

Элементы временного меню могут быть отмечены галочкой. Для включения и выключения такой отметки можно использовать функцию CheckMenuItem :

BOOL WINAPI CheckMenuItem(HMENU hmenu, UINT idItem, UINT fuCheck);

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

Параметр idItem определяет элемент меню, над которым выполняется операция. Интерпретация этого параметра зависит от значения параметра fuCheck.

Параметр fuCheck может принимать значения MF_CHECKED или MF_UNCHECKED в комбинации с одним из значений: MF_BYCOMMAND или MF_BYPOSITION.

Для включения отметки элемента меню необходимо использовать значение MF_CHECKED. Для выключения отметки элемента меню укажите значение MF_UNCHECKED.

Если в параметре fuCheck указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор элемента меню, отметка которого будет изменена. Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер элемента меню, отметка которого будет изменена.

Выделение строк

Для выделения строк меню верхнего уровня, расположенных в полосе меню ниже заголовка окна, можно использовать функцию HiliteMenuItem :

BOOL WINAPI HiliteMenuItem(HWND hwnd, HMENU hmenu, UINT idItem, UINT fuHilite);

Параметр hwnd должен содержать идентификатор окна, для которого выполняется операция выделения.

Через параметр hMenu необходимо передать идентификатор соответствующего меню верхнего уровня.

Параметр idItem определяет элемент меню, над которым выполняется операция выделения. Интерпретация этого параметра зависит от значения параметра fuHilite.

Параметр fuHilite может принимать значения MF_HILITE или MF_UNHILITE в комбинации с одним из значений: MF_BYCOMMAND или MF_BYPOSITION.

Для выделения строки меню необходимо использовать значение MF_HILITE. Для отмены выделения строки меню укажите значение MF_UNHILITE.

Если в параметре fuHilite указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор строки меню, для которого выполняется операция выделения или отмены выделения. Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер этой строки.

Получение информации

В программном интерфейсе операционной системы Windows существует несколько функций для получения различной информации о меню и о состоянии строк меню.

Идентификатор меню

С помощью функции GetMenu вы можете определить идентификатор меню, связанного с окном:

HMENU WINAPI GetMenu(HWND hwnd);

Идентификатор окна задается при помощи параметра hwnd.

Функция возвращает идентификатор меню или NULL, если окно не имеет меню. Дочернее окно не может иметь меню, однако в документации к SDK говорится, что если вы вызовете данную функцию для дочернего окна, возвращенное значение будет неопределено.

Идентификатор временного меню

Для определения идентификатора временного меню следует вызвать функцию GetSubMenu :

HMENU WINAPI GetSubMenu(HMENU hmenu, int nPos);

Эта функция для меню верхнего уровня с идентификатором hmenu возвращает идентификатор временного меню, порядковый номер которого задается параметром nPos. Первому временному меню соответствует нулевое значение параметра nPos.

Если функция GetSubMenu вернула значение NULL, то меню верхнего уровня не содержит в указанной позиции временное меню.

Проверка идентификатора меню

Для того чтобы убедиться, что идентификатор не является идентификатором меню, вы можете использовать функцию IsMenu :

BOOL WINAPI IsMenu(HMENU hmenu);

Эта функция появилась в программном интерфейсе Windows версии 3.1.

Функция возвращает значение FALSE, если переданный ей через параметр hmenu идентификатор не является идентификатором меню. Можно было бы ожидать, что если функция IsMenu вернула значение TRUE, то проверяемый идентификатор является идентификатором меню, однако в описании функции сказано, что это не гарантируется.

Количество элементов в меню

Функция GetMenuItemCount возвращает количество элементов в меню верхнего уровня или во временном меню, заданном параметром hmenu:

int WINAPI GetMenuItemCount(HMENU hmenu);

Идентификатор элемента меню

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

UINT WINAPI GetMenuItemID(HMENU hmenu, int nPos);

Параметр hmenu задает меню, идентификатор элемента которого требуется определить. Порядковый номер элемента определяется параметром nPos, причем первому элементу соответствует нулевое значение. В случае ошибки (если параметр hmenu указан как NULL или указанный элемент является временным меню) функция GetMenuItemID возвращает значение -1. Если вы попытаетесь определить идентификатор горизонтальной разделительной линии (сепаратора), функция вернет нулевое значение.

Текст строки меню

С помощью функции GetMenuString вы можете переписать в буфер текстовую строку, соответствующую элементу меню.

int WINAPI GetMenuString(HMENU hmenu, UINT idItem, LPSTR lpsz, int cbMax, UINT fuFlags);

Параметр hmenu определяет меню, для которого будет выполняться операция.

Параметр idItem определяет элемент меню, над которым выполняется операция. Интерпретация этого параметра зависит от значения параметра fuFlags.

Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор строки меню, для которого выполняется операция. Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер этой строки.

Адрес буфера, в который будет выполняться копирование, задается параметром lpsz, размер буфера без учета двоичного нуля, закрывающего строку, - оператором cbMax. Символы, не поместившиеся в буфер, будут обрезаны.

Функция GetMenuString возвращает количество символов, скопированных в буфер, без учета двоичного нуля, закрывающего строку.

Флаги состояния элемента меню

Функция GetMenuState возвращает флаги состояния для заданного элемента меню:

UINT WINAPI GetMenuState(HMENU hmenu, UINT idItem,

UINT fuFlags);

Параметр hmenu определяет меню, для которого будет выполняться операция.

Параметр idItem определяет элемент меню, для которого будут получены флаги состояния. Интерпретация этого параметра зависит от значения параметра fuFlags.

Если в параметре fuFlags указан флаг MF_BYCOMMAND, параметр idItem определяет идентификатор строки меню, для которого выполняется операция. Если указан флаг MF_BYPOSITION, параметр idItem определяет порядковый номер этой строки.

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

Флаг

Описание

MF_BITMAP

Для изображения строки меню используется графическое изображение bitmap

MF_CHECKED

Строка меню отмечена галочкой " "

MF_DISABLED

Строка меню находится в неактивном состоянии

MF_ENABLED

Строка меню разблокирована и отображается в нормальном виде. Этому состоянию соответствует возвращаемое функцией GetMenuState значение, равное нулю

MF_GRAYED

Строка меню отображается серым цветом и находится в неактивном состоянии. Такую строку нельзя выбрать

MF_MENUBREAK

Для меню верхнего уровня элемент меню выводится с новой строки. Для временного меню элемент выводится в новом столбце

MF_MENUBARBREAK

Аналогично MF_MENUBREAK, но дополнительно столбец отделен вертикальной линией

MF_SEPARATOR

Строка является горизонтальной разделительной линией во временных меню

MF_UNCHECKED

Строка не отмечена галочкой " "

Если указанный элемент меню не существует, функция GetMenuState возвращает значение -1.

Создание меню при помощи шаблона

Для создания меню вы можете использовать три метода.

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

Во-вторых, можно создать меню "с нуля" при помощи специальных функций программного интерфейса Windows. Этот способ хорош для приложений, меняющих внешний вид меню, когда вы не можете создать заранее подходящий шаблон. Разумеется, второй метод пригоден и для создания статических меню.

В-третьих, можно подготовить шаблон меню непосредственно в оперативной памяти и создать меню на базе этого шаблона.

Создание шаблона меню

Шаблон меню можно создать в текстовом виде либо при помощи приложения Resource Workshop, либо обычным текстовым редактором, например, входящим в состав Borland Turbo C++ for Windows. В любом случае перед сборкой приложения текстовое описание шаблона меню должно находиться в файле ресурсов с расширением имени .rc, указанном в проекте приложения (или в файле, включаемом в файл проекта оператором #include).

Описание шаблона меню имеет следующий вид:

nameID MENU [load] [mem]

BEGIN

. . .

. . .

. . .

END

Поле nameID используется для идентификации шаблона меню. Оно может указываться либо в виде текстовой строки, либо в виде числа от 1 до 65535.

Параметр load - необязательный. Он используется для определения момента загрузки меню в память. Если этот параметр указан как PRELOAD, меню загружается в память сразу после запуска приложения. По умолчанию используется значение LOADONCALL, в этом случае загрузка шаблона в память происходит только при отображении меню.

Параметр mem также необязательный. Он влияет на тип памяти, выделяемой для хранения шаблона, и может указываться как FIXED (ресурс всегда остается в фиксированной области памяти), MOVEABLE (при необходимости ресурс может перемещаться в памяти, это значение используется по умолчанию) или DISCARDABLE (если ресурс больше не нужен, занимаемая им память может быть использована для других задач). Значение DISCARDABLE может использоваться вместе со значением MOVEABLE.

Между строками BEGIN и END в описании шаблона располагаются операторы описания строк MENUITEM и операторы описания временных меню POPUP.

Оператор MENUITEM имеет следующий формат:

MENUITEM text, id [, param]

Параметр text определяет имя строки меню. Вы должны указать текстовую строку в двойных кавычках, например, "File". Текстовая строка может содержать символы &, \t, \a.

Если в текстовой строке перед буквой стоит знак &, при выводе меню данная буква будет подчеркнута. Клавиша, соответствующая подчеркнутой букве, может быть использована в комбинации с клавишей <Alt> для ускоренного выбора строки. Для того чтобы записать в строку сам символ &, его следует повторить дважды. Аналогично, для записи в строку меню символа двойной кавычки " его также следует повторить дважды.

Символ \t включает в строку меню символ табуляции и может быть использован при выравнивании текста в таблицах. Этот символ обычно используется только во временных и плавающих меню, но не в основном меню приложения, расположенном под заголовком главного окна.

Символ \a выравнивает текст по правой границе временного меню или полосы меню.

Параметр id представляет собой целое число, которое должно однозначно идентифицировать строку меню. Приложение получит это число в параметре wParam сообщения WM_COMMAND, когда вы выберете данную строку.

Необязательный параметр param указывается как совокупность атрибутов, разделенных запятой или пробелом. Эти атрибуты определяют внешний вид и поведение строки меню:

Атрибут

Описание

CHECKED

При выводе меню на экран строка меню отмечается галочкой "

"

GRAYED

Строка меню отображается серым цветом и находится в неактивном состоянии. Такую строку нельзя выбрать. Этот атрибут несовместим с атрибутом INACTIVE

HELP

Слева от текста располагается разделитель в виде вертикальной линии

INACTIVE

Строка меню отображается в нормальном виде (не серым цветом), но находится в неактивном состоянии. Этот атрибут несовместим с атрибутом GRAYED

MENUBREAK

Если описывается меню верхнего уровня, элемент меню выводится с новой строки. Если описывается временное меню, элемент меню выводится в новом столбце

MENUBARBREAK

Аналогично атрибуту MENUBREAK, но дополнительно новый столбец отделяется вертикальной линией (используется при создании временных меню)

Для описания временных меню используется оператор POPUP :

POPUP text [, param]

BEGIN

. . .

. . .

. . .

END

Между строками BEGIN и END в описании временного меню располагаются операторы описания строк MENUITEM и операторы описания вложенных временных меню POPUP.

Параметры text и param указываются так же, как и для оператора MENUITEM .

Для того чтобы создать в меню горизонтальную разделительную линию, используется специальный вид оператора MENUITEM:

MENUITEM SEPARATOR

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

#include "menu.hpp"

APP_MENU MENU

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&New", CM_FILENEW

MENUITEM "&Open...", CM_FILEOPEN

MENUITEM "&Save", CM_FILESAVE

MENUITEM "Save &as...", CM_FILESAVEAS

MENUITEM SEPARATOR

MENUITEM "&Print...", CM_FILEPRINT

MENUITEM "Page se&tup...", CM_FILEPAGE_SETUP

MENUITEM "P&rinter setup...", CM_FILEPRINTER_SETUP

MENUITEM SEPARATOR

MENUITEM "E&xit", CM_FILEEXIT

END

END

Шаблон меню начинается с оператора MENU, в котором определено меню с именем APP_MENU. Это меню состоит из трех временных меню, описанных оператором POPUP.

Для определения строк временных меню используется оператор MENUITEM. В качестве второго оператора используются константы, символическое имя которых имеет префикс CM_. Мы определили эти константы в файле menu.hpp, включаемом в файл описания ресурсов оператором #include (можно использовать любые целые неодинаковые значения):

#define CM_FILEEXIT 24338

#define CM_FILEPRINTER_SETUP 24337

#define CM_FILEPAGE_SETUP 24336

#define CM_FILEPRINT 24335

#define CM_FILESAVEAS 24334

#define CM_FILESAVE 24333

#define CM_FILEOPEN 24332

#define CM_FILENEW 24331

Вы сможете легко подготовить описание шаблона меню при помощи текстового редактора в файле описания ресурсов, однако удобнее воспользоваться редактором ресурсов Resource Workshop. С помощью этого редактора вы можете создать шаблон меню и выполнить его тестирование, получив в результате текстовое описание шаблона, которое впоследствии можно редактировать. Именно так мы и поступили, создавая описанное выше меню.

Опишем кратко процесс создания шаблона меню при помощи приложения Resource Workshop.

Для того чтобы создать шаблон меню редактором ресурсов Resource Workshop, запустите его и из меню "File" выберите строку "New project...". В появившейся на экране диалоговой панели выберите тип ресурса - файл .RC, вслед за чем нажмите на кнопку "OK". Если файл описания ресурсов проектируемого приложения уже существует, вы можете открыть его, выбрав из меню "File" строку "Open project...".

Далее из меню "Resource" выберите строку "New...". На экране появится диалоговая панель "New resource". В списке "Resource type" выберите строку "MENU" и нажмите кнопку "OK". В главном окне приложения Resource Workshop вы увидите несколько окон, предназначенных для проектирования шаблона меню.

Подключение меню к окну приложения

Следующий этап - подключение меню к окну приложения. Обычно меню определяется для класса окна при регистрации или для отдельного окна при его создании функцией CreateWindow.

Подключение меню при регистрации класса окна

Если при регистрации класса окна в поле lpszMenuName структуры типа WNDCLASS указать адрес текстовой строки, содержащей имя шаблона меню в файле ресурсов, все перекрывающиеся и временные окна, создаваемые на базе этого класса, будут иметь меню, определенное данным шаблоном. Дочерние окна (child window) не могут иметь меню.

Например, пусть в файле описания ресурсов шаблон меню определен под именем APP_MENU:

APP_MENU MENU

BEGIN

....

....

....

END

В этом случае для подключения меню при регистрации класса вы должны записать адрес текстовой строки "APP_MENU" в поле lpszMenuName структуры wc, имеющей тип WNDCLASS:

wc.lpszMenuName = "APP_MENU";

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

Например, пусть в файле описания ресурсов и в файле исходного текста приложения определена константа:

#define APP_MENU 123

В этом случае ссылка на меню при регистрации класса окна должна выполняться следующим образом:

wc.lpszMenuName = MAKEINTRESOURCE(APP_MENU);

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

Когда для класса окна определено меню, все перекрывающиеся и временные окна, создаваемые на базе этого класса, будут иметь меню, если при создании окна функцией CreateWindow идентификатор меню указан как 0.

Графическое изображение типа bitmap

В ресурсы приложения вы можете включить произвольное графическое изображение в виде битового образа (в дальнейшем мы будем называть такое изображение изображением типа bitmap или просто изображением bitmap).

С помощью графического редактора, входящего в состав Resource Workshop, или с помощью стандартного приложения Paint Brush вы можете нарисовать прямоугольное графическое изображение типа bitmap. При этом для представления цвета одного пикселя может использоваться разное количество бит. Изображение записывается в файл с расширением имени bmp.

Изображения bitmap удобно использовать для оформления внешнего вида окон приложения. С помощью таких изображений вы можете, например, создать органы управления любой формы (например, круглые кнопки с картинками), нарисовать фотографию, введенную сканером, заставку или эмблему фирмы.

Создание изображения типа bitmap

Для создания изображения bitmap запустите Resource Workshop и из меню "File" выберите строку "New Project". В появившейся диалоговой панели включите переключатель ".BMP" и нажмите кнопку "OK".

Включение изображения bitmap в файл описания ресурсов

Для включения изображения типа bitmap в файл описания ресурсов используется такой же способ, как и для включения пиктограмм. Файл описания ресурсов должен содержать оператор BITMAP:

BitmapID BITMAP [параметры загрузки] [тип памяти] имя файла

Параметры загрузки и тип памяти указывается так же, как и для описанной нами ранее таблицы строк STRINGTABLE, пиктограммы и курсора. В качестве имени файла необходимо указать имя файла, содержащего изображение bitmap, например:

AppBitmap BITMAP mybrush.bmp

Идентификатор изображения bitmap BitmapID можно указывать как символическое имя или как целое число - идентификатор ресурса.

Загрузка изображения bitmap

Для загрузки изображения bitmap вы должны использовать функцию LoadBitmap:

HBITMAP WINAPI LoadBitmap(HINSTANCE hInst,LPCSTR lpszBitmap);

Назначение параметров этой функции аналогично назначению параметров функций LoadIcon и LoadCursor. Параметр hInst указывает идентификатор текущей копии приложения, параметр lpszBitmap - идентификатор bitmap в файле описания ресурсов.

Функция возвращает идентификатор изображения, который следует использовать для рисования bitmap.

Перед завершением работы приложение должно удалить загруженное изображение, вызвав функцию DeleteObject:

BOOL WINAPI DeleteObject(HGDIOBJ hGDIObj);

В качестве параметра этой функции следует передать идентификатор изображения, полученный от функции LoadBitmap.

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

Девятый параметр функции CreateWindow используется для подключения меню к создаваемому окну:

Значение параметра идентификатора меню может быть получено, например, от функции LoadMenu , определенной в программном интерфейсе Windows:

HMENU WINAPI LoadMenu(HINSTANCE hInstance, LPCSTR lpszMenuName);

Параметр hInstance должен содержать идентификатор текущей копии приложения, полученный через соответствующий параметр функции WinMain.

Параметр lpszMenuName является указателем на строку символов, закрытую двоичным нулем, содержащую имя загружаемого шаблона меню. Если для идентификации шаблона меню используется целое число, необходимо сформировать этот указатель при помощи макрокоманды MAKEINTRESOURCE.

Функция LoadMenu возвращает идентификатор загруженного меню или NULL при ошибке.

Перед завершением своей работы приложение должно уничтожить загруженное меню функцией DestroyMenu :

BOOL WINAPI DestroyMenu(HMENU hmenu);

В качестве единственного параметра функции DestroyMenu необходимо указать идентификатор уничтожаемого меню.

Функция DestroyMenu возвращает в случае успеха значение TRUE, при ошибке - FALSE.

Сообщения, поступающие от меню

Меню посылает сообщения в функцию создавшего его окна.

Сообщение WM_INITMENU посылается перед отображением меню и может быть использовано для инициализации. Сообщение WM_COMMAND посылается после того, как пользователь выберет одну из активных строк меню. Системное меню посылает в окно приложения сообщение WM_SYSCOMMAND, которое обычно не обрабатывается приложением (передается функции DefWindowProc). В процессе выбора строки из меню, когда курсор перемещается по строкам меню, функция окна, создавшего меню, получает сообщение WM_MENUSELECT. Перед инициализацией временного меню функция окна получает сообщение WM_INITMENUPOPUP.

Из всех этих сообщений наибольший интерес представляют сообщения WM_INITMENU, WM_INITMENUPOPUP, WM_COMMAND, WM_SYSCOMMAND.

Сообщение WM_INITMENU

Сообщение WM_INITMENU посылается окну, создавшему меню, в момент отображения меню. Это происходит, когда вы нажимаете на строку в полосе меню или активизируете временное меню при помощи клавиатуры.

Вместе с этим сообщением в параметре wParam передается идентификатор активизируемого меню. Параметр lParam не используется.

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

Сообщение WM_INITMENUPOPUP

Сообщение WM_INITMENUPOPUP посылается окну, когда операционная система Windows готова отобразить временное меню. Младшее слово параметра lParam содержит порядковый номер временного меню в меню верхнего уровня, старшее слово содержит 1 для системного меню или 0 для обычного меню.

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

Сообщение WM_COMMAND

Сообщение WM_COMMAND , как мы уже говорили, посылается функции окна приложения, создавшего меню, когда вы выбираете нужную вам строку. Параметр wParam содержит идентификатор строки, определенный в шаблоне меню.

Задача функции окна, обрабатывающей сообщения от меню, заключается в проверке значения параметра wParam и выполнении соответствующей функции.

Сообщение WM_SYSCOMMAND

Сообщение WM_SYSCOMMAND приходит в функцию окна приложения, когда пользователь выбирает строку из системного меню. Параметр wParam, как и для сообщения WM_COMMAND, содержит идентификатор строки меню, в данном случае, идентификатор строки системного меню. Параметр lParam не используется (за исключением идентификатора SC_HOTKEY).

Приведем список идентификаторов с кратким описанием.

Идентификатор

Описание

SC_CLOSE

Удаление окна (строка "Close")

SC_HOTKEY

Активизация окна, связанного с комбинацией клавиш, определенной приложением. Младшее слово параметра lParam содержит идентификатор активизируемого окна

SC_HSCROLL

Свертка по горизонтали

SC_KEYMENU

Выбор из меню при помощи комбинации клавиш

SC_MAXIMIZE или SC_ZOOM

Максимизация окна (строка "Maximize")

SC_MINIMIZE или SC_ICON

Минимизация окна (строка "Minimize")

SC_MOUSEMENU

Выбор из меню при помощи мыши

SC_MOVE

Перемещение окна (строка "Move")

SC_NEXTWINDOW

Переключение на следующее окно

SC_PREVWINDOW

Переключение на предыдущее окно

SC_RESTORE

Восстановление нормального положения и размера окна

SC_SCREENSAVE

Запуск приложения, предназначенного для предохранения экрана дисплея от преждевременного выгорания (screen-saver application), определенного в разделе [boot] файла system.ini

SC_SIZE

Изменение размера окна (строка "Size")

SC_TASKLIST

Запуск или активизация приложения Task Manager

SC_VSCROLL

Свертка по вертикали

При анализе параметра wParam учтите, что младшие четыре бита этого параметра могут принимать любые значения и должны игнорироваться:

if((wParam & 0xfff0) == SC_SIZE)

{

return 0;

}

Скоро мы расскажем вам о том, как можно добавлять строки в системное меню. При добавлении строк в системное меню вы должны указывать идентификатор строки. Этот идентификатор (с учетом сказанного выше относительно младших четырех битов) вы получите в параметре wParam сообщения WM_SYSCOMMAND при выборе добавленной вами строки.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]