рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

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

Обработка сообщений - Конспект Лекций, раздел Науковедение, КОНСПЕКТ ЛЕКЦИЙ по дисциплине Операционные системы   Хотя Операционная Система И Использует Целые Числа Для Иденти...

 

Хотя операционная система и использует целые числа для идентификации событий, но в тексте про­граммы мы будем иметь дело с символьными идентификаторами. Огромное количество директив #define связывает символьные идентификаторы с соответствующими числами и позволяет программистам в разго­ворах между собой в кругу посвященных манипулировать словечками вроде wm_paint или wm_size. Префикс wm означает Window Message (сообщение Windows). Сообщению известно, какому окну оно предназначено. Оно может иметь до двух параметров. Часто в эти два параметра упаковываются несколько совершенно различных величин, но это уже другое дело. Обработка разных сообщений выполняется разными компонентами операционной системы и приложения. Например, когда пользователь передвигает мышь по полю окна, формируется сообщение WM_MOUSEMOVE, которое передается окну, а окно, в свою очередь, передает это сообщение операционной системе. И уже последняя перерисовывает указатель мыши в новом месте. Когда пользователь щелкает левой кнопкой мыши на экранной кнопке, кнопка, которая также есть особый вид окна, получает сообщение wm_lbuttondown. В процессе обработки этого сообщения кнопка часто формирует новое сообщение окна, в котором она находится, причем это сообщение гласит: "Ой, на мне щелкнули!"

Библиотека MFC позволяет программистам в подавляющем большинстве случаев полностью отстраниться от сообщений нижнего уровня, таких как wm_mousemove или wm_lbuttondown. Программист может полностью сосредоточиться на сообщениях более высокого уровня, которые гласят что-нибудь вроде “Выбран третий элемент такого-то списка" или "Произошел щелчок на кнопке Move". Такого рода сообщения поступают в те программы, которые пишет программист, и в компоненты операционной системы точно так же, как и сообщения нижнего уровня. Единственная разница в том, что MFC берет на себя значительную часть работы по обработке сообщений низкого уровня и позволяет заметно облегчить распределение сообщений между разными классами объектов, на уровне которых и будет производиться их обработка

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

 

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

 

Сердцем любой Windows-программы является цикл обработки сообщений (Message Loop), который практически всегда находится в функции WinMain (). Эта функция в Windows-приложениях играет ту же роль, что и функция Main () в DOS-приложениях, — ее вызывает операционная система сразу же после загрузки приложения в память. К большому облегчению программистов, теперь они могут не отвлекаться на набивку текста WinMain (), поскольку за них это сделает Appwizard. Но это не значит, что сама функция исчезла. Текст типичной функции WinMain () представлен в листинге ниже.

 

int APIENTRY WinMain (HINSTANCE hinstance,

HINSTANCE hPrevInstance,

LPSTR IpCmdLine,

int nCmdShow)

{

MSG msg;

if(!InitApplication(hInstance))

return (FALSE)

if(!InitInstance( hInstance, nCmdShow))

return (FALSE) ;

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

{

TranslateMessage ( &msg) ;

DispatchMessage( &msg);

}

return (msg.wParain) ;

}

В С-программах для Windows, похожих на эту, функция initApplication() вызывает RegisterWindow(), a Initlnstance() - CreateWindow(). Затем наступает очередь цикла обработки сообщений . Он представляет собой типичную циклическую конструкцию С на базе оператора while, внутри которой вызывается функция GetMessage (). Эта функция API заполняет msg кодом сообщения, которое операционная система распределила для этого приложения, и почти всегда возвращает TRUE. Таким образом, цикл повторяется снова и снова до тех пор, пока работает приложение. Единственный вариант, при котором GetMessage() возвращает false, — это, когда получено сообщение wm_quit.

