- •История Windows
- •Windows 1.0
- •Windows 1.3
- •Windows 2.0
- •Windows 3.0
- •Windows 3.1
- •Windows for Workgroups версии 3.11
- •Windows NT
- •Windows 95
- •Windows NT Workstation 4.0 и Windows NT Server 4.0
- •Windows NT 4.0 Server Enterprise Edition
- •Windows 98
- •Windows 98 Second Edition (SE)
- •Windows 2000
- •Windows 98 Millenium Edition (ME)
- •Windows XP
- •Общая архитектура WINDOWS приложения.
- •Каркас приложения
- •Регистрация класса окна
- •Создание окна.
- •Отображение окна.
- •Цикл обработки очереди сообщений.
- •WndProc
- •Обработка сообщений типового приложения.
- •Контрольные вопросы
- •Сообщения
- •Типы сообщений
- •Обработка клавиатурных сообщений
- •Обработка сообщений от мыши
- •Сообщения таймера
- •Контрольные вопросы
- •Организация ввода
- •Разупорядоченный ввод
- •Разделение потоками виртуальных очередей ввода
- •Локальное состояние ввода
- •Ввод с клавиатуры и фокус
- •Управление курсором мыши
- •Контрольные вопросы
- •Окна
- •Окна Windows
- •Иерархия окон
- •Оконная процедура, стандартные оконные процедуры
- •Стиль класса окна
- •Стили окон, окна основных стилей
- •Перекрывающиеся окна
- •Временные окна
- •Дочерние окна
- •Сообщения управления окнами
- •Окна с полосами прокрутки
- •Системные метрики
- •Определение размера окна
- •Определение расположения окна
- •Контрольные вопросы
- •Графический интерфейс устройств(GDI).
- •Создание (получение) контекста устройств.
- •Сохранение контекста устройства.
- •Прямые (отрезки) и кривые
- •Закрашенные области
- •Битовые шаблоны (растровые шаблоны, растровые образы)
- •Текст и шрифты
- •Режимы масштабирования и преобразования
- •Метафайл
- •Регионы
- •Путь
- •Палитры
- •Контрольные вопросы
- •Объекты ядра
- •Что такое объект ядра
- •Учет пользователей объектов ядра
- •Защита
- •Таблица описателей объектов ядра
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Синхронизация объектов
- •Именованные объекты
- •Дублирование описателей объектов
- •Изменение флагов описателя
- •Синхронизация потоков
- •Критическая секция
- •Мьютексы
- •Семафоры
- •События
- •Ожидаемые таймеры
- •Контрольные вопросы
- •Ресурсы Windows приложения
- •Файлы ресурсов приложения
- •Значки (пиктограммы)
- •Курсоры
- •Битовые образы
- •Символьные строки
- •Ресурсы, определяемые пользователем
- •Таблица акселераторов
- •Меню
- •Диалоговые окна
- •Контрольные вопросы
- •Элементы управления
- •Дочерние окна управления
- •Создание дочерних окон
- •Сообщения дочерних окон родительскому окну
- •Сообщения родительского окна дочерним окнам
- •Дочерние окна и фокус ввода
- •Дочерние окна управления и цвет
- •Кнопки различных стилей (класс BUTTON)
- •Нажимаемые кнопки
- •Флажки-переключатели
- •Переключатели
- •Окна групп
- •Кнопки, определяемые пользователем
- •Статические поля (класс static)
- •Текстовые редакторы (класс edit)
- •Списки разных стилей (класс listbox)
- •Комбинированные списки (класс combobox)
- •Полосы прокрутки (класс scrollbar)
- •Контрольные вопросы
- •Расширенные элементы управления
- •Основы общих элементов управления
- •Инициализация библиотеки элементов общего пользования
- •Создание элементов управления общего пользования
- •Посылка сообщений общим элементам управления
- •Уведомляющие сообщения от общих элементов управления
- •Контрольные вопросы
- •Стандартные диалоговые окна.
- •Окна сообщений
- •Диалоговые окна общего пользования
- •Контрольные вопросы
- •Динамически подключаемые библиотеки
- •Создание DLL
- •Проецирование DLL на адресное пространство процесса
- •Функция входа/выхода
- •Функция DllMain и стандартная библиотека С
- •Функция LibEntry
- •Функция LibMain
- •Функция WEP
- •Экспорт функций и переменных из DLL
- •Импорт функций и переменных из DLL
- •Заголовочный файл DLL
- •Разделы в ЕХЕ- и DLL-файлах
- •Контрольные вопросы
- •Файлы, проецируемые в память
- •Контрольные вопросы
- •Классы, определяющие архитектуру приложения
- •Классы приложения и поддержки потоков
- •Классы приложения и потоков
- •Классы фреймов окон
- •Получение информации о приложении
- •Соглашения об именах MFC
- •Включаемые файлы
- •Функция WinMain
- •Класс CWinApp
- •Класс CWnd
- •Класс CFrameWnd
- •Создание главного окна SDI-приложения
- •Примечание
- •Создание главного окна SDI-приложения
- •Создание дочерних окон
- •Ограничение размеров окна
- •Ограничение доступа к окну
- •Создание многодокументных приложений
- •Класс CMDIChildWnd
- •MDI — пример приложения
- •Категории сообщений
- •Карта сообщений
- •Компоненты карты сообщений
- •Класс CCmdTarget
- •Стандартный маршрут команды
- •Команды обновления и класс CCmdlll
- •Функции для работы с сообщениями
- •Основные положения
- •Класс CDocTemplate
- •Класс CSingleDocTemplate
- •Роль фреймов в архитектуре "документ/представление"
- •Документ и его представления
- •Документы
- •Класс CDocument
- •Класс CArchive
- •Представления
- •Класс CView
- •Панели элементов управления
- •Класс CStatusBar
- •Класс CStatusBarCtrl
- •Класс CDialogBar
- •ControlBars — пример приложения
- •НЕКОТОРЫЕ КЛАССЫ MFC
- •Класс CObject - основной класс MFC
- •Конструкторы класса
- •Оператор присваивания
- •Диагностика
- •Проверка целостности объектов класса
- •Получение дампа объекта класса
- •Сохранение и восстановление состояния объекта
- •Метод IsSerializable
- •Виртуальный метод Serialize
- •Информация о классе
- •Виртуальный метод GetRuntimeClass
- •Метод IsKindOf
- •Класс CPoint - точка на плоскости
- •Класс CSize - относительные координаты
- •Класс CString - текстовые строки
- •Конструктор класса
- •Коллекции
- •Массивы - шаблон CArray
- •Списки - шаблон CList
- •Словари - шаблон CMap
- •Класс CTime - дата и время
- •Файловая система - класс CFile
- •Открытие и создание файлов
- •Идентификатор открытого файла
- •Закрытие файлов
- •Чтение и запись файлов
- •Метод Flush
- •Операции с файлами
- •Блокировка
- •Позиционирование
- •Характеристики открытого файла
- •Файловая система - классы CMemFile и CStdioFile
- •Модификация класса CMemFile
- •Файловая система - класс CStdioFile
- •Запись и восстановление объектов
- •Запись в архивный файл
- •Чтение из архивного файла
- •Исключения - класс CException
- •Класс CException
- •Класс CMemoryException
- •Класс CFileException
- •Приложение Except
- •Класс CArchiveException
- •Класс CNotSupportedException
- •Класс CResourceException
- •Класс CUserException
g_dwTimes[g_nIndex - 1] = GetTickCount(),
в результате чего элементу g_dwTimes[0] присваивается текущее системное время. Тогда получалось, что у элемента g_dwTimes[l] значение меньше, чем у g_dwTimes[0]. В этот момент, даже если система решит вытеснить второй поток, она не предоставит процессорного времени первому потоку, потому что он ждет, когда станет доступной критическая секция. Рано или поздно второй поток вновь получит квант времени и исполнит оператор:
LeaveCriticalSection(&g_CriticalSection)
После исполнения этой строки переменная g_CriticalSection будет указывать, что защита с общих структур данных снята и они доступны любому другому потоку. Первый поток, ожидавший этого события, вновь активизируется, вызовет EnterCriticalSection, и та закрепит за ним переменную g_CriticalSection. Когда EnterCriticalSection вернет управление, FirstThread продолжит работу.
Как видно, применение критических секций позволяет организовать поочередное обращение двух потоков к каким-то общим данным. Но в некоторых случаях существует вероятность одновременного обращения к общим данным и большего числа потоков. Тогда каждый из них должен предварительно вызвать EnterCriticalSection. Если один поток уже захватил критическую секцию, то ожидающие доступа к этой секции засыпают. Когда поток освободит критическую секцию, вызвав LeaveCriticalSection, система «разбудит» только одного ожидающего, отдав ему права на эту секцию. Другие спящие потоки останутся в том же состоянии.
Мьютексы
Эти объекты весьма похожи на критические секции — за исключением того,
что с их помощью можно синхронизировать доступ к данным со стороны нескольких процессов. Для использования объекта-мьютекса один из процессов должен сначала создать его вызовом CreateMutex:
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES Ipsa, BOOL flnitialOwner LPTSTR IpszMutexName)
Одно из главных отличий объектов-мьютексов от критических секций в том,
что первые способны синхронизировать потоки, выполняемые в разных процессах. С этой целью поток в каждом процессе должен располагать своим, специфичным для
данного процесса описателем единственного объекта-мьютекса. Эти описатели можно получить несколькими способами. Самый распространенный: один из потоков
каждого процесса вызывает CreateMutex, и все передают ей через параметр
IpszMutexName идентичные строки. Первый вызов функции приводит к созданию
объекта ядра «мьютекс», остальные вызовы просто возвращают соответствующим потокам описатели этого объекта, значения которых специфичны для каждого процесса.
Проверить, действительно ли CreateMutex создала новый объектмьютекс, можно через GetLastError (но обращаться к ней нужно сразу после вызова
CreateMutex). Если GetLastError вернет ERROR_ALREADY_EXISTS, значит, новый объект-мьютекс не создан. Если же Вы просто хотите сделать этот объект доступным другим процессам, нет нужды обращаться к последней функции.
Еще один способ получить описатель мьютекса— вызвать OpenMutex:
HANDLE OpenMutex(DWORD fdwAccess BOOL flnhent LPTSTR IpszName)
При вызове OpenMutex система сканирует существующие объекты-мьютексы, проверяя, нет ли среди них объекта с именем, указанным в параметре IpszName. Обнаружив таковой, она создает описатель объекта, специфичный для данного процесса, и возвращает его вызвавшему потоку. В дальнейшем любой поток из данного процесса может использовать этот описатель при вызове любой функции, требующей такой описатель. А если обьекта-мьютекса с указанным именем нет, функция возвращает NULL.
Оба описанных выше способа требуют, чтобы у объекта-мьютекса было какое-то имя. Но существуют еще два способа, в которых имя мьютекса не нужно. Один из них построен на вызове DuphcateHandle, в другом используется механизм наследования описателей BOOL ReleaseMutex(HANDLE hMutex). Эти способы рассмотрены в предыдущем разделе.
Объект-мьютекс отличается от других синхронизирующих объектов ядра тем,
что занявшему его потоку передаются права на владение им. Прочие синхронизирующие объекты могут быть либо свободны, либо заняты — и все. А
объекты-мьютексы способны еще и запоминать, какому потоку они принадлежат.
Отказ от объекта-мьютекса происходит, когда ожидавший его поток захватывает этот объект (переводя в занятое состояние), а затем завершается. В таком случае получается, что мьютекс занят и никогда не освободится, поскольку) никакой другой поток не сможет этого сделать вызовом ReleaseMutex.
С объектом-мьютексом сопоставляется счетчик, фиксирующий, сколько раз данный объект передавался во владение какому-либо потоку. Поэтому, если поток вызовет WaitForSingleObject для уже принадлежащего ему объекта-мьютекса, он сразу же получит доступ к защищаемым этим объектом данным (так как система — по упомянутому счетчику — тут же определит, что поток уже владеет этим объектом-
мьютексом). Кроме того, при каждом вызове WaitForSingleObject потоком — владельцем объекта-мьютекса этот счетчик увеличивается на 1. А значит, чтобы перевести мьютекс в свободное состояние, потоку придется соответствующее число раз
вызывать ReleaseMutex. Функции EnterCritical-Section и LeaveCriticalSection действуют по отношению к критическим секциям аналогичным образом.
Семафоры
Объекты ядра «семафор» используются для учета ресурсов. Они позволяют потоку запрашивать число доступных ресурсов. Если один или более ресурсов свободны, счетчик доступных ресурсов после запроса уменьшается на 1. Семафоры осуществляют такую операцию на атомном уровне. Иными словами, когда Вы
запрашиваете у семафора ресурс, операционная система сама проверяет, свободен ли данный ресурс, и уменьшает счетчик доступных ресурсов, исключая вмешательство другого потока. Только после уменьшения счетчика доступных ресурсов система разрешает другому потоку запрашивать какой-либо ресурс.
Допустим, у компьютера 3 последовательных порта, их могут использовать не более трех потоков одновременно, и каждый порт можно закрепить за одним потоком. Эта ситуация — отличная возможность применить семафор. Чтобы отслеживать занятость последовательных портов, Вы создаете семафор со счетчиком, равным 3 (ведь последовательных портов у Вас именно 3). При этом нужно учесть, что семафор считается свободным, если его счетчик больше 0, и занятым, если счетчик равен 0 (меньше 0 он быть не может). При каждом вызове из потока WaitForSingleObject с передачей ей описателя семафора система проверяет: больше ли 0 счетчик ресурсов у данного семафора? Если да, уменьшает счетчик на 1 и «будит» поток. Если при вызове WaitForSingleObject счетчик семафора равен 0, система оставляет поток неактивным до того, как другой поток освободит семафор (т. е. увеличит его счетчик ресурсов).
Поскольку на счетчик ресурсов семафора могут влиять несколько потоков, семафоры (в отличие от критических секций или мьютексов) не передаются во владение какому-либо потоку. И значит, один поток может ждать объект «семафор» (уменьшив его счетчик ресурсов), а другой — освободить семафор (тем самым увеличив его счетчик).
Семафор создается вызовом CreateSemaphore:
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTE Ipsa, LONG cSemlnitial
LONG cSemMax LPTSTR IpszSemName)
Эта функция создает семафор, максимальное значение счетчика которого может достигать cSemMax В нашем примере в этот параметр следовало бы поместить число 3 (три последовательных порта). Параметр cSemlmtial позволяет задать начальное состояние счетчика. При запуске системы все последовательные порты будут свободны, значит, и сюда надо занести число 3. Но если при инициализации операционной системы Вы хотите указать, что все они заняты, введите в этот параметр 0.
Последний параметр функции, IpszSemName, присваивает семафору имя в виде строки. В дальнейшем это имя позволит получить описатель семафора из других процессов с помощью CreateSemaphore или OpenSemaphore:
HANDLE OpenSemaphore(DWORD fdwAccess |
BOOL flnhent |
LPTSTR |
IpszName) |
|
|
По семантике эта функция идентична уже рассмотренной OpenMutex.
Чтобы перевести семафор в свободное состояние (увеличить его счетчик ресурсов), вызовите ReleaseSemaphore:
BOOL ReleaseSemaphore(HANDLE hSemaphore LONG cRelease LPLONG
IplPrevious)
Она похожа на ReleaseMutex, но имеет ряд отличий.
•любой поток может вызвать ее когда угодно, поскольку объекты «семафор» не принадлежат лишь какому-то одному потоку.
•с ее помощью счетчик ресурсов можно увеличивать более чем на 1 единовременно.
Параметр cRelease как раз и определяет, какими порциями должен освобождаться семафор.
Например, у нас есть программа, копирующая данные из одного последовательного порта в другой. Она должна дважды запрашивать ресурсы у семафора, соответственно дважды вызывая WaitForSmgleObject. Но освободить оба ресурса программа может через один вызов ReleaseSemaphore. Вот как это делается:
// получаем два последовательных порта |
WaitForSingleObject(g_hSemSerialPort |
|
INFINITE) WaitForSingleObject(g_hSemSenalPort |
INFINITE) |
|
// чего-то делаем с ними |
|
|
// освобождаем последовательные порты |
чтобы и другие // приложения могли ими |
|
попользоваться ReleaseSemaphore(g_hSemSenalPort |
2 NULL) |
События
События — самая примитивная разновидность синхронизирующих объектов, резко отличающаяся от семафоров и мьютексов. Cобытия просто уведомляют об окончании какой-либо операции. Объекты «событие» бывают двух типов: со сбросом
вручную (manual-reset events) и с автосбросом (auto-reset events). Первые позволяют уведомлять об окончании операции сразу несколько потоков, вторые — только один поток.
События обычно используют в том случае, когда какой-то поток
выполняет инициализацию, а затем сигнализирует другому потоку, что тот может продолжить работу. Инициализирующий поток переводит объект «событие» в занятое состояние и приступает к своим операциям. По окончании инициализации поток сбрасывает событие в свободное состояние. В то же время рабочий поток приостанавливает свое исполнение и ждет перехода события в свободное состояние. Как только инициализирующий поток освободит событие, рабочий поток «проснется» и продолжит работу.
Например, в процессе исполняются два потока. Первый считывает данные из файла в буфер и оповещает второй, что можно заняться обработкой данных. Закончив обработку, второй поток сигнализирует первому, чтобы тот загрузил в буфер новую порцию данных из файла и т. д.
Cоздания событий. Семантика функций, оперирующих с событиями, идентична семантике соответствующих функций, предназначенных для объектов-
мьютексов и семафоров. Событие создается функцией CreateEvent
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES Ipsa, BOOL fManualReset BOOL fInitialState, LPTSTR IpszEventName),
Параметр fManualReset (Булева переменная) сообщает системе, какое событие Вы хотите создать: со сбросом вручную (TRUE) или с автосбросом (FALSE). Параметр flnitialState определяет начальное состояние события: свободное (TRUE) или занятое (FALSE). После того как система создает объект «событие», CreateEvent возвращает описатель события, специфичный для конкретного процесса.
Потоки из других процессов могут получить доступ к этому объекту:
•вызовом CreateEvent с тем же параметром IpszEventName;
•наследованием описателя;
•применением DuplicateHandle;
•вызовом OpenEvent с передачей в параметре IpszEventName имени, совпадающего с тем, что было указано в аналогичном параметре функции
CreateEvent.
Вот что представляет собой OpenEvent
HANDLE OpenEvent(DWORD fdwAccess BOOL fInherit LPTSTR IpszName),
Закрытие событий осуществляется функцией CloseHandle.
События со сбросом вручную автоматически не переустанавливаются в занятое состояние функциями WaitForSingleObjectK и WaitForMultipleObjects. У Вас может быть несколько потоков, ждущих возникновения одного события. Когда оно наконец происходит, каждый из ожидавших потоков получает возможность выполнить