Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Metodich_ukaz_k_prakt_SPO_5_semestr.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
715.26 Кб
Скачать

4.3. Краткие теоретические сведения

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

10

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

3.5. Контрольные вопросы и задания

Какие средства ввода информации Вы знаете?

Дайте определение таким понятием как «каретка» и «курсор»?

Чем отличаются аппаратные (keystrokes) и символьные (characters) сообщения?

Какие сообщения от клавиатуры обрабатываются оконной процедурой?

Перечислите сообщения, поступающие от мыши.

Что такое «фокус ввода»?

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

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

Как обрабатываются нажатия кнопки в рабочей области окна, созданной функцией CreateWindow?

Каким образом можно получить информацию о наличии мыши в системе и ее характеристики?

4. ИСПОЛЬЗОВАНИЕ РЕСУРСОВ

4. 1. Цель работы

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

4.2. Указания по подготовке к выполнению работы

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

При подготовке к работе необходимо изучить методические указания данного раздела, а также разделы, указанные в [16, c.441-469], [17, c.25-27].

4.3. Краткие теоретические сведения

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

Для выполнения лабораторной работы необходимо изучить следующие виды ресурсов: иконки (icons), курсоры (cursors), битовые образы (bitmaps), символьные строки (character strings), меню (menus), быстрые клавиши (keyboard accelerators), окна диалога (dialog boxes), ресурсы пользователя (user defined resources), - а также набор функций API работы с ресурсами.

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

Особое внимание следует уделить следующим группам функций API:

Чтения объектов из ресурсов (LoadCursor, LoadIcon, LoadString, LoadResource, LoadBitmap, LoadMenu):

Создания объектов ;

Выбора объекта в контекст (SelectObject):

Управления объектами: например, для меню существует группа функций изменения меню в процессе выполнения программы (AppendMenu, DeleteMenu, InsertMenu, ModifyMenu, RemoveMenu, GetSubMenu и т.п.):

Управления диалоговыми окнами (DialogBox):

Компиляция ресурсов

При создании программы ресурсы определяются в файле описания ресурсов (resource script), который представляет собой ASCII-файл с расширением .RC. Файл описания ресурсов может содержать представление ресурсов в ASCII- кодах, а также может ссылаться и на другие файлы (ASCII или бинарные файлы), в которых содержатся остальные ресурсы. С помощью компилятора ресурсов (файл RC.EXE) файл описания ресурсов компилируется и становится бинарным файлом с расширением .RES. Задав в командной строке LINK файл с расширением .RES, вы можете заставить компоновщик включить скомпилированный файл описания ресурсов в файл с расширением .EXE программы вместе с обычными кодом и данными программы из файлов с расширением .OBJ и .LIB.

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

Значки и курсоры

Можно создавать значки и курсоры с помощью Microsoft Developer Studio или любой другой интегрированной среды разработки для Windows. Значки и курсоры хранятся в бинарном формате. Файлы значков имеют расширение .ICO, а файлы курсоров - .CUR. Ссылки на эти файлы имеются в файле описания ресурсов

RESOURC1.RC.

Редактор и изакже в списках программ меню Start. Значки, появляющиеся на рабочем столе, имеют стандартный размер. В программе Windows Explorer и меню Start пользователь может произвольно выбирать стандартный или маленький значок.

В программе можно получить горизонтальный (X) и вертикальный (Y) размеры значков и курсоров, используя функцию GetSystemMetrics с параметрами SM_CXICON и SM_CYICON (для стандартного значка), SM_CXSMICON и SM_CYSMICON (для маленького значка) и SM_CXCURSOR и SM_CYCURSOR для курсоров мыши. Для большинства дисплеев размеры стандартных курсоров и значков одинаковы.

Редактор изображений, включенный в состав программы Developer Studio, может создавать файл с расширением .ICO, содержащий один из трех различных образов значка:

• Стандартный: 16-цветный площадью 32 квадратных пикселя

• Монохромный: черно-белый площадью 32 квадратных пикселя

• Маленький: 16-цветный площадью 16 квадратных пикселей

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

В программе на С, для получения описателя значка используется функция LoadIcon. В функции LoadIcon имеется два параметра.

Первым является описатель экземпляра вашей программы, который в WinMain обычно называется hInstance. Этот описатель требуется для Windows, чтобы определить, в каком файле с расширением .EXE содержится ресурс значка.

Вторым параметром является имя значка из описания ресурсов, заданное в виде указателя на оканчивающуюся нулем строку. Возвращаемым значением функции LoadIcon является значение типа HICON, которое определяется в WINDOWS.H.

Имеется связь между именем значка в описании ресурсов и в инструкции, содержащей функцию LoadIcon вашей программы на С:

Описание ресурсов: myicon ICON iconfile.ico

Исходный текст программы: hIcon = LoadIcon (hInstance, "myicon");

Компилятор преобразует в файле описания ресурсов имя значка в символы верхнего регистра и вставляет это имя в таблицу ресурсов в заголовке файла программы с расширением .EXE. При первом вызове функции LoadIcon, Windows преобразует строку, заданную вторым параметром в символы верхнего регистра и отыскивает в таблице ресурсов файла с расширением .EXE совпадающее с этой строкой имя.

Вместо имени также можно использовать число (16-разрядное беззнаковое WORD). Это число называется идентификатором (ID) значка. Ниже показано как это делается:

Описание ресурсов: 125 ICON iconfile.ico

Исходный текст программы: hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (125));

MAKEINTRESOURCE (make an integer into a resourse string - преобразовать целое в строку ресурса) является макросом, определенным в заголовочных файлах Windows, который преобразует число в указатель, но со старшими 16 разрядами, установленными в нуль. Так Windows узнает, что второй параметр функции LoadIcon является числом, а не указателем на символьную строку.

Использование значков в вашей программе

Хотя для обозначения программ Windows использует значки несколькими способами, во множестве программ для Windows значок задается только при определении класса окна:

wndclass.hIcon = LoadIcon(hInstance, "MyIcon");

.

.

.

wndclass.hIconSm = LoadIcon(hInstance, "MySmIcon");

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

Использование альтернативных курсоров

Инструкции для задания курсора в файле описания ресурсов и для получения описателя курсора в вашей программе очень похожи на показанные ранее инструкции для значков:

Описание ресурсов: mycursor CURSOR cursfile.cur

Исходный текст программы: hCursor = LoadCursor (hInstance, "mycursor");

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

Можно использовать описатель курсора, полученный при вызове функции LoadCursor, при задании поля hCursor структуры класса окна:

wndclass.hCursor = LoadCursor(hInstance, "mycursor");

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

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

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

SetCursor(hCursor);

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

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

resourc1 CURSOR resourc1.cur

Когда в исходном тексте программы RESOURC1.С задается класс окна, переменная szAppName используется следующим образом в функции LoadCursor :

wndclass.hCursor = LoadCursor(hInstance, szAppName);

Битовые образы: картинки в пикселях

В Windows также включен тип ресурсов с именем BITMAP.

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

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

Символьные строки

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

Ресурсы-символьные строки предназначены, главным образом, для облегчения перевода вашей программы на другие языки. Если вместо непосредственного использования строк в исходном тексте программы используются ресурсы-символьные строки, то весь текст программы, окажется в одном файле – файле описания ресурсов. Если текст в файле описания ресурсов переводится, то все, что нужно сделать для иноязычной версии программы, это перекомпоновать программу и добавить переведенные ресурсы в файл с расширением .EXE. Этот способ намного безопасней, чем редактирование исходных кодов программы. (Конечно, можно определить все символьные строки в качестве макросов и хранить их в заголовочном файле. Такой способ также позволяет избежать изменения исходного кода программы при переводе на другие языки.)

