
- •История 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

Чтобы определить, спроецирована ли DLL на адресное пространство процесса, поток может вызвать функцию GetModuleHandle.
HINSTANCE GetModuleHandle(LPCTSTR IpszModuleName);
Например, следующий код загружает MYLIB.DLL, только если она еще не спроецирована на адресное пространство процесса:
HINSTANCE hinstDll;
hinstDll = GetModuleHandle("MyLib"); // подразумевается расширение DLL if (hinstDll == NULL) {
hinstDll = LoadLibrary("MyLib"); // подразумевается расширение DLL
}
Если уже есть значение HINSTANCE для DLL, можно определить и полное имя
DLL (или ЕХЕ) с помощью GetModuleFileName:
DWORD GetModuleFileName(HINSTANCE hinstModule, LPTSTR IpszPath, DWORD cchPath);
Первый параметр функции — это значение HINSTANCE для ЕХЕ или DLL. Второй задает адрес буфера, в который она запишет полное имя образа файла. Третий, и последний, параметр (cchPath) определяет размер буфера в символах.
Уменьшить счетчик числа пользователей DLL можно и с помощью другой Win32функции:
VOID FreeLibraryAndExitThread(HINSTANCE hinstDll, DWORD dwExitCode);
Она реализована в KERNEL32.DLL так:
VOID FreeLibraryAndExitThread(HINSTANCE hinstDll, DWORD dwExitCode) { FreeLibrary(hinstDll);
ExitTh read (dwExitCode);
}



Функция входа/выхода
У Win32 DLL может быть одна функция входа/выхода, но она необязательна.
Она используется DLL для инициализации и очистки в конкретных процессах или потоках. Эта функция в DLL должна выглядеть так:
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason,
LPVOID flmpLoad) { switch (fdwReason) {
case DLL_PROCESS_ATTACH:
//DLL проецируется на адресное пространство процесса break;
case DLL_THREAD_ATTACH:
//создается поток
break;
case DLL_THREAD_DETACH:
// поток завершается корректно break;
case DLL_PROCESS_DETACH:
// DLL отключается от адресного пространства процесса break;
return(TRUE); // используется только для DLL_PROCESS_ATTACH
}
Операционная система вызывает функцию входа/выхода в различных ситуациях.
•При этом параметр hinstDll должен содержать описатель экземпляра DLL. Как и hinstExe функции WinMain, это значение — виртуальный адрес, по которому файл DLL проецируется на адресное пространство процесса.
•Параметр fdwReason сообщает о причине, по которой система вызвала эту функцию. Он принимает одно из 4 значений: DLL_PROCESS_ATTACH,
DLL_PROCESS_DETACH, DLL_THREAD_ATTACH или DLL_THREAD_DETACH.
DLL_PROCESS_ATTACH
Система вызывает DllMain с этим значением параметра fdwReason сразу после того, как DLL спроецирована на адресное пространство процесса. А это происходит, только когда образ DLL-файла проецируется в первый раз.
•Обрабатывая DLL_PROCESS_ATTACH, библиотека должна выполнить в процессе инициализацию, необходимую ее функциям.
•При обработке уведомления DLL_PROCESS_ATTACH значение, возвращаемое функцией DllMain, указывает, корректно ли прошла инициализация DLL.
Конечно, где-то в системе должен быть поток, отвечающий за выполнение кода DllMain. При создании нового процесса система выделяет для него адресное пространство, куда проецируется ЕХЕ - файл и все необходимые ему DLL-модули. Далее создается первичный поток процесса, используемый системой для вызова DllMain из каждой DLL со значением DLL_PROCESS_ATTACH. Когда все спроецированные DLL ответят на это уведомление, система заставит первичный поток процесса исполнить стартовый код из стандартной библиотеки С, а потом — функцию WinMain (ЕХЕ - файла). Если DllMain хотя бы одной из DLL вернет FALSE, сообщая об ошибке при инициализации, система завершит процесс, удалив из его адресного пространства образы всех файлов; после чего пользователь увидит окно с сообщением о том, что процесс запустить не удалось.
DLL_PROCESS_DETACH

При отключении DLL от адресного пространства процесса вызывается ее функция
DllMain с передачей в параметре fdwReason значения DLL_PROCESS_DETACH.
Обрабатывая это значение, DLL должна провести очистку в данном процессе.
Например, вызвать HeapDestroy чтобы разрушить кучу, созданную ею при обработке уведомления DLL_PROCESS_ATTACH
Если DllMain вызвана со значением DLL_PROCESS_ATTACH и возвращает в результате FALSE, сообщая о неудачной инициализации, система впоследствии все равно вызовет эту функцию со значением DLL_PROCESS_DETACH. Взгляните для примера на показанную ниже функцию DllMain и попробуйте определить, где может возникнуть нарушение доступа (защиты памяти):
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, |
LPVOID |
|
flmpLoad) |
|
{ |
static PVOID pvData = NULL; |
|
|
BOOL fOk = TRUE; // пока предполагаем, что все идет хорошо |
|
|
switch (fdwReason) { |
|
|
case DLL_PROCESS_ATTACH: |
|
|
pvData = HeapAlloc(GetProcessHeap(), 0, 1000); |
|
|
if (pvData == NULL) |
|
|
fOk = FALSE; |
|
|
break; |
|
|
case DLL_PROCESS_DETACH: |
|
|
HeapFree(GetProcessHeap(), |
0, pvData); |
|
break; |
|
|
} |
|
|
return(fOk); // используется только для DLL_PROCESS_ATTACH
}
При подключении DLL к адресному пространству процесса выделяется небольшой блок памяти. Если его выделить не удается, DllMain возвращает FALSE, указывая на ошибку инициализации. Выделенный блок памяти освобождается при отключении DLL от адресного пространства процесса. Тут-то и возникает проблема.
•В приведенном фрагменте, если DllMain при обработке DLL_PROCESS_-АТТАСН возвращает FALSE, система вызывает ее потом со значением
DLL_PROCESS_DETACH.
Чтобы исправить ошибку, перепишем обработку DLL_PROCESS_DETACH
case DLL_PROCESS_DETACH: if (pvData != NULL)
HeapFree(GetProcessHeap(), 0, pvData); break;
•Если DLL отключается по причине завершения процесса, то за исполнение кода DHMain отвечает поток, вызвавший ExitProcess, — обычно первичный поток приложения. Когда Ваша функция WinMain возвращает управление стартовому коду из стандартной библиотеки С, тот явно вызывает ExitProcess и завершает процесс.
•Если DLL отключается в результате вызова FreeLibrary, код DHMain исполняется потоком, вызвавшим FreeLibrary. Управление от FreeLibrary не передается до завершения DHMain.
•Если процесс завершается в результате вызова TerminateProcess, система не вы-
зывает DllMain со значением DLL_PROCESS_DETACH. Значит, ни одна из спроецированных на адресное пространство процесса DLL-библиотек не получит шанса на очистку до завершения процесса.
DLL_THREAD_ATTACH
Когда в процессе создается новый поток, система просматривает все DLL, спроецированные в данный момент на адресное пространство этого процесса, и в каждой из таких DLL вызывает DllMain со значением DLL_THREAD_ATTACH.
•Если в момент проецирования DLL на адресное пространство процесса в нем исполняется несколько потоков, система не вызывает DllMain со значением DLL_THREAD_ATТАСН ни для одного из существующих потоков.
•Вызов DllMain с этим значением осуществляется, только если DLL проецируется на адресное пространство процесса в момент создания потока.
Обратите также внимание, что система не вызывает функции DllMain со значением DLL_THREAD_ATTACH и для первичного потока процесса. Любая DLL, проецируемая на адресное пространство процесса в момент его создания, получает уведомление
DLL_PROCESS_ATTACH, но не DLL_THREAD_ATTACH.
DLL_THREAD_DETACH
Завершая поток вызовом ExitThread, система просматривает все образы DLLфайлов, спроецированные в данный момент на адресное пространство процесса, и в каждой из этих DLL вызывает DllMain со значением DLL_THREAD_DETACH.
•Тем самым она уведомляет DLL-модули о необходимости очистки, связанной с данным потоком. Например, DLL-версия стандартной библиотеки С освобождает блок данных, используемый для управления многопоточными приложениями.
Если поток завершается из-за того, что другой поток вызвал для него TerminateThread, система не вызывает DllMain со значением DLL_THREAD_DETACH. Значит, ни одна из спроецированных на адресное пространство процесса DLL не получит шанса на выполнение очистки до завершения потока, что может привести к потере данных. Поэтому TerminateThread следует использовать лишь в самом крайнем случае!
Если при отключении DLL еще исполняются какие-то потоки, то для них DllMain не вызывается со значением DLL_THREAD_DETACH.