При работе с сообщениями, поступающими с клавиатуры, некоторую часть предварительной обработки берет на себя функция API TranslateMessage(). Назначение ее в том, что прикладной части программы нет дела до сообщений типа "Нажата клавиша <А>" или "Отпущена клавиша <А>" и т.п. Прикладную часть в конце концов только интересует, какую литеру (символ) ввел пользователь, т.е. ее вполне удовлетворит сообщение "Введен символ <А>". Вот это преобразование — нескольких сообщений в одно сообщение — и выполняет функция TranslateMessage(). Она перехватывает сообщения wm_keydown и wm_keyup и вместо них посылает сообщение WM_char. Конечно, если пользоваться библиотекой MFC, то такие мелочи, как ввод символа <А>, проходят, как правило, мимо вас. Пользователь вводит текст в текстовое поле или в другой элемент управления и забота программиста - извлечь введенный текст из этого объекта после того, как пользователь щелкнет на ОК. Как был организован прием символов с клавиатуры — теперь уже не наша забота. Таким образом, на функцию TranslateMessage () можно особенно не обращать внимания.

Функция API DispatchMessage() вызывает в свою очередь функцию WndProc() того окна, которому предназначено сообщение. Типичная функция WndProc() в С-программе для Windows представляет собой огромный оператор switch с отдельными case для каждого сообщения, которое приложение намеревается самостоятельно обрабатывать. Текст ее приведен ниже.

 

LONG APIENTRY WndProc( HWND hwnd, // дескриптор окна

UINT msg, //тип сообщения

WPARAM wParam, //дополнительная информация

LPARAM IParam) //дополнительная информация

