Окт 13 2009

Использование утилиты Spy++

В процессе отладки приложений иногда бывает полезным увидеть, какие сообщения вырабатывает Windows в ответ на те или иные действия пользователя. В составе интегрированной среды Visual Studio 6 есть удобное инструментальное средство для решения данной проблемы — утилита Spy++.
Утилиту можно вызвать при помощи команды меню интегрированной среды: Tools ► Spy++.


Окт 13 2009

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

Предположим, что в вашей программе обработка какого-то события является очень ресурсоемкой, занимая процессор в течение нескольких минут. Несмотря на то что Windows является многозадачной операционной системой, выделяющей принудительно кванты времени для выполнения других приложений, пользователь будет не в состоянии что-нибудь сделать с этой злополучной программой, пока не завершится указанная обработка. Он не сможет даже перетащить окно в другое место экрана или закрыть приложение. Внешне это выглядит как зависание приложения. Вряд ли пользователю понравится такое поведение программы.
Рассмотрим эту проблему на конкретном примере. Предположим, в приложении имеется обработка некоторой команды меню (например, Play) с помощью следующей функции:
void OnPlayO {
while (IfReadData.eofO) { fReadData.read(buf, 512); DoSomethingO ; // какая-нибудь обработка } }
Здесь fReadData — это объект класса ifstream. В цикле while осуществляютс чтение файла fReadData блоками по 512 байт и последующая обработка каждогс блока с помощью функции DoSomething. Допустим, что размер файла достигает 5 Мбайт, а время выполнения функции DoSomething составляет 10 мс.


Окт 13 2009

Получение дескриптора экземпляра приложения

Для многих функций Win32 API необходимо передавать дескриптор экземпляра приложения в качестве одного из параметров. Напомним, что значение этого дескриптора hlnstance функция WinMain получает от операционной системы через свой первый параметр.
Если значение дескриптора hlnstance используется в теле функции WinMain, проблем никаких нет, если в теле другой функции — возникает вопрос, как получить значение hlnstance? Есть три способа решения проблемы.
Первый способ (наихудший, с точки зрения стиля программирования на C++) — объявить глобальную переменную
HINSTANCE hlnst; и в теле функции WinMain запомнить значение дескриптора hlnstance. Естественно, значение глобальной переменной будет доступно во всех других функциях.
Во втором и третьем способах переменная hlnst объявляется как локальная. Во втором способе ее значение определяют при помощи функции GetQassLong:
hlnst = (HINSTANCE)GetClassLong(hWnd, GCL_HMODULE);
В третьем способе для этого используется функция GetModuleHandle:
hlnst = GetModuleHandle(NULL):


Окт 13 2009

Использование глобальных или статических переменных

Иногда бывает нужно, чтобы информация, полученная в процессе обработки какого-то сообщения, была сохранена и использована позже при обработке другого сообщения.
Рассмотрим следующий пример. В программе Hellol размеры клиентской области окна были получены при помощи функции GetClientRect. Но существует и другой способ получить эту информацию — обрабатывая сообщение WM_SIZE. Параметр IParam этого сообщения содержит в своем младшем слове значение ширины, а в старшем слове — значение высоты клиентской области окна. Допустим, что мы определили две локальные переменные в теле функции WndProc:
int width, height; и добавили обработку сообщения WM_SIZE, обеспечивающую сохранение соответствующих значений:
case WMJIZE:
width = LOWORDCIParam); height = HIWORD(lParam): return 0;
Здесь LOWORD и HIWORD — макросы, определенные в заголовочных файлах Windows; первый из них извлекает младшее слово, а второй — старшее слово из 32-разрядного аргумента.
Закомментируем вызов функции GetClientRect в блоке обработки сообщения WM_PAINT и вместо него вставим следующий код:
rect.left = 0: rect.top = 0; rect.right = width; rect.bottom = height;
После компиляции и запуска программы будет показано пустое окно, не содержащее никакого текста.
Неудача объясняется тем, что при повторном вызове WndProc значения ее локальных переменных от предыдущего вызова не сохраняются.
Но эту ошибку легко исправить. Надо лишь объявить переменные width и height либо как статические (static), либо как глобальные.


Окт 13 2009

Функция PostMessage

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


Окт 13 2009

Функция SendNotifyMessage

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


Окт 13 2009

Функция SendMessage

Эта функция посылает асинхронное сообщение указанному окну или нескольким окнам. Она имеет следующий прототип:
LRESULT SendMessage(
HWND hWnd, // дескриптор окна-получателя
UINT Msg, // код сообщения
WPARAM wParam, // первый параметр сообщения
LPARAM lParam // второй параметр сообщения );
Параметры функции те же, что и параметры, передаваемые в оконную процедуру.
Когда приложение вызывает SendMessage, Windows, в свою очередь, вызывает оконную процедуру с дескриптором окна hWnd, передавая ей эти четыре параметра. После того как оконная процедура заканчивает обработку сообщения, система Windows передает управление инструкции, следующей за вызовом SendMessage. Оконная процедура, которой отправляется сообщение, может быть той же самой оконной процедурой, другой оконной процедурой той же программы или даже оконной процедурой другого приложения.
Если первый параметр функции имеет значение HWND_BROADCAST, то сообщение посылается всем окнам верхнего уровня, существующим в настоящий момент в системе.
Параметры wParam и lParam содержат дополнительную информацию, интерпретация которой зависит от кода сообщения. Чтобы получить справочную информацию в MSDN об интерпретации этих параметров, применяйте поиск по коду сообщения, то есть по идентификаторам WM_PAINT, WM_TIMER, WM_SETFONT и им подобным.


Окт 13 2009

Синхронные и асинхронные сообщения

Синхронными сообщениями называются сообщения, которые Windows помещает в очередь сообщений приложения. Такие сообщения извлекаются и диспетчери-зуются в цикле обработки сообщений.
Асинхронные сообщения передаются непосредственно окну, когда Windows вызывает оконную процедуру.
Приведем примеры синхронных сообщений. Прежде всего, к ним относятся сообщения о событиях пользовательского ввода, таких как нажатие клавиш (WM_KEYDOWN и WM_KEYUP), перемещение мыши (WM_M0USEM0VE) или щелчок левой кнопкой мыши (WM_LBUTT0ND0WN). Кроме этого синхронными являются сообщения от таймера (WM_TIMER), сообщение о необходимости перерисовки клиентской области (WM_PAINT) и сообщение о выходе из программы (WM_QUIT). Приложение может само направить в очередь синхронное сообщение, вызвав функцию PostMessage.
Остальные сообщения, как правило, являются асинхронными. Во многих случаях асинхронные сообщения являются результатом обработки синхронных сообщений. Вообще, когда асинхронное сообщение обрабатывается функцией DefWindowProc, Windows часто генерирует другие сообщения, направляемые оконной процедуре. Приложение также может послать асинхронное сообщение, вызвав функцию SendMessage.
Таким образом, оконная процедура должна быть повторно входимой (reentrant program). Это означает, что Windows часто вызывает функцию WndProc с новым сообщением, появившимся в результате вызова DefWindowProc из WndProc при обработке предыдущего сообщения. В большинстве случаев повторная входимость оконной процедуры не создает каких-то особых проблем, но знать об этом полезно.
Рассмотрим, например, какие события произойдут после щелчка кнопкой мыши на кнопке закрытия окна приложения Hellol. Все начнется с того, что Windows отправит асинхронное сообщение WM_SYSCOMMAND оконной процедуре WndProc. Оконная процедура передаст это сообщение на обработку функции DefWindowProc. Функция DefWindowProc реагирует на него, отправляя сообщение WM_CL0SE оконной процедуре. В рассматриваемом примере предусмотрена обработка этого сообщения — вызывается функция DestroyWindow. Однако если не предусмотреть эту обработку, то функция DefWindowProc сделала бы то же самое, то есть вызвала бы функцию DestroyWindow. Функция DestroyWindow заставляет Windows отправить оконной процедуре сообщение WM_DESTROY. И наконец, WndProc, обрабатывая это сообщение, вызывает функцию PostQuitMessage, которая посылает синхронное сообщение WM_QUIT в очередь сообщений приложения. Сообщение WM_QUIT прерывает цикл обработки сообщений в WinMain, и приложение завершает свою работу.
Сообщения не похожи на аппаратные прерывания. Пока оконная процедура обрабатывает одно сообщение, программа не может быть прервана другим сообщением. Только в том случае, когда функция, выполняемая в теле оконной процедуры, генерирует новое асинхронное сообщение, оно вызывает повторно оконную процедуру, и только после его обработки выполнение прерванной функции продолжается.


Окт 13 2009

Модификация характеристик окна

Если окно уже создано, например, в виде объекта класса KWnd, то его характеристики могут быть изменены с помощью одной из двух функций: SetClassLong или SetWindowLong.
Рассмотрим применение функции SetClassLong, предназначенной для изменения одного из полей структуры WNDCLASSEX. Напомним, что указатель на эту структуру с начальными значениями ее полей передавался функции RegisterClassEx для регистрации класса окна.


Окт 13 2009

Программа «Hello, World!» — второй вариант

Программу «Hello, World!» часто используют как каркас нового Windows-приложения. Иными словами, начало работы над новой программой обычно происходит по такому сценарию: вы создаете новый проект, открываете новый пустой файл для функции WinMain, копируете в него содержимое файла Hellol.cpp\ а потом вносите необходимые изменения и дополнения.
Но такой способ работы, мне кажется, должен оскорблять чувства программиста, пишущего на C++. Почему бы не воспользоваться известными средствами языка, позволяющими реализовать повторное использование кода гораздо более изящным способом? Вы уже догадались, что речь идет о программировании с классами? — Да, конечно.
Здесь предлагается одна из возможных реализаций этой идеи. Она основана на создании класса KWnd, в котором инкапсулируется код, отвечающий за регистрацию оконного класса, создание окна и показ его на экране.
Откомпилируйте эту программу (проект Hello2), чтобы убедиться в ее работоспособности. Поэкспериментируйте с передачей других значений аргументов конструктору объекта mainWnd. Например, замените строку
KWnd mainWnd("Hellо application", hlnstance, nQndShow, WndProc);
на строку
KWnd mainWnd("Hello application", hlnstance. nCmdShow, WndProc. NULL. 50. 100, 200, 150);
и посмотрите на поведение измененной программы.
Разработка второго варианта программы «Hello, World!» завершена. Реализованный в ней класс KWnd можно использовать для сокращения кода новых приложений.


Следующая страница »