- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 32 бита
- •Выделение памяти
- •Библиотечные функции C
- •Фундаментальное выделение памяти в Windows 95
- •Перемещаемая память
- •Удаляемая память
- •Другие функции и флаги
- •Хорошо ли это?
- •Функции работы с "кучей"
- •Файловый ввод/вывод
- •Старый путь
- •Отличия Windows 95
- •Ввод/вывод с использованием файлов, проецируемых в память
- •Режимы многозадачности
- •Многозадачность в DOS
- •Невытесняющая многозадачность
- •Решения, использующие многопоточность
- •Многопоточная архитектура
- •Коллизии, возникающие при использовании потоков
- •Преимущества Windows
- •Новая программа! Усовершенствованная программа! Многопоточная!
- •Многопоточность в Windows 95
- •И снова случайные прямоугольники
- •Задание на конкурсе программистов
- •Решение с использованием многопоточности
- •О пользе использования функции Sleep
- •Синхронизация потоков
- •Критический раздел
- •Объект Mutex
- •Программа BIGJOB1
- •Объект Event
- •Локальная память потока
- •Печать, буферизация и функции печати
- •Контекст принтера
- •Формирование параметров для функции CreateDC
- •Измененная программа DEVCAPS
- •Вызов функции PrinterProperties
- •Проверка возможности работы с битовыми блоками (BitBlt)
- •Программа FORMFEED
- •Печать графики и текста
- •Каркас программы печати
- •Прерывание печати с помощью процедуры Abort
- •Реализация процедуры прерывания
- •Добавление диалогового окна печати
- •Добавление печати к программе POPPAD
- •Обработка кодов ошибок
- •Техника разбиения на полосы
- •Разбиение на полосы
- •Реализация разбиения страницы на полосы
- •Принтер и шрифты
- •Глава 16 Буфер обмена
- •Простое использование буфера обмена
- •Стандартные форматы данных буфера обмена
- •Передача текста в буфер обмена
- •Получение текста из буфера обмена
- •Открытие и закрытие буфера обмена
- •Использование буфера обмена с битовыми образами
- •Метафайл и картина метафайла
- •Более сложное использование буфера обмена
- •Использование нескольких элементов данных
- •Отложенное исполнение
- •Нестандартные форматы данных
- •Соответствующая программа просмотра буфера обмена
- •Цепочка программ просмотра буфера обмена
- •Функции и сообщения программы просмотра буфера обмена
- •Простая программа просмотра буфера обмена
- •Основные концепции
- •Приложение, раздел и элемент
- •Типы диалогов
- •Символьные строки и атомы
- •Программа сервер DDE
- •Программа DDEPOP1
- •Сообщение WM_DDE_INITIATE
- •Оконная процедура ServerProc
- •Функция PostDataMessage программы DDEPOP1
- •Сообщение WM_DDE_ADVISE
- •Обновление элементов данных
- •Сообщение WM_DDE_UNADVISE
- •Сообщение WM_DDE_TERMINATE
- •Программа-клиент DDE
- •Инициирование диалога DDE
- •Сообщение WM_DDE_DATA
- •Сообщение WM_DDE_TERMINATE
- •Управляющая библиотека DDE
- •Концептуальные различия
- •Реализация DDE с помощью DDEML
- •Элементы MDI
- •Windows 95 и MDI
- •Пример программы
- •Три меню
- •Инициализация программы
- •Создание дочерних окон
- •Дополнительная информация об обработке сообщений в главном окне
- •Дочерние окна документов
- •Освобождение захваченных ресурсов
- •Сила оконной процедуры
- •Основы библиотек
- •Библиотека: одно слово, множество значений
- •Пример простой DLL
- •Разделяемая память в DLL
- •Библиотека STRLIB
- •Точка входа/выхода библиотеки
- •Программа STRPROG
- •Работа программы STRPROG
- •Разделение данных между экземплярами программы STRPROG
- •Некоторые ограничения библиотек
- •Динамическое связывание без импорта
- •Библиотеки, содержащие только ресурсы
- •Глава 20 Что такое OLE?
- •Основы OLE
- •Связь с библиотеками OLE
- •Расшифровка кода результата
- •Интерфейсы модели составного объекта (COM-интерфейсы)
- •Услуги интерфейса IUnknown
- •Является ли OLE спецификацией клиент/сервер?
- •Сервер закрытого компонента
- •IMALLOC.DLL
- •Теперь о макросах
- •Услуги, предоставляемые интерфейсом IUnknown
- •Клиент закрытого компонента
- •Сервер открытого компонента
- •Назначение реестра
- •Способы генерации и использования идентификаторов CLSID
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
18
Существует немного причин для использования функций, работающих с "кучей".
Файловый ввод/вывод
Повторим еще раз: для файлового ввода/вывода используйте библиотечные функции языка C везде, где это возможно. Это уже было сделано в программе POPPAD3 в главе 11. Там использовались функции fopen, fseek, fread, fwrite и fclose.
Старый путь
Работа с файлами под Windows постепенно совершенствовалась год от года. В те времена, когда использовались Windows 1.0 и Windows 2.0, единственной функцией файлового ввода/вывода была функция OpenFile, и официально рекомендовавшимся подходом к чтению и записи файлов была запись маленьких файлов на ассемблере, которые непосредственно осуществляли доступ к функциям MS DOS. Хотя использование стандартных функций библиотеки времени выполнения языка C было возможно, в маленькой и средней моделях памяти эти функции работали только с ближними указателями. Это было неудобно в программах, хранивших файлы данных в блоках глобальной памяти. Даже имена файлов, которые часто получали из диалоговых окон, были доступны с помощью дальних указателей.
К счастью, многие программисты вскоре обнаружили несколько недокументированных функций для работы с файлами с использованием дальних указателей. Они имели имена _lopen, _lread, _lwrite и т. д., и содержали непосредственные вызовы функций MS DOS. Начиная с Windows 3.0, эти функции были документированы и приняты как стандартные функции работы с файлами при программировании под Windows. Но применять их при программировании для Windows 95 не рекомендуется.
Отличия Windows 95
Windows 95 реализует несколько усовершенствований файлового ввода/вывода по сравнению с более ранними версиями Windows.
Первое, Windows 95 так же как и Windows 3.1 поддерживает библиотеку диалоговых окон общего пользования (common dialog box library), которая содержит диалоговые окна FileOpen и FileSave. Использование этих диалоговых окон было показано в главе 11. Рекомендуется при программировании использовать именно эти диалоговые окна. При их использовании исчезает необходимость разбора имени файла, который может быть системно-зависимым.
Второе, Windows 95 является 32-разрядной системой. Это значит, что вы можете читать и записывать файл большими блоками информации за один прием, используя однократный вызов функций fread и fwrite (или их эквивалентами, поддерживаемыми Windows 95). Изменения по отношению к существующему коду состоит в том, что отпадает необходимость в использовании циклов при работе с файлами большого размера.
Третье, Windows 95 поддерживает длинные имена файлов. Самое лучшее, что могут делать ваши программы с длинными именами, это просто ничего с ними не делать. (Хорошо звучит, не правда ли?) В документации по Windows сказано, что вы можете использовать данные, возвращаемые функцией GetVolumeInformation, для динамического выделения буферов для хранения имен файлов. Но, обычно в этом нет необходимости. Вам рекомендуется использовать две константы, определенные в файле STDLIB.H: _MAX_PATH (равно 260) и _MAX_FNAME (256) для статического выделения памяти.
Функции файлового ввода/вывода, поддерживаемые Windows 95
Если вы не пользуетесь функциями файлового ввода/вывода стандартной библиотеки времени выполнения языка C, то вы можете использовать функции, поддерживаемые Windows 95. Функция CreateFile является достаточно мощной:
hFile = CreateFile(szName, dwAccess, dwShare, NULL, dwCreate, dwFlags, 0);
Несмотря на имя, эта функция используется также для открытия существующего файла. Кроме того, эта функция используется для открытия каналов, используемых при обмене между процессами, а также коммуникационных устройств.
Для файлов — первый параметр является именем файла. Второй имеет значение либо GENERIC_READ, либо GENERIC_WRITE, либо GENERIC_READ | GENERIC_WRITE. Использование нулевого значения позволяет получить информацию о файле без доступа к его содержимому. Параметр dwShare открывает файл с общими атрибутами, позволяя другим процессам читать из него (FILE_SHARE_READ), или записывать в него
(FILE_SHARE_WRITE), или и то и другое вместе.
19
Флаг dwCreate — это одна из нескольких констант, показывающая, каким образом файл должен быть открыт. Их имена сжаты и прекрасно поясняют суть. Флаг CREATE_NEW вызывает ошибку, если файл уже существует, в то время как флаг CREATE_ALWAYS приводит к удалению содержимого существующего файла. Аналогичным образом, флаг OPEN_EXISTING вызывает ошибку, если файл не существует, а флаг OPEN_ALWAYS создает файл, если он не существует. Флаг TRUNCATE_EXISTING приводит к ошибке, если файл не существует, и удаляет все содержимое, если файл существует.
Параметр dwFlags может быть комбинацией констант, начинающихся со слов FILE_ATTRIBUTE и FILE_FLAG, для установки атрибутов файла и других особенностей.
Функция CreateFile возвращает переменную типа HANDLE. При завершении работы с файлом его необходимо закрыть, используя функцию CloseHandle с описателем файла в качестве параметра. Функции ReadFile и WriteFile похожи:
ReadFile(hFile, pBuffer, dwToRead, &dwHaveRead, NULL);
WriteFile(hFile, pBuffer, dwToWrite, &dwHaveWritten, NULL);
Второй параметр — это указатель на буфер, содержащий данные; третий параметр содержит количество байтов для чтения или записи; четвертый параметр — указатель на переменную, в которую при возврате из функции будет занесено количество байтов, которые были реально считаны или записаны. (Последний параметр используется только для файла, открываемого с флагом FILE_FLAG_OVERLAPPED, но этот случай не входит в предмет рассмотрения данной книги.)
Ввод/вывод с использованием файлов, проецируемых в память
При работе в Windows 95 (и это является одним из усовершенствований системы по сравнению с более ранними 16-разрядными версиями Windows) существует возможность читать и записывать данные в файл так, как будто это блок памяти. На первый взгляд это может показаться несколько странным, но со временем становится понятно, что это очень удобный механизм. Это прием рекомендуется использовать также при разделении памяти между двумя и более процессами. Пример такого использования файлов, проецируемых в память, приведен в главе 19 "Динамически подключаемые библиотеки".
Вот простейший подход к вводу/выводу с использованием файлов, проецируемых в память (memory mapped files): Сначала создается обычный файл с использованием функции CreateFile. Затем вызывается функция:
hMap = CreateFileMapping(hFile, NULL, dwProtect, 0, 0, szName);
Параметр dwProtect может принимать одно из следующих значений, и должен быть совместим с режимом разделения файла: PAGE_READONLY, PAGE_WRITECOPY, PAGE_READWRITE. Последний параметр функции
— необязательное имя, обычно используемое для разделения данных между процессами. В этом случае, функция OpenFileMapping открывает тот же файл с указанным именем. Обе функции возвращают значение типа HANDLE.
Если вам необходимо осуществить доступ к части файла, вызовите функцию MapViewOfFile:
p = MapViewOfFile(hMap, dwAccess, dwHigh, dwLow, dwNumber);
Весь файл или его часть могут быть спроецированы в память, начиная с заданного 64-разрядного смещения, которое задается параметрами dwHigh и dwLow. (Очевидно, что dwHigh будет иметь нулевое значение, если файл имеет размер менее 4 ГБ.) Параметр dwNumber задает количество байтов, которое вы хотите спроецировать в память. Параметр dwAccess может быть равен FILE_MAP_WRITE (данные можно записывать и считывать) или FILE_MAP_READ (данные можно только считывать), и должен соответствовать параметру dwProtect функции
CreateFileMapping.
После этого вы можете использовать указатель, возвращаемый функцией, для доступа или модификации данных в файле. Функция FlushViewOfFile записывает на диск все измененные страницы файла, спроецированного в память. Функция UnmapViewOfFile делает недействительным указатель, возвращаемый функцией MapViewOfFile. Затем необходимо закрыть файл, используя функцию CloseHandle.
Мы рассмотрим пример этого процесса в главе, посвященной динамически подключаемым библиотекам.