{

switch( msg){

case WM_mousemove: {

//обработка перемещения мыши

break;

case WM_LBUTTONDOWn: {

// обработка щелчка левой кнопкой мыши

break;

case WM_RBUTTONDOWN: {

// обработка щелчка правой кнопкой мыши

break;

case WM_PAINT ;

// перерисовать окно

break;

case WM DESTROY : // сообщение: окно будет уничтожено

PostQuitMessage( 0);

return 0;

break;

default:

return (DefWindowProc( hwnd, msg, wParam, IParam));

}

return (0);

}

 

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

 

3.6. Карты сообщений

 

Использование карты сообщений (Message maps) лежит в основе подхода, который реализуется в MFC для программирования Windows-приложений. Вместо того, чтобы написать функцию WinMain(), которая будет передавать сообщения другой написанной вами функции — WndProc(), в которой будет анализироваться сообщение и вызываться соответствующая функция обработки (также написанная программистом), новый подход требует от разработчика только написать функции обработки сообщений и включить в свою карту сообщений, которая, фактически, скажет "Я буду обрабатывать такое-то сообщение". Теперь главная программа будет заботиться о том, чтобы сообщение было передано именно той функции, которая будет его обрабатывать.

Карта сообщений состоит из двух частей — одна в файле заголовка .h для класса, а другая в соответствующем файле .срр. Они, как правило, формируются Мастерами, хотя в некоторых случаях вы можете сделать это (или частично отредактировать) и самостоятельно. Ниже в листинге представлена часть текста заголовка одного из классов простого приложения ShowString.

 

Карта сообщений из файла ShowString. h

//{{AFX_MSG(CShowStringApp) afx_msg void OnAppAbout () ;

// ВНИМАНИЕ!! Здесь ClassWizard будет добавлять и

// удалять функции-члены.

// НЕ РЕДАКТИРУЙТЕ текст в этих блоках!

//}}AFX_MSG

DECLARE MESSAGE MAP()

Здесь объявляется функция OnAppAbout (). Специальным образом оформленный комментарий позволяет ClassWizard определить, какие именно сообщения перехватываются этим классом. declare_message_map — это макрос, расширяемый препроцессором компилятора Visual C++, в котором объявляются некоторые пере­менные и функции, принимающие участие в этом фокусе с перехватом сообщений.

Карта сообщений в файле .срр, как показано ниже, также достаточно проста.

 

Карта сообщений из файла showString .срр

 

BEGIN_MESSAGE_MAP(CShowStringApp, CWinApp)

//{(AFX_MSG_MAP(CShowStringApp)

ON_COMMAND(ID_APP_ABOUT,OnAppAbout) ON_COMMAND(ID_HELP_UNDERSTANDINGCENTERING, OnHelpUnderstandingcentering)

//}}AFX_MSG_MAP

// Стандартные команды для файловых документов

ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)

ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)

// Стандартные команды настройки принтера

ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END MESSAGE MAP()

 

3.7. Макросы карты сообщений

 

Макросы BEGIN_MESSAGE_MAP И END_MESSAGE_MAP так же, как DECLARE_MESSAGE_MAP в файле заголовка, объявляют некоторые члены — переменные и функции, — которые программа должна использовать для того, чтобы разобраться в картах всех объектов системы. Существует довольно большой набор макросов, используемых для работы с картой сообщений. Некоторые из них перечислены ниже.

- declare_message_map. Используется в файле заголовка для того, чтобы объявить, что в файл собственно текста программы будет включена карта сообщений.

- END_MESSAGE_MAP. Отмечает начало карты сообщений в тексте программы.

- declare_message_map.Отмечает конец карты сообщений в тексте программы.

- on_command. Используется для того, чтобы перенаправить обработку некоторой команды функции - члену класса,

- on_command_range — используется для того чтобы перенаправить обработку группы команд, заданной как некоторый интервал в списке идентификаторов команд, одной функции-члену класса. |

- ON_CONTROL. Используется для того, чтобы перенаправить обработку кода извещения от элемента управления, введенного программистом, функции-члену класса.

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

- on_message. Используется для того, чтобы перенаправить обработку некоторого сообщения, денного программистом, функции-члену класса.

- on_registered_message. Используется для того, чтобы перенаправить обработку некоторого зарегистрированного сообщения, введенного программистом, функции-члену класса.

- on_command_update_ui.Используется для того, чтобы перенаправить обновление, связанное с заданной командой, функции-члену класса.

- on_command_update_ui_range. Используется для того, чтобы перенаправить обновление, связанное с группой команд, заданной как некоторый интервал в списке идентификаторов команд, одной функции-члену класса.

- on_notify. Используется для того, чтобы перенаправить обработку заданного кода извещения элемента управления функции-члену класса.

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

- on_notify_ex. Используется для того, чтобы перенаправить обработку заданного кода извещения от элемента управления, сопровождаемого дополнительными данными, функции-члену класса. Последняя, в свою очередь, должна вернуть true или false с тем, чтобы сигнализировать, нужно ли передать дальше это уточненное сообщение другому объекту для возможной реакции,

- on_notify_ex_range. Используется для того, чтобы перенаправить обработку группы кодов извещений от элемента управления, специфицированной интервалом кодов вторичного идентификатора, которая сопровождается дополнительными данными, одной функции-члену класса. Последняя, в свою очередь, должна вернуть TRUE или false с тем, чтобы сигнализировать, нужно ли передать дальше это уточненное сообщение другому объекту для возможной реакции. Элементы управления, которые передают сообщения такого формата, являются дочерними окнами по отношению к тому окну, которое эти сообщения перехватывает.

В дополнение к перечисленным, существует еще около 100 макросов, по одному на каждое стандарт сообщение, которые направляют соответствующее сообщение функции-члену. Например, on_create направляет сообщение wm_create функции On_Create(). При пользовании такими макросами нельзя из­менять имена функций.

Карты сообщений, приведенные в листингах, созданы для класса CShowStringApp приложения—к ShowString. Этот класс обрабатывает задачи достаточно высокого уровня, как, например, открытие нового файла или отображение окна About. Элементы, которые добавлены в карту сообщений файла заголовка, могут быть истолкованы следующим образом: "Существует функция OnAppAbout(), которая не имеет параметров". Элемент, который добавлен в собственно текст программы (файл.срр), означает: “Когда придет сообщение от команды id_app_about, вызовите OnAppAbout()”. Ничего неожиданного в том, что функция-член OnAppAbout() выведет на экран окно About этого приложения, естественно, нет.

Но что же в действительности происходит при этом с картой сообщений? Каждое приложение имеет объект, который является наследником класса cWinApp, и имеет функцию-член Run(). Эта функция обращается к CWinThread::Run(), которая значительно длиннее, чем продемонстрированная вам ранее функция WinMain (), но имеет точно такой же цикл обработки сообщений — вызов GetMessage (), вызов TranslateMessage() и вызов DispatchMessage (). Почти все объекты-окна используют тот же самый класс окна, характерный для прежней технологии программирования, и ту же самую функцию WndProc(), но теперь названную AfxWndProc(). Функция WndProc(), как мы уже видели, знает дескриптор окна hWnd, для которого предназначено сообщение. Библиотека MFC, в свою очередь, содержит нечто, называемoe картой дескрипторов (handle map), — таблицу дескрипторов окон и указателей объектов, и таким образом главная программа может, используя всю эту информацию, найти указатель объекта cWnd*. Далее она вызывает WindowProc() — виртуальную функцию этого объекта. Кнопки или окна просмотра, естественно, имеют разные реализации этой функции, но волшебные свойства полиморфизма приводят к тому, что вызывается именно та реализация, которая нужна.

Функция WindowProc() вызывает OnWndMsg() — функцию C++, которая собственно и обрабатывает сообщения. Во-первых, она проверяет, что же это было — сообщение, команда или код извещения. Предположим, поступило сообщение. Тогда функция просматривает карту сообщений для своего класса, используя члены класса, — переменные и функции, — которые были установлены макросами BEGIN_MESSAGE_MAP, END MESSAGE MAP И DECLARE MESSAGE MAP. Среди прочих возможностей, которые организуют эти макросы, есть и доступ к элементам карты сообщений базового класса посредством функций, которые анализируют карту сообщений производного класса. Это означает, что если класс является производным от CView, но не перехватывает сообщений, которые обычно перехватываются базовым клас-сом, то сообщение будет перехвачено функцией CView, которая унаследована производным классом. Этот механизм наследования в карте сообщений работает параллельно с механизмом наследования C++, но не-зависимо от него, и таким образом позволяет избежать многих сложностей, связанных с использованием виртуальных функций.

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

 

3.7.1. Полиморфизм

 

Виртуальные функции и полиморфизм есть базовые понятия концепции объектно-ориентированного программирования. Они должны быть известны любому программисту, работающему на языке C++, особенно если он пользуется библиотеками такого класса, как MFC. С ними встречаешься всякий раз при использовании указателей на объекты, особенно если эти объекты принадлежат классам, производным от других классов. В качестве примера рассмотрим некоторый класс CDerived, производный от другого класса CBase. Пусть этот класс имеет функцию-член Function (), которая объявлена в базовом классе, и перегружена в производном классе. Теперь в нашем распоряжении две функции: полное имя одной— CBase:: Function (), а другой— CDerived::Function().

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

 

CDerived derivedobject;

CBase* basepointer;

Basepointer = &derivedobject;

BasepointerpFunction();

 

В этом случае будет вызвана функция CBbase:: Function (). Но может оказаться, что это совсем не то, что вам нужно. Хотя и используется указатель на объект класса CBase, в действительности вам нужно вызвать функцию класса CDerived. Чтобы указать на такой способ обращения к функции Function(), она объявляется в базовом классе как виртуальная функция. Это можно интерпретировать как указание компилятору перегрузить данную функцию при первой же возможности в производном классе. Поскольку функция Function () объявлена в базовом классе CBase как виртуальная, приведенный выше фрагмент программы действительно вызовет CDerived: : Function(), как того и добивался разработчик.Это и есть полиморфизм на практике. Подобные случаи вы сможете неоднократно встретить при работе с классами, объявленными в MFC. Например, вы используете указатель, объявленный как CWnd*, для обращения к объекту производного класса CButton или CView, или любого аналогичного. Затем, если вызывается функция типа WindowProc(), то компилятор сформирует обращение к функции-члену производного класса CButton::WindowProc()

 

3.7.2. Сообщения, которые перехватываются функциями MFC

 

Другое громадное преимущество MFC состоит в том, что в этой библиотеке уже имеются готовые классы, которые перехватывают и обрабатывают большинство распространенных сообщений, причем дела это безо всяких усилий со стороны разработчика программы. Например, вам не нужно заботиться об обработке таких сообщений, как вызов команды File-Save As. Классы MFC самостоятельно отловят это сообщение, выведут на экран диалоговое окно для ввода нового имени файла, обработают все манипуляции пользователя в этом окне, короче говоря, сделают для вас всю черновую работу и в конце вызовут разработанную уже вами функцию Serialize(), которая и запишет данные в файл. AppWizard, как правило, формирует пустую функцию Serialized, в которую разработчик должен вставить необходимый текст Таким образом, вам необходимо вставлять в карту сообщений элементы только для тех случаев, когда обработка некоторого сообщения в данном приложении отличается от общепринятой методики.

Читать карту сообщений в листинге программы совсем непросто, но зато ее очень просто формировать с помощью ClassWizard. В Visual C++ версии 6.0 существуют два способа включения элемента в карту сообщений — с помощью главного диалогового окна ClassWizard и с помощью одного из новых диалоговых окон, которые вставляют в программу обработчики сообщений или виртуальные функции.

 

3.7.3. Список сообщений

 

Существует почти 900 различный сообщений, так что исчерпывающий список их просто не уместился бы в этой главе. Поскольку обычно для организации перехвата сообщений в приложении используеся ClassWizard, то представленный в нем список будет значительно короче, поскольку в него отбираются только те сообщения, которые подходят к выбранному классу. Отнюдь не каждое окно может получить или иное сообщение. Например, только класс, который является наследником CListBox, может получи сообщение типа lb_setsel, которое заставляет элемент управления типа список передвинуть подсветка на некоторый элемент списка. Префикс в имени сообщения указывает тип окна, для которого предназначено сообщение или которое его породило.

 

3.8. Команды

 

Что такое команда? Это не более чем, когда пользователь выбирает пункт меню, щелкает на кнопке или каким-либо другим способом дает системе понять, что ему что-то от нее нужно. В прежних версиях Windows и выбор из меню и щелчок на кнопке формировали сообщение wm_command. Наступили новые времена, и теперь только выбор из меню порождает сообщение wm_command, а щелчок на кнопке или выбор в списке порождает сообщение wm_notify с кодом извещения от элемента управления. Команды и коды извещений проскакивают через операционную систему точно так же, как и любые другие сообщения, но только до тех пор, пока не попадут в функцию OnWndMsg(). Здесь кончаются владения Windows, и в дело вступает маршрутизация команд, выполняемая MFC.

Все сообщения команд содержат в качестве первого параметра идентификатор ресурса — выбранный пункт меню или кнопку, на которой щелкнули. Этот идентификатор ресурса присваивается в соответствии со стандартом на форматы подобного рода идентификаторов — например, для пункта Save As меню FILE идентификатор будет иметь вид id_file_save.

Маршрутизация команд — это механизм, который использует функция OnWndMsg() для передачи команд и кодов извещения объектам, которые не могут получать сообщения. Получить сообщения могут только объекты классов-наследников CWnd, а все объекты классов, которые наследуют CCmdTarget, включая CWnd и CDocument, могут получать команды и коды извещений. Это означает, что класс, который наследует CDocument, может иметь карту сообщений, причем в ней не должно быть ни одного элемента, соответствующего сообщению, а только элементы для команд и кодов извещений. Тем не менее, она по-прежнему называется картой сообщений.

Каким же образом все-таки команды и коды извещения передаются классу? Посредством механизма маршрутизации команд. (Это довольно занудная процедура, так что, если вас не интересуют подробности ее реализации, можете со спокойной совестью перейти к следующему абзацу.) Функция OnWndMsg() вы-зывает CWnd::OnCommand() вы- или CWnd::OnNotify(). Функция OnCommand() вы- исследует всю подноготную команды (например, не нужно ли будет заблокировать этот пункт меню после того, как пользователь его выбрал, но перед тем, как соответствующий фрагмент программы будет выполнен), и затем вызывает OnCmdMsg(). Функция OnNotify() анализирует другие условия и затем также вызывает OnCmdMsg(). Функция OnCmdMsg() вы- является виртуальной, а это означает, что различные классы, для которых предназначены различные команды, имеют разные реализации этой функции. Реализация для рамки окна пересылает команду представлению или документу, который она (рамка) содержит.

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

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

 

3.8.1. Обновление команд

 

Наша краткая экскурсия по подземельям MFC, где мы попробовали познакомить вас с тем, как связываются действия пользователя с текстом программы, завершается. Остался последний зал, в котором можно будет узнать, как программа выполняет блокировку определенных пунктов меню или кнопок в соответствии с контекстом задачи. Этот процесс назван обновлением команд (command updating).

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

Один состоит в том, чтобы иметь огромную таблицу, элементами которой будут все имеющиеся в приложении пункты меню, каждому из которых сопоставлен флаг. Состояние флага —true или false-и указывает, доступен этот пункт меню или нет. Как только возникает необходимость вывести меню на экран, можно быстро просмотреть таблицу, и все сразу станет ясно. При любой операции, которая может повлечь за собой изменения в статусе какого-либо пункта меню, таблица обновляется. Все это в совокупности называется подходом непрерывного обновления (continuous-updating approach).

Другой подход состоит в том, чтобы, не имея такой таблицы, перед каждым выводом меню на экран анализировать все условия, которые влияют на возможную блокировку. Он называется подходом обновления по требованию (update-on-demand approach). Именно такой подход и реализован в Windows. До широкого внедрения идей объектно-ориентированного программирования в разработку Windows-приложений такой анализ выполнялся следующим образом: система посылала сообщение wm_initmenupopup, которое означало: "Я готовлюсь вывести на экран меню", огромный оператор switch в функции WinProc() перехватывал это сообщение и быстро разрешал и блокировал определенные пункты меню. Эта методика абсолютно противоречит главным идеям объектно-ориентированного программирования, которые требуют, чтобы разные части информации хранились в разных объектах и не морочили голову остальной программе.

Когда наступает время выводить на экран меню, разные объекты "знают", нужно ли блокировать связанный с ними пункт меню. Например, документ знает, был ли он модифицирован после последнего сохранения, и таким образом решает, стоит ли блокировать пункт Save меню File. Но только весь объект-представление знает, есть ли выделенный фрагмент текста, и таким образом он может решить, как поступить с пунктами Cut и Copy меню Edit. Все это означает, что комплексная задача блокировки пунктов меню в соответствии с контекстом приложения распределяется между различными объектами приложения, а не возлагается на главную вызывающую подпрограмму WndProc ().

Подход, реализованный в MFC, состоит в том, чтобы использовать небольшой объект класса CCmdUI (класс command user interface - интерфейс с командами пользователя), и предоставить ему возможность перехватывать любые сообщения cn_update_command_ui. Организовать такой перехват можно, добавив (или предоставив возможность ClassWizard добавить) макрос ON_UPDATE_COMMAND_UI в карту сообщений. Если вас интересует, что же происходит там, за ширмой макроса, то вкратце процесс идет следующим образом: операционная система по-прежнему посылает wm_initmenupopup, а затем в дело вступают базовые классы MFC, такие как CFrameWnd. Они формируют объект CCmdUI, устанавливают значения его членов-переменных соответственно первому пункту меню и вызывают один из методов этого объекта — DoUpdate (). Затем DoUpdate () посылает сообщение cn_update_command_ui, направляя его самому себе, так как обработчиком сообщений указан объект CCmdUI. Затем тот же самый объект CCmdUI перенастраивается соответственно второму пункту меню и т.д., пока все меню не будет подготовлено к выводу. Объект класса CCmdUI также используется для блокировки или разблокировки командных кнопок и других элементов управления по той же технологии. ice CCmdUI имеет следующие функции-члены.

- Enable(). Принимает аргументы true или false. Блокирует элемент интерфейса пользователя, ес­ли передан аргумент false, в противном случае делает элемент доступным.

- SetCheck(). Включает или выключает элемент управления.

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

- SetText(). Устанавливает текст пункта меню или кнопки, если элемент управления — кнопка.

- DoUpdate(). Формирует сообщение.

Как правило, выбор, какая из перечисленных функций-членов нужна вам для определенных целей, очевиден. Ниже приведена упрощенная версия карты сообщений объекта класса CWhoisView, производного CFormView, который выводит информацию на экран для пользователя. Соответствующая экранная имеет несколько текстовых полей, и пользователь может вставлять текст в одно из них. Карта сообщений содержит элемент перехвата обновления для команды id_edit_paste, что в тексте программы выглядит следующим образом:

BEGIN_MESSAGE_MAP(CWhoisView, CFormView)

On_UpDATE_COMMAND_ID(ID_EDIT_PASTE, OnUpdateEditPaste)

END_MESSAGE_MAP()

Функция OnUpdateEditPaste (), которая перехватывает обновление, выглядит следующим образом:

Void CWhoisView::OnUpdateEditPaste(CCmdUI* pCmdUI)

{

pCmdUI->Enable(::IsClipboardFonnatAvailable(CF_TEXT));

}

Здесь вызывается функция API ::IsClipboardFormatAvailable(), которая проверяет, есть ли текст в системном буфере clipboard. Дело в том, что другое приложение может загрузить из Clipboard изображение или другую нетекстовую информацию, но данное приложение такими возможностями не располагает. Поэтому, если в Clipboard не текст, текстовое поле в экранной форме блокируется. Большинство функций обновления команд выглядят примерно также — они вызывают Enable () с аргументом, который формируется в результате вызова некоторой функции, возвращающей true или false, или — другой вариант — аргумент является просто логическим выражением. Обработчики обновления команд должны работать очень быстро, поскольку с момента, когда пользователь вызвал на экран меню, щелкнув в нужном месте окна, нужно успеть пропустить пять-десять циклов обработки и только после этого обновленное меню будет выведено на экран.

В диалоговом окне ClassWizard, представленном на рис. 3.1, в списке Object IDs выделено имя класса. Ниже его имеются идентификаторы всех ресурсов (меню, панелей инструментов, элементов управления и т.д.), которые могут формировать команду или сообщение при условии, что объект данного класса присутствует на экране. Если вы выделите один из них, то список связанных с ним сообщений Messages станет значительно короче, как это видно на рис. 3.2.

С каждым идентификатором ресурса связаны только две строки в списке сообщений — COMMAND и UPDATE_COMMAND_UI.

 

 

 

Рис. 3.1. ClassWizard позволяет программировать перехват или обновление команд

 

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

Если вы считаете нужным ввести новую функцию для перехвата команды или обновления, щелкните на кнопке ADD Function. Это включит в процесс разработки еще один этап — ClassWizard предоставит вам возможность изменить имя функции, которое он сформировал по стандартной схеме (рис. 3.2). Эта возможность оставлена для самых привередливых, поскольку стандартная схема формирования имени, как правило, не вызывает возражений даже у опытных программистов, давно имеющих дело с MFC. Имя функции обработки команды начинается с ON. Оставшаяся часть формируется следующим образом — удаляется ID и символы подчеркивания из идентификатора ресурса, а каждое слово пишется строчными литерами, кроме первого символа. Имена обработчиков обновления команд начинаются с OnUpdate, а далее используется то же преобразование идентификатора ресурса. Например, функция, которая перехватывает команду от id_app_exit, будет называть OnAppExit, а функция, которая обновляет id_app_exit, будет называться OnUpdateAppExit.

Далеко не каждая команда нуждается в обработчике обновления. Объект-рамка окна выполняет некоторую работу в части блокировки элементов управления самостоятельно, безо всяких указаний на то со стороны разработчика. Пусть, скажем, у вас есть меню Network (Сеть), а в нем — пункт Sent (Послать). Команда этого пункта меню перехватывается документом. Если же в приложении не открыт ни один документ, этот пункт меню будет заблокирован главным окном приложения, причем блокировка организуется безо всяких усилий с вашей стороны. Для многих команд этого вполне достаточно, т.е. команда блокируется, если объект, который должен ее обрабатыватъ, не существует. Для других же, могут иметь смысл только в случае, если что-то выбрано или выделено, схема блокировки значительно сложнее. Вот здесь и нужно участие разработчика в программировании процесса обновления команды.

 

 

Рис. 3.2. Classwizard позволяет изменить сформированное им имя функции обработки команды или обновления команды, что, однако, не рекомендуется

 

 

РЕКОМЕНДУЕМАЯ ЛИТЕРАТУРА

1. К. Паппас, У. Мюррей. Эффективная работа: Visual C++ .NET. – СПб.: Питер, 2002. – 816 с.

2. Шефферд Джордж. Программирование на Microsoft Visual C++ .NET /Пер. с англ. – М.: Издательско-торговый дом «Русская редакция», 2003. – 928 с.

3. Черносвитов А. Visual C++ 7: учебный курс. – СПб.: Питер, 2001. – 528 с.

4. Троелсен Э. C# и платформа .NET. Библиотека программиста. – СПб.: Питер, 2002. – 800 с.

5. Корнелл Г., Моррисон Дж. Программирование на VB.NET: учебный курс. – СПб.: Питер, 2002. – 400 с.

6. Эндрюс Грегори Р. Основы многопоточного, параллельного и распределённого программирования. / Пер. с англ. К.: Диалектика, 2002. – 512 с.

7. Оуглтри Терри. Microsoft Windows XP. / Пер. с англ. К.: DiaSoftUP, 2002. – 848 с.

8. Питрек М. Секреты системного программирования в Windows/Пер. с англ. К.: Диалектика, 1996. – 448 с.

9. Кинг Адриан. Windows 95 изнутри. / Пер. с англ. – СПб: Питер, 1995. – 512 с.

10. MSDN Library – January 2000. CD-ROM: №1,2,3

 

 

– Конец работы –

Эта тема принадлежит разделу:

КОНСПЕКТ ЛЕКЦИЙ по дисциплине Операционные системы

ВОСТОЧНОУКРАИНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ... Северодонецкий технологический институт... КОНСПЕКТ ЛЕКЦИЙ...

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

Что будем делать с полученным материалом:

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

Все темы данного раздела:

УПРАВЛЕНИЕ ПРОЦЕССОРАМИ И ПАМЯТЬЮ В ОПЕРАЦИОННЫХ СИСТЕМАХ WINDOWS
  1.1. Обзор компонент архитектуры ОС Windows.   К работе над графической средой для персональных компьютеров IBM PC компания Microsoft приступила еще в 1981 го

АРХИТЕКТУРА ПРИЛОЖЕНИЙ WIN32 ДЛЯ ОС WINDOWS
2.1. Прохождение сообщений в системе Рассмотрим ситуацию, когда пользователь приложения нажимает клавишу, а система вырабатывает сообщение об этом событии. Вы знаете, что Windows обеспечив

АРХИТЕКТУРА ПРИЛОЖЕНИЙ ОС WINDOWS, ПОСТРОЕННЫХ НА БАЗЕ БИБЛИОТЕКЫ MFC
  3.1. Введение   Первое решение, которое должен принять программист при разработке приложения для операционных систем Windows 98, Windows NT и Windows 2000 – с

Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • Популярное
  • Облако тегов
  • Здесь
  • Временно
  • Пусто
Теги