Использование ресурсов-символьных строк

Ресурсы-символьные строки определяются в описании ресурсов с помощью ключевого слова STRINGTABLE:

STRINGTABLE

{

id1, "character string 1"

id2, "character string 2"

[определения остальных строк]

}

В описании ресурсов может содержаться только одна таблица строк. Максимальный размер каждой строки – 255 символов. В строке не может быть управляющих символов языка С, за исключением \t (табуляция). Однако, символьные строки могут содержать восьмеричные константы:

Табуляция (Tab) \011

Перевод строки (Linefeed) \012

Возврат каретки (Carriage return) \015

Эти управляющие символы распознаются функциями DrawText и MessageBox.

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

LoadString(hInstance, id, szBuffer, iMaxLength);

Параметр id соответствует идентификатору, который предшествует каждой строке в файле описания ресурсов;

szBuffer - это указатель на символьный массив, в который заносится символьная строка;

iMaxLength – это максимальное число передаваемых в szBuffer символов.

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

Использование ресурсов-строк в функции MessageBox

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

#define IDS_FILENOTFOUND 1

#define IDS_FILETOOBIG 2

#define IDS_FILEREADONLY 3

Файл описания ресурсов выглядит следующим образом:

#include "program.h"

[описание других ресурсов]

STRINGTABLE

{

IDS_FILENOTFOUND, "File %s not found."

IDS_FILETOOBIG, "File %s too large to edit."

IDS_FILEREADONLY, "File %s is read-only."

}

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

#include "program.h"

[другие строки программы]

OkMessage(HWND hwnd, int iErrorNumber, char *szFileName)

{

char szFormat[40];

char szBuffer[60];

LoadString(hInst, iErrorNumber, szFormat, 40);

sprintf(szBuffer, szFormat, szFileName);

return MessageBox(hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION);

}

Для вывода на экран окна сообщений с сообщением "File not found." программа вызывает функцию:

OkMessage(hwnd, IDS_FILENOTFOUND, szFileName);

Ресурсы, определяемые пользователем

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

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

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

Например, предположим, что у вас есть файл PROGHELP.TXT, в котором содержится текст "подсказок" для

вашей программы. Такой файл не должен быть в чистом виде ASCII-файлом: в нем также могут содержаться

бинарные данные, например, указатели, которые могли бы помочь при ссылках на различные части этого файла.

Создание ресурса собственного типа, который называется TEXT.

helptext TEXT proghelp.txt

Таким образом описывается ссылка на файл proghelp.txt в файле описания ресурсов:

Имена helptext (имя ресурса) и TEXT (тип ресурса) в этом выражении могут быть любыми. Слово TEXT написано прописными буквами просто, чтобы сделать его похожим на слова ICON, CURSOR и BITMAP.

Получить описатель этого ресурса:

Получить описатель этого ресурса можно в процессе инициализации программы (например, при обработке сообщения WM_CREATE):

hResource = LoadResource(hInstance, FindResource(hInstance, "TEXT", "helptext"));

Переменная hResource определяется как имеющая тип HGLOBAL. Несмотря на свое имя, функция LoadResource фактически не загружает сразу ресурс в оперативную память. Используемые вместе, так как это было показано, функции LoadResource и FindResource по существу эквивалентны функциям LoadIcon и LoadCursor. Фактически, функции LoadIcon и LoadCursor используют функции LoadResource и FindResource.

Вместо имен и типов ресурсов можно использовать числа. Числа могут быть преобразованы в дальние указатели при вызове функции FindResource с использованием MAKEINTRESOURCE. Числа, используемые в качестве типа ресурса, должны быть больше 255. (Числа от 1 до 9 при вызове функции FindResource используются в Windows для существующих типов ресурсов.)

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

pHelpText = LockResource(hResource);

Функция LockResource загружает ресурс в память (если он еще не был загружен), и возвращает указатель на него.

Овободить оперативную память

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

FreeResource(hResource);

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

В фрагменте программе на экран, в рабочую область окна программы, выводится текст поэмы Эдгара Алана По "Annabel Lee". Ресурс, определяемый пользователем - это файл POEPOEM.ASC, в котором находится текст поэмы.

В файле ресурсов будет такая запись:

AnnabelLee TEXT poepoem.asc

Текстовый файл заканчивается символом обратной косой черты (\).

Получить описатель этого ресурса

static char *pText;// для указателя буфера, куда будет загружен файл

hResource = LoadResource(hInst,

FindResource(hInst, szPoemRes, "TEXT"));

Загрузить текст файла в память

pText =(char *) LockResource(hResource);

Следующий фрагмент заменяет символ обратной косой черты (‘\’) в конце файла на 0. Это сделано для адаптации текста к функции DrawText, которая может быть применена для отображения этого текста на экране, при обработке сообщения WM_PAINT.

iNumLines = 0;

while(*pText != '\\' && *pText != '\0')

{

if(*pText == '\n')

iNumLines ++;

pText = AnsiNext(pText);

}

*pText = '\0';

Меню

Строка меню выводится на экране непосредственно под строкой заголовка. Эта строка иногда называется главным меню (main menu) или меню верхнего уровня (top-level menu) программы. Выбор элемента главного меню обычно приводит к вызову другого меню, появляющегося под главным, и которое обычно называют всплывающим меню (popup menu) или подменю (submenu).

Структура меню

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

Каждый пункт меню определяется тремя характеристиками.

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

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

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

Во многих приложениях Windows в описании ресурсов имеется только одно меню. Ссылка в программе на это меню имеет место в определении класса окна:

wndclass.lpszMenuName = "MyMenu";

Часто программисты используют строку имени программы в качестве имени меню, имени класса окна, имени значка программы. Хотя задание имени меню в классе окна является наиболее обычным способом ссылки на ресурс меню, существуют альтернативные варианты. В приложении для Windows ресурс меню можно загрузить в память с помощью функции LoadMenu, которая аналогична функциям LoadIcon и LoadCursor, описанных выше. Если в описании ресурса меню используете имя, то возвращаемым значением функции LoadMenu является описатель меню:

hMenu = LoadMenu(hInstance, "MyMenu");

При использовании числа функция LoadMenu принимает один издвух видов:

hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(45));

hMenu = LoadMenu(hInstance, "#45");

Затем этот описатель меню можно указать в качестве девятого параметра функции CreateWindow:

hwnd = CreateWindow("MyClass", "Window Caption",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL,

hMenu,

hInstance,

NULL);

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

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

SetMenu(hwnd, hMenu);

Такая форма позволяет динамически изменять меню окна.

Любое меню, связанное с окном, удаляется при удалении окна.

Меню и сообщения

Когда пользователь выбирает пункт меню, Windows посылает оконной процедуре несколько различных сообщений. Большинство из этих сообщений могут игнорироваться программой, и просто передаваться DefWindowProc. Одним из таких сообщений является сообщение WM_INITMENU, которое имеет следующие параметры wParam и lParam.

Значением параметра wParam является описатель главного меню, даже если пользователь выбирает пункт системного меню. В программах для Windows сообщение WM_INITMENU обычно игнорируется. Хотя это сообщение существует для того, чтобы дать впользователю возможность изменить меню перед тем, как будет выбран пункт меню.

Кроме того, программа получает сообщения WM_MENUSELECT. Если пользователь перемещает курсор мыши по пунктам меню, программа может получить множество сообщений WM_MENUSELECT. Это полезно при использовании строки состояния, содержащей полное описание опции меню. Данное сообщение имеет следующие параметры:

Младшее слово (LOWORD)

wParam

Старшее слово

(HIWORD) wParam

lParam

Выбранный пункт:

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

описатель всплывающего меню

Флаги выбора

Описатель меню,

содержащего

выбранный пункт

Сообщение WM_MENUSELECT -это сообщение для отслеживания перемещения по меню. Младшее слово параметра wParam говорит о том, какой пункт меню выбран (подсвечен) в данный момент. В старшем слове параметра wParam могут быть комбинации из следующих флагов выбора: MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP, MF_POPUP, MF_HELP, MF_SYSMENU и MF_MOUSESELECT. Сообщение WM_MENUSELECT может понадобиться для того, чтобы изменить что-нибудь в рабочей области окна на основе информации о перемещении подсветки по пунктам меню. В большинстве программ это сообщение передается в DefWindowProc.

Когда Windows готова вывести на экран всплывающее меню, она посылает оконной процедуре сообщение WM_INITMENUPOPUP со следующими параметрами:

wParam

Младшее слово

(LOWORD) lParam

Старшее слово

(HIWORD) lParam

Описатель всплывающего

меню

Индекс всплывающего

меню

Для системного меню 1, в противном случае 0

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

Самым важным сообщением меню является WM_COMMAND. Это сообщение показывает, что пользователь выбрал разрешенный пункт меню окна.

Младшее слово (LOWORD)

wParam

Старшее слово

(HIWORD) wParam

lParam

Меню:

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

0

0

Элемент

управления:

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

элемента управления

Код уведомления

Описатель

дочернего окна

Сообщение WM_SYSCOMMAND похоже на сообщение WM_COMMAND за исключением того, что сообщение WM_SYSCOMMAND сигнализирует, что пользователь выбрал разрешенный пункт системного меню:

Младшее слово (LOWORD)

wParam

Старшее слово

(HIWORD) wParam

lParam

Системное Меню:

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

0

0

(Если сообщение

WM_SYSCOMMAND

является результатом щелчка мыши, тогда старшее и младшее слово lParam являются, соответственно,

экранными координатами X и

Y курсора мыши.)

Идентификатор меню показывает, какой пункт системного меню выбран. Для предопределенных пунктов системного меню, четыре младших разряда должны быть замаскированы. Результирующая величина будет одной из следующих: SC_SIZE, SC_MOVE, SC_MINIMIZE, SC_MAXIMIZE, SC_NEXTWINDOW, SC_PREVWINDOW, SC_CLOSE, SC_VSCROLL, SC_HSCROLL, SC_ARRANGE, SC_RESTORE и SC_TASKLIST. Кроме того, младшее слово параметра wParam может быть SC_MOUSEMENU или SC_KEYMENU.

Если добавить к системному меню новые пункты, то младшее слово параметра wParam станет идентификатором, который задан при определении. Для избежания конфликта с предопределенными идентификаторами меню следует использовать значения меньше чем 0xF000. Важно, чтобы обычные сообщения WM_SYSCOMMAND передавались в DefWindowProc. Если этого не сделать, то обычные команды системного меню будут отключены.

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

Параметры сообщения WM_MENUCHAR являются следующими:

Младшее слово (LOWORD)

wParam

Старшее слово

(HIWORD) wParam

lParam

Код ASCII

Код выбора

Описатель меню

Код выбора равен:

• 0 — всплывающее меню не отображается.

• MF_POPUP — отображается всплывающее меню.

• MF_SYSMENU — отображается системное меню.

Как правило, в программах для Windows это сообщение передается DefWindowProc, которая обычно возвращает 0 в Windows, после чего Windows издает звуковой сигнал (гудок).

Диалоговые окна

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

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

Окно диалога обычно имеет вид всплывающего окна с разнообразными дочерними окнами элементов управления внутри. Размер и расположение этих дочерних окон задается в шаблоне окна диалога (dialog box template) в файле описания ресурсов программы. Microsoft Windows обеспечивает возможность создания всплывающих окон диалога и дочерних окон элементов управления в нем, и возможность обработки оконной процедурой сообщений окна диалога (включая все сообщения клавиатуры и мыши). Тот код внутри Windows, который дает возможность все это сделать, иногда называют менеджером окна диалога (dialog box manager).

Многие сообщения, которые обрабатываютообщения WM_MENUCHAR являются следующими:

Младшее слово (LOWORD)

wParam

Старшее слово

(HIWORD) wParam

lParam

Код ASCII

Код выбора

Описатель меню

Код выбора равен:

• 0 — всплывающее меню не отображается.

• MF_POPUP — отображается всплывающее меню.

• MF_SYSMENU — отображается системное меню.

Как правило, в программах для Windows это сообщение передается DefWindowProc, которая обычно возвращает 0 в Windows, после чего Windows издает звуковой сигнал (гудок).

Диалоговые окна

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

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

Окно диалога обычно имеет вид всплывающего окна с разнообразными дочерними окнами элементов управления внутри. Размер и расположение этих дочерних окон задается в шаблоне окна диалога (dialog box template) в файле описания ресурсов программы. Microsoft Windows обеспечивает возможность создания всплывающих окон диалога и дочерних окон элементов управления в нем, и возможность обработки оконной процедурой сообщений окна диалога (включая все сообщения клавиатуры и мыши). Тот код внутри Windows, который дает возможность все это сделать, иногда называют менеджером окна диалога (dialog box manager).

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

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

Создание окна диалога

Шаблон окна диалога

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

Rc include filename.dlg

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

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

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

Диалоговая процедура

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

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

BOOL CALLBACK MainDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

switch(iMsg)

{

case WM_INITDIALOG :

return TRUE;

case WM_COMMAND :

switch(LOWORD(wParam))

{

case IDOK :

case IDCANCEL :

EndDialog(hDlg, 0);

return TRUE;

}

break;

}

return FALSE;

}

Параметры этой функции те же, что и параметры обычной оконной процедуры; как и оконная процедура, процедура диалога должна быть определена как функция типа CALLBACK. Хотя в качестве описателя окна диалога использовался описатель hDlg, вместо него можно при желании использовать hwnd. Обратите внимание на отличия между этой функцией и оконной процедурой:

Оконная процедура возвращает значение типа LRESULT; а процедура диалогового окна -значение типа BOOL (определяемое в заголовочных файлах Windows как int).

Если оконная процедура не обрабатывает какое-то сообщение, она вызывает DefWindowProc; процедура диалога, если она не обрабатывает сообщение, возвращает FALSE (0), а если обрабатывает, то TRUE

(ненулевое значение).

Процедура диалога не обрабатывает сообщения WM_PAINT и WM_DESTROY. Процедура диалога неполучит сообщения WM_CREATE; вместо этого она выполняет инициализацию при обработке специального сообщения WM_INITDIALOG.

Сообщение WM_INITDIALOG является первым сообщением, которое получает процедура диалога. Это сообщение посылается только процедурам диалога. Если процедура диалога возвращает TRUE, то Windows помещает фокус ввода на первое дочернее окно элемента управления, которое имеет стиль WS_TABSTOP. (о котором будет рассказано при изучении программы ABOUT2). В приведенном окне диалога первым дочерним окном элемента управления, которое имеет стиль WS_TABSTOP, является кнопка. С другой стороны, при обработке совается для вывода на экран окна диалога, не возвращает управление в WndProc до тех пор, пока окно диалога не будет закрыто. Возвращаемым значением функции DialogBox является второй параметр функции EndDialog, которая вызывается в процедуре диалога. Затем WndProc может передать управление Windows.

Даже при выводе на экран окна диалога, WndProc может продолжать получать сообщения. Можно посылать в WndProc сообщения из процедуры диалога. Поскольку главным окном программы является родительское окно всплывающего окна диалога, то вызов функции SendMessage в MainDlgProc должен начинаться следующим образом:

SendMessage(GetParent(hDlg), .

Пример. Определить в ресурсном файле ресурс пользователя, соответствующий битовому изображению иконки. В приложении использовать этот ресурс для создания новой иконки и поместить созданную иконку в заголовок приложения. Меню программы содержит пункт "О программе", выводящий строку : “Учимся использовать ресурсы”.

// MainDlg.cpp

//

#include<Winuser.h>

#include<windows.h>

#include <afxwin.h>

#include "stdafx.h"

#include "resource.h"

#define MAX_LOADSTRING 100

// Глобальные переменные

HINSTANCE hInst;

TCHAR szTitle[MAX_LOADSTRING];

TCHAR szWindowClass[MAX_LOADSTRING];

// прототипы функций

ATOM MyRegisterClass(HINSTANCE hInstance);

BOOL InitInstance(HINSTANCE, int);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow)

{

//

MSG msg;

HACCEL hAccelTable;

/ *инициализация строки заголовка программы и класса окна

с помощью ресурса “символьные строки”

*/

LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

LoadString(hInstance, IDC_NEW, szWindowClass, MAX_LOADSTRING);

MyRegisterClass(hInstance);

if (!InitInstance (hInstance, nCmdShow))

{

return FALSE;

}

hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_NEW);

// Main message loop:

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

{

if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

return msg.wParam;

}

ATOM MyRegisterClass(HINSTANCE hInstance)

{

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = (WNDPROC)WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_NEW);

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

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

wcex.lpszMenuName = (LPCSTR)IDC_NEW;

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_ICON1);

return RegisterClassEx(&wcex);

}

//

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

HWND hWnd;

hInst = hInstance;

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)

{

return FALSE;

}

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

return TRUE;

}

//

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

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

switch (message)

{

// обработка команд меню

case WM_COMMAND:

wmId = LOWORD(wParam);

wmEvent = HIWORD(wParam);

// Parse the menu selections:

switch (wmId)

{

case IDM_MENU1:

DialogBox(hInst, (LPCTSTR)IDD_DIALOG2, hWnd, (DLGPROC)About);

break;

case IDM_MENU2:

DialogBox(hInst, (LPCTSTR)IDD_DIALOG1, hWnd, (DLGPROC)About);

break;

case IDM_MENU3:

DestroyWindow(hWnd);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

RECT rt;

GetClientRect(hWnd, &rt);

//изменяем значение ссылки на иконку, которая была создана при регистрации окна

SetClassLong(hWnd,GCL_HICON,(LONG)LoadIcon(hInst, (LPCTSTR)IDI_ICON2) );

// выводим иконку в клиентскую область ..

DrawIconEx( hdc, rt.left+150, rt.top+20, LoadIcon(hInst, (LPCTSTR)IDI_ICON2), 0, 0, 0, NULL, DI_NORMAL );

DrawText(hdc,"Учимся использовать различные ресурсы!",-1,&rt,DT_CENTER|DT_VCENTER|DT_SINGLELINE);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

// обработчик диалогового окна

LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{ // по данному сообщению выводится диалоговое окно

case WM_INITDIALOG:

return TRUE;

case WM_COMMAND: // по кнопке да - окно закрывается

if (LOWORD(wParam) == IDOK )

{

EndDialog(hDlg, LOWORD(wParam));

return TRUE;

}

break;

}

return FALSE;

}

4.4. Задание на лабораторную работу

Номер варианта

Задание

1

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

2

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

3

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

4

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

5

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

4.5. Контрольные вопросы и задания

Какие виды ресурсов вы знаете?

Каким образом создаются ресурсы?

Опишите процесс использования ресурсов.

Каким образом можно использовать альтернативные ресурсы?

Какие функции работы с иконками Вы знаете?

Дайте характеристику «меню». Какие виды меню Вы знаете?

Каким образом можно создавать меню?

Опишите процесс использования битовых образов в меню.

Каким образом создаются окна диалога?

Объясните отличия модального окна.

5. ТЕМА: «МНОГОЗАДАЧНОСТЬ В ОС WINDOWS»

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

5.2. Методические указания по подготовке к занятию

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

При подготовке к работе необходимо изучить конспект лекций (Лекция 5) по указанной теме, методические указания, а также разделы, указанные в [16, c.763-809], [17, c.59-87].

5. 3. Краткие теоретические сведения.

Процессы и потоки.

Одним из основных понятий, которые вводятся при рассмотрении механизма организации мультизадачного режима исполнения программ в 32-х разрядных операционных системах MS Windows, является поток (thread). Поток это последовательность инструкций микропроцессора. Каждая работающая программа содержит как минимум один поток. Одновременно в системе может существовать несколько потоков, которые исполняются в отведённые им интервалы времени. Система выделяет эти интервалы времени каждому потоку периодически. Иначе говоря, система распределяет процессорное время между потоками. Продолжительность этих интервалов относительно небольшая (несколько десятков миллисекунд), что создаёт иллюзию параллельного исполнения потоков. Операция смены текущего исполняемого потока называется переключением потоков. Операционная система при поддержке аппаратуры сохраняет и восстанавливает параметры потоков при их переключении. Параметры каждого потока хранятся в специальной системной структуре, которая называется контекстом потока.

Контекст потока в частности содержит значения регистров МП и стек потока на момент переключения потока. Для каждого приложения Win32 ОС создаёт отдельный процесс. Контекст процесса включает виртуальное адресное пространство приложения и ряд других системных ресурсов, которые используются потоками. Один или несколько потоков могут быть организованы внутри одного процесса. Такие потоки совместно используют память и другие ресурсы выделенные процессу.

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

Каждый процесс должен содержать, по крайней мере, один поток который называется первым потоком (primary thread). Этот поток создаётся при создании процесса. Другие потоки процесса могут быть созданы впоследствии из любого существующего потока процесса с помощью функций API.

Создание процесса

Процесс создается при вызове приложением функции CreateProcess:

BOOL CreateProcess

(LPCTSTR IpszAppHcationName,

LPCTSTR IpszCommandLine,

LPSECURITY_ATTRIBUTES IpsaProcRSS,

LPSECURITY_ATTRIBUTES IpsaThread,

BOOL fInheritHandles,

DWORD fdwCreate,

LPVOID IpvEnviror'ment,

LPTSTR IpszCurDir,

LPSTARTUPINFO IpsiStartInfo,

LPPROCESS_INFORMATION IpplProcInfo);

Когда поток в приложении вызывает CreateProcess, система создает объект ядра «процесс» с начальным значением счетчика числа его пользователей, равным 1. Этот объект — не сам процесс, а компактная структура данных, через которую операционная система управляет процессом. (Объект ядра «процесс» следует рассматривать как структуру данных со статистической информацией о процессе.)

Затем система создает для нового процесса виртуальное адресное пространство размером 4 Гб и загружает в него код и данные как для исполняемого файла, так и для любых DLL (если таковые требуются).

Далее система формирует объект ядра «поток» (со счетчиком, равным 1) для первичного потока нового процесса. Как и в первом случае, объект ядра «поток» — это компактная структура данных, через которую система управляет потоком. Первичный поток начнет с исполнения стартового кода из стандартной библиотеки С, который — как всегда — вызовет функцию WinMain в Вашей программе (или main — если приложение относится к консольному типу). Если системе удастся создать новый процесс и его первичный поток, CreateProcess вернет TRUE.

Рассмотрим параметры функции CreateProcess.

IpszAppHcationName - определяет имя исполняемого файла (и путь к нему) обязательно с раширением EXE. Если путь к файлу не указан и в текущем каталоге файла нет, функция не станет искать его в других каталогах. Если задать NULL, то имя исполняемого файла ищется в параметре lpszCommandLine.

lpszCommandLine - определяет командную строку, передаваемую процессу. Если в имени файла не указано расширение, она считает его ЕХЕ. Если путь к файлу не задан, функция приступает к поиску заданного файла и делает это в следующем порядке:

1. Каталог, содержащий ЕХЕ-файл вызывающего процесса.

2. Текущий каталог вызывающего процесса.

3. Системный каталог Windows.

4. Основной каталог Windows.

5. Каталоги, перечисленные в переменной окружения PATH.

Найдя нужный исполняемый файл, она создает новый процесс и проецирует код и данные исполняемого файла на адресное пространство этого процесса. Затем обращается к процедурам стартового кода из стандартной библиотеки С. Тот в свою очередь, как уже говорилось, анализирует командную строку процесса и передает WinMain адрес первого (за именем исполняемого файла) аргумента как IpszCmdLine.

Но даже при указанном в IpszApplicationName имени файла CreateProcess все равно передает новому процессу содержимое параметра IpszCommandLine как командную строку.

Параметры IpsaProcess, IpsaThread и bInheritHandles

Чтобы создать новый процесс, система должна сначала создать объекты ядра «процесс» и «поток» (для первичного потока процесса). Поскольку это объекты ядра, родительский процесс получает возможность связать с ними атрибуты защиты. Параметры IpsaProccss и IpsaThread позволяют определить нужные атрибуты защиты для объектов «процесс» и «поток» соответственно. В эти параметры можно занести NULL, и система закрепит за данными объектами дескрипторы защиты по умолчанию. В качестве альтернативы можно объявить и инициализировать две структуры SECURITY_ATTRIBUTES; тем самым Вы создадите и присвоите объектам «процесс» и «поток» их собственные атрибуты защиты.

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

5-й параметр (bInheritHanles) – определяет, предоставляются ли новому процессу доступ к своим обїектам ядра (TRUE, FALSE).

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

7-й параметр (lpvEnvironment) – указывает на блок памяти, хранящий строки переменных окружения для использования новым процессом. Если – NULL, то наследуются переменные родительского процесса.

8-й параметр (lps2CurDir) – устанавливает текущие диск и каталог для дочерного процесса. Если NULL, то рабочий каталог совпадает с рабочим каталогом родительного процесса.

9-й параметр (lpsiStartInfo) – указывает на структуру STARTUPINFO, все элементы которой необходимо инициализировать (информация об имени рабочего стола, координатах размещения окна приложения, его размеры, о консольных окнах, о необходимости использовать элементы данной структуры).

10-й параметр (lppiProcInfo) – указывает на структуру PROCESS_INFORMATION, которую необходимо создать. Ее элементы инициализируются самой функцией CreateProcess(описатели и идентификаторы процесса, потока).

Завершение процесса

Его можно выполнить тремя способами.

1-й способ: один из потоков процесса вызывает функцию

VOID ExitProcess (UNIT fuExitCode).

Функция завершает процесс, а параметр заносит код завершения процесса. При этом прекращается и выполнение всех потоков процесса.

Эта функция вызывается и автоматически, когда WinMain возвращает управление стартовому коду из стандартной библиотеки С.

2-й способ: вызов любым процессом (другим), потоком функции

BOOL TerminateProcess (HANDLE hProcess, UINT fuExitCode);

1-й параметр – описатель завершаемого процесса;

2-й параметр – код завершения процесса.

При этом система не уведомляет о завершении все DLL-модули, связанные с процессом, поэтому возможно некорректное завершение процесса.

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

3-й способ: если все потоки процесса вызвали ExitThread, или их закрыли вызовом TerminateThread, то система немедленно закрывает процесс. При этом код завершения процесса приравнивается коду завершения последнего потока.

Дочерние процессы

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

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

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

DWORD WaitForSinglObject (HANDLE hObject, DWORD dwTimeOut).

Она задерживает выполнение кода, пока объект определяемый hObject, не перейдет в свободное состояние (завершится). Код завершения дочернего процесса определяется через функцию GetExitCodeProcess. Как только необходимость в дочерних процесах отпадает, необходимо закрывать описатель процесса:

CloseHandle (pi.hProcess).

При этом значение счетчика пользователей процесса уменьшается на 1.

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

Для этого необходимо закрыть свои описатели, связаные с новым прцессом и его первичным потоком:

CloseHandle (pi.hThread);

CloseHandle (pi.hProcess).

Потоки.

Поток определяет последовательность исполнения кода в процессе. При инициализации процесса система всегда создаёт первичный поток (WinMain).

Создание дополнительных потоков процессами позволяет добиться минимального простоя процессора и работать эффективнее.

Многопоточность (multithreading) — это разделение программы на отдельные потоки выполнения (threads), которые, как кажется, выполняются параллельно.

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

Примеры создания дополнительных потоков:

- обработка заданий на печать;

- отмена длительных операций;

- моделирование событий реальной жизни;

- контроль синтаксиса или грамматики;

- повторный пересчёт каких-либо величин;

- многооконные приложения.

Функция CreateThread.

Создаёт дополнительные потоки.

HANDLE CreateThread (LPSECURITY_ATTRIBUTES lpsa (1);

DWORD cbStack (2);

LPTHREAD_START_ROUTINE lpStartAddr (3);

LPVOID lpvThreadParm (4);

DWORD fdwCreate (5);

LPDWORD lpIDThread (6));

1) Функция создаёт объект ядра ‘потока’ и возвращает описатель этого объекта (возращаемое значение):

HANDLE hThread;

2) Инициализирует код завершения потока идентификатором STILL_ACTIVE и присваивает счетчику простоя потока 1.

3) Создает для нового потока структуру CONTEXT.

4) Формирует стек потока.

5) Третий и четвертый параметры помещают в самый верх стека для передачи их в качестве параметров StartOfThread.

6) Инициализирует регистры – указатель стека и указатель команд в структуре CONTEXT.

Параметры:

1-ый (lpsa) – указатель на структуру SECURITY_ATTRIBUTES, определяющую атрибуты защиты. Если NULL – атрибуты защиты присваиваются по умолчанию. В противном случае данная структура д.б. инициализирована и элемент в InheritHandle д.б. равен TRUE.

2-ой (cbStack) – определяет размер адресного пространства, которое может использовать поток под стек. По умолчанию – 1Mb. Функция CreateProcess заносит в этот параметр значение, хранящееся в исполняемом файле и определяемое ключом компоновщика /STACK.

/STACK: [reserve][commit]

reserve – объём резервируемого адресного пространства;

commit – объём физической памяти, передаваемый потоку изначально (по умолчанию – 1 страница).

Если cbStack=0 тогда будет использовано значение commit.

3-ий параметр (lpStartAddr) – определяет адрес функции потока, с которой дальше будет начинать работу создаваемый поток. Можно создать несколько потоков с одним и тем же значением адреса стартовой функции.

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

5-ый параметр (fdwCreate) – определяет дополнительные флаги, управляющие созданием потока.

СREATE_SUSPENDED – после подготовки к исполнению поток придерживается до последующих указаний.

0 – исполняется немедленно.

6-ой параметр (lplDThread) – адрес переменной типа DWORD, в которую функция вернёт идентификатор нового потока.

Завершение потока

Первый способ – самоуничтожение с помощью функции:

VOID ExitThread (UINT fuExitCode);

Параметр – код завершения потока.

Второй способ – один из потоков данного или стороннего вызывает функцию

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

1-ый параметр – идентификатор завершаемого потока;

2-ой параметр – код завершения.

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

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

Третий способ – завершение процесса, содержащего данный поток.

Четвёртый способ – завершение выходом из функции потока.

При завершении потока счётчик числа пользователей объекта ядра ’поток’ уменьшается на 1. Если данный поток – последний активный поток в процессе, завершается и сам процесс.

Объект ядра ‘поток’ не освобождается, пока не будут закрыты все внешние ссылки на этот поток.

Другие, активные потоки, могут при этом проверить, завершён ли поток и, если да, то с каким кодом завершения с помощью функции

BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpdwExitCode);

1-ый параметр – описатель интересуемого потока.

2-ой параметр – код завершения потока.

Получение потоком описателя своего процесса:

HANDLE GetCurrentProcess(VOID);

Получение потоком уникального идентификатора текущего процесса:

DWORD GetCurrentProcessId(VOID);

Получение потоком своего описателя:

HANDLE GetCurrentThread(VOID);

Получение потоком своего идентификатора:

DWORD GetCurrentThreadId(VOID);

Поддержка многопоточности в С.

Microsoft вместе с Visual C++ поставляет в стандартных библиотеках (LIBCMT.LIB, LIBCMID.LIB, MSVCRT.LIB, MVSCRTD.LIB)(C чип-time-libraries).Часть этих библиотек служит для поддержки многопоточных приложений.

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

unsigned long _beginthreadex(void*security,

unsigned stack_size,

unsigned (*start_addres)(void*),

void * arglist,

unsigned initflag,

unsigned * threaddr);

Эта функция имеет тот же список параметров, что и CreateThread, несмотря на разные их имена и типы.

Данная функция выполняет следующие действия:

1) создаёт недокументированную внутреннюю структуру данных, в которой размещается вся информация, специфичная для данного потока;

2) обращается к функции CreateThread и создаёт новый поток;

3) возвращает описатель только что созданного потока или 0 – в случае ошибки.

Новый поток начнётся с функции _threadstart:

- связывает адрес блока данных потока с самим потоком;

- вызывает функцию потока;

- после возврата управления функцией потока вызывает функцию _endthreadex, передавая ей значение, возвращённое функцией потока.

Завершение потока, созданного таким образом выполняет функция:

void _endthreadex(unsigned retval);

Параметр – код завершения потока.

Данная функция выполняет следующие действия:

1) удаляет блок данных связанных с потоком;

2) завершает поток вызовом функции ExitThread;

Функция

unsigned long beginthread(_cdect*start_address)(void*),

unsigned stack_size, void * arglist);

представляет собой аналог функции _beginthreadex, но возможности её ограничены:

- нельзя создать поток с атрибутами защиты;

- нельзя создать поток и тут же задержать его.

Функция:

void _endthread(void);

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

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

Функция _beginthread() после возврата ей управления функцией потока вызывает функцию _endthread(), а функция _beginthreadex() – функцию endthreadex.

Распределение процессорного времени между потоками

Система выделяет процессорное время всем активным потокам, исходя из их уровней приоритета (от 0 до 31 (высший)).

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

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

Уровни приоритета в Win32

Они присваиваются самой системой в 2 этапа:

1) – присваивается класс приоритета самому процессу;

2) – относительные уровни приоритета присваиваются потокам процесса.

Классы приоритета процессов и уровни приоритетов потоков:

1) простаивающий – IDLE_PRIORITY_CLASS (4-й уровень приоритета);

2) нормальный – NORMAL_PRIORITY_CLASS (8-й уровень);

3) высокий – HIGH_PRIORITY_CLASS (13-й уровень);

4) реального времени – REALTIME_PRIORITY_CLASS (24-й уровень).

Изменение класса приоритета самим процессом возможно с помощью функции:

BOOL SetPriorityClass (HANDLE hProcess, DWORD fdwPriority);

1-й параметр – описатель процесса, для которого изменяется класс приоритета.

2-й параметр – указывает класс приоритета.

Таким образом можно изменить класс приоритета любметра, т.е. нельзя передать код завершения потока.

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

Функция _beginthread() после возврата ей управления функцией потока вызывает функцию _endthread(), а функция _beginthreadex() – функцию endthreadex.

Распределение процессорного времени между потоками

Система выделяет процессорное время всем активным потокам, исходя из их уровней приоритета (от 0 до 31 (высший)).

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

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

Уровни приоритета в Win32

Они присваиваются самой системой в 2 этапа:

1) – присваивается класс приоритета самому процессу;

2) – относительные уровни приоритета присваиваются потокам процесса.

Классы приоритета процессов и уровни приоритетов потоков:

1) простаивающий – IDLE_PRIORITY_CLASS (4-й уровень приоритета);

2) нормальный – NORMAL_PRIORITY_CLASS (8-й уровень);

3) высокий – HIGH_PRIORITY_CLASS (13-й уровень);

4) реального времени – REALTIME_PRIORITY_CLASS (24-й уровень).

Изменение класса приоритета самим процессом возможно с помощью функции:

BOOL SetPriorityClass (HANDLE hProcess, DWORD fdwPriority);

1-й параметр – описатель процесса, для которого изменяется класс приоритета.

2-й параметр – указывает класс приоритета.

Таким образом можно изменить класс приоритета любого процесса, описатель которого известен.

Узнать класс приоритета процесса можно с помощью функции:

DWORD GetPriorityClass (HANDLE hProcess);

Она возвращает один из перечисленных выше флагов.

Установка относительного приоритета потока

Уровень приоритета потока сразу после создания соответствует классу приоритета процесса. Но его можно как повысить, так и понизить. Тем не менее, приоритет потока всегда относителен к классу приоритета его процесса.

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

BOOL SetThreadPriority (HANDLE hThread, int nPriority);

1-й параметр – описатель потока, приоритет которого изменяют;

2-й параметр – задает приоритет потока и принимает одно из следующих значений:

THREAD_PRIORITY_LOWEST – на 2 единицы ниже класса приоритета процесса;

THREAD_PRIORITY_BELOW_NORMAL – на 1 единицу ниже класса приоритета процесса;

THREAD_PRIORITY_NORMAL – соответствует классу приоритета процесса;

THREAD_PRIORITY_ABOVE_NORMAL – на 1 единицу выше класса приоритета процесса;

THREAD_PRIORITY_HIGHEST – на 2 единицы выше класса приоритета процесса.

THREAD_PRIORITY_IDLE – равен 1, если класс приоритета процесса idle, normal или high; для класса приоритета realtime уровень приоритета потока равен 16.

THREAD_PRIORITY_TIME_CRITICAL – при классах приоритета процесса idle, normal или high уровень приоритета потока равен 15; если класс приоритета процесса – realtime, уровень приоритета потока равен 31.

Узнать относительный приоритет потока позволяет функция

INT GetThreadPriority (HANDLE hThread);

Она возвращает один из перечисленных выше флагов, а при ошибке – флаг THREAD_PRIORITY_ERROR_RETURN.

Изменение класса приоритета процесса не сказывается на относительных приоритетах его потоков.

Ни одна Win32-функция не возвращает уровень приоритета потока. Это связано с возможностью изменения алгоритма распределения процессорного времени фирмой Microsoft.

Уровень приоритета, получаемый комбинацией класса приоритета процесса и относительного приоритета потока называют базовым уровнем приоритета потока. Система может автоматически изменять уровень приоритета потока в ответ на некоторые события, связанные с вводом/выводом (например, оконные сообщения, чтение диска). Новый уровень приоритета называют динамическим приоритетом потока. Он никогда не опускается ниже базового уровня приоритета и не поднимается выше 15-го уровня. Уровни приоритетов потоков от 16 до 31 системой никогда не меняются.

Включить или отключить автоматическое изменение приоритетов всех потоков в указанном процессе позволяет функция (в Windows NT 4)

BOOL SetProcessPriorityBoost (HANDLE hProcess, BOOL DisablePriorityBoost);

2-й параметр: TRUE – включает автоматическое изменение приоритета, FALSE – отключает.

Определить, разрешено или нет изменение приоритетов всех потоков процесса позволяет функция:

BOOL GetProcessPriorityBoost (HANDLE hProcess, PBOOL pDisablePriorityBoost);

Те же действия применительно к потоку выполняют функции

SetThreadPriorityBoost

GetThreadPriorityBoost,

имеющие те же параметры и возвращаемое значение.

Задержка и возобновление потоков

Выполнение потока можно приостановить не только при его создании (флаг CREATE_SUSPENDED), но и вызовом функции:

DWORD SuspendThread (HANDLE hThread);

Задерживать выполнение потока можно несколько раз (max=127 раз). Это можно сделать как из этого же потока, так и из другого.

Чтобы такой поток начал работать, необходимо из другого потока вызвать функцию

DWORD ResumeThread (HANDLE hThread);

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

5. 4. Задания

Номер варианта

Задание

1

Разработать программу, создающую процесс для запуска Exe - файла. Запуск должен осуществляться по нажатию кнопки.

2

Разработать программу, создающую процесс для консольного приложения для запуска Tasm. Exe . Дождаться результатов дочернего процесса, при успешном завершении создать процесс для запуска консольного приложения tlink.exe. Запуск должен осуществляться по нажатию клавиши Enter.

3

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

Критические секции

Критическая секция – это участок кода, во время выполнения которого поток не может быть прерван. Их можно использовать для обеспечения монопольного доступа к набору глобальных переменных.

Создание критической секциии

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

Перед синхронизацией потоков необходимо инициализировать критическую секцию вызовом функции (в первичном потоке, например):

VOID InitializeCriticalSection (LPCRITICAL_SECTION lpCriticalSection);

Параметр – адрес структуры CRITICAL_SECTION.

Обращение к данным из критической секции необходимо вызвать функцию

Перед обращением к данным из критической секции необходимо вызвать функцию

VOID EnterCriticalSection (LPCRITICAL_SECTION lpCriticalSection);

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

Освободить критическую секцию

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

LeaveCriticalSection (&g_CriticalSection);

Параметр – указатель на структуру типа CRITICAL_SECTION.

Синхронизация потоков с объектами ядра

Для синхронизации потоков можно использовать также объекты ядра:

процессы;

потоки;

файлы;

консольный ввод;

уведомления об изменении файлов;

объекты – мьютексы;

семафоры;

события;

ожидаемые таймеры.

Каждый объект может находиться в одном из 2-х состояний: свободном или занятом. Потоки могут остановиться и ждать освобождения какого-либо объекта.

Часть этих объектов (уведомления об изменении файлов, мьютексы, семафоры, события, ожидаемые таймеры) предназначены только для синхронизации потоков. Ряд Win32-функций позволяет создавать и открывать эти объекты, синхронизировать с ними потоки и закрывать их.

Поток вводит себя в состояние ожидания освобождения какого-либо объекта ядра с помощью функции:

DWORD WaitForSingleObject (HANDLE hObject, DWORD dwTimeout);

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

Эта функция может возвращать одно из 4-х значений:

WAIT_OBJECT_O – объект перешел в свободное состояние;

WAIT_TIMEOUT - объект не перешел в свободное состояние за указанный интервал времени;

WAIT_ABANDONED – объект-мьютекс освободился из-за отказа от него;

WAIT_FAILED – произошла ошибка.

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

DWORD WaitForMultipleObjects (DWORD cObjects, LPHANDLE lpHandles, BOOL bWaitAll, DWORD dwTimeout);

1-ый параметр указывает количество объектов;

2-ой параметр – указатель на массив описателей, идентифицирующих эти объекты;

3-ий параметр – указывает: необходимо ждать освобождения всех объектов из списка или только одного (FALSE).

Возвращает данная функция те же коды, что и предыдущая функция.

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

Применение мьютексов

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

Создание мьютексов

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

HANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpsa, BOOL FInitlOwner, LPTSTR lpszMutexName);

1-ый параметр – указывает на структуру типа SECURITY_ATTRIBUTES.

2-ой параметр – определяет, должен ли поток, создающий мьютекс, быть его первоначальным владельцем (TRUE) FALSE – означает, что мьютекс свободен и может быть занят любым ожидающим его потоком.

3-ий параметр – это либо NULL, либо адрес строки, идентифицирующий мьютекс, т. е. имени мьютекса.

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

HANDLE OpenMutex (DWORD fdwAccess, BOOL fInherit, LPTSTR lpsxName);

1-ый параметр – либо SYNCHRONIZE, либо MUTEX_ALL_ACCESS;

2-ой параметр – определяет, наследует ли дочерний процесс данный описатель данного объекта-мьютекса;

3-ий параметр – имя объекта-мьютекса в виде строки с нулевым символом в конце.

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

Поток, использующий мьютекс должен вести себя в состоянии ожидания мьютекса с помощью функции WaitForSingleObject.

После выполнения необходимых действий с данными, защищаемыми с помощью мьютекса, необходимо его освободить с помощью функции:

ReleaseMutex (g_hMutex).

Применение семафоров

Семафоры используют для учета ресурсов, которые разделяются потоками.

Семафору соответствуют счетчик ресурсов. Если он = 0, то данный ресурс занят, если > 0, то свободен и может быть выделен потоку.

Создание семафора

Семафор создают для определенного ресурса с помощью функции

HANDLE CreateSemaphore (LPSECURITY_ATTRIBUTE lpsa, LONG cSemInitial, LONG cSemMax, LPTSTR lpszSemName);

1-ый параметр – указывает на структуру типа LPSECURITY_ATTRIBUTE (атрибуты защиты объекта);

2-ой параметр – начальное состояние счетчика при инициализации ОС;

3-ий параметр – максимальное значение счетчика;

4-ый параметр – имя семафора в виде строки, возможен NULL.

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

Получение описателя семафора из других процессов:

HANDLE OpenSemaphore (DWORD fdwAccess, BOOL fInherit, LPTSTR lpzName);

эта функция идентична OpenMutex.

Перевод семафора в свободное состояние:

BOOL ReleaseSemaphore (HANDLE hSemaphore, LONG cRelease, LPLONG lplPrevious);

эта функция похожа на ReleaseMutex, но отличается тем, что ее можно вызвать когда угодно и счетчик ресурсов может увеличиваться за один вызов более, чем на 1 (2-ой параметр).

3-ий параметр – значение счетчика ресурсов, предшествующее увеличению, если не интересует, указывают NULL.

Применение событий

События обычно уведомляют об окончании какой-либо операции. Объекты ”событие” бывают 2-х типов:

- со сбросом вручную;

- с автосбросом.

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

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

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

Создание события

Событие создается функцией:

HANDLE CreateEvent (LPSECURITY_ATTRIBUTES lpsa, BOOL fManualReset, BOOL fInitialState, LPTSTR lpszEventName);

2-ой параметр – указывает тип события со сбросом вручную (TRUE) или с автосбросом (FALSE);

3-ий параметр – определяет начальное состояние события: свободное (TRUE) или занятое;

4-ый параметр – указывает имя события.

Потоки из других процессов могут получить доступ к событию с помощью функции CreateEvent и OpenEvent.

Закрытие события осуществляется функцией CloseHandle.

Перевод потоком объекта ”событие” в свободное состояние выполняется вызовом функции:

BOOL SetEvent (HANDLE hEvent);

В случае событий с ручным сбросом поток может перевести (сбросить) событие в занятое состояние с помощью функции

BOOL ResetEvent (HANDLE hEvent).

Применение ожидаемых таймеров

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

Создание ожидаемого таймера выполняется функцией

HANDLE CreatWaitableTimer (LPSECURITY_ATTRIBUTES lpsa, BOOL bManualReset, LPCTSTR lpszTimerName);

2-ой параметр – задает пробуждение либо всех потоков, ожидающих этот таймер (TRUE), либо одного из потоков;

3-ий параметр – задает имя данного объекта.

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

BOOL SetWaittableTimer (6 параметров);

2-ой параметр – задает время первого срабатывания;

3-ий параметр – задает частоту дальнейших срабатываний таймера.

Освобождается ожидаемый таймер функцией CloseHandle.

Приостановка выполнения потоков

VOID Sleep (DWORD cMilliseconds);

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

Приостановить поток пока в заданном процессе не опустеет очередь ввода-вывода потока, создавшего первое окно можно с помощью функций

DWORD MsgWaitForInputIdle (HANDLE hProcess, DWORD dwTimeOut);

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

DWORD MsgWaitForMultiple (5 парметров);

Она позволяет задавать с помощью маски определенные типы сообщений, которые необходимо обработать (5-ый параметр).

6. 4. Варианты заданий

Номер варианта

Задание

1

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

Программа завершается по клавише ESC.

2

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

3

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

4

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

Каждый из потоков запускается по таймеру. Размеры фигуры и координаты – генерируются случайным образом при каждом запуске.. Программа завершается по клавише TAB. Синхронизацию выполнить с использованием объекта мьютекс

5

Разработать приложение с двумя потоками. Один поток рисует в окне приложения окружность. Второй перемещает ее по диагонали. Синхронизацию выполнить с использованием объекта “Событие”. Программа завершается по клавише ESC.

Контрольные вопросы

Каким образом осуществляется синхронизация потоков?

Назовите объекты синхронизации

Что такое "критический раздел"?

Что такое "событие"?

Охарактеризуйте событие со сбросом вручную и с автосбросом.

Объясните назначение локальной и глобальной памяти потоков

Каким образом поток вводит себя в состояние ожидания

7. РАЗРАБОТКА ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫХ БИБЛИОТЕК

7.1. Цель работы

Ознакомление с одним из наиболее важным структурным элементом Windows - библиотеками динамической компоновки (Dynamic Link Libraries, DLL), получение практических навыков по созданию динамических библиотек.

7.2. Указания по подготовке к выполнению лабораторной работы

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

При подготовке к работе необходимо изучить конспект лекций по указанной теме, методические указания, а также разделы, указанные в [16, c.986-1013].

7.3. Обзор темы работы

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

Термин динамическое связывание (dynamic linking) относится к процессам, которые Windows использует для того, чтобы связать вызов функции в одном из модулей с реальной функцией из модуля библиотек. Статическое связывание (static linking) имеет место в процессе создания программы, когда для создания исполняемого (.exe) файла связываются воедино все объектные (.obj) модули, файлы библиотек (.lib) и, как правило, скомпилированные файлы описания ресурсов (.res). В отличие от этого, динамическое связывание имеет место во время выполнения программы.

Файлы KERNEL32.DLL, USER32.DLL, GDI32.DLL, файлы драйверов - все это динамически подключаемые библиотеки. Эти библиотеки можно использовать во всех программах Windows.

Динамически подключаемые библиотеки могут содержать только ресурсы либо данные и не содержать программ. Хотя модуль динамически подключаемой библиотеки может иметь любое расширение (например, .exe, .fon), стандартным расширением, принятым в Windows, является .dll. Только те динамически подключаемые библиотеки, которые имеют расширение .dll, Windows загрузит автоматически. Если файл имеет другое расширение, то программа должна загрузить модуль библиотеки явно. Для этого используется функция LoadLibrary, LoadLibraryEx.

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

7.4. Задание на лабораторную работу

Вариант 1

Создать динамически подключаемую библиотеку, содержащую две функции, вычисляющие числа Фибоначчи и n-факториал. Использовать созданную DLL в другой программе.

Вариант 2

Создать динамически подключаемую библиотеку, содержащую графические процедуры: рисования вписанных в прямоугольную область равностороннего пятиугольника и звезды. Прямоугольник задается объектом RECT. Использовать созданную DLL в другой программе.

Вариант 3

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

Вариант 4

Создать динамически подключаемую библиотеку функций поворота изображения на 90, 180, 270 градусов. Использовать созданную DLL в другой программе.

Вариант 5

Создать динамически подключаемую библиотеку математических функций: ХУ, Хn + Xn-1 + .... + X1.Использовать созданную DLL в другой программе.

Вариант 6

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

Вариант 7

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

Вариант 8

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

Вариант 9

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

7. 5. Контрольные вопросы и задания

Для чего используют библиотеки?

Поясните разницу между динамическим и статическим связыванием.

В чем специфика динамически подключаемых библиотек?

В каких целях используется разделяемая память в DLL?

Чем отличается динамическое связывание без импорта?

Приведите примеры системных DLL.

Опишите процесс создания DLL.

ЛИТЕРАТУРА

1. Ю. Щупак. Win32 API. Эффективная разработка приложений. Питер 2007.

2. Петзольд Ч. Программирование под Windows 95. В двух книгах: BHV – Санкт – Петербург, 1997, silt.

3. Бек Л. Введение в системное программирование. –М.: Мир,1968.-440 с.

4. Зелковиц И., Шоу А., Геннон Дж. Принципы разработки программного обеспечения. – М.: Мир, 1962. – 368 с.

5. Д.Ван Тассол. Стиль, разработка, эффективность, отладка и испытание программ. – М.: Мир, 1985. – 332 с.

6. М. Эйбраш. Оптимизация кода исходных текстов программ для микропроцессоров семейства 80х86 //Журнал д-ра Добба. -1991. - № 2. – С.42-49.

7. Синев Л. Как создать оконный интерфейс //Компьютер Пресс. – 1991. - № I. – С.16-33.

8. Майерс Г. Искусство тестирования программ. – М.: Финансы и статистика, 1982. – 176 с.

9. Абель П. Язык ассемблера для IВМ РС и программирования. –М.: Высш.юк.,1992. – 447 с.

10. Страуструп Б. Язык программирования С++: В 2-х кн. –К.: Диасофт, 1993.

11. Буч Г. Объективно-ориентированное проектирование с примерами применения. – К.: Диалектика., 1992. – 528 с.

12. Чижов А.А. Системные программные средства ПЭВМ. –М.: Финансы и статистика, 1990. – 360 с.

13. Боэм Б.У. Инженерное проектирование программного обеспе­чения. – М.: Радио и связь, 1905. – 512 с..

14. Гантер Р. Методы управления проектированием программного обеспечения. – М.: Мир, 1981. – 392 с.

15. Коутс Р., Влейминк И. Интерфейс «Человек-компьютер». –М.: Мир, 1990. – 502,с.

16. ГОСТ 19.701 – 90. ЕСПД. Схемы алгоритмов, программ, дан­ных и систем. Условные обозначения и правила выполнения.

17. ДСТУ 3008-95.

18. Мешков А., Тихомиров У. Visual C ++ MFC. Программирование под Windows NT and Windows 95: В двух книгах. - : BHV – Санкт – Петербург, 1997., silt.

19. Tompson Н. Secrets of programming the three-dimensional diagrams for Windows 95., BHV – St.-Petersburg, 1997., silt.1997. – 352p.

20. Black, Ulysses. TCP/IP & Related Protocols, Second Edition. McGraw-Hill, 1994.

21. Джеффри Рихтер WINDOWS. Создание эффективных WIN32-приложений

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