- •Лабораторный практикум «Основы разработки приложений Windows» Книга 2
- •Часть 1. Теоретические сведения4
- •Часть 2. Лабораторный практикум73
- •Часть 1 Теоретические сведения
- •1. Основы архитектурЫ защищенного режима Регистры процессора
- •Адресация памяти
- •2. Логические шрифты Создание логических шрифтов
- •Вывод на экран текстовых строк
- •3. Таймеры Windows Организация и обслуживание таймеров
- •Мультимедийные таймеры
- •Измерение интервалов времени
- •Организация периодического процесса
- •Задание однократного интервала времени
- •4. Дочерние окна Создание и использование дочерних окон
- •Окна предопределенных классов в главном окне
- •5. Вывод растровых изображений
- •Процедура вывода растрового изображения
- •Компоновка составных изображений
- •6. Обслуживание файлов в 32-разрядных приложениях Windows
- •Базовые операции с файлами Открытие и создание файла
- •Запись и чтение файла
- •Файлы, проецируемые в память
- •7. Процессы и потоки
- •Создание дочернего процесса
- •Создание дочернего потока
- •Синхронизация потоков Общие характеристики объектов Windows
- •Синхронизация с помощью состояний потока
- •Синхронизация с помощью событий
- •Критические секции и защита данных
- •8. Библиотеки динамической компоновки
- •Часть 2 Лабораторный практикум Работы лабораторного практикума Работа 1. Создание логических шрифтов
- •Работа 2. Таймеры Windows(индивидуальное задание а)
- •Работа 3. Дочернее окно в главном окне приложения
- •Работа 4. Вывод растровых изображений с использованием совместимой памяти
- •Работа 5. Измерение временных характеристик программы с помощью мультимедийного таймера
- •Работа 6. Вывод движущихся изображений с синхронизацией от системного таймера (индивидуальное задание b)
- •Работа 7. Повышение качества движущихся изображений с помощью совместимой памяти
- •Работа 8. Движение изображения по фоновому рисунку
- •Работа 9. Работа с файлами (индивидуальное задание c)
- •Работа 10. Стандартные диалоги Windows для работы с файлами
- •Работа 11. Проецирование файла в память
- •Работа 12. Потоки (индивидуальное задание d)
- •Работа 13. Синхронизация потоков с помощью событий
- •Работа 14. Защита данных с помощью критической секции
- •Работа 15. Библиотеки динамической компоновки
- •Работа 16. Передача параметров в функции dll-библиотек
- •Индивидуальные задания лабораторного практикума
- •Задание c2.Массив записываемых в файл данных должен представлять собой последовательный ряд из 2000 целых четных чисел.
- •Лабораторный практикум «Основы разработки приложений Windows» Книга 2
Задание однократного интервала времени
При необходимости отработки однократного интервала времени необходимо выполнить все описанные выше действия, только в качестве режима установки временного события указывается константа TIME_ONESHOT:
timeBeginPeriod(1);//Установим максимальное разрешение
mmr=timeSetEvent(1000,1,TimeProc,0,TIME_ONESHOT);
...//Продолжение программы
void CALLBACK TimeProc(UINT,UINT,DWORD,DWORD,DWORD){
PostMessage(hwnd,WM_USER,(WPARAM)parm1,(LPARAM)parm2);
timeEndPeriod(1);//Отмена установленного ранее разрешения
..timeKillEvent(mmr);//В предположении, что mmr – глобальная
} // переменная
В данном варианте сообщение WM_USERпосылается лишь один раз по истечении точно 1с (с погрешностью в 1 мс). В прикладной функцииTimeProcобработки этого сообщения, помимо активизации содержательных действий (посредством посылки сообщенияWM_USER), необходимо отменить установленное ранее временное разрешение, как это и показано в приведенных выше строках.
4. Дочерние окна Создание и использование дочерних окон
Любое реальное приложение Windows содержит большое количество окон. Хотя все графические элементы, составляющие экранный кадр приложения (текстовые строки, геометрические фигуры, растровые изображения) можно вывести непосредственно в главное окно, такой способ формирования экранного кадра обычно оказывается неудобным, главным образом из-за невозможности раздельного взаимодействия (с помощью мыши или клавиатуры) с этими элементами. Организация же системы вложенных, или порожденных, окон позволяет для каждого такого окна или группы окон иметь, например, свою форму курсора или свой цвет фона; главное же достоинство порожденных окон заключается в возможности придания каждому окну индивидуальной оконной функции. Это позволяет отдельно и по-разному обрабатывать для каждого окна поступающие в него сообщения Windows (или, наоборот, посылать в окна программно сформированные сообщения).
Так, на рис. 4.1 приведен для примера возможный вид главного окна программы, управляющей некоторой измерительной установкой. В окне имеется поле для ввода экспозиции, поясняющая надпись “Введите Экспозицию:”, кнопка пуска, а также большое дочернее окно для динамического вывода значения времени, оставшегося до конца сеанса измерения.
Рис. 4.1. Дочернее окно в главном окне приложения
Реально почти все изображения, которые мы видим на экране, – разнообразные кнопки, полосы прокрутки, ползунки, поля для ввода текста, изображения различных приборов и индикаторов – представляют собой окна с теми или иными характеристиками. Обычно эти окна образуют иерархическую систему. Например, любой диалог прежде всего представляет собой окно; однако в нем обычно имеются еще и внутренние окна – кнопки, списки, полосы прокрутки и т. д.
Модальные диалоги, широко используемые в приложениях Windows в качестве средства управления работой программы, относятся к числу всплывающих окон, что определяется указанием при описании их стиля константы WS_POPUP. Модальные диалоговые окна могут быть только всплывающими. Вообще же вложенные окна, в частности немодальные диалоги, могут быть как всплывающими, так и дочерними; в последнем случае в описании их стиля присутствует константаWS_CHILD. Всплывающие и дочерние окна характеризуются следующими различиями:
если в главном окне приложения одновременно образованы и всплывающие, и дочерние окна, то всплывающие окна будут изображаться поверх дочерних, “всплывать” над ними, что и определило их название;
дочерние окна могут перемещаться только в пределах родительского окна, в то время как положение всплывающих окон не ограничено какими-либо границами; всплывающее окно можно вытащить за пределы главного окна приложения и поместить в любом месте экрана (для чего необходимо, чтобы у всплывающего окна, кроме рамки, была еще и полоса заголовка);
координаты дочерних окон задаются относительно границ рабочей области родительского окна; координаты всплывающих окон задаются относительно границ экрана;
всплывающее окно может содержать собственное меню; в дочерних окнах меню не бывает.
Указанные различия делают всплывающиеокна более удобными для организации активного диалога с пользователем, в процессе которого он работает с органами управления окном, вводит входные данные в соответствующие поля, выбирает требуемые режимы и т. д.Дочерниеокна часто используются как информационные, а также для придания отдельным областям главного окна специфических свойств. Выделив, например, в тексте справки некоторые ключевые слова в отдельные окна, можно с классом этих окон связать курсор характерной формы (обычную стрелку заменить на изображение ладони), задать в контексте устройства другой цвет символов и вдобавок определить функцию обработки сообщенийWM_LBUTTONDOWN, чтобы при щелчке мышью по выделенному слову вызывался поясняющий это слово текст.
Диалоговые окна относятся к окнам предопределенных классов, описанных в Windows. Это ограничивает их возможности, но упрощает взаимодействие с элементами управления, входящими в состав диалоговых окон, так как в Windows для этого предусмотрены стандартные средства.
В тех случаях, когда вложенное окно должно обеспечивать более широкие возможности, целесообразно создать в программе не диалоговое, а обычное порожденное окно и наделить его требуемыми характеристиками. При этом, если форма диалоговых окон описывается в файле ресурсов, а создание осуществляется функциями DialogBox()илиCreateDialog(), то порожденные (как дочерние, так и всплывающие) окна описываются непосредственно в программе, а создаются универсальными функциямиCreateWindow()илиCreateWindowEx(). Для определенности мы далее будем говорить о дочерних окнах, хотя фактически весь этот материал в равной степени относится и к окнам всплывающим.
Процедура создания и обслуживания дочернего, как и любого другого, окна состоит из трех этапов:
регистрации класса дочернего окна, в процессе которой за окном закрепляется его оконная функция;
создания окна функцией Windows CreateWindow()с указанием стиля окна и его местоположения;
обработки сообщений, поступающих в дочернее окно.
Классы всех дочерних окон удобно регистрировать в функции WinMain()вслед за регистрацией класса главного окна; создаются же дочерние окна обычно в функцииOnCreate(), вызываемой Windows в процессе создания главного окна.
Сообщения, поступающие в главное окно, обрабатываются, как известно, оконной функцией главного окна; для обработки сообщений, поступающих в дочерние окна, должна быть предусмотрена отдельная оконная функция. Если при этом дочерних окон несколько, и все они обладают существенно разными характеристиками, для них предусматривают отдельные классы и, соответственно, отдельные оконные функции. Если же дочерние окна более однородны, все они могут принадлежать одному классу, и тогда оконная функция для всех дочерних окон будет одна, а различать отдельные окна придется по их дескрипторам.
На рис. 4.2 приведен вид главного окна приложения, назначение которого – вывод на экран информации из базы данных пациента.
Рис. 4.2. Главное окно приложения с несколькими дочерними окнами
В главном окне образовано одно большое дочернее окно (с рамкой и полосой заголовка) для вывода данных в графической форме и пять “управляющих” окон меньшего размера с названиями отображаемых данных. Управляющие окна не имеют ни заголовка, ни рамки, и поэтому никак не выделяются на общем фоне главного окна. Их размеры соответствуют размерам выводимых в них строк текста. Все они принадлежат одному классу, для которого задана специфическая форма курсора – изображение кисти руки. В единую оконную функцию этих окон включена обработка сообщения WM_LBUTTONDOWN.При щелчке мышью по той или иной строке с названием данного в большое дочернее окно выводится соответствующая информация в виде графика.
Таким образом, в данном приложении зарегистрированы классы трех окон – главного окна, большого окна для вывода графика и пяти управляющих окон с названиями данных. Соответственно, в приложении имеются и три оконных функции. Легко сообразить, что оконная функция главного окна почти пуста – в ней может обрабатываться одно единственное сообщение WM_DESTROYо завершении приложения. Оконная функция окна графика содержит обработку сообщенийWM_PAINT, а окна с названиями данных, как уже отмечалось, должны обрабатывать сообщенияWM_LBUTTONDOWN.
Приведем теперь в качестве образца слегка сокращенный текст простой программы, в которой, кроме главного окна, предусмотрено еще одно дочернее окно для вывода графика (рис. 4.3).
Рис. 4.3. Главное окно приложения с одним дочерним окном
#include <windows.h>
#include <windowsx.h>
/*Глобальные переменные*/
HINSTANCE hI;
char szClassName[]="MainWindow";
char szDataClassName[]="DataWindow";
char szTitle[]="Дочернее окно в главном";
/*Главная функция приложения*/
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int){
hI=hInst;
MSG msg;
WNDCLASS wc;
ZeroMemory(&wc,sizeof(wc));
/*Регистрируем класс главного окна*/
wc.lpszClassName=szClassName;
wc.hInstance=hInst;
wc.lpfnWndProc=WndProc;
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.hbrBackground=GetStockBrush(WHITE_BRUSH);
RegisterClass(&wc);
/*Регистрируем класс дочернего окна*/
wc.lpszClassName=szDataClassName;//Другой класс
wc.lpfnWndProc=DataWndProc;//Другая оконная функция
wc.hbrBackground=GetStockBrush(LTGRAY_BRUSH);//Другой цвет
RegisterClass(&wc);//Регистрируем
/*Создаем главное окно обычным образом*/
HWND hwnd = CreateWindow(szClassName,szTitle,
WS_OVERLAPPEDWINDOW,10,10,228,280,HWND_DESKTOP,
NULL,hInst,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);//Делаем главное окно видимым
while(GetMessage(&msg,NULL,0,0))//Цикл обработки сообщений
DispatchMessage(&msg);
return 0;
}
/*Оконная процедура главного окна*/
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,
WPARAM wParam,LPARAM lParam){
switch(msg){
HANDLE_MSG(hwnd,WM_CREATE,OnCreate);
HANDLE_MSG(hwnd,WM_PAINT,OnPaint);
HANDLE_MSG(hwnd,WM_DESTROY,OnDestroy);
default:
return(DefWindowProc(hwnd,msg,wParam,lParam));
}
}
/*Функция обработки сообщения WM_CREATE главного окна*/
BOOL OnCreate(HWND hwnd,LPCREATESTRUCT){
CreateWindow(szDataClassName, NULL,//Создадим дочернее окно
WS_CHILD | WS_DLGFRAME | //Окно дочернее, с толстой рамкой,
WS_VISIBLE,//видимое (не нужна функция ShowWindow())
10,10,200,200,//Положение дочернего окна в главном
hwnd, NULL, hI, NULL);
return TRUE;
}
/*Функция обработки сообщения WM_PAINT главного окна*/
void OnPaint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
/*Выведем поясняющий текст*/
char str[]="График Декартова листа";
TextOut(hdc,20,220,str,strlen(str));
EndPaint(hwnd,&ps);
}
/*Оконная процедура дочернего окна */
LRESULT CALLBACK DataWndProc(HWND hwnd, UINT msg,
WPARAM wParam,LPARAM lParam){
switch(msg){
HANDLE_MSG(hwnd,WM_PAINT,DataOnPaint);
default:
return(DefWindowProc(hwnd,msg,wParam,lParam));
}
}
/*Функция обработки сообщения WM_PAINT дочернего окна*/
void DataOnPaint(HWND hwnd){
PAINTSTRUCT ps;
float x,y,t;
int a=-5;
HDC hdc=BeginPaint(hwnd,&ps);
/*Построим график функции*/
for(t=-50;t<50;t+=0.01){
x=a*t/(1+t*t*t)*30;
y=a*t*t/(1+t*t*t)*30;
Rectangle(hdc,x+100,y+100,x+102,y+102);
}
EndPaint(hwnd,&ps);
}
Как видно из текста программы, оконная функция дочернего окна и объявляется, и выглядит так же, как и для главного окна. В ней удобно использовать макрос HANDLE_MSG; единственным обрабатываемым здесь сообщением являетсяWM_PAINT(поскольку дочернее окно мы отдельно закрывать не намерены, в оконной функции отсутствует обработка сообщенияWM_DESTROY). Разумеется, при необходимости можно было обрабатывать и другие сообщения для этого окна.
Главное окно мы обычно сначала создаемфункциейCreateWindow(), а затемделаем видимымвызовом функцииShowWindow(). Дочернее окно можно сразу вывести на экран, указав в его стиле, среди прочих, константуWS_VISIBLE. В этом случае отпадает необходимость в вызове для него функцииShowWindow()(впрочем, таким же приемом можно было воспользоваться и при создании главного окна). КонстантаWS_DLGFRAMEделает дочернее окно выпуклым, что характерно для диалоговых окон. При использовании, например, константыWS_BORDERмы получили бы менее красивое “плоское” дочернее окно в тонкой черной рамке.