Содержание
Клуб
Работа
Другое
|
Оглавление
Windows 3.0/pg/2#3 = 1 =
Глава 8. Блоки управления.....................................6
8.1 Что такое блок управления?................................6
8.2 Создание блока управления.................................6
8.2.1 Определение класса блока управления.....................7
8.2.2 Выбор типа блока управления.............................8
8.2.3 Определение родительского окна..........................9
8.2.4 Выбор идентификатора блока управления..................10
8.3 Использование блоков управления..........................10
8.3.1 Получение ввода от пользователя........................10
8.3.2 Передача управляющих сообщений.........................11
8.3.3 Как сделать блок управления доступным или...............11
8.3.4 Перемещение и изменение размера блока управления.......12
8.3.5 Разрушение блока управления............................12
8.4 Создание и использование некоторых наиболее часто........12
8.4.1 Использование клавишных блоков управления..............13
8.4.2 Использование статических блоков управления............18
8.4.3 Использование панелей перечней.........................18
8.4.4 Комбинированные блоки управления.......................30
8.4.5 Редактируемые блоки....................................32
8.4.6 Строки прокрутки.......................................35
8.5 Пример прикладной программы EditCntl.....................38
8.5.1 Добавление константы во включаемый файл................39
8.5.2 Добавление новых переменных............................39
8.5.3 Добавление функции CreateWindow........................39
8.5.4 Модификация фрагмента WM_COMMAND.......................41
8.5.5 Добавление фрагмента WM_SETFOCUS.......................41
8.5.6 Добавление фрагмента WM_SIZE...........................41
8.5.7 Трансляция и компоновка................................42
8.6 Заключение...............................................42
Глава 9. Панель диалога.......................................44
9.1 Что такое панель диалога.................................44
9.1.1 Модальные панели диалога...............................45
9.1.2 Немодальные панели диалога.............................45
9.2 Использование панели диалога.............................46
9.2.1 Создание функции панели дилога.........................47
9.2.2 Использование блоков управления в панелях диалога......48
9.3 Пример прикладной программы FileOpen.....................48
9.3.1 Добавление константы во включаемый файл................50
9.3.2 Создание шаблона панели диалога Open...................50
9.3.3 Добавление новых переменных............................51
9.3.4 Добавление фрагмента IDM_OPEN..........................52
9.3.5 Создание функции OpenDlg...............................53
9.3.6 Добавление вспомогательных функций.....................56
9.3.7 Экспортирование функции диалога........................58
9.3.8 Трансляция и компоновка................................58
9.4 Заключение...............................................58
Глава 10. Ввод и вывод из файлов..............................60
10.1 Правила работы с файлами в среде Windows.................60
10.2 Создание файлов.........................................62
10.3 Открытие существующих файлов............................63
10.4 Чтение и запись файлов..................................64
10.5 Повторное открытие файлов...............................64
10.6 Запрос имени файла......................................65
.
Windows 3.0/pg/2#3 = 2 =
10.7 Проверка состояния файла................................65
10.8 Простой редактор файлов EditFile........................65
10.8.1 Добавление констант во включаемый файл................66
10.8.2 Добавление панели диалога SaveAs......................66
10.8.3 Добавление операторов include.........................67
10.8.4 Добавление новых переменных............................67
10.8.5 Замена фрагмента WM_COMMAND...........................68
10.8.6 Добавление фрагментов WM_QUERYENDSESSION и
WM_CLOSE.....................................................71
10.8.7 Модификация функции диалога OpenDlg...................71
10.8.8 Добавление функции диалога SaveAsDlg..................72
10.8.9 Добавление вспомогательных функций.....................74
10.8.10 Экспорт функции диалога SaveAsDlg....................77
10.8.11 Расширение динамической области памяти................77
10.8.12 Трансляция и компоновка..............................78
10.9 Заключение..............................................78
11.1 Что такое растровая карта...............................80
11.2 Создание растровых карт.................................80
11.2.1 Создание и загрузка файлов растровых карт.............80
11.2.2 Создание и заполнение пустых растровых карт...........82
11.2.3 Создание растровых карт с помощью фиксированных.......83
11.2.4 Рисование цветных растровых карт......................87
11.3 Отображение растровых карт на экране....................88
11.3.1. Индицирование растровой карты с помощью
функции......................................................89
11.3.2 Растяжение растровых карт.............................91
11.3.3 Использование растровых карт в кисти шаблона..........92
11.3.4 Индицирование аппаратно-независимых растровых
карт.........................................................94
11.3.5 Использование растровой карты в качестве элемента....95
11.4 Добавление цвета к монохромной растровой карте..........95
11.5 Удаление растровых карт.................................96
11.6 Пример прикладной программы Bitmap......................96
11.6.1 Модификация включаемого файла.........................98
11.6.2 Добавление ресурсов растровой карты...................98
11.6.3 Добавление меню Bitmap, Pattern и Mode................98
11.6.4 Добавление глобальных и локальных переменных..........99
11.6.5 Добавление фрагмента WM_CREATE.......................100
11.6.6 Модификация фрагмента WM_DESTROY.....................104
11.6.7 Добавление фрагментов WM_LBUTTONUP, WM_MOUSEMOVE.....104
11.6.8 Добавление фрагмента WM_RBUTTONUP....................105
11.6.9 Добавление фрагмента WM_ERASEBKGND...................105
11.6.10 Добавление фрагмента WM_COMMAND.....................106
11.6.11 Модификация make-файла..............................108
11.6.12 Трансляция и компоновка.............................108
11.7 Заключение.............................................108
Глава 12. Вывод на печать....................................110
12.1 Печать в среде Windows.................................110
12.1.1 Использование управляющих последовательностей........111
12.2 Получение информации о текущем принтере................111
12.3 Печать строки текста...................................113
12.4 Печать растровой карты.................................114
12.5 Обработка ошибок во время печати.......................116
.
Windows 3.0/pg/2#3 = 3 =
12.6 Отмена операции печати.................................118
12.6.1 Определение панели диалога...........................118
12.6.2 Определение функции панели диалога...................119
12.6.3 Функция прерывания печати............................119
12.6.4 Выполнение прерываемых операций печати...............121
12.6.5 Отмена операции печати с помощью ABORTDOC............122
12.7 Печать порциями........................................122
12.8 Пример прикладной программы PrntFile...................124
12.8.1 Добавление панели диалога AbortDlg..................124
12.8.2 Добавление переменных для печати.....................125
12.8.3 Добавление фрагмента IDM_PRINT.......................125
12.8.4 Создание функций AbortDlg и AbortProc................128
12.8.5 Добавление функции GetPrinterDC......................129
12.8.6 Экспортирование функции AbortDlg и AbortProc.........130
12.6.7 Компиляция и компоновка..............................130
12.9 Заключение.............................................130
Глава 13. Системный буфер....................................132
13.1 Использование системного буфера........................132
13.1.1 Копирование текста в системный буфер.................133
13.1.2 Вставка текста из системного буфера..................136
13.1.3 Вставка растровых карт из системного буфера..........138
13.1.4 Инструментальная программа Clipboard.................140
13.2 Использование специальных возможностей системного...141
13.2.1 Представление данных по запросу......................141
13.2.2 Представление форматов перед завершением работы......142
13.2.3 Регистрация собственных форматов.....................142
13.2.4 Управление отображением данных Clipboard...........142
13.3. Пример прикладной программы ClipText...................145
13.3.1 Добавление новых переменных..........................146
13.3.2 Модификация программы инициализации экземпляра.......146
13.3.3 Добавление фрагмента WM_INITMENU.....................147
13.3.4 Модификация фрагмента WM_COMMAND.....................147
13.3.5 Добавление фрагмента WM_PAINT........................150
13.3.6 Добавление функции OutOfMemory.......................150
13.3.7 Трансляция и компоновка..............................150
13.4 Заключение.............................................151
ЧАСТЬ 3. БОЛЕЕ СЛОЖНЫЕ РАЗДЕЛЫ...............................152
Глава 14. Язык С и язык Ассемблера...........................153
14.1 Выбор модели памяти....................................153
14.2 Использование NULL.....................................154
14.3 Использование аргументов командной строки и...155
14.4 Написание экспортируемых функций.......................156
14.4.1 Создание функции многократного вызова................156
14.4.2 Создание функции WinMain.............................157
14.5 Использование функций исполняющей системы С............158
14.5.1 Использование библиотек С............................158
14.5.2 Выделение памяти.....................................159
14.5.3 Работа со строками...................................159
14.5.4 Использование функций файлового ввода/вывода.........161
14.5.5 Использование функций ввода и вывода на консоль......162
14.5.6 Использование графических функций....................162
14.5.7 Использование вычислений с плавающей точкой..........162
14.5.8 Запуск других прикладных программ....................163
.
Windows 3.0/pg/2#3 = 4 =
14.5.9 Использование функций интерфейса с MS-DOS и BIOS.....163
14.5.10 Исключение стартового кода С........................164
14.6 Написание программ на языке ассемблера.................165
14.6.1 Изменение состояния флага прерывания.................167
14.6.2 Как на ассемблере написать экспортируемую функцию....168
14.6.3 Использование регистра ES............................169
14.7 Заключение.............................................170
Глава 15. Управление памятью................................172
15.1 Использование памяти...................................172
15.1.1 Использование глобальной динамической области
памяти......................................................173
15.1.2 Использование локальной динамической области
памяти......................................................174
15.1.3 Работа со сбрасываемой памятью.......................176
15.2 Использование сегментов................................178
15.2.1 Использование кодовых сегментов......................179
15.2.2 Сегменты данных......................................180
15.3 Пример прикладной программы Memory.....................180
15.3.1 Разделение исходного С-файла.........................181
15.3.2 Модификация включаемого файла........................182
15.3.3 Добавление новых определений сегментов...............182
15.3.4 Модификация файла make...............................183
15.3.5 Трансляция и компоновка..............................184
15.4 Заключение.............................................184
.
Windows 3.0/pg/2#3 = 5 =
----------------------------------------------------------------
ПРОГРАММА-СПРАВОЧНИК ПО
Microsoft Windows
Версия 3.0
Руководство по програмированию в среде Microsoft Windows
2#3
Москва 1991 г.
----------------------------------------------------------------
.
Windows 3.0/pg/2#3 = 6 =
Глава 8. Блоки управления.
----------------------------------------------------------------
Блоки управления - это специальные окна, позволяющие осу-
ществить простое взаимодействие с пользователем.
В данной главе описаны следующие разделы:
- Что такое блок управления.
- Создание блока управления.
- Использование блоков управления в прикладных программах.
В данной главе также описано создание прикладной программы
EditCntl, которая иллюстрирует использование блоков управления.
8.1 Что такое блок управления?
Блоки управления - это встроенные дочерние окна, через
которые осуществляется специальный вид ввода/вывода. Например,
если необходимо получить от пользователя имя файла, можно
создать и индицировать редактируемый блок управления для того,
чтобы дать возможность пользователю ввести имя. "Редактируемый
блок управления" - это предопределнное дочернее окно, которое
получает и отображает ввод с клавиатуры.
Блок управления подобно любому другому окну принадлежит
классу окна. Класс окна определяет атрибуты по умолчанию блока
управления и, что более важно, функцию окна блока управления.
Функция окна определяет, как блок управления будет выглядеть и
как он будет отвечать на действия пользователя. Функции окон
для блоков управления являются встроенными в Windows, так что
для их использования не требуется специального
программирования.
8.2 Создание блока управления.
Для создания блока управления можно использовать два
метода:
- Внутри панели диалога.
- Внутри области пользователя любого другого типа окон.
В данной главе обсуждается использование блоков управления
в стандартном окне. В главе 9, "Панели диалога", описано
создание блоков управления в панели диалога.
Можно создать блок управления, используя функцию
CreateWindow. В отличие от обычного применения этой функции для
создания окна программы необходимо указать:
- класс блока управления;
.
Windows 3.0/pg/2#3 = 7 =
- тип блока управления;
- родительское окно блока управления;
- идентификатор блока управления.
Функция CreateWindow возвращает дескриптор блока
управления, который может быть использован последующими
функциями для перемещения, рисования, указания размера или
разрушения окна, или для того, чтобы заставить окно выполнить
свои задачи.
В приведенном ниже примере показано, как создать блок
управления типа альтернативная клавиша:
hButtonWnd = CreateWindow(
"Button", /* класс клавиши */
"OK",
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
20, /* координата по оси X */
40, /* координата по оси Y */
30, /* ширина в пикселях */
12, /* высота в пикселях */
hWnd, /* родительское окно */
IDOK, /* ID блока управления */
hInstance, /* дескриптор экземпляра */
NULL);
В данном примере создается блок управления типа
альтернативная клавиша, который принадлежит классу окна
"Button" и имеет тип BS_PUSHBUTTON. Блок управления является
дочерним окном и будет виден при первом создании. Необходимо
использовать тип WS_CHILD; однако, если для индикации блока
управления будет использоваться функция ShowWindow, то тип
WS_VISIBLE применять не нужно. Функция CreateWindow поместит
блок управления в точку с координатами (20, 40) в области
пользователя родительского окна. Его ширина и высота равны
соответственно 30 и 12 пикселей. Родительское окно
идентифицируется дескриптором hWnd. Константа IDOK - это
идентификатор блока управления.
Оставшаяся часть данного раздела посвящена обсуждению
того, как указать класс окна блока управления, его тип,
родительское окно и идентификатор.
8.2.1 Определение класса блока управления.
Класс окна блока управления, или класс блока управления
определяет функцию и атрибуты блока управления, используемые по
умолчанию. Вы указываете класс блока управления при его
создании. Для этого имя класса (например, BUTTON") включается в
качестве параметра lpClassName функции CreateWindow.
.
Windows 3.0/pg/2#3 = 8 =
Windows предоставляет следующие встроенные классы блоков
управления:
Класс Описание
---------------------------------------------------------------
BUTTON Небольшие помеченные окна, которые может выби-
рать пользователь, задавая возможные ответы
типа да/нет или вкл/выкл.
EDIT Окна, в которые пользователь может войти для
ввода и редактирования текста.
LISTBOX Окна, которые содержат перечни имен, из кото-
рых пользователь может выбрать нужные имена.
COMBOBOX Комбинированный блок управления, состоящий из
редактируемого или статического блока управле-
ния и подключенной к ним панели перечня. Поль-
зователь может выбирать элементы из панели пе-
речня и/или вводить в редактируемом блоке
управления.
SCROLLBAR Окна, которые выглядят и работают как строки
прокрутки.
STATIC Небольшие окна, содержащие текст или простое
изображение. Они часто используются для того,
чтобы пометить другие блоки управления или
отделить одну группу блоков управления от
другой.
---------------------------------------------------------------
8.2.2 Выбор типа блока управления.
Тип блока управления зависит от класса и определяет вид и
работу блока управления. Тип задается при создании блока
управления. Для этого он включается в параметре dwStyle
(например, BS_PUSHBUTTON) функции CreateWindow.
Windows содержит много предопределенных типов блоков
управления. Ниже приведены наиболее часто встречающиеся типы:
Тип Описание
---------------------------------------------------------------
BS_PUSHBUTTON Специфицирует блок управления типа
альтернативная клавиша, содержащий
метку, которую пользователь может
выбрать для уведомления родительского
окна.
BS_DEFPUSHBUTTON Специфицирует альтернативную клавишу по
умолчанию, которая идентична обычной
.
Windows 3.0/pg/2#3 = 9 =
альтернативной клавише, но имеет
специальную окантовку.
BS_CHECKBOX Специфицирует блок управления типа
панель контроля. Пользователь может
включать/выключать панель. Когда панель
контроля включена, то в ней появляется
знак "Х".
BS_RADIOBUTTON
Специфицирует блок управления типа
клавиша установки (небольшой кружок).
Пользователь может включать/выключать
данный блок управления. Когда клавиша
установки включена, кружок закрашен.
ES_LEFT Специфицирует однострочный выравненный
влево редактируемый блок управления.
ES_MULTILINE Специфицирует многострочный
редактируемый блок управления.
SS_LEFT Специфицирует выравненный влево
статический блок управления.
SS_RIGHT Специфицирует выравненный вправо
статический блок управления.
LBS_STANDARD Специфицирует стандартную панель
перечня. Стандартная панель перечня
включает строку прокрутки и уведомляет
ее родителькое окно, когда пользователь
делает выборку.
CBS_DROPDOWN Определяет комбинированный блок управ-
ления, содержащий редактируемый блок
управления и панель перечня, которая
отображается, когда пользователь
нажимает на блок, следующий за полем
выбора. Если выбирается элемент в панели
перечня, в редактируемом блоке
отображается выбранный элемент.
---------------------------------------------------------------
8.2.3 Определение родительского окна.
Поскольку каждый блок управления является дочерним окном,
необходимо родительское окно. Родительское окно указывается при
создании блока управления. Для этого включите дескриптор
родительского окна в качестве параметра hWndParent в вызов
функции CreateWindow.
Как любое дочернее окно блок управления подвергается
.
Windows 3.0/pg/2#3 = 10 =
изменеиям со стороны родительского окна. Например, если
родительское окно делается недоступным, блок управления также
будет недоступен. Если Windows рисует, перемещает или разрушает
родительское окно, он также рисует, перемещает или разрушает
блок управления.
Хотя блок управления может быть любого размера и
перемещаться в любое положение, он ограничен областью
пользователя родительского окна. Windows вырезает окно, если
оно перемещается за пределы области пользователя или становится
больше области пользователя.
8.2.4 Выбор идентификатора блока управления.
При создании блока управления он получает уникальный
идентификатор (или ID) блока управления. Идентификатор блока
управления при создании блока управления. Идентификатор
указывается в функции CreateWindow вместо дескриптора меню
(hMenu). Блок управления определяется этим идентификатором в
любом уведомляющем сообщении, которое он посылает функции
родительского окна. Идентификатор блока управления особенно
полезен, когда в окне имеются несколько блоков управления. Это
наиболее простой и быстрый способ отличить один блок управления
от другого.
8.3 Использование блоков управления.
После создания блока управления вы можете:
- Получить ввод от блока управления.
- Сообщить блоку управления, что необходимо выполнить
определенные задачи, например, вернуть строку текста.
- Сделать доступным или недоступным ввод в блок
управления.
- Переместить или изменить размер блока управления.
- Разрушить блок управления.
В данном разделе описано, как выполнить все эти задачи.
8.3.1 Получение ввода от пользователя.
При взаимодействии пользователя с блоком управления
последний посылает родительскому окну информацию об этом
взаимодействии в форме уведомляющих сообщений. Уведомляющее
сообщение - это сообщение WM_COMMAND, в котором:
- параметр wParam содержит идентификатор блока управления;
- параметр lParam содержит код уведомления и дескриптор
блока управления.
.
Windows 3.0/pg/2#3 = 11 =
Например, пользователь нажимает кнопку мыши, когда курсор
находится на блоке управления BUTTON, этот блок управления
посылает функции окна родителя сообщение WM_COMMAND. Сообщение
WM_COMMAND содержит в параметре lParam идентификатор блока
управления, а в старшем слове параметра lParam содержится код
уведомления BN_CLICKED, который указывает на то, что
пользователь нажал на данную мягкую клавишу.
Поскольку уведомляющее сообщение имеет ту же основную
форму, что и при вводе из меню, оно может обрабатываться во
многом аналогично. Если идентификаторы блоков управления были
выбраны так, что они не входят в конфликт с идентификаторами
элементов меню, можно обрабатывать уведомляющие сообщения в том
же операторе switch, который используется для обработки ввода
из меню.
8.3.2 Передача управляющих сообщений.
Большинство блоков управления принимают и обрабатывают ряд
сообщений. Это специальные сообщения, которые побуждают блок
управления выполнять некоторые уникальные для данного блока
управления задачи. Например, сообщение WM_GETTEXTLENGTH
предписывает редактируемому блоку управления возвратить длину
выбранной строки текста.
Сообщение блоку управления посылается функцией
SendMessage. Необходимо задать номер сообщения и значения
параметров wParam и lParam. Например, приведенный ниже оператор
посылает сообщение EM_GETTEXTLENGTH редактируемому блоку
управления, идентифицированному дескриптором hEditWnd, а затем
возвращает длину выбранной строки.
nLength=SendMessage(hEditWnd, WM_GETTEXTLENGTH, 0, 0L);
Многие блоки управления также обрабатывают стандартные
сообщения окон, такие как WM_HSCROLL и WM_VSCROLL. Можно
послать эти сообщения блокам управления тем же способом,
который использовался для посылки управляющих сообщений.
8.3.3 Как сделать блок управления доступным или
недоступным для ввода.
Можно сделать блок управления доступным или недоступным
для ввода, использовав функцию EnableWindow.
Когда вы делаете ввод в блок управления недоступным, этот
блок управления не реагирует на ввод от пользователя. Windows
делает данный блок "серым", чтобы указать пользователю, что
данный блок управления недоступен. В приведенном примере
показано, как сделать блок управления недоступным:
.
Windows 3.0/pg/2#3 = 12 =
EnableWindow(hButton, FALSE);
Можно возобновить ввод в блок управления, сделав его
доступным с помощью функции EnableWindow, указав параметр TRUE:
EnableWindow(hButton, TRUE);
8.3.4 Перемещение и изменение размера блока управления.
Можно перемещать или изменять размер блока управления,
используя функцию MoveWindow. Эта функция перемещает блок
управления в указанную точку области пользователя родительского
окна и устанавливает для блока управления заданную ширину и
высоту. Следующий пример демонстрирует, как переместить и
изменить размеры блока управления:
MoveWindow(hButtonWnd, 10, 10, 30, 12, TRUE);
В этом примере блок управления перемещается в точку (10,
10) в области пользователя; ширина и высота устанавливается
равной 30 и 12 пикселям соответственно. Значение TRUE говорит о
том, что после перемещения блок управления должен быть
перерисован.
Windows автоматически перемещает блок управления при
перемещении родительского окна. Положение блока управления
берется всегда относительно левого верхнего угла области
пользователя родительского окна, так что когда родительское
окно перемещается, блок управления остается фиксированным в
области пользователя, но перемещается относительно экрана.
Windows не изменяет размеры блока управления при изменении
родительского окна, но посылает сообщение WM_SIZE родительскому
окну для указания его нового размера. При желании можно
использовать это сообщение для того, чтобы дать блоку
управления новые размеры.
8.3.5 Разрушение блока управления.
Можно разрушить блок управления с помощью функции
DestroyWindow. Эта функция удаляет все внутренние записи и сам
блок управления из области пользователя родительского окна. В
приведенном примере показано, как разрушить блок управления:
DestroyWindow(hEditWnd);
Windows автоматически разрушает блок управления при
разрушении родительского окна. Вообще говоря, блок управления
необходимо разрушать только тогда, когда он в родительском окне
больше не нужен.
.
Windows 3.0/pg/2#3 = 13 =
8.4 Создание и использование некоторых наиболее часто
встречающихся блоков управления.
Далее в этой главе приведена более подробная информация об
использовании наиболее часто встречающихся блоков управления:
- Блок управления типа клавиша.
- Статический блок управления.
- Панель перечня.
- Комбинированный блок управления.
- Редактируемый блок управления.
- Строки прокрутки.
8.4.1 Использование клавишных блоков управления.
Клавишный блок управления - это небольшое окно,
используемое для простого ввода типа да/нет или вкл/выкл.
Существуют следующие наиболее часто используемые клавишные
блоки управления:
- Альтернативная клавиша.
- Альтернативная клавиша по умолчанию.
- Клавиша установки.
- Панель контроля.
- Клавиша, рисуемая владельцем.
- Групповая панель.
Альтернативные клавиши.
Клавишный блок управления - это клавиша, которая позволяет
пользователю выбрать определенное действие. Кдавиша содержит
текст, который определяет ее действие. Когда пользователь
"нажимает" ее, прикладная программа обычно выполняет связанные
с ней действия. Например, если пользователь в панели диалога
выбирает клавишу "Cancel", то прикладная программа немедленно
убирает панель диалога и отменяет все произведенные в панели
диалога установки (если они были произведены).
Можно создать клавишный блок управления, используя класс
"Button" и специфицируя тип клавиши. Например, приведенный ниже
вызов функции CreateWindow создает блок управления типа
альтернативная клавиша с меткой "Cancel":
.
Windows 3.0/pg/2#3 = 14 =
HWND hCancelButton;
.
.
.
hCancelButton=CreateWindow("Button", "Cancel",
BS_PUSHBUTTON ! WS_CHILD ! WS_VISIBLE,
20, 40, 30, 12, hWnd, ICANCEL, hInstance, NULL);
В данном примере специфицирован тип WS_VISIBLE, так что
при создании блока управления он индицируется. Идентификатор
блока управления равен ICANCEL. Эта константа определена в
файле windows.h и предназначена для использования с
альтернативными клавишами , такими как клавиша "Cancel".
Альтернативные клавиши по умолчанию.
Альтернативная клавиша по умолчанию обычно используется
для того, чтобы дать пользователю возможность сигнализировать
об окончании некоторой работы, например такой, как запись в
редактируемый блок управления имени файла. Блок управления типа
альтернативная клавиша по умолчанию (также как и другие типы
клавишных блоков управления) отвечают на ввод от "мыши" или
клавиатуры. Если пользователь перемещает курсор "мыши" в блок
управления и нажимает кнопку на ней, клавиша посылает
уведомляющее сообщение BN_CLICKED родительскому окну. Клавиша
не должна захватывать ввод для ответа на действия с "мышью", но
это необходимо делать в ответ на ввод с клавиатуры. Для того,
чтобы дать возможность пользователю использовать клавиатуру,
необходимо захватить ввод для клавиши с помощью функции
SetFocus. Пользователь может затем нажать клавишу пробела для
того, чтобы побудить клавишу послать уведомляющее сообщение
BN_CLICKED родительскому окну.
Создание альтернативной клавиши по умолчанию во многом
аналогично созданию обычной альтернативной клавиши. Укажите
класс блока управления "Button", а тип укажите в параметре
dwStyle функции CreateWindow. Например, приведенный ниже вызов
функции CreateWindow создает блок управления типа
альтернативная клавиша по умолчанию с меткой "OК":
HWND hDefButton;
. . .
hDefButton=CreateWindow("Button", "OК",
BS_DEFPUSHBUTTON ! WS_CHILD ! WS_VISIBLE,
20, 40, 30, 12, hWnd, IDOK, hInstance, NULL);
В данном примере специфицирован тип WS_VISIBLE, так что
при создании блока управления он индицируется. ID блока уп-
равления равен IDOK. Эта константа определена в файле
windows.h и предназначена для использования с альтернативны-
ми клавишами по умолчанию, такими как клавиша "OК".
Панель контроля.
Панель контроля обычно используется для выбора некоторого
.
Windows 3.0/pg/2#3 = 15 =
параметра, применяемого в данной задаче. По соглашению внутри
группы панелей контроля можно выбрать более одного параметра.
(Для представления параметров, в которых можно выбрать только
один, используйте блоки управления типа клавиши установки.)
Например, можно использовать группу панелей контроля для
того, чтобы дать возможность пользователю выбрать параметры
шрифта для операции вывода. Пользователь может выбрать как
Жирный шрифт, так и Курсив, указав "Bold" и "Italic".
Можно создать блок управления типа панель контроля,
используя тип BS_CHECKBOX, как в приведенном примере:
#define ID_ITALIC 201
HWND hCheckBox;
.
.
.
hCheckBox=CreateWindow("Button", "Курсив",
BS_CHECKBOX ! WS_CHILD ! WS_VISIBLE,
20, 40, 80, 20, hWnd, ID_ITALIC, hInstance,
NULL);
В этом примере метка панели контроля - "Курсив", а
идентификатор блока управления - ID_ITALIC.
Панель контроля отвечает на ввод от "мыши" или клавиатуры
во многом также, как и блок управления типа альтернативная
клавиша. Другими словами, она посылает уведомляющее сообщение
родительскому окну, когда пользователь нажимает клавишный блок
управления или клавишу пробела. Однако панель контроля может
индицировать метку "Х" для того, чтобы показать, что она в
данный момент включена (выбрана).
Можно побудить блок управления показать отметку,
используя сообщение BM_SETCHECK. Можно также проверить, имеет
ли панель контроля отметку, используя сообщение BM_GETCHECK.
Например, для помещения отметки в панель контроля используйте
следующую функцию:
SendMessage(hCheckBox, BM_SETCHECK, 1, 0L);
Это означает, что можно поместить или удалить отметку из
панели контроля в любой момент (например, когда функция
родительского окна получает уведомляющее сообщение BN_CLICKED).
Windows также обеспечивает режим BS_AUTOCHECKBOX, который
служит для автоматического размещения или удаления контрольной
отметки.
Клавиши установки.
Блоки управления типа клавиша установки работают во многом
также, как и панели контроля. Однако, клавиши установки обычно
.
Windows 3.0/pg/2#3 = 16 =
используются группами, которые представляют взаимно исключающие
возможности. Например, вы можете использовать группу клавиш
установки для того, чтобы дать пользователю возможность указать
выравнивание текста (влево, вправо или по центру). Клавиши
установки позволяют пользователю выбрать только один тип
выравнивания.
Клавиша установки создается аналогично другим блокам
управления. Укажите класс "Button" в качестве класса окна и
укажите тип посредством параметра dwStyle. Например, ниже
приведены операторы, создающие клавишу установки с текстом
"Right":
HWND hRightJustifyButton;
#define IDC_RIGHTJUST
.
.
.
hRightJustifyButton = CreateWindow("Button", "Right",
BS_RADIOBATTON | WS_CHILD | WS_VISIBLE,
20, 40, 80, 20, IDC_RIGHTJUST, hInstance, NULL);
Как и в случае с панелью контроля вы можете послать
клавише установки сообщение BM_SETCHECK, чтобы клавиша
установки отобразила пометку (обычно закрашенный кружок).
Также, поскольку клавиша установки предоставляет возможность
выбора из взаимно исключающих возможностей, то необходимо
послать сообщение BM_SETCHECK клавише, которая была выбрана до
этого (если такая была), чтобы сбросить отметку. Вы можете
определить, какая из клавиш установки была выбрана, посылая
каждой клавише в группе сообщение BM_GETCHECK.
В панели диалога вы можете создать клавишу установки с
типом BS_AUTORADIOBUTTON. Если все клавиши установки в группе
имеют этот тип, Windows автоматически удалит старую отметку при
выборе нового элемента.
Для проверки и удаления отметок с клавиш установки в
панели диалога вы можете использовать функцию CheckRadioButton.
При вызове функции CheckRadioButton вы передаете ей
идентификаторы первой и последней клавиши установки в группе, а
также идентификатор клавиши в этом диапазоне, которая должна
быть помечена. Windows удаляет отметки со всех клавиш в
указанном диапазоне и помечает данную клавишу. Например, в
группе клавиш установки, представляющих выравнивание текста, вы
можете вызвать функцию CheckRadioButton для отметки клавиши
"Right". Это делается следующим образом:
CheckRadioButton(hDlg, ID_RIGHTLEFTJUST, ID_LEFTJUST,
ID_RIGHTJUST);
В этом примере CheckRadioButton отмечает клавишу установки
с идентификатором ID_RIGHTJUST и удаляет все отметки с клавиш в
.
Windows 3.0/pg/2#3 = 17 =
диапазоне от ID_RIGHTLEFTJUST до ID_LEFTJUST.
Клавиши, рисуемые владельцем.
Рисуемые владельцем клавиши аналогичны остальным типам
клавишных блоков управления, за исключением того, что
прикладная программа отвечает за поддержку вида клавиши,
включая те моменты, когда клавиша захватывает ввод, делается
недоступной или когда ее выбирают. Windows просто сообщает
прикладной программе, когда пользователь нажимает кнопку мыши,
когда ее курсор находится на этой клавише.
Для создания клавиши, рисуемой владельцем, используется
тип BS_OWNERDRAW, как показано в следующем примере:
hMyOwnButton = CreateWindow("Button", NULL,
BS_OWNERDRAW | WS_CHILD | WS_VISIBLE,
20, 40, 30, 12, hWnd, ID_MYBUTTON,
hInstance, NULL);
Когда возникает необходимость нарисовать клавишу, Windows
посылает сообщение WM_DRAWITEM окну, которому принадлежит
данный блок управления. Параметр lParam сообщения WM_DRAWITEM
содержит указатель на структуру данных типа DRAWITEMSTRUCT. Эта
структура кроме всего прочего содержит идентификатор блока
управления, значение, определяющее тип требуемой операции
рисования, значение, определяющее состояние клавиши,
прямоугольник, являющийся границей клавиши, и дескриптор
контекста устройства для данной клавиши.
В ответ на получение сообщения WM_DRAWITEM ваша программа
должна выполнить следующие действия:
1. Определить тип требуемой операции рисования. Для этого
проверьте содержимое поля itemAction структуры данных
DRAWITEMSTRUCT.
2. Нарисуйте клавишу соответствующим образом, используя
прямоугольник, ограничивающий клавишу и контекст
устройства, полученные из структуры DRAWITEMSTRUCT.
3. Восстановите объекты GDI, выбранные в контексте
устройства.
Например, когда клавиша теряет захват ввода, Windows
посылает в поле itemAction структуры данных DRAWITEMSTRUCT
значение ODA_FOCUS, но не устанавливает бит ODS_FOCUS в поле
itemState. Затем ваша программа должна нарисовать клавишу таким
образом, чтобы показать, что она потеряла захват ввода.
Групповые блоки.
Групповой блок - это прямоугольник, заключающий две или
.
Windows 3.0/pg/2#3 = 18 =
более зависимых клавиш или других блоков управления. Вы можете
послать групповому блоку сообщение WM_SETTEXT, чтобы поместить
в правый верхний угол заголовок. Групповые блоки не отвечают на
ввод от пользователя, т.е. они не посылают уведомляющих
сообщений.
8.4.2 Использование статических блоков управления.
Статический блок управления - это небольшое окно,
содержащее текст или изображение. Статический блок управления
обычно используется для пометки некоторого другого блока
управления или для создания панелей и линий, которые отделяют
одну группу блоков управления от другой.
Наиболее часто используемый статический блок управления -
SS_LEFT. Это выравненная влево строка текста. Текст
записывается, начиная с левого конца блока управления,
индицируя столько символов, сколько входит в блок управления и
обрезая остальное. Для текста блок управления использует
системные шрифты так, что можно вычислить размеры блока
управления, получив характеристики этого шрифта (более подробно
см. главу 18 "Шрифты").
Подобно групповым панелям статические блоки управления не
отвечают на ввод от пользователя; т.е. при выборке они не
генерируют уведомляющих сообщений. Однако имеется возможность в
любой момент времени изменить внешний вид и расположение
статических блоков управления. Например, можно изменить текст,
связанный со статическим блоком управления, используя функцию
SetWindowText или сообщение WM_SETTEXT.
8.4.3 Использование панелей перечней.
Панель перечня - это блок управления для перечня
символьных строк, таких как имена файлов. Обычно панель перечня
используется для индицирования перечня элементов, из которых
пользователь может выбрать одно или несколько. Имеется
несколько типов пенелей перечня. Наиболее употребительными
типами являются:
Тип Описание
---------------------------------------------------------------
LBS_BORDER Панель перечня с окантовкой вокруг нее.
LBS_NOTIFY Панель перечня посылает уведомляющие сообщения
родительскому окну при выборе элемента
пользователем.
LBS_SORT Панель перечня с отсортированными в алфавитном
порядке элементами.
WS_SCROLL Панель перечня имеет вертикальную строку про-
крутки.
---------------------------------------------------------------
.
Windows 3.0/pg/2#3 = 19 =
Эти типы включены в панель перечня типа LBS_STANDART. В
следующем примере создается стандартная панель перечня:
HWND hListBox;
#define IDC_LISTBOX 203
.
.
.
hListBox = CreateWindow("Listbox", NULL,
LBS_STANDART | WS_CHILD | WS_VISIBLE,
20, 40, 120, 56, hWnd, IDC_LISTBOX,
hInstance, NULL);
Добавление строки к панели перечня.
Можно добавить строку к панели перечня, используя
сообщение LB_ADDSTRING. Это сообщение копирует данную строку в
панель перечня, которая индицирует ее в составе перечня. Если
панель перечня имеет тип LBS_SORT, строка сортируется в
алфавитном порядке, иначе она просто добавляется в конец
списка. В приведенном ниже примере показано, как добавить
строку:
int nIndex;
.
.
.
nIndex=SendMessage(hListBox,
LB_ADDSTRING, NULL,
(LONG)(LPSTR) "Horseradish");
Это сообщение возвращает целое, представляющее индекс
строки в перечне. Можно использовать этот индекс в последующих
сообщениях панелей перечня для идентификации строки, но только
до тех пор, пока пользователь не добавит, удалит или вставит
другую строку, так как эти действия могут изменить индекс
строки.
Удаление строки из панели перечня.
Можно удалить строку из панели перечня, задав индекс с
сообщением LB_DELETESTRING, как это сделано в следующем
примере:
SendMessage(hListBox, LB_DELETESTRING, nIndex,
(LPSTR)NULL);
Вы можете добавить строку в панель перечня, послав
сообщение LB_INSERTSTRING. В отличие от сообщения LP_ADDSTRING,
LB_INSERTSTRING позволяет вам определить, куда необходимо
вставить строку в панели перечня. При получении сообщения
.
Windows 3.0/pg/2#3 = 20 =
сообщение LB_INSERTSTRING панель перечня не производит
сортировки элементов, даже если она имеет тип LBS_SORT.
Добавление к панели перечня имен файлов.
Как уже было сказано раньше, чаще всего панели перечня
используются для вывода списка имен файлов, директорий и/или
идентификаторов дисководов. Сообщение LB_DIR сообщает панели
перечня, что она должна заполнить себя такой информацией.
Параметр wParam этого сообщения содержит атрибуты файлов DOS, а
lParam содержит указатель на строку, содержащую спецификацию
файла.
Например, чтобы заполнить панель перечня именами всех
файлов текущей директории, имеющих расширение .TXT, и плюс
списком директорий и дисководов, вы должны послать сообщение
LB_DIR, как показано в следующем примере:
#define FILE_LIST 4010;
.
.
.
int nFiles;
.
.
.
nFiles = SendMessage(hListBox, LB_DIR, FILE_LIST,
(LPSTR)"*.TXT");
Возвращаемое значение после посылки сообщения LB_DIR
определяет число элементов, которые содержатся в панели
перечня.
Примечание: Если панель перечня принадлежит панели
диалога, то для выполнения той же задачи вы можете вызвать
функцию DlgDirList.
.
Windows 3.0/pg/2#3 = 21 =
Панель перечня отвечает на ввод от мыши и клавиатуры. Если
пользователь нажимает на кнопку мыши или нажимает клавишу
пробела, находясь на строке, происходит выборка строки из
панели перечня, на что указывает инверсия текста строки и
удаление выборки с предыдущего выбранного элемента.
Пользователь может также нажать символьную клавишу, и будет
выбран следующий элемент, который начинается с данного символа.
Если панель перечня имеет тип LBS_NOTIFY, она еще посылает
родительскому окну уведомляющее сообщение LBN_SELCHANGE. Если
пользователь дважды нажмет на кнопку мыши, находясь на строке,
и определен тип LBS_NOTIFY, панель перечня посылает
родительскому окну сообщения LBN_SELCHANGE и LBN_DBLCLK.
В любой момент можно определить индекс выбираемой строки,
используя сообщения LB_GETCURSEL и LB_GETTEXT. Сообщение
LB_GETCURSEL возвращает индекс выборки в панели перечня, а
сообщение LB_GETTEXT делает выборку из панели перечня, копируя
ее в заданный буфер.
.
Windows 3.0/pg/2#3 = 22 =
В таблице 8.1 приводится интерфейс клавиатуры и мыши для
стандартной панели перечня.
Таблица 8. Интерфейс пользователя со стандартной панелью
перечня.
---------------------------------------------------------------
Действие Результат
---------------------------------------------------------------
Интерфейс при использовании мыши
---------------------------------------------------------------
Единичное нажатие Выбирает элемент, и удаляет выборку с пре-
кнопки мыши дыдущего выбранного элемента (если такой
есть).
Двойное нажатие Аналогично единичному нажатию.
кнопки мыши
---------------------------------------------------------------
Интерфейс при использовании клавиатуры
---------------------------------------------------------------
Пробел Выбирает элемент.
Клавиша стрелка Выбирает следующий элемент в списке и
вправо или вниз удаляет выборку с предыдущего выбранного
элемента (если такой есть).
Клавиша стрелка Выбирает предшествующий элемент в списке
вверх или влево и удаляет выборку с предыдущего выбранного
элемента (если такой есть).
Клавиша PgUp Прокручивает текущий выбранный элемент
вниз панели перечня, выбирает первый
видимый элемент в панели перечня и снимает
выборку с предыдущего выбранного элемента
(если такой есть).
Клавиша PgDn Прокручивает текущий выбранный элемент
вверх панели перечня, выбирает последний
видимый элемент в панели перечня и снимает
выборку с предыдущего выбранного элемента
(если такой есть).
Клавиша HOME Прокручивает первый элемент панели перечня
вверх панели, выбирает первый видимый
элемент в панели перечня и снимает выборку
с предыдущего выбранного элемента (если
такой есть).
Клавиша END Прокручивает последний элемент панели пе-
речня вниз панели, выбирает последний
видимый элемент в панели перечня и снимает
выборку с предыдущего выбранного элемента
(если такой есть).
---------------------------------------------------------------
.
Windows 3.0/pg/2#3 = 23 =
Использование панелей перечня с множественной выборкой.
По умолчанию, панель перечня позволяет пользователю
выбирать одновременно только один элемент. Чтобы пользователь
мог выбрать одновременно несколько элементов, создайте панель
перечня с одним из следующих типов:
Тип Описание
---------------------------------------------------------------
LBS_MULTIPLESEL Панель перечня с типом LBS_MULTIPLESET
полностью аналогична стандартной панели
перечня, за исключением того, что
пользователь может выбирать больше, чем
один элемент.
LBS_EXTENDEDSEL Панель перечня с типом LBS_EXTENDEDSEL
предоставляет удобные средства для выбора
нескольких последовательных элементов, а
также для выбора отдельных элементов.
---------------------------------------------------------------
В оставшейся части данного раздела эти типы панелей с
множественной выборкой описаны более подробно.
Панели перечня с типом LBS_MULTIPLESEL.
Панель перечня, созданная с типом LBS_MULTIPLESEL,
полностью аналогична стандартной панели перечня, за исключением
того, что пользователь может выбирать в панели перечня больше,
чем один элемент. Нажатие мышкой или клавишей пробела в панели
перечня переключает состояние выборки элемента. Если
пользователь нажимает символьную клавишу, когда панель перечня
захватила ввод, то курсор панели перечня перемещается на
следующий элемент, который начинается с этого символа. Этот
элемент не будет выбран до тех пор, пока пользователь не нажмет
клавишу пробел.
.
Windows 3.0/pg/2#3 = 24 =
Таблица 8.2 Интерфейс пользователя с панелью перечня, имеющей
тип LBS_MULTIPLESEL.
---------------------------------------------------------------
Действие Результат
---------------------------------------------------------------
Интерфейс при использовании мыши
---------------------------------------------------------------
Единичное нажатие Переключает состояние выборки, но не
сникнопки мыши мает состояние выборки с предыдущего
выбранного элемента (если такой есть).
Двойное нажатие Аналогично единичному нажатию.
кнопки мыши
---------------------------------------------------------------
Интерфейс при использовании клавиатуры
---------------------------------------------------------------
Пробел Переключает состояние выборки, но не сни-
мает состояние выборки с предыдущего
выбранного элемента (если такой есть).
Клавиша стрелка Перемещает курсор панели перечня на
вправо или вниз следующий элемент.
Клавиша стрелка Перемещает курсор панели перечня на
вверх или влево предшествующий элемент.
Клавиша PgUp Прокручивает текущий выбранный элемент
вниз панели перечня и перемещает курсор на
первый видимый элемент в панели перечня.
Клавиша PgDn Прокручивает текущий выбранный элемент
вверх панели перечня и перемещает курсор
на последний видимый элемент в панели
перечня.
Клавиша HOME Прокручивает первый элемент панели перечня
вверх панели и перемещает курсор на первый
видимый элемент в панели перечня.
Клавиша END Прокручивает последний элемент панели пе-
речня вниз панели и перемещает курсор на
последний видимый элемент в панели
перечня.
---------------------------------------------------------------
Панели перечня с типом LBS_EXTENDEDSEL.
Панели перечня, созданные с типом LBS_EXTENDEDSEL,
предоставляют удобные средства для выбора нескольких
последовательных элементов в панели перечня, а также для выбора
отдельных элементов. В таблице 8.3 приведен интерфейс при
использовании мыши и клавиатуры для панелей перечня с типом
LBS_EXTENDEDSEL.
.
Windows 3.0/pg/2#3 = 25 =
Таблица 8.3 Интерфейс пользователя с панелью перечня, имеющей
тип LBS_EXTENDEDSEL.
---------------------------------------------------------------
Действие Результат Результат
(выкл. режим добавления) (вкл. режим добавления)
---------------------------------------------------------------
Интерфейс при использовании мыши
---------------------------------------------------------------
Единичное Выбирает элемент, уда- Как и при выключенном
нажатие ляет выборку с других режиме добавления, плюс
клавиши мышы элементов и устанав- выключает режим добавл-
ливает на него "якорь" ления.
выборки.
SHIFT + Выбирает элементы, на- Как и при выключенном
единичное ходящиеся между якорем режиме добавления, плюс
нажатие выборки и текущим эле- выключает режим добавл-
клавиши мышы ментом, и удаляет вы- ления.
борку с элементов, ко-
торые не попадают в
этот диапазон.
Двойное Аналогично единичному Как и при выключенном
нажатие нажатию. режиме добавления, плюс
SHIFT + Аналогично единичному выключает режим добавл-
Двойное нажатию + SHIFT. ления.
нажатие
CONTROL + Перемещает курсор вы- Как и при выключенном
единичное борки и переключает режиме добавления, плюс
нажатие состояние выборки для выключает режим добавл-
клавиши мышы выбираемых элементов, ления.
но не удаляет выборку
остальных элементов.
CONTROL + Не удаляет выборку с Как и при выключенном
SHIFT + других элементов (за режиме добавления, плюс
единичное исключением тех, кото- выключает режим добавл-
нажатие рые попадают в диапа- ления.
клавиши мышы зон между текущей и
последней установкой
якоря) и переключает
состояние всех элемен-
тов (в тоже состояние,
что и элемент, на кото-
ром установлен якорь)
от якоря до текущего
элемента. Не перемещаеь
якорь.
Тащить Помещает якорь выборки Как и при выключенном
.
Windows 3.0/pg/2#3 = 26 =
в точку, где пользова- режиме добавления, плюс
тель нажал на кнопку выключает режим добавл-
мыши, выбирает элементы ления.
от якоря до точки, в
которой пользователь
отпустил кнопку, и уда-
ляет выборку со всех
остальных элементов.
SHIFT + Выбирает элементы от Как и при выключенном
тащить якоря выборки до эле- режиме добавления, плюс
мента, на котором поль- выключает режим добавл-
зователь отпустил кноп- ления.
ку, и удаляет выборку
со всех остальных
элементов. Не переме-
щает якорь выборки.
CONTROL + Помещает якорь выборки Как и при выключенном
тащить в точку, где пользова- режиме добавления, плюс
тель нажал кнопку мыши. выключает режим добавл-
Не удаляет выборку с ления.
остальных элементов, но
переключает состояние
всех элементов в состо-
яние, соответствующее
элементу, на котором
находится якорь, от
якоря, до точки где
пользователь отпустил
кнопку мыши.
CONTROL + Не удаляет выборку с Как и при выключенном
SHIFT + остальных элементов режиме добавления, плюс
тащить (за исключением тех, выключает режим добавл-
которые входят в диапа- ления.
зон выборки, определяе-
мый последним положе-
нием якоря), но пере-
ключает состояние всех
элементов (в состояние,
соответсвующее элементу
на котором установлен
якорь) от якоря до
элемента, на котором
пользователь отпустил
кнопку мыши. Не пере-
мещает якорь.
---------------------------------------------------------------
Интерфейс при использовании клавиатуры (а)
---------------------------------------------------------------
SHIFT + F8 Включает режим добав- Выключает режим добав-
ления. В этом режиме ления.
курсор панели перечня
мигает.
.
Windows 3.0/pg/2#3 = 27 =
Пробел Выбирает элемент, уда- Переключает состояние
ляет выборку с других элемента и устанавли-
элементов и устанав- вает якорь, но не уда-
ливает на него "якорь" ляет выборку с других
выборки. элементов.
SHIFT + Удаляет выборку с пре- Не удаляет выборку с
Пробел дыдущих выбранных эле- остальных элементов
ментов и переключает (за исключением тех,
состояние элементов (в которые входят в диапа-
соответствии с сосоя- зон выборки, определяе-
нием элемента, на кото- мый последним положе-
ром установлен якорь) нием якоря), но пере-
от якоря до текущей по- ключает состояние всех
зиции. элементов (в состояние,
соответсвующее элементу
на котором установлен
якорь) от якоря до
текущего элемента. Не
перемещает якорь.
Клавиши нап- Перемещают курсор па- Перемещают курсор па-
равления (б) нели перечня в соот- нели перечня в соот-
ветствии с клавишей, ветствии с клавишей, но
выбирают элемент под не выбирают элемент
курсором, перемещают удаляют выборку со всех
на него якорь и уда- остальных элементов
ляют выборку со всех или перемещают на него
остальных элементов. якорь
SHIFT + Удаляет выборку со всех Не удаляет выборку с
Клавиши нап- остальных элементов, остальных элементов
равления перемешает курсор в (за исключением тех,
соответсвии с клавишей которые входят в диапа-
и переключает зон выборки, определяе-
состояние элементов (в мый последним положе-
соответствии с сосоя- нием якоря), перемещает
нием элемента, на кото- курсор в соответствии
ром установлен якорь) с клавишей и пере-
от якоря до текущей по- ключает состояние всех
зиции. Не перемещает элементов (в состояние,
якорь. соответсвующее элементу,
на котором установлен
якорь) от якоря до
текущего элемента. Не
перемещает якорь.
---------------------------------------------------------------
а) За исключением SHIFT + F8 все клавиши и комбинации клавиш
могут использоваться в комбинации с CONTROL. Например, CONTROL
.
Windows 3.0/pg/2#3 = 28 =
+ SHIFT + SPACEBAR имеет то же значение, что и SHIFT +
SPACEBAR.
б) Клавиши направления включают клавиши со стрелками и клавиши
HOME, END, PAGE UP и PAGE DOWN. Описание того, как каждая
клавиша управляет перемещением курсора, вы найдете в таблице
8.2 "Интерфейс пользователя для панелей перечня, имеющих тип
LBS_MULTIPLESEL".
Использование панелей перечня, состоящих из нескольких
столбцов.
Обычно панели перечня выводят элементы в один столбец.
Если вы знаете, что панель перечня будет содержать много
элементов, то вы можете создать панель перечня с типом
LBS_MULTICOLOMN. Этот тип определяет панель перечня, в которой
элементы отображаются в несколько столбцов. Элементы такой
панели перечня "змеятся" с нижней части одной колонки в другую.
По этой причине для таких панелей перечня не предусмотрена
вертикальная прокрутка. Однако, если панель перечня может
содержать больше элементов, чем могут быть отображены
одновременно, вы можете задать тип WM_HSCROLL для того, чтобы
пользователь мог выполнять горизонтальную прокрутку. Ниже
приведен пример создания панели перечня, которая содержит
несколько столбцов и занимает всю область пользователя
родительского окна:
#define IDC_MULTILISTBOX
RECT Rect;
HWND hMultiListBox;
.
.
.
GetClientRect(hWnd, (LPRECT) &Rect);
hMultiListBox = CreateWindow("Listbox",
NULL,
WS_CHILD | WS_VISIBLE | LBS_SORT |
LBS_MULTICOLOMN | WS_HSCROLL | LBS_NOTIFY,
Rect.left,
Rect.top,
Rect.right,
Rect.botton,
hWnd,
IDC_MULTILISTBOX,
hInst,
NULL);
В этом примере функция GetClientRect используется для
получения координат области пользователя родительского окна,
которые затем передаются функции CreateWindow для установки
местоположения и размеров панели перечня.
.
Windows 3.0/pg/2#3 = 29 =
Примером панели перечня, содержащей несколько столбцов,
может служить окно директории, которое отображает File Manager.
Чтобы установить ширину колонки панели перечня, которая
содержит несколько колонок, можно послать ей сообщение
LB_SETCOLOMNWIGTH.
Использование рисуемых панелей перечня.
Как и клавиши, панели перечня можно также создать как
рисуемый блок управления. Однако в случае панелей перечня ваша
программа отвечает только за рисование элементов панелей
перечня.
Для создания рисуемых панелей перечня можно использовать
один из следующих типов: LBS_OWNERDRAWFIXED или
LBS_OWNERDRAWVARIABLE. Тип LBS_OWNERDRAWFIXED определяет панель
перечня, в которой все рисуемые элементы имеют одинаковую
высоту.
Для добавления элемента к панели перечня пошлите сообщение
LB_ADDSTRING или LB_INSERTSTRING. Параметр lParam может
содержать любое 32-битовое значение, которое вы хотите связать
с элементом. Если lParam содержит указатель на строку, то тип
панели перечня LBS_HASSSTRING позволяет обрабатывать память и
указатели на строки. Это позволяет прикладной программе
использовать сообщение LB_GETTEXT для получения текста
определенного элемента. Аналогично, если вы создаете панель
перечня с типами LBS_SORT или LBS_HASSTRING, то Windows
автоматически сортирует элементы панели перечня.
Если вы создаете панель перечня с типом LBS_SORT, но без
LBS_HASSTRING, Windows не может определить порядок элементов
внутри панели перечня. В этом случае при добавлении элемента в
панель перечня (с помощью сообщения LB_ADDSTRING) Windows
посылает одно или несколько сообщений WM_COMPAREITEM владельцу
панели перечня. В этих сообщениях параметр lParam указывает на
структуру данных COMPAREITEMSTRUCT, которая содержит
информацию, идентифицирующую два элемента панели перечня. При
возврате управления после обработки этого сообщения прикладная
программа возвращает значение, определяющее какой (если такой
есть) элемент должен находиться выше другого. Windows посылает
эти сообщения до тех пор, пока не отсортирует все элементы
панели перечня.
При добавлении или вставке элемента в панель перечня
Windows определяет размер элемента, посылая сообщение
WM_MEASHUREITEM владельцу панели перечня. Windows требуется
информация, которая позволила бы осуществить взаимодействие с
пользователем. Если вы создали панель перечня с типом
LBS_OWNERDRAWFIXED, то Windows посылает это сообщение только
один раз, поскольку все элементы в панели перечня будут иметь
одинаковую высоту. Для панели перечня с типом
.
Windows 3.0/pg/2#3 = 30 =
LBS_OWNERDRAWVARIABLE Windows посылает сообщение
WM_MEASHUREITEM для каждого добавляемого элемента.
Параметр lParam сообщения WM_MEASHUREITEM содержит
указатель на структуру MEASHUREITEMSTRUCT. Кроме идентификатора
и типа блока управления эта структура содержит номер элемента
панели перечня, инофрмация о котором необходима (для панелей
перечня, имеющих тип LBS_OWNERDRAWVARIABLE), и необязательное
32-битовое значение, связанное с данным элементом. При
получении сообщения WM_MEASHUREITEM программа должна занести в
поле itemHeight структуры MEASHUREITEMSTRUCT высоту элемента.
Высота вычисляется в вертикальных единицах панели диалога.
Вертикальная единица панели диалога равна 1/8 базовой единицы
панели диалога, которая вычисляется из высоты системного
шрифта. Для определения высоты базовой единицы панели диалога в
пикселях можно воспользоваться функцией GetDialogBaseUnit.
Когда Windows выводит панель перечня, или когда необходимо
изменить вид элемента в панели перечня, Windows посылает окну
сообщение WM_DRAWITEM. Параметр lParam сообщения WM_DRAWITEM
содержит указатель на структуру данных DRAWITEMSTRUCT. Эта
структура содержит информацию, идентифицирующую элемент панели
перечня и тип требуемой операции. Как и в случае с рисуемой
клавишей, ваша программа использует эту инофрмацию для
рисования элемента.
Для удаления элемента из рисуемой панели перечня пошлите
панели перечня сообщение LB_DELETESTRING. Windows в свою
очередь пошлет сообщение WM_DELETEITEM владельцу панели
перечня. (Windows также посылает это сообщение при разрушении
панели перечня.) Параметр lParam этого сообщения содержит
указатель на структуру данных DELETEITEMSTRUCT. Эта структура
определяет панель перечня и элемент, который должен быть
удален, а также 32-битовое число, связанное с этим элементом.
Ваша прикладная программа может использовать эту инофрмацию для
освобождения памяти, занятой инофрмацией об этом элементе.
8.4.4 Комбинированные блоки управления.
Комбинированный блок управления состоит из панели перечня
и статического или редактируемого блока. В зависимости от типа
панели перечня, она может отображаться все время или быть
скрытой до тех пор, пока пользователь ее не отобразит.
Интерфейс с панелью перечня и редактируемым блоком управления
аналогичны стандартным, за исключением случаев, которые
описаны.
Тип CBS_SIMPLE создает комбинированный блок с
редактируемым блоком и панелью перечня, которая отображается
под редактируемым блоком. Когда комбинированный блок захватил
ввод, пользователь может набирать в редактируемом блоке
управления текст. Если элемент в панели перечня совпадает с
тем, который пользователь набрал, то соответствующий элемент
.
Windows 3.0/pg/2#3 = 31 =
прокручивается в начало панели перечня. Пользователь может
затем выбрать элементы из панели перечня, используя мышь или
клавиатуру (клавиши стрелка вверх и стрелка вниз).
Тип CBS_DROPDOWN аналогичен типу CBS_SIMPLE, за
исключением того, что панель перечня отображается только в том
случае, если пользователь выбирает элемент, следующий за
редактируемым блоком управления, или если пользователь нажимает
клавиши ALT + DOWN ARROW или ALT + UP ARROW. Даже если панель
перечня скрыта, пользователь может выбирать элементы в панели
перечня с помощью клавиш стрелка вверх и стрелка вниз.
Комбинированный блок управления с типом CBS_DROPDOWNLIST
выглядит аналогично блоку с типом CBS_DROPDOWN, за исключением
того, что в нем редактируемый блок управления заменен на
статический. Вместо того, чтобы набирать элемент в
редактируемом блоке, пользователь может выбирать элементы в
панели перечня, вводя только его первый символ. Конечно
пользователь может кроме этого использовать для выбора
элементов мышь и клавиши стрелка вверх и стрелка вниз.
Вы можете добавлять и удалять элементы в панели перечня
комбинированного блока тем же путем, как и из обычной панели
перечня, однако вы должны использовать сообщения CB_ADDSTRING,
CB_INSERTSTRING, CB_DIR и CB_DELETESTRING. Windows
предоставляет кроме этого дополнительные сообщения для
получения содержимого редактируемого блока, сравнения с
элементами панели перечня и для работы с содержимым
редактируемого блока управления.
Со многих точек зрения комбинированный блок аналогичен
панели перечня в том, как пользователь с ним взаимодействует.
Все уведомляющие коды панели перечня используются для
комбинированного блока. Кроме них Windows посылает уведомляющие
сообщения для указания следующих событий:
- Выпадает панель перечня из комбинированного блока
(CBN_DROPDOWN).
- Пользователь изменил текст в редактируемом блоке, и
Windows модифицирует изображение (CBN_EDITCHANGE).
- Пользователь изменил текст в редактируемом блоке, но
Windows не модифицировала изображение (CBN_EDITUPDATE).
- Комбинированный блок потерял захват ввода
(CBN_KILLFOCUS). В случае, если комбинированный блок
управоения имеет тип CBS_DROPDOWN или CBS_DROPDOWNLIST,
это приводит к тому, что Windows удаляет с экрана панель
перечня.
- Комбинированный блок осуществляет захват ввода
(CBN_SETFOCUS).
.
Windows 3.0/pg/2#3 = 32 =
Как и панель перечня, комбинированный блок может
создаваться с типом, определяющим переменную или фиксированную
высоту рисуемых элементов. Однако в случае комбинированных
блоков управления программа отвечает за вывод элементов в
панели перечня и в редактируемом или статическом блоке.
Например, если пользователь выбирает элемент в панели перечня,
программа для панели перечня получает сообщение WM_DRAWITEM,
определяющее, что элемент должен быть выведен как выбранный, и
другое сообщение (WM_DRAWITEM) для редактируемого блока.
Вы можете также указать для комбинированного блока тип
CBS_SORT. Windows сортирует элементы комбинированного блока тем
же путем, что и рисуемые панели перечня.
Для комбинированных блоков не существует типов,
предусматривающих несколько столбцов для панели перечня.
8.4.5 Редактируемые блоки.
Редактируемый блок управления - это прямоугольное дочернее
окно, в котором пользователь может вводить и редактировать
текст. Редактируемые блоки имеют широкие возможности, такие как
редактирование нескольких строк и прокрутка. Вы указываете
необходимые вам функции в типе блока управления.
Тип редактируемого блока управления определяет, как он
будет отображаться и функционировать. Например, тип
ES_MULTILINE определяет редактируемый блок, в который можно
вводить больше одной строки текста. Типы ES_AUTOHSCROLL и
ES_AUTOVSCROLL определяют, что блок управления будет
производить прокрутку, если пользователь введет текст, который
не будет помещаться в области пользователя редактируемого блока
управления. Если эти типы не указаны и пользователь вводит
больше, чем может поместиться в одной строке, то ввод будет
продолжен на следующей строке, если блок управления содержит
несколько строк. Вы можете кроме этого использовать типы
WS_HSCROLL и (в случае многострочного блока управления)
WS_VSCROLL, которые также позволяют пользователю прокручивать
текст в блоке управления.
Ваша прикладная программа может использовать редактируемый
блок управления для ввода паролей или другого секретного текста
таким образом, что он не будет отображаться на экране. Тип
ES_PASSWORD определяет редактируемый блок управления, который
не отображает текст при вводе его пользователем. Вместо этого в
блоке управления отображается для каждого набранного символа
необязательный символ (по умолчанию звездочка "*"). Вы можете
изменить этот символ с помощью сообщения EM_SETPASSWORDCHAR.
С помощью сообщения EM_SETTABSTOPS вы можете установить
позиции табуляции в многострочном редактируемом блоке
управлления. Это сообщение определяет число пробелов между
.
Windows 3.0/pg/2#3 = 33 =
следующими позициями табуляции.
Редактируемый блок управления посылает уведомляющее
сообщение своему родительскому окну. Например, редактируемый
блок управления посылает сообщение EN_CHANGE, когда
пользователь изменяет текст. Редактируемый блок управления
может также получать сообщения, такие как EM_GETLINE и
EM_LINELENGTH. В этом случае он выполняет указанные действия.
Очень удобная функция редактируемого блока управления -
это функция отмена изменений, произведенных в блоке
управлления. Для определения того, может ли блок управления
выполнить эту функцию, вы можете послать сообщение EM_CANUNDO.
Если Windows вернет ненулевое значение, то значит можно
отменить последнее изменение. В этом случае для выполнения этой
функции вы можете послать сообщение EM_UNDO редактируемому
блоку управления.
В таблице 8.4 описан пользовательский интерфейс с
редактируемым блоком управления.
Таблица 8.4 Интерфейс пользователя с редактируемым блоком
управления.
---------------------------------------------------------------
Действие Результат
---------------------------------------------------------------
Интерфейс при использовании мыши
---------------------------------------------------------------
Единичное нажатие Позиционирует точку ввода и помещает якорь
кнопки мышы выборки.
Двойное нажатие Выбирает слово.
кнопки мышы
SHIFT + Позиционирует точку ввода и расширяет вы-
единичное нажатие борку от "якоря" выборки до точки ввода.
кнопки мышы
Тащить Помещает "якорь" выборки, перемещает точку
ввода и расширяет выборку от "якоря" до
точки ввода.
---------------------------------------------------------------
Интерфейс при использовании клавиатуры
---------------------------------------------------------------
Клавиши направления Убирают выборку с любого текста и переме-
щают точку ввода в соответствующем направ-
лении.
SHIFT + Перемещает "якорь" выборки (если он еще не
клавиши направления установлен), перемещает точку ввода и вы-
бирает текст между точкой ввода и "якорем"
выборки.
.
Windows 3.0/pg/2#3 = 34 =
CONTROL + стрелка Перемещает точку ввода на начало слова в
влево, CONTROL + указанном направлении.
стрелка вправо
SHIFT + CONTROL + Устанавливает "якорь" выборки (если он еще
стрелка влево, не установлен), перемещает точку ввода на
SHIFT + CONTROL + начало слова в указанном направлении и
стрелка вправо выбирает текст между "якорем" и точкой
ввода.
HOME Удаляет выборку и перемещает точку ввода
в начало строки.
SHIFT + HOME Устанавливает "якорь" выборки (если он еще
не установлен), перемещает точку ввода в
начало строки и выбирает текст между
"якорем" и точкой ввода.
CONTROL + HOME Помещает точку ввода перед первым симво-
лом в редактируемом блоке управления.
SHIFT + CONTROL + Помещает "якорь" выборки (если он еще не
HOME установлен), помещает точку ввода перед
первым символом в редактируемом блоке
управления и выбирает текст между "яко-
рем" и точкой ввода.
END Удаляет выборку и перемещает точку ввода
в конец строки.
SHIFT + END Устанавливает "якорь" выборки (если он еще
не установлен), перемещает точку ввода в
конец строки и выбирает текст между
"якорем" и точкой ввода.
CONTROL + END Помещает точку ввода после последнего
символа в редактируемом блоке управления.
SHIFT + CONTROL + Устанавливает "якорь" выборки (если он еще
END не установлен), помещает точку ввода после
последнего символа в редактируемом блоке
управления, и выбирает текст между "якорем"
и точкой ввода.
DELETE Если текст выбран, то удаляет этот текст.
Если нет, то удаляет символ, справа от
точки ввода.
SHIFT + DELETE Если текст выбран, вырезает его и помещает
в системный буфер. Иначе удаляет символ пе-
ред точкой ввода.
.
Windows 3.0/pg/2#3 = 35 =
SHIFT + INSERT Вставляет текст из системного буфера, на-
чиная с точки ввода.
CONTROL + INSERT Копирует, но не удаляет, выбранный текст
в системный буфер.
PAGE UP В многострочном редактируемом блоке
управления прокручивает текст вверх на
строку меньше, чем высота блока управле-
ния.
CONTROL + PAGE UP В многострочном редактируемом блоке
управления прокручивает текст влево на
ширину блока управления минус один
символ.
PAGE DOWN В многострочном редактируемом блоке
управления прокручивает текст вниз на
строку меньше, чем высота блока управле-
ния.
CONTROL + PAGE DOWN В многострочном редактируемом блоке
управления прокручивает текст вправо на
ширину блока управления минус один
символ.
CONTROL + ENTER В многострочном редактируемом блоке
управления в панели диалога завершает
строку и перемещает курсор в начало сле-
дующей строки.
CONTROL + TAB В многострочном редактируемом блоке
управления в панели диалога вставляет
символ табуляции.
---------------------------------------------------------------
В конце данной главы описан пример EditCntl, который
использует многострочный редактируемый блок управления для
выполнения базовых функций по вводу и редактированию текста.
8.4.6 Строки прокрутки.
Строки прокрутки - это предопределенные блоки управления,
которые могут располагаться в любом месте окна. Они позволяют
пользователю выбирать значение в непрерывном диапазоне. Строки
прокрутки посылают уведомляющие сообщения родительскому окну в
тех случаях, когда пользователь нажимает кнопку мыши, когда
курсор находится на блоке управления или перемещает бегунок с
помощью клавиатуры. Это позволяет прикладной программе
обрабатывать сообщения для того, чтобы определить выбранное
пользователем значение и чтобы в соответствии с ним переместить
бегунок.
.
Windows 3.0/pg/2#3 = 36 =
Для создания дочернего окна строки прогрутки вы можете
использовать типы SBS_HORZ или SBS_VERT. Строка прокрутки может
иметь любой необходимый размер. Если вы хотите, чтобы ширина
(для вертикальной строки прокрутки) или высота (для
горизонтальной строки прокрутки) соответствовала размерам окна,
вы можете использовать соответствующие размерности окна, как
показано в следующем примере:
hScrollBar = CreateWindow("Scrollbar", NULL
WS_CHILD | WS_VISIBLE | SBS_VERT,
20,20,
GetSystemMetrics(SM_CXVSCROLL),50,
hWnd, IDSCROLLBAR, hInst, NULL);
Функция GetSystemMetrics возвращает значение SM_CXVSCROLL,
которое представляет собой ширину стандартной строки прокрутки
окна.
Строка прокрутки не имеет специальных уведомляющих
сообщений. Вместо этого они посылают сообщения WM_HSCROLL и
WM_VSCROLL как строки поркрутки окна. Параметр wParam этих
сообщений содержит значение, определяющее тип выполняемой
прокрутки. Прикладная программа использует эту инофрмацию для
определения местоположения бегунка и в своих внутренних целях.
Таблица 8.5 содержит список возможных значений параметра wParam
и действия пользователя, которые приводят к их генерации.
.
Windows 3.0/pg/2#3 = 37 =
Таблица 8.5 Пользовательский интерфейс со строкой прокрутки.
---------------------------------------------------------------
Значение пара- Мышь Клавиатура
метра wParam
---------------------------------------------------------------
SB_LINEUP Пользователь нажима- Пользователь нажал на
ет мышкой на стрелку клавишу стрелка влево
вверх или стрелку или стрелка вверх.
влево в строке прок-
рутки.
SB_LINEDOWN Пользователь нажима- Пользователь нажал на
ет мышкой на стрелку клавишу стрелка вправо
вниз или стрелку или стрелка вниз.
вправо в строке прок-
рутки.
SB_PAGEUP Пользователь нажал Пользователь нажал
мышкой выше или левее PAGE UP.
бегунка строки прок-
рутки.
SB_PAGEDOWN Пользователь нажал Пользователь нажал
мышкой ниже или PAGE DOWN.
правее бегунка строки
прокрутки.
SB_ENDSCROLL Пользователь нажал Нет.
мышкой в любом месте
строки прокрутки, за
исключением бегунка.
SB_THUMBTRACK Пользователь "тащит" Нет.
бегунок.
SB_THUMBPOSITION Пользователь отпус- Нет.
кает бегунок.
SB_TOP Нет. Пользователь нажимает
HOME.
SB_BOTTOM Нет. Пользователь нажимает
END.
---------------------------------------------------------------
Windows может позиционировать бегунок строки прокрутки,
связанной с панелью перечня или редактируемым блоком
управления, в соответствии с его содержимым. Однако, строка
прокрутки, являющаяся дочерним окном, представляет диапазон
значений, которые имеют смысл только для вашей программы.
Следовательно она отвечает за установку диапазона строки
прокрутки и за позиционирование бегунка после каждого
.
Windows 3.0/pg/2#3 = 38 =
перемещения его пользователем.
Функция SetScrollRange устанавливает диапазон значений,
представляемых строкой прокрутки. Например, в вашей программе
есть строка прокрутки, с помощью которой пользователь может
выбрать день месяца. В этом случае диапазон строки прокрутки
для Января может быть установлен следующим образом:
SetScrollRange(hScrollBar, SB_CTL, 1, 31, 1);
В данном примере константа SB_CTL сообщает Windows, что
строка прокрутки является отдельным блоком управления, не
связанным с окном. Третий и четвертый параметры определяют
диапазон, а пятый параметр (равный 1) заставляет Windows
перерисовать строку прокрутки для отображения нового диапазона.
Даже после того, как вы определили диапазон строки
прокрутки, Windows не может устанавливать бегунок в
соответствии с действиями пользователя, это остается на вашей
программе. Когда ваша программа получает от строки прокрутки
сообщения WM_HSCROLL или WM_VSCROLL, вы должны проверить
параметр wParam для определения того, насколько далеко
пользователь переместил бегунок. После этого вы вызываете
функцию SetScrollPos для позиционирования бегунка. Так же, если
пользователь может изменить положение бегунка, не используя
строку прокрутки (например, набирая текст в редактируемом блоке
управления), ваша программа должна переустановить позицию
бегунка в соответствии с новым значением.
8.5 Пример прикладной программы EditCntl
Эта прикладная программа иллюстрирует, как можно
использовать редактируемый блок управления в основном окне
прикладной программы для того, чтобы обеспечить ввод
многострочного текста и его редактирование. Прикладная
программа EditCntl заполняет область пользователя основного
окна многострочным редактируемым блоком управления и управляет
размерами области пользователя для уверенности в правильном ее
заполнении. Основное окно программы EditCntl приведено на
рисунке 8.1.
Рисунок 8.1 Окно прикладной программы EditCntl.
1. Вся область пользователя окна представляет собой один
блок управления.
Для создания прикладной программы скопируйте и
переименуйте исходные файлы ПП EditMenu, а затем выполните
следующие изменения:
1. Добавьте новую константу во включаемый файл.
2. Добавьте новые переменные.
.
Windows 3.0/pg/2#3 = 39 =
3. Добавьте функцию CreateWindow.
4. Модифицируйте фрагмент WM_COMMAND.
5. Добавьте фрагмент WM_SETFOCUS.
6. Добавьте фрагмент WM_SIZE.
7. Оттранслируйте и скомпонуйте программу.
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
8.5.1 Добавление константы во включаемый файл.
Необходимо добавить константу, которая будет
использоваться в качестве идентификатора редактируемого блока
управления во включаемый файл. Добавьте следующий оператор:
#define ID_EDIT 300
8.5.2 Добавление новых переменных.
Необходимо иметь глобальную переменную для хранения
дескриптора окна редактируемого блока управления. Добавьте
следующий оператор в начало исходного С-файла:
HWND hEditWnd; /* дескриптор редактируемого окна */
Кроме того, необходимо иметь локальную переменную в
функции WinMain для хранения координат прямоугольной области
пользователя. Эти координаты используются для определения
размера блока управления. Добавьте следующий оператор в начало
функции WinMain:
RECT Rect;
8.5.3 Добавление функции CreateWindow.
Необходимо создать редактируемый блок управления,
используя функцию CreateWindow; но перед этим нужно получить
размеры области пользователя, чтобы можно было установить
размеры блока управления. Добавьте следующие операторы к
функции WinMain сразу после создания основного окна:
GetClientRect(hWnd, (LPRECT) &Rect);
hEditWnd = CreateWindow("Edit",
NULL,
WS_CHILD | WS_VISIBLE |
ES_MULTILINE |
WS_VSCROLL | WS_HSCROLL |
ES_AUTOHSCROLL | ES_AUTOVSCROLL,
.
Windows 3.0/pg/2#3 = 40 =
0,
0,
(Rect.right-Rect.left),
(Rect.bottom-Rect.top),
hWnd,
ID_EDIT,
hInst,
NULL);
if (!hEditWnd) {
DestroyWindow(hWnd);
return (NULL);
}
Функция GetClientRect получает размеры области
пользователя основного окна и помещает эту информацию в
структуру Rect. Функция CreateWindow создает редактируемый блок
управления, используя ширину и высоту, вычисленную при помощи
этой структуры.
Функция CreateWindow создает редактируемое окно. Для
создания редактируемого блока управления необходимо
использовать встроенный класс блока управления "Edit" и
указать тип окна WS_CHILD. Встроенные блоки управления могут
быть использованы только как дочерние окна. Они не могут быть
использованы как основные или накладываемые окна. Поскольку
дочернее окно подразумевает наличие родительского окна, в
вызове функции указывается дескриптор основного окна hWnd.
Для этого блока управления также указывается ряд типов
редактируемых блоков управления. Подобно типу окон, тип
редактируемого блока управления определяет, как он будет
выглядеть и как он будет работать. Редактируемый блок
управления, описываемый в данном примере, является
многострочным, т.е. позволяющим вводить в окно более одной
строки текста. Кроме того, блок управления будет осуществлять
горизонтальную и вертикальную прокрутку, если пользователь
вводит больше текста, чем может поместиться в окне.
Верхний левый угол редактируемого блока управления
размещается в верхнем левом углу области пользователя
родительского окна (координаты дочернего окна всегда берутся
относительно области пользователя родительского окна).
Следующие два аргумента Rect.right-Rect.left и
Rect.bottom-Rect.top определяют высоту и ширину редактируемого
блока управления, давая уверенность в том, что этот блок
заполняет область пользователя при первой индикации окна.
Поскольку редактируемый блок управления посылает
уведомляющие сообщения своему родительскому окну, он должен
быть задан своим ID (идентификатром). Дочерние окна не могут
иметь меню, так что вместо него аргумент меню в функции
CreateWindow используется для указания ID блока управления. Для
.
Windows 3.0/pg/2#3 = 41 =
данного блока управления устанавливается ID, равный ID_EDIT.
Любое уведомляющее сообщение, посылаемое родительскому окну
редактируемым блоком управления, будет содержать этот ID.
Если редактируемый блок управления не может быть создан,
функция CreateWindow возвращает NULL. В этом случае прикладная
программа не может продолжать свою работу, и функция
DestroyWindow используется для разрушения основного окна перед
завершением программы.
8.5.4 Модификация фрагмента WM_COMMAND.
Дочерние блоки управления уведомляют родительское окно
посредством сообщения WM_COMMAND. Параметр wParam этого
сообщения содержит идентификатор блока управления, который
послал это сообщение.
Для распознавания сообщения о недостатке памяти для
редактируемого блока управления добавьте следующий код в
фрагмент WM_COMMAND:
case IDC_EDIT:
if(HIWORD (lParam) == EN_ERRSPACE) {
MessageBox(
GetFocus(),
"Не хватает памяти",
"Программа EditCntl",
MB_ICONHAND | MB_OK);
}
break;
8.5.5 Добавление фрагмента WM_SETFOCUS.
Для направления ввода в редактируемый блок управления при
активизации родительского окна добавьте следующие операторы в
функцию окна:
case WM_SETFOCUS:
SetFocus(hEditWnd);
break;
8.5.6 Добавление фрагмента WM_SIZE.
Необходимо добавить фрагмент WM_SIZE к функции окна.
Windows посылает сообщение WM_SIZE функции окна каждый раз,
когда изменяется ширина или высота окна. Поскольку изменение
размеров основного окна не изменяет автоматически размеров
редактируемого блока управления, то для их изменения необходим
фрагмент WM_SIZE.
Добавьте следующие операторы к функции окна:
case WM_SIZE:
.
Windows 3.0/pg/2#3 = 42 =
MoveWindow(hEditWnd, 0, 0, LOWORD(lParam),
HIWORD(lParam), TRUE);
break;
8.5.7 Трансляция и компоновка.
В файле make изменений не требуется. Оттранслируйте и
скомпонуйте программу EditCntl, затем запустите Windows и
прикладную программу. Теперь можно вставить текст, использовать
клавишу BackSpace для удаления текста и сделать выборку при
помощи мыши, а не клавиатуры. Поскольку при создании блока
управления специфицировано ES_MULTILINE, ES_AUTOVSCROLL и
ES_AUTOHSCROLL, блок управления может редактировать текст на
всем экране, а также осуществлять прокрутку.
Прикладная программа EditCntl иллюстрирует первый шаг,
требующийся для того, чтобы сделать простой текстовый редактор.
Для того, чтобы сделать полный редактор, можно добавить меню
File к основному окну для открытия и сохранения файлов текстов,
а также для копирования или поиска текста из редактируемого
блока управления и добавить меню Edit к основному окну для
копирования, разрезания и склеивания текста с помощью
системного буфера. Последующие главы проиллюсртируют некоторые
простые пути добавления этих возможностей в вашу программу.
8.6 Заключение.
В данное главе описано использование блоков управления в
прикладной программе. Блок управления - это специальное
дочернее окно, которое вы можете добалять к своей программе для
выполнения ввода от пользователя. Windows выполняет
автоматическую поддержку большинства типов блоков управления.
Например, Windows может автоматически рисовать блок управления
в указанной вами точке, при выборе пользователем блока
управления Windows посылает вашей программе сообщение,
содержащее идентификатор этого блока управления.
В данной главе также описано, как использовать наиболее
часто встречающиеся блоки управления.
Дополнительную информацию относительно блоков управления
вы найдете в:
Раздел Руководство
---------------------------------------------------------------
Обработка сообщений Руководство программиста, Глава 4, "Ввод с
ввода использованием мыши и клавиатуры"
Использование бло- Руководство программиста, Глава 9, "Панели
ков управления в диалога"
панелях дилога
.
Windows 3.0/pg/2#3 = 43 =
Управляющие функции Справочное руководство, том 1, глава 1,
"Функции интерфейса управления окнами",
Операторы описания Справочное руководство, том 2, глава 8,
ресурсов "Операторы описания ресурсов".
Демонстрационная Диск "SDK Sample Source Code Disk"
программа
OWNCOMBO.EXE,
иллюстрирует исполь-
зование каскадных
меню, меню, рисуемых
владельцем и конт-
рольных отметок, ри-
суемых владельцем.
.
Windows 3.0/pg/2#3 = 44 =
Глава 9. Панель диалога.
Панель диалога - это накладываемое окно, которое
прикладная программа использует для взаимодействия с
пользователем. Обычно панели диалога содержат один или
несколько блоков управления.
В данной главе описаны следующие разделы:
- Что такое панель диалога.
- Создание и использование модальных и немодальных панелей
диалога.
- Создание функций панелей диалога.
- Использование блоков управления в панели диалога.
В данной главе описан пример прикладной программы
FileOpen, которая иллюстрирует создание и использование
модальной панели диалога, содержащей несколько блоков
управления.
9.1 Что такое панель диалога.
Панель диалога - это накладываемое окно, которое
прикладные программы использует для индицирования или запроса
информации. Панели диалога обычно используют для запроса у
пользователя информации, необходимой для завершения команды.
Панель диалога содержит один или несколько блоков управления, с
помощью которых пользователь может ввести текст, выбрать
указания или заставить выполнить отдельную команду.
Пример панели диалога уже рассматривался в программе
Generic - это панель диалога About. Эта панель диалога содержит
текстовый блок управления, дающий информацию о прикладной
программе, а также блок управления типа альтернативная клавиша,
который может использоваться для закрытия панели диалога и
возврата в основное окно. Для обработки панели диалога
необходимо задать шаблон панели диалога, функцию диалога и
некоторые средства для вызова панели диалога.
Шаблон панели диалога - это описание панели диалога и
блоков управления, которые она содержит. Шаблон можно создать с
помощью текстового редактора или Редактора диалога среды
Windows 3.0, и затем добавить его к файлу описания ресурсов.
Функция диалога - это функция многократного вызова,
которую вызывает Windows, когда она имеет сообщение для панели
диалога. Хотя функция диалога аналогична функции окна, Windows
выполняет специальную обработку панелей диалога таким образом,
что функция диалога (по сравнению с функцией окна) отвечает за
другие действия.
.
Windows 3.0/pg/2#3 = 45 =
Обычный способ вызова панели диалога - в ответ на ввод из
меню. Например, команды "Open" и "Save As" в меню File требуют
дополнительной информации для завершения их работы. Обе они
индицируют панели диалога для запроса дополнительной
информации.
Имеется два типа панелей диалога: модальные и немодальные.
9.1.1 Модальные панели диалога.
Модальная панель диалога (About) уже использовалась в
прикладной программе Generic. Модальная панель диалога - это
накладываемое окно, которое индицирует информацию и запрос на
ввод от пользователя. Она называется модальной, поскольку
делает родительское окно временно недоступным и заставляет
пользователя закончить запрашиваемые действия перед возвратом
управления родительскому окну. Например, программа Windows
Notepad выводит модальную панель диалога в ответ на выбор
команды Open в меню File. Notepad не может продолжить
выполнение команды Open до тех пор, пока пользователь не укажет
файл.
Хотя можно дать модальной панели диалога почти любой тип,
рекомендованными типами являются DS_MODALFRAME, WS_CAPTION,
и WS_SYSMENU. Тип DS_MODALFRAME задает панели диалога в качестве
параметра двойную линию окантовки.
Модальная панель диалога запускает свой собственный цикл
обработки сообщений из очереди прикладной программы без
возврата в функцию WinMain. Для того, чтобы закрыть
родительскому окну обработку ввода, панель диалога делает его
недоступным перед этой обработкой. По этой причине модальная
панель диалога никогда не может быть создана с заданием типа
WS_CHILD, поскольку, если родительское окно делается
недоступным, также делается недоступным и окно дочернего типа,
принадлежащее этому родительскому окну.
Для отображения модальной панели диалога используется
функция DialogBox. Работа модальной панели диалога
заканчивается с помощью функции EndDialog.
9.1.2 Немодальные панели диалога.
Немодальная панель диалога - это просто накладываемое
окно, которое индицирует информацию или запрашивает ввод от
пользователя. В отличие от модальной панели диалога,
немодальная панель диалога не делает недоступным родительское
окно. Это означает, что можно продолжать работать в
родительском окне, пока немодальная панель диалога
индицируется. Например, Windows Write использует немодальную
панель диалога для команды Find. Это позволяет пользователю
продолжать редактировать текст не закрывая панель диалога Find.
.
Windows 3.0/pg/2#3 = 46 =
Большинство немодальных панелей диалога имеют типы
WS_POPUP, WS_CAPTION, WS_BORDER и WS_SYSTEMMENU. Обычно
немодальные панели диалога имеют системное меню, строку
заголовка и тонкую черную окантовку.
Хотя Windows автоматически делает недоступными некоторые
команды системного меню в панели диалога, меню все же содержит
команду Close. Пользователь может ее использовать вместо
альтернативной клавиши для завершения работы с панелью диалога.
Можно также включить в панель диалога блоки управления, такие
как редактируемые блоки управления и панели контроля.
Немодальная панель диалога получает информацию через цикл
обработки сообщений функции WinMain. Если в панели диалога
имеются блоки управления и необходимо дать пользователю
возможность переместить или выбрать блоки управления с помощью
клавиатуры, необходимо вызвать функцию IsDialogMessage в
основном цикле обработки сообщений. Эта функция определяет,
имеется ли сообщение о вводе с клавиатуры для панели диалога, и
при необходимости обрабатывает его. Цикл обработки сообщений
для программы, которая имеет немодальную панель диалога, будет
выглядеть следующим образом:
while (GetMessage(&msg, NULL, NULL, NULL) {
if (hDlg == NULL || !IsDialogMessage(hDlg, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Поскольку немодальная панель диалога может и не всегда
присутствовать, необходимо проконтролировать переменную hDlg,
которая содержит дескриптор, чтобы определить, допустима ли
панель. Если она допустима, функция IsDialogMessage определяет,
предназначено ли сообщение для панели диалога. Если это так,
сообщение обрабатывается и не должно в дальнейшем
обрабатываться с помощью функций TranslateMessage и
DispatchMessage.
Завершение работы немодальной панели диалога происходит с
помощью функции DestroyWindow.
9.2 Использование панели диалога.
Для создания панели диалога необходимо выполнить следующие
шаги:
1. Создать шаблон панели диалога и добавить его к файлу
описания ресурсов.
2. Создать функцию диалога для поддержки работы панели.
.
Windows 3.0/pg/2#3 = 47 =
3. Экспортировать функцию диалога.
4. Панель диалога отображается с помощью функций DialogBox
и CreateDialog. Эти функции запускают модальную и
немодальную панели диалога соответственно.
5. Закрыть панели диалога с помощью функций EndDialog или
DestroyWindow для модальной и немодальной панели
диалога соответственно.
В следующих разделах эти шаги описаны более подробно.
9.2.1 Создание функции панели дилога.
Функция диалога имеет следующий вид:
BOOL FAR PASCAL DlgFunc(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
DWORD lParam;
{
switch (message) {
/* здесь помещаются фрагменты с обработкой сообщений */
default:
return FALSE;
}
}
Это в основном функция окна за исключением того, что не
вызывается функция DefWindowProc. Обработка сообщений панелей
диалога по умолчанию осуществляется встроенными средствами
системы, поэтому функция диалога не должна вызывать функцию
DefWindowProc.
Функция диалога должна быть определена как процедура FAR
PASCAL и должна иметь указанные здесь параметры. Возвращаемое
значение должно иметь тип BOOL.
Как и в случае функций окна, среда Windows посылает
сообщения функции диалога, когда имеет для нее информацию и
когда необходимо , чтобы функция выполнила некоторые действия.
В отличие от функции окна функция диалога отвечает на сообщение
посылкой булевского значения. Если функция обрабатывает
сообщение, она возвращает TRUE. В противном случае она
возвращает FALSE.
В этой функции переменная hDlg получает дескриптор панели
диалога. Другие параметры служат тем же самым целям, что и
параметры функции окна. Оператор switch служит в качестве
фильтра для различных сообщений. Большинство функций диалога
.
Windows 3.0/pg/2#3 = 48 =
обрабатывает сообщения WM_INITDIALOG и WM_COMMAND и очень редко
другие.
Сообщение WM_INITDIALOG, посылаемое панели диалога
непосредственно перед ее индицированием, предоставляет функции
диалога возможность захватить ввод для любого блока управления.
Если функция возвращает TRUE, Windows обеспечит захват ввода
для выбранного блока управления.
Сообщение WM_COMMAND посылается в функцию диалога блоком
управления, принадлежащим панели диалога. Если в панели диалога
существуют блоки управления, они посылают уведомляющие
сообщения, когда пользователь выполняет некоторые действия с
ними. Например, функция обработки панели диалога с
альтернативными клавишами может проверять сообщения WM_COMMAND
на ID блока управления альтернативной клавиши. ID блока
управления содержится в параметре wParam. Когда функция диалога
находит ID, то выполняются соответствующие действия.
Если панель диалога создана с типом WS_SYSMENU, вы должны
включить в оператор switch обработку идентификатора IDCANCEL,
который посылается при выборе пользователем команды Close в
системном меню. Обработка этого сообщения должна включать вызов
функции EndDialog.
9.2.2 Использование блоков управления в панелях диалога.
Блоки управления в панелях диалога используются во многом
также, как они используются в обычных окнах. Однако, когда блок
управления находится в панели диалога, можно использовать
некоторые специальные функции для доступа к блоку управления и
посылки ему сообщений. Например, функция SendDlgItemMessage
посылает сообщение блоку управления в панели диалога, а функция
SetDlgItemText устанавливает текст блока управления. Не следует
задавать этим функциям дескриптор блока управления. Вместо
этого нужно задать дескриптор диалога и ID блока управления.
Для получения дескриптора блока управления можно использовать
функцию GetDlgItem.
9.3 Пример прикладной программы FileOpen.
Данный пример прикладной программы показывает, как
построить и использовать модальную панель диалога для поддержки
команды Открыть в меню Файл. Назначение и работа с панелью
диалога полностью описаны в System Application Architecture,
Common User Access: Advanced Interface Design Guide. На рисунке
9.1 показан вид панели диалога, которую отображает программа
FileOpen при выборе команды Open в меню File.
Рисунок 9.1 Панель диалога программы FileOpen.
1. Редактируемый блок управоления.
2. Статический блок управления.
3. Панель перечня.
.
Windows 3.0/pg/2#3 = 49 =
4. Альтернативные клавиши.
Панель диалога FileOpen содержит следующие блоки
управления:
1. Блок управления типа альтернативная клавиша по умол-
чанию, помеченный "Open", используемый для того,
чтобы побудить программу открыть выбранный файл.
2. Клавишный блок управления, помеченный "Cancel",
используется для отмены команды Открыть.
3. Однострочный редактируемый блок управления, в который
пользователь может вводить имя открываемого файла.
4. Панель перечня, содержащая имена файлов в текущем
каталоге, из которых пользователь может выбирать файл
для открытия.
Панель перечня также содержит имена каталогов и
устройств, которые могут быть использованы для
изменения текущего каталога или устройства.
5. Несколько статических блоков управления, используемых
для пометки панели перечня и редактируемого блока
управления, а также для индицирования текущего имени
каталога.
Для создания программы FileOpen скопируйте и переименуйте
исходные файлы прикладной программы EditCntl, а затем сделайте
следующие изменения:
1. Добавьте новые константы во включаемый файл.
2. Создайте шаблон панели диалога Open и добавьте его к
файлу описания ресурсов.
3. Добавьте новые переменные.
4. Добавьте фрагмент IDM_OPEN к фрагменту WM_COMMAND.
5. Создайте функцию диалога OpenDlg.
6. Добавьте вспомогательные функции для поддержки функции
диалога OpenDlg.
7. Экспортируйте функцию диалога OpenDlg.
8. Оттранслируйте и скомпонуйте программу.
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
.
Windows 3.0/pg/2#3 = 50 =
9.3.1 Добавление константы во включаемый файл.
Необходимо иметь несколько новых констант во включаемом
файле для идентификации блоков управления панели диалога
FileOpen. Добавьте следующие операторы:
#define ID_FILENAME 400
#define ID_EDIT 401
#define ID_FILES 402
#define ID_PATH 403
#define ID_LISTBOX 404
Хотя можно выбрать любое целое для ID блока управления,
для каждого блока управления в заданной панели диалога оно
должно быть уникальным. Обычно встроенные ID, такие как IDOK
или IDCANCEL, меньше 100, так что любое число, большее 100,
может использоваться для других блоков управления.
9.3.2 Создание шаблона панели диалога Open.
В файле описания ресурсов необходимо иметь шаблон для
определения размеров и внешнего вида панели диалога Open.
Оператор DIALOG специфицирует имя и размеры панели диалога, а
также блоки управления, которые она содержит. Добавьте
следующие операторы:
(1) Open DIALOG 10, 10, 148, 112
STYLE WS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "FileOpen"
(2) BEGIN
(3) LTEXT "Open File &Name", ID_FILENAME, 4, 4, 60, 10
(4) EDITTEXT ID_EDIT, 4, 16, 100, 12, ES_AUTOHSCROLL
LTEXT "&Files in", ID_FILES, 4, 40, 32, 10
(5) LISTBOX ID_LISTBOX, 4, 52, 70, 56, WS_TABSTOP
(6) LTEXT "", ID_PATH, 40, 40, 100, 10
(7) DEFPUSHBUTTON "&Открыть", IDOK, 87, 60, 50, 14
(8) PUSHBUTTON "Отменить", IDCANCEL 87, 80, 50, 14
END
В этом операторе DIALOG:
1) Панель диалога имеет ширину и высоту, равную
соответственно 148 и 112 (в единицах диалога). Единица
диалога - это доля размера символа системного шрифта по
умолчанию, которая используется в панелях диалога для
того, чтобы панель диалога имела одни и те же
относительные размеры вне зависимости от используемого
типа персональной ЭВМ.
2) Необходимы операторы BEGIN и END.
3) Первый оператор LTEXT создает выравненный влево
.
Windows 3.0/pg/2#3 = 51 =
статический блок управления, который содержит строку
"Open File &Name". Эта строка служит в качестве метки для
панели диалога. В некоторых панелях диалога все
статические блоки управления имеют один и тот же ID. Хотя
по общим правилам надо иметь уникальный ID для каждого
блока управления в панели диалога, допустимо использовать
-1 для статических блоков управления до тех пор, пока
функции диалога не надо будет различать их (например, до
тех пор, пока функция диалога не попытается изменить текст
или расположение статического блока управления).
4) Оператор EDITTEXT добавляет редактируемый блок управления
к панели диалога и идентифицирует его как ID_EDIT. Задан
тип ES_AUTOHSCROLL, так что пользователь может ввести имя
файла, которое длиннее ширины блока управления.
5) Оператор LISTBOX создает панель перечня. ID панели перечня
равен ID_LISTBOX. Ширина и высота панели перечня равны 70
и 56 соответственно (в единицах диалога). Задан тип
WS_TABSTOP, так что пользователь может захватить ввод для
панели перечня, используя клавиатуру. Если этот тип не
использовать, то единственным способом получения доступа к
панели перечня является нажатие на кнопку мыши.
6) Последний оператор LTEXT создает выравненный влево
статический блок управления, используемый для
указания текущего каталога и устройства. Блок управления
первоначально пуст; позже добавляется имя маршрута. Этот
блок управления имеет уникальный ID, равный ID_PATH, для
того, чтобы отличать его от других статических блоков
управления. Это важно, поскольку для заполнения блока
управления будет использоваться функция DlgDirList.
7) Оператор DEFPUSHBUTTON создает альтернативную клавишу по
умолчанию, которая помечена "Open" и имет ID блока
управления IDOK. В модальных панелях диалога нажатие
клавиши Enter генерирует уведомляющее сообщение, которое
использует тот же самый ID, так что для того, чтобы
открыть выбранный файл, пользователь может или нажать
мягкую клавишу, или клавишу Enter.
8) Оператор PUSHBUTTON создает альтернативную клавишу
"Cancel". Ее ID равен IDCANCEL (встроенный ID, находящийся
в файле windows.h). В модальных панелях диалога нажатие
клавиши Escape генерирует уведомляющее сообщение,
использующее тот же самый ID, так что для отмены команды
Open можно разрешить пользователю нажать или мягкую
клавишу, или клавишу Escape.
9.3.3 Добавление новых переменных.
Необходимо объявить несколько новых глобальных и локальных
переменных для хранения имени файла и отдельных частей, из
.
Windows 3.0/pg/2#3 = 52 =
которых оно строится. Добавьте следующие операторы в начало
файла ресурсов:
char FileName[128]; /* текущее имя файла */
char PathName[128]; /* текущий маршрут */
char OpenName[128]; /* имя открываемого файла */
char DefPath[128]; /* маршрут по умолчанию
для панели перечня */
char DefSpec[13] = "*.*"; /* выборка по умолчанию */
char DefExt[] = ".txt"; /* расширение имени файла
по умолчанию */
char str[255]; /* строка для вызова функ-
ции sprintf() */
Необходима также новая локальная переменная для хранения
адреса экземпляра процедуры для панели диалога FileOpen.
Добавьте следующий оператор к функции окна:
FAPROC lpOpenDlg;
9.3.4 Добавление фрагмента IDM_OPEN.
Необходимо занести фрагмент IDM_OPEN для сообщения
WM_COMMAND и индицировать панель диалога Open, когда
пользователь выбирает команду. Добавьте следующие операторы к
функции окна:
case IDM_OPEN:
lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hInst));
DialogBox(hInst, "Open", hWnd, lpOpenDlg);
FreeProcInstance(lpOpenDlg);
break;
Функция MakeProcInstance создает адрес экземпляра
процедуры для функции OpenDlg. Это дает уверенность в том, что
при вызове функции диалога используются сегменты данных
текущего экземпляра. Функции типа OpenDlg, которые
экспортируются прикладной программой, могут быть вызваны только
через адрес экземпляра процедуры и не могут быть вызваны
непосредственно.
Функция FreeProcInstance используется для освобождения
адреса экземпляра процедуры, когда он становится не нужен.
После того, как функция DialogBox возвратит управление, адрес
экземпляра процедуры lpOpenDlg становится не нужен и может быть
освобожден. При следующем вызове панели диалога он должен быть
восстановлен.
Функция DialogBox возвращает управление только после того,
как функция диалога завершит работу с панелью диалога. Это
означает, что панель диалога должна выполнить все действия,
запрошенные пользователем, прежде чем программа может
продолжить работу. Такая панель диалога называется модальной,
.
Windows 3.0/pg/2#3 = 53 =
поскольку пока она находится на экране, программа работает в
другом режиме. Это означает, что пользователь может ответить
только панели диалога, а команды прикладной программы остаются
недоступными.
9.3.5 Создание функции OpenDlg.
Для обработки различных блоков управления необходимо
создать панель диалога Open. Когда панель диалога впервые
индицируется, функция диалога должна заполнить панель перечня и
редактируемый блок управления, а затем захватить ввод для этого
блока управления и выбрать полную спецификацию. Если
пользователь выбирает имя файла из панели перечня, функция
диалога должна скопировать имя в редактируемый блок управления.
Если пользователь нажмет клавишу Open, функция диалога должна
получить имя файла из редактируемого блока управления и
подготовить файл к открытию. Если пользователь дважды нажмет на
кнопку мыши, находясь на имени файла в панели перечня, функция
диалога должна найти имя файла, скопировать его в редактируемый
блок управления и подготовить файл к открытию.
Добавьте следующую функцию к файлу ресурсов:
HANDLE FAR PASCAL OpenDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
WORD index; /* индекс имени файла в панели перечня */
PSTR pTptr; /* временный указатель */
HANDLE hFile; /* дескриптор открываемого файла */
switch (message) {
case WM_COMMAND:
switch (wParam) {
case ID_LISTBOX:
switch (HIWORD) {
case LBN_SELCHANGE:
if (!DlgDirSelect(hDlg, str,
ID_LISTBOX)) {
SetDlgItemText(hDlg,
ID_EDIT, str);
SendDlgItemMessage(hDlg,
ID_EDIT, EM_SETSEL,
NULL,
MAKELONG(0, 0x7fff));
}
else {
strcat(str, DefSpec);
DlgDirList(hDlg, str,
ID_LISTBOX, ID_PATH,
0x4010);
.
Windows 3.0/pg/2#3 = 54 =
}
break;
case LBN_DBLCLK:
goto openfile; /* конец фраг-
мента
ID_LISTBOX */
}
return (TRUE);
case IDOK:
openfile:
GetDlgItemText(hDlg, ID_EDIT, OpenName,
128);
if (strchr(OpenName, '*') ||
strchr(OpenName, '?')) {
SeparateFile(hDlg, (LPSTR) str,
(LPSTR) DefSpec,
(LPSTR) OpenName);
if (str[0])
strcpy(DefPath, str);
ChangeDefExt(DefExt, DefSpec);
UpdateListBox(hDlg);
return (TRUE);
}
if (!OpenName[0]) {
MessageBox(hDlg,
"Имя файла не задано", NULL,
MB_OK | MB_ICONQUESTION);
return (TRUE);
}
AddExt(OpenName, DefExt);
EndDialog(hDlg, NULL);
return (TRUE);
case IDCANCEL:
EndDialog(hDlg, NULL);
return (TRUE);
}
break;
case WM_INITDIALOG: /* запрос на инициали-
зацию */
UpdateListBox(hDlg);
SetDlgItemText(hDlg, ID_EDIT, DefSpec);
SendDlgItemMessage(hDlg, /* дескриптор панели
диалога */
ID_EDIT, /* куда послать сооб-
щение */
EM_SETSEL, /* выбор символов */
NULL, /* добавочная информа-
ция */
MAKELONG(0, 0x7fff)); /* прием полного со-
держимого */
SetFocus(GetDlgItem(hDlg, ID_EDIT));
return (FALSE); /* индикатор захвата ввода бло-
.
Windows 3.0/pg/2#3 = 55 =
ком управления */
}
return (FALSE);
}
Когда функция диалога получает сообщение WM_INITDIALOG,
функция SetDlgItemText копирует начальное имя файла в
редактируемый блок управления, а функция SendDlgItemMessage
посылает сообщение EM_SETSEL блоку управления для того, чтобы
выбрать его полное содержимое для редактирования. Функция
SetFocus захватывает ввод для редактируемого блока управления
(функция GetDlgItem получает дескриптор этого блока
управления). Функция UpdateListBox, заданная в начале фрагмента
WM_INITDIALOG, является локально определенной функцией, которая
заполняет панель перечня списком файлов текущего каталога.
Когда функция диалога получает сообщение WM_COMMAND, она
его обрабатывает для трех различных значений: IDOK, ID_LISTBOX
и IDCANCEL.
В случае ID_LISTBOX функция диалога проверяет тип
уведомляющего сообщения. Если он равен LBN_SELCHANGE, функция
диалога делает новую выборку с помощью функции DlgDirSelect.
Затем она копирует новое имя файла в редактируемый блок
управления, используя функцию SetDlgItem, и выбирает его для
редактирования, посылая сообщение EM_SETSEL. Если текущая
выборка не является именем файла, функция диалога копирует
спецификацию по умолчанию в панель перечня, используя функцию
DlgDirList. При этом панель перечня заполняется именами файлов
текущего каталога.
Если тип уведомляющего сообщения ID_LISTBOX равен
LBN_DBLCLK, функция диалога выполняет те же самые действия, что
для IDOK. Панель перечня посылает сообщение LBN_DBLCLK только
после того, как будет послано сообщение LBN_SELCHANGE. Это
означает, что когда пользователь получает уведомление о двойном
нажатии, не следует искать новое имя файла.
В случае IDOK функция диалога получает содержимое
редактируемого блока управления и проверяет имя файла на
корректность. Функция strchr ищет в имени универсальные
символы. Если она находит такой символ, то разделяет имя файла
на отдельные части - маршрут и имя файла, используя локально
определенную функцию SeparateFile. Функция strcpy корректирует
переменную DefPath с учетом нового маршрута, если он есть.
Локально определенная функция ChangeDefExt корректирует
переменную DefExt с учетом нового расширения имени файла по
умолчанию, если оно есть. После указанной корректировки функция
UpdateListBox корректирует содержимое панели перечня, а функция
диалога возвращает управление, чтобы дать пользователю
возможность выбрать корректное имя файла из нового перечня.
.
Windows 3.0/pg/2#3 = 56 =
Если имя файла не содержит универсальных символов, функция
диалога должна удостовериться, что файл не пуст. Если он пуст,
функция диалога индицирует предупреждающее сообщение, но не
завершает работу с панелью диалога. Это дает пользователю
возможность сделать еще одну попытку. Если имя файла не
содержит универсальных символов и файл не пуст, и если
пользователь вводит имя файла, не содержащее расширения,
функция диалога использует локально определенную функцию AddExt
для добавления расширения имени файла по умолчанию. Затем
функция диалога вызывает функцию EndDialog для завершения
работы модальной панели диалога и установки возвращаемого
значения в NULL.
В случае IDCANCEL функция диалога вызывает функцию
EndDialog для завершения работы с панелью диалога и отмены
команды. Возвращаемое значение устанавливается в NULL.
Перед завершением работы с панелью диалога функция диалога
может также проверить существование и режим доступа к заданному
файлу. Проверка на существование - дело самой прикладной
программы и в данном примере не производится. Некоторые простые
способы такой проверки приведены в главе 10 "Ввод и вывод в
файл".
9.3.6 Добавление вспомогательных функций.
Необходимо добавить несколько функций к исходному
С-файлу для поддержки функции диалога OpenDlg. Эти функции
перечислены ниже:
Функция Описание
---------------------------------------------------------------
UpdateListBox Заполняет панель перечня в панели диалога
Open именами указанных файлов.
SeparateFile Разделяет полное имя файла на имя маршрута
и имя файла.
ChangeDefExt Копирует расширение имени файла в буфер,
если только расширение не содержит универ-
сальных символов.
AddExt Добавляет расширение к имени файла, ес-
ли оно не имело расширения.
---------------------------------------------------------------
Функция UpdateListBox строит полное имя файла, используя
маршрут по умолчанию и имя файла, а затем передает его панели
перечня с помощью функции DlgDirList. Эта функция заполняет
панель перечня именами файлов и каталогов, каждый из которых
идентифицирован полным именем маршрута файла. Добавьте
следующие операторы к исходному С-файлу:
.
Windows 3.0/pg/2#3 = 57 =
void UpdateListBox(hDlg)
HWND hDlg;
{
strcpy(str, DefPath);
strcat(str, DefSpec);
DlgDirList(hDlg, str, ID_LISTBOX, ID_PATH, 0x4010);
SetDlgItemText(hDlg, ID_EDIT, DefSpec);
}
Функция SetDlgItemText копирует имя файла по умолчанию в
редактируемый блок управления панели диалога.
Функция SeparateFile разделяет полное имя файла на две
части и копирует их в раздельные буферы. Она сначала
перемещается в конец полного имени файла и использует функцию
AnsiPrev для обратного перемещения, находя при этом разделитель
перед именем драйвера или каталога. Добавьте следующие
операторы к исходному С-файлу:
void SeparateFile(hDlg,lpDestPath, lpDestFileName,
lpSrcFileName)
HWND hDlg;
LPSTR lpDestPath, lpDestFileName, lpSrcFileName;
{
LPSTR lpTmp;
CHAR cTmp;
lpTmp = lpSrcFileName + (long) lstrlen(lpSrcFileName);
while (*lpTmp != ':' && *lpTmp != '\' &&
lpTmp > lpSrcFileName)
lpTmp = AnsiPrev(lpSrcFileName, lpTmp);
if (*lpTmp != ':' && *lpTmp != '\') {
lstrcpy(lpDestFileName, lpSrcFileName);
lpDestPath[0] = 0;
return;
}
lstrcpy(lpDestFileName, lpTmp + 1);
cTmp = *(lpTmp + 1);
lstrcpy(lpDestPath, lpSrcFileName);
*(lpTmp + 1) = cTmp;
lpDestPath[(lpTmp - lpSrcFileName) + 1] = 0;
}
Функции ChangeDefExt и AddExt используют стандартные
операторы языка С для выполнения поставленной перед ними
задачи. Добавьте следующие операторы к исходному С-файлу:
void ChangeDefExt(Ext, Name)
PSTR Ext, Name;
{
PSTR pTptr;
pTptr = Name;
.
Windows 3.0/pg/2#3 = 58 =
while (*pTptr && *pTptr != '.')
pTptr++;
if (*pTptr) /* true, если это - рас-
ширение */
if (!strchr(pTptr, '*') && !strchr(pTptr, '?'))
strcpy(Ext, pTptr); /* копировать расшире-
ние */
}
void AddExt(Name, Ext)
PSTR Ext, Name;
{
PSTR pTptr;
pTptr = Name;
while (*pTptr && *pTptr != '.')
pTptr++;
if (*pTptr != '.') /* если нет расширения -
добавить расширение
по умолчанию */
strcat(Name, Ext);
}
9.3.7 Экспортирование функции диалога.
Необходимо экспортировать функцию диалога OpenDlg, т.к.
это функция многократного вызова и вызывается только Windows.
Добавьте следующую строку к оператору EXPORTS в файле
определения модуля:
OpenDlg @3
9.3.8 Трансляция и компоновка.
В файле make не требуется проводить изменений.
Оттранслируйте и скомпонуйте прикладную программу, запустите
Windows и программу FileOpen. Когда будет открыто меню File и
выбрана команда Open, панель диалога примет вид, представленный
на рис. 9.1. в начале данного раздела. Выберите файл из панели
перечня или введите имя файла в редактируемый блок управления,
а затем выберите клавишу "Open".
9.4 Заключение.
В данной главе описано как создавать и использовать в
прикладных программах панели диалога. Панель диалога - это
специальное окно, которое перекрывает основное окно прикладной
программы. Имеется два типа панелей диалога: модальные и
немодальные. Модальные панели диалога требуют завершения ввода
от пользователя до того, как они вернут управление основному
окну прикладной программы. Немодальные панели диалога не
требуют завершения ввода от пользователя для того, чтобы
передать управления другим окнам программы.
.
Windows 3.0/pg/2#3 = 59 =
Windows предоставляет набор специальных функций для работы
с блоками управления в панели диалога.
Для создания панелей диалога можно использовать Dialog
Editor.
Дополнительную информацию относительно панелей диалога вы
найдете в:
Раздел Руководство
---------------------------------------------------------------
Обработка сообщений Руководство программиста, Глава 4, "Ввод с
ввода использованием мыши и клавиатуры".
Блоки управления Руководство программиста, Глава 8, "Блоки
управления".
Функции блоков уп- Справочное руководство, том 1, глава 1,
равления и панелей "Функции интерфейса управления окнами".
диалога
Операторы описания Справочное руководство, том 2, глава 8,
ресурсов "Операторы описания ресурсов".
Использование "Tools", Глава 5, "Создание панелей диа-
Dialog Editor лога: The Dialog Editor".
Демонстрационная Диск "SDK Sample Source Code Disk".
программа
OWNCOMBO.EXE,
иллюстрирующая ис-
пользование комби-
нированных блоков и
рисуемых блоков уп-
равоения.
.
Windows 3.0/pg/2#3 = 60 =
Глава 10. Ввод и вывод из файлов.
Ввод и вывод файлов в среде Windows аналогичен вводу и
выводу файлов в стандартных программах исполняющей системы С.
Однако имеются и существенные отличия. Например, хотя во время
выполнения программ в Windows можно использовать поточные
функции ввода и вывода исполняющей системы С, предпочтительнее
оказываются функции ввода и вывода низкого уровня. Кроме того,
поскольку Windows является многозадачной средой, необходимо
особенно внимательно следить за открытыми файлами.
Для того, чтобы поддерживать указанные различия, имеется
функция OpenFile. Функция OpenFile открывает файлы и управляет
ими, возвращая дескриптор файла, который можно использовать в
функциях низкого уровня исполняющей системы С для чтения и
записи данных.
В данной главе описаны следующие темы:
- Работа с файлами в среде Windows.
- Как использовать функцию OpenFile для создания,
открытия, повторного открытия, запроса и проверки
состояния файлов.
- Работа с низкоуровневыми функциями исполняющей системы С
для чтения и записи в файлы.
В данной главе описано создание простой прикладной
программы EditFile, которая иллюстрирует использование этих
возможностей.
10.1 Правила работы с файлами в среде Windows.
Многозадачность накладывает некоторые специальные
ограничения на доступ к файлам, с которыми не сталкиваются в
обычных С-программах. Поскольку может быть несколько программ,
одновременно работающих с файлами, необходимо следовать
некоторым простым правилам для того, чтобы избежать конфликтов
и возможного затирания файлов.
Держите файл открытым только когда ваша программа имеет
управление.
Необходимо закрывать файлы перед вызовом функции
GetMessage или любой другой функции, которая берет на себя
управление выполнением программы. Закрытие файла предотвратит
его от влияния изменений файловой среды, которые могут быть
результатом работы другой программы. Предположим ваша программа
производит запись на гибкий диск и временно передала управление
другой программе, которая может дать указание удалить гибкий
диск и заменить его другим. Если первая программа вновь получит
управление и попытается писать на диск, как делала это ранее,
.
Windows 3.0/pg/2#3 = 61 =
то без закрытия и повторного открытия файла она разрушит данные
на новом диске.
Другая причина держать файлы закрытыми состоит в
ограниченном числе открытых файлов (ограничение операционной
системы). Если сразу большое число программ попытается открыть
и использовать файлы, то указанный предел может быстро
исчерпаться.
Для решения проблемы открытых файлов функция OpenFile
имеет параметр OF_REOPEN, который позволяет легко закрывать и
повторно открывать файлы. Как только файл открывается или
создается, функция OpenFile автоматически копирует
соответствующую информацию о файле, включающую полное имя файла
и текущую позицию указателя файла, в структуру OFSTRUCT. Это
означает, что можно закрыть файл, а затем его повторно открыть,
задавая только эту структуру.
Если диск был изменен во время работы с другой программой,
функция OpenFile выдает ошибку при повторном открытии файла.
Если при повторном открытии файла было специфицировано указание
OF_PROMPT, функция OpenFile автоматически индицирует панель
сообщений, делая запрос на установку нужного диска.
При выполнении операций над файлами необходимо следовать
соглашениям DOS.
В конечном счете, при вводе и выводе файлов Windows
зависит от функций управления файлами операционной системы. Это
означает, что при выполнении операций над файлами необходимо
следовать соглашениям, принятым в операционной системе.
Например, имя файла может иметь от одного до восьми символов, а
число символов в расширении файла может изменяться от нуля до
трех. Имя файла не должно содержать пробелов или специальных
символов. Более того, имена файлов должны быть специфицированы
набором символов OEM, а не набором символов по умолчанию
(ANSI).
Дело программиста - следить за тем, чтобы имя файла имело
определенную длину или состояло из соответствующих знаков, но
он может не беспокоиться о преобразовании набора знаков, если
использует функцию OpenFile. Для удобства эта функция
автоматически преобразует имена файлов из набора символов ANSI
в набор символов OEM. Делается это с помощью функции AnsiToOem.
Примечание: Все редактируемые блоки управления и панели
перечней по умолчанию используют набор символов ANSI, так что,
если планируется индицировать имена файлов DOS или позволить
пользователю ввести имена файлов, можно получить
непредсказуемые символы там, где символ OEM не идентичен
символу ANSI.
Если необходимо работать с международными именами файлов,
.
Windows 3.0/pg/2#3 = 62 =
необходимо подготовить для обработки имена файлов, которые
содержат многобайтные значения символов (например, иероглифы).
Для таких имен файлов следует использовать функции AnsiNext и
AnsiPrev для перемещения по строке. Эти функции правильно
управляют строками, которые содержат символы, длина которых не
равна одному байту, например, как строки из символов японского
алфавита.
Используйте в каждом экземпляре вашей программы уникальные
имена файлов.
Поскольку несколько экземпляров одной программы могут
работать одновременно, один из них может стереть временный или
рабочий файл другого экземпляра, если не использовать
уникальное имя файла для каждого экземпляра.
Уникальное имя файла можно создать с помощью функции
GetTempFilename. Эта функция создает уникальное имя, объединяя
уникальное целое с префиксом и расширением имени файла,
задаваемыми пользователем. Временные имена удовлетворяют
требованиям, предъявляемым к именам файлов операционной
системой.
Примечание: Функция GetTempFilename использует переменную
среды TEMP для создания полного имени временного файла. Если
пользователь не задаст эту переменную, временный файл будет
помещен в корневой каталог текущего устройства. Если переменная
определяет недоступный каталог, пользователь не сможет создать
временный файл.
Закрывайте файлы перед отображением панели сообщения или
используйте системные модальные панели сообщений об
ошибках.
Как было сказано выше, программа не должна передавать
управление, пока держит файлы открытыми. Если ваша программа
использует не системную модальную панель выдачи сообщений, то
пользователь может перейти к другой программе, когда на экране
индицируется панель сообщений. Если в программе есть открытые
файлы, то переключение на другую программу может вызвать
проблемы ввода вывода.
Для исключения этих проблем, ваша программа при
отображении панели сообщений должна делать по крайней мере одно
из:
- Закрыть открытые файлы перед отображением панели
сообщений.
- Если закрытие файлов неудобно, то необходимо сделать
панель диалога системно-модальной.
.
Windows 3.0/pg/2#3 = 63 =
10.2 Создание файлов.
Для создания нового файла можно использовать функцию
OpenFile с ключем OF_CREATE. При вызове функции OpenFile
необхожимо указать:
- Оканчивающееся нулем имя файла.
- Буфер типа OFSTRUCT.
- Ключ OF_CREATE.
В приведенном ниже примере создается файл file.txt и
возвращается дескриптор файла, который может быть использован в
функциях ввода/вывода низкого уровня библиотеки С:
int hFile;
OFSTRUCT OfStruct;
.
.
.
hFile=OpenFile("file.txt", &OfStruct, OF_CREATE);
Функция OpenFile создает файл (если это необходимо) и
открывает его для записи. Если файл уже существует, функция
усекает его до нулевой длины и открывает для записи.
Если не допускается стирание существующего файла, то перед
созданием нового файла можно проверить, существует ли данный
файл, вызвав функцию OpenFile, как это показано ниже:
hFile = OpenFile("file.txt", &OfStruct, OF_EXIST);
if (hFile >= 0) {
wAction = MessageBox(hWnd,
(LPSTR) "Файл существует. Переписать?",
(LPSTR) "File",
MB_OKCANCEL);
if (wAction == IDCANCEL)
/* закончить эту обработку */
}
}
/* открыть файл */
10.3 Открытие существующих файлов.
Можно открыть существующий файл, использовав параметры
OF_READ, OF_WRITE или OF_READWRITE. Эти параметры побуждают
функцию OpenFile открыть существующие файлы для чтения, записи
или чтения и записи. В приведенном ниже примере файл file.txt
открывается для чтения:
hFile = OpenFile("file.txt", &OfStruct, OF_READ);
Если произошла ошибка при открытии файла, можно
.
Windows 3.0/pg/2#3 = 64 =
индицировать панель диалога и указать, что файл не был найден.
Можно также использовать функцию OpenFile для подсказки
пользователю о файле, как это описано в подразделе 10.6 "Запрос
имени файла".
10.4 Чтение и запись файлов.
Как только файл открыт, то можно из него читать или в него
записывать, используя функции С низкого уровня. В приведенном
ниже примере файл file.txt открывается для чтения и затем из
него считывается 512 байтов:
char buffer[512];
int count;
.
.
.
hFile = OpenFile("file.txt", &OfStruct, OF_READ);
if (hFile >= 0) {
count = read(hFile, buffer, 512);
close(hFile);
}
В этом примере перед считыванием из файла проверяется
дескриптор файла. Функция OpenFile возвращает -1, если файл не
может быть найден или открыт. Функция close закрывает файл
сразу же после чтения.
В приведенном ниже примере файл file.tmp открывается для
записи, и, затем, в него записывается содержимое буфера в виде
массива символов:
hFile = OpenFile("file.tmp", &OfStruct, OF_READ);
if (hFile >= 0) {
write (hFile, buffer, count);
close (hFile);
}
Всегда необходимо закрывать файлы на гибких дисках после
чтения или записи. Это необходимо делать для того, чтобы обойти
проблемы, возникающие при удалении текущего диска во время
работы с другой программой. Всегда можно повторно открыть файл
на диске, используя параметр OF_REOPEN.
10.5 Повторное открытие файлов.
Если вы открыли файл на гибком диске, его необходимо
закрыть перед тем, как передать управление другой программе.
Наиболее подходящий для этого момент - непосредственно после
чтения или записи. Файл всегда можно повторно открыть с помощью
функции OpenFile с ключем OF_REOPEN:
hFile=OpenFile((LPSTR)NULL, &OfStruct, OF_REOPEN | OF_READ);
.
Windows 3.0/pg/2#3 = 65 =
В этом примере функция OpenFile использует имя файла из
структуры OfStruct для открытия файла. При повторном открытии
файла его указатель, отслеживающий текущую позицию в файле,
перемещается в ту же самую точку файла, в которой он находился
непосредственно перед закрытием файла.
10.6 Запрос имени файла.
С помощью ключа OF_PROMPT можно автоматически подсказать
пользователю вставить нужную дискету перед повторным открытием
файла. Функция OpenFile использует имя файла для создания
строки подсказки. При повторном открытии файла надо в
добавление к режиму открытия использовать параметры OF_REOPEN
или OF_PROMPT:
hFile = OpenFile((LPSTR)NULL, &OfStruct, OF_PROMPT |
OF_REOPEN | OF_READ);
Если файл повторно открыт только для чтения, Windows будет
проверять, соответствует ли дата и время той дате и времени,
когда файл был открыт впервые.
10.7 Проверка состояния файла.
Можно определить текущее состояние открытого файла,
использовав функцию С низкого уровня fstat. Эта функция
заполняет структуру информацией о файле, такой как длина файла
в байтах (специфицирована полем size), дата и время его
создания. В приведенном ниже примере структура FileStatus
заполняется информацией о файле file.txt:
stat FileStatus;
.
.
.
fstat(hFile, FileStatus);
10.8 Простой редактор файлов EditFile.
В данном примере показано, как создать простую программу,
которая использует функцию OpenFile и функции исполняющей
системы С для открытия и сохранения небольших текстовых файлов.
Для создания программы EditFile необходимо скопировать и
переименовать ресурсы программы FileOpen, описанные в главе 9
"Панели диалога" и модифицировать их следующим образом:
1. Добавить константы во включаемый файл.
2. Создать шаблон панели диалога SaveAs и добавить его к
файлу описания ресурсов.
3. Добавить новые операторы включения в исходный С-файл.
.
Windows 3.0/pg/2#3 = 66 =
4. Добавить новые переменные.
5. Заменить фрагмент WM_COMMAND.
6. Добавить фрагменты WM_QUERYENDSESSION и WM_CLOSE.
7. Модифицировать функцию диалога OpenDlg.
8. Создать функцию диалога SaveAs.
9. Создать вспомогательные функции для функции диалога
SaveAs.
10. Экспортировать функцию диалога SaveAs.
11. Модифицировать оператор HEAPSIZE.
12. Оттранслировать и скомпоновать программу.
Когда прикладная программа завершена, можно просмотреть
текстовые файлы в редактируемом блоке управления. Команда
прикладной программы Open из меню File позволяет указать
открываемый файл. Можно также делать изменения в файле или
вводить новый текст, а также сохранять текст, используя команды
"Save" или "SaveAs" в панели диалога.
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
10.8.1 Добавление констант во включаемый файл.
Для поддержки панели диалога SaveAs необходимо добавить
определения констант во включаемый файл. Добавьте следующий
оператор в этот файл:
#define MAXFILESIZE 0x7FFF
10.8.2 Добавление панели диалога SaveAs.
Для поддержки команды "SaveAs" необходима новая панель
диалога. Панель диалога "SaveAs" запрашивает имя файла и дает
возможность пользователю ввести имя в редактируемый блок
управления. Добавьте оператор DIALOG к файлу ресурсов: SaveAs
Saveas DIALOG 10, 10, 180, 53
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "Save As "
BEGIN
LTEXT "Save As File &Name:", IDC_FILENAME, 4, 4, 72, 10
LTEXT "", IDC_PATH, 84, 4, 92, 10
EDITTEXT IDC_EDIT, 4, 16, 100, 12
.
Windows 3.0/pg/2#3 = 67 =
DEFPUSHBUTTON "Save", IDOK, 120, 16, 50, 14
PUSHBUTTON "Cancel", IDCANCEL, 120, 36, 50, 14
END
Константы ID_PATH, ID_FILENAME, ID_EDIT, IDCANCEL и IDOK -
те же самые, что использовались в панели диалога Open.
Поскольку панели диалога Open и SaveAs никогда не будут открыты
одновременно, нет необходимости беспокоиться о конфликтах ID
блоков управления.
10.8.3 Добавление операторов include.
Для поддержки операций ввода и вывода файлов необходимо
включить дополнительные включаемые файлы. Добавьте следующие
операторы в начало исходного С-файла:
#include
#include
10.8.4 Добавление новых переменных.
Для открытия и сохранения файлов необходимо объявить
некоторые глобальные переменные. В начале файла должны быть
объявлены следующие переменные:
HANDLE hEditBuffer; /* дескриптор буфера редактиро-
вания */
HANDLE hOldBuffer; /* дескриптор старого буфера */
HANDLE hHourGlass; /* дескриптор курсора "песочные
часы" */
HANDLE hSaveCursor; /* дескриптор текущего курсора */
int hFile; /* дескриптор файла */
int count; /* число считываемых или запи-
сываемых символов */
PSTR pBuffer; /* адрес буфера чтения/записи */
OFSTRUCT OfStruct; /* информация из OpenFile() */
struct stat FileStatus; /* информация из fstat() */
BOOL bChanges = FALSE; /* TRUE, если файл изменяется */
BOOL bSaveEnabled = FALSE; /* TRUE, если текст - в буфере
редактирования */
PSTR pEditBuffer; /* адрес буфера редактирования */
char Untitled[] = /* заголовок окна по умолчанию */
" Edit File - (untitled)";
Переменная hEditBuffer содержит дескриптор текущего буфера
редактирования. Этот буфер, расположенный в динамической
области памяти прикладной программы, содержит текст текущего
файла. Для загрузки файла необходимо отвести буфер, загрузить
файл, а затем передать дескриптор буфера редактируемому блоку
управления. Переменная hOldBuffer используется для замены
старого буфера новым. Дескрипторы hHourGlass и hSaveCursor
содержат дескрипторы курсора при длительных операциях.
.
Windows 3.0/pg/2#3 = 68 =
Переменная hFile содержит дескриптор файла, который
возвратила функция OpenFile. Переменная count содержит счетчик
числа символов, которые должны быть считаны или записаны.
Переменная pBuffer является указателем; она содержит адрес
символа, начиная с которого осуществляется считывание или
запись. Структура OfStruct содержит информацию о файле.
Структура FileStatus тоже содержит информацию о файле.
Переменная bChanges равна TRUE, если пользователь изменил
содержимое файла. Переменная bSaveEnabled равна TRUE, если
пользователь выбрал правильное имя для сохраняемого файла.
Переменная Untitled содержит заголовок основного окна, который
изменяется при каждой загрузке нового файла.
10.8.5 Замена фрагмента WM_COMMAND.
Для обработки команд меню File (кроме команды Print)
необходимо заменить фрагмент WM_COMMAND. Для команды New
необходимо очистить текущее имя файла и сделать пустым
редактируемый блок управления, если в нем имеется какой-либо
текст. Для команды Open необходимо получить выбранное имя
файла, открыть файл и заполнить редактируемый блок управления.
Для команды Save необходимо записать содержимое редактируемого
блока управления обратно в текущий файл. Наконец, для команды
SaveAs необходимо запросить имя файла и записать содержимое
редактируемого блока управления.
Если пользователь выбирает команду New, и текст в текущем
модифицируемом файле существует, необходимо дать пользователю
подсказку в виде панели сообщения, которая определяет, нужно ли
сохранить сделанные изменения. Добавьте следующие операторы к
фрагменту WM_COMMAND:
case IDM_NEW:
if (!QuerySaveFile(hWnd))
return (NULL);
bChanges=FALSE;
FileName[0]=0
SetNewBuffer(hWnd, NULL, Untitled);
break;
Локально определенная функция QuerySaveFile проверяет файл
на наличие изменений и подсказывает пользователю сохранить эти
изменения. Если изменения сохраняются, имя файла очищается и
буфер редактирования делается пустым с помощью функции
SetNewBuffer.
Если пользователь выбирает команду Open, и текст в текущем
модифицируемом файле существует, необходимо запросить
пользователя, следует ли сохранить изменения перед открытием
нового файла. Добавьте следующие операторы в фрагмент
WM_COMMAND:
.
Windows 3.0/pg/2#3 = 69 =
case IDM_OPEN:
if (!QuerySaveFile(hWnd))
return (NULL);
lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hInst);
hFile = DialogBox(hInst, "Open", hWnd, lpOpenDlg);
FreeProcInstance(lpOpenDlg);
if (!hFile)
return (NULL);
hEditBuffer =
LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
FileStatus.st_size+1);
if (!hEditBuffer) {
MessageBox(hWnd, "Не хватает памяти.",
NULL, MB_OK | MB_ICONHAND);
return (NULL);
}
hSaveCursor = SetCursor(hHourGlass);
pEditBuffer = LocalLock(hEditBuffer);
IOStatus = read(hFile, pEditBuffer, FileStatus.st_size);
close(hFile);
if (IOStatus != FileStatus.st_size) {
sprintf(str, "Ошибка чтения %s.", FileName);
SetCursor(hSaveCursor);
MessageBox(hWnd, str, NULL,
MB_OK | MB_ICONEXCLAMATION);
}
LocalUnlock(hEditBuffer);
sprintf(str, "Редактирумый файл - %s", FileName);
SetNewBuffer(hWnd, hEditBuffer, str);
SetCursor(hSaveCursor); /* восстановление курсора */
break;
При обработке фрагмента IDM_OPEN функция QuerySaveFile
проверяет существующий файл на наличие изменений перед
индицированием панели диалога Open. Функция DialogBox теперь
возвращает дескриптор открытого файла. Этот дескриптор
создается функцией OpenDlg. Если файл не может быть открыт,
функция возвращает NULL, и обработка заканчивается. В противном
случае функция LocalAlloc отводит место, необходимое для
загрузки файла в память. Потребный объем памяти определяется
структурой FileStatus, которая заполняется информацией о файле,
открытом функцией диалога OpenDlg. Если нет достаточного объема
памяти, индицируется панель сообщений и обработка
заканчивается. В противном случае, функция SetCursor индицирует
песочные часы, функция LocalLock блокирует новый буфер, и
функция исполняющей системы С read копирует содержимое файла в
память. Если файл не был считан полностью, индицируется панель
сообщений. Функция SetCursor восстанавливает курсор перед
вызовом функции MessageBox. Функция LocalUnlock разблокирует
буфер редактирования, и после создания нового заголовка окна
локально определенная функция SetNewBuffer изменяет буфер
редактирования и заголовок.
.
Windows 3.0/pg/2#3 = 70 =
Если пользователь выбирает команду Save и текущее имя
файла отсутствует, следует выполнить некоторые действия, как и
при команде SaveAs. Добавьте следующие операторы к фрагменту
WM_COMMAND:
case IDM_SAVE;
if (!FileName[0])
goto saveas;
if (bChanges)
SaveFile(hWnd);
break;
Фрагмент IDM_SAVE проверяет имя файла и, если оно не
существует, пропускает операторы до фрагмента IDM_SAVEAS. Если
имя файла существует, локально определенная функция SaveFile
сохраняет файл только в том случае, если в нем были изменения.
Команда SaveAs всегда должна запрашивать имя файла.
Следует сохранить файл только тогда, когда пользователь задал
правильное имя файла. Добавьте следующие операторы к фрагменту
WM_COMMAND:
case IDM_SAVEAS:
saveas:
lpSaveAsDlg = MakeProcInstance(SaveAsDlg, hInst);
Success = DialogBox(hInst, "SaveAs", hWnd,
lpSaveAsDlg);
FreeProcInstance(lpSaveAsDlg);
if (Success == IDOK) {
sprintf(str, "Редактируемый файл %s",
FileName);
SetWindowText(hWnd, str);
SaveFile(hWnd);
}
break;
Функция DialogBox индицирует панель диалога SaveAs.
Функции MakeProcInstance и FreeProcInstance создают и
освобождают адрес процедуры экземпляра для функции диалога
SaveAsDlg. Функция DialogBox получает IDOK из функции диалога
SaveAsDlg, если пользователь вводит правильное имя файла. Затем
функция SetWindowText изменяет заголовок окна, а функция
SaveFile сохраняет в файле содержимое буфера редактирования.
Теперь команда Exit должна запросить пользователя и
определить, должен ли быть сохранен текущий файл. Кроме того,
чтобы отследить изменения в файле, необходимо обработать
уведомляющие сообщения редактируемого блока управления.
Модифицируйте фрагмент IDM_EXIT и добавьте фрагмент IDC_EDIT к
фрагменту WM_COMMAND, как указано ниже:
case IDM_EXIT:
QuerySaveFile(hWnd);
.
Windows 3.0/pg/2#3 = 71 =
DestroyWindow(hWnd);
break;
case ID_EDIT:
if (HIWORD(lParam) == EN_CHANGE)
bChanges = TRUE;
return (NULL);
10.8.6 Добавление фрагментов WM_QUERYENDSESSION и WM_CLOSE.
Необходимо обработать сообщения WM_QUERYENDSESSION и
WM_CLOSE для того, чтобы не потерять содержимое файлов.
Добавьте следующие операторы к функции окна:
case WM_QUERYENDSESSION: /* окончить сеанс? */
return (QuerySaveFile(hWnd));
case WM_CLOSE: /* закрыть окно? */
if (QuerySaveFile(hWnd));
DestroyWindow(hWnd);
break;
Windows посылает сообщение WM_QUERYENDSESSION функции
окна, когда пользователь решил закончить работу с Windows.
Сеанс работы заканчивается только тогда, когда возвращено TRUE.
Функция QuerySaveFile проверяет файл на наличие изменений,
сохраняет их, если это необходимо, и возвращает TRUE или FALSE
в зависимости от того, отменяет пользователь операцию или
подтверждает ее.
Windows посылает сообщение WM_CLOSE функции окна, если
пользователь выбрал команду Close в системном меню основного
окна. Функция QuerySaveFile выполняет ту же самую задачу, что и
сообщение WM_QUERYENDSESSION, но для того, чтобы завершить
фрагмент WM_CLOSE, необходимо также разрушить основное окно,
используя функцию DestroyWindow.
10.8.7 Модификация функции диалога OpenDlg.
Необходимо модифицировать фрагмент IDOK в функции OpenDlg
для того, чтобы открыть и проверить размер файла, который
выбран пользователем. Добавьте следующие операторы сразу после
вызова функции AddExt в фрагмент IDOK функции OpenDlg:
if ((hFile = OpenFile(OpenName, (LPOFSTRUCT) &OfStruct,
OF_READ)) < 0) {
sprintf(str, "Ошибка %d при открытии %s.",
OfStruct.nErrorCode, OpenName);
MessageBox(hDlg, str, NULL, MB_OK |
MB_ICONQUESTION);
}
else {
fstat(hFile, &FileStatus);
.
Windows 3.0/pg/2#3 = 72 =
if (FileStatus.st_size > MAXFILESIZE) {
sprintf(str,
"Не хватает памяти для размещения %s.\n%s
превышает %ld байтов.", OpenName,
OpenName, MAXEFILESIZE);
MessageBox(hDlg, str, NULL,
MB_OK | MB_ICONQUESTION);
return (TRUE);
}
strcpy(FileName, OpenName);
EndDialog(hDlg, hFile);
return (TRUE);
Функция OpenFile открывает указанный файл для чтения и,
если эта операция прошла успешно, возвращает дескриптор файла.
Если файл не может быть открыт, индицируется панель сообщений,
содержащая номер ошибки, генерируемой DOS. Если файл открыт,
функция C fstat копирует информацию о файле в структуру
FileStatus. Размер файла проверяется для уверенности в том, что
файл не превышает максимальные размеры, заданные константой
MAXFILESIZE. Если размер файла слишком велик, индицируется
сообщение об ошибке. В противном случае функция strcpy копирует
новое имя в переменную FileName, а функция EndDialog завершает
работу панели диалога и возвращает дескриптор hFile функции
DialogBox.
10.8.8 Добавление функции диалога SaveAsDlg.
Для панели диалога SaveAs необходимо задать функцию
диалога. Функция будет получать имя файла из редактируемого
блока управления и копировать это имя в глобальную переменную
FileName. Функция диалога должна выглядеть следующим образом:
int FAR PASCAL SaveAsDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
char TempName[128];
switch (message) {
case WM_INITDIALOG:
if (!FileName[0])
bSaveEnabled = FALSE;
else {
bSaveEnabled = TRUE;
DlgDirList(hDlg, DefPath, NULL, ID_PATH,
0x4010);
SetDlgItemText(hDlg, ID_EDIT, FileName);
SendDlgItemMessage(hDlg, ID_EDIT, EM_SETSEL,
0, MAKELONG(0, 0x7fff);
}
EnableWindow(GetDlgItem(hDlg, IDOK),
.
Windows 3.0/pg/2#3 = 73 =
bSaveEnabled);
SetFocus(GetDlgItem(hDlg, ID_EDIT));
return (FALSE); /* FALSE, если захват ввода
меняется */
case WM_COMMAND:
switch (wParam) {
case ID_EDIT:
if (HIWORD(lParam) == EN_CHANGE &&
!bSaveEnabled)
EnableWindow(GetDlgItem(hDlg, IDOK),
bSaveEnabled = TRUE);
return (TRUE);
case IDOK:
GetDlgItemText(hDlg, ID_EDIT, TempName,
128);
if (CheckFileName(hDlg, FileName,
TempName)) {
SeparateFile(hDlg, (LPSTR) str,
(LPSTR)DefSpec, (LPSTR)FileName);
if (str[0])
strcpy(DefPath, str);
EndDialog(hDlg, IDOK);
}
return (TRUE);
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
return (TRUE);
}
break;
}
return (FALSE);
}
Фрагмент WM_INITDIALOG делает доступной или недоступной
мягкую клавишу Save. Эта клавиша должна быть недоступной, если
не существует текущего имени файла. Функция EnableWindow
(вместе с переменной bSaveEnabled) делает мягкую клавишу
доступной или недоступной. Если текущее имя файла существует,
то оно должно быть предлагаемым именем. Функция SetDlgItemText
копирует имя файла в редактируемый блок управления, а функция
SendDlgItemMessage выбирает полное имя для редактирования.
Функция DlgDirList устанавливает блок управления ID_PATH на
текущий каталог. Поскольку не существует заполняемой панели
перечня, то ее ID не задается.
Фрагмент WM_COMMAND обрабатывает уведомляющие сообщения из
блоков управления панели диалога. Когда функция получает
уведомление EN_CHANGE от редактируемого блока управления
ID_EDIT, она использует функцию EnableWindow для того, чтобы
сделать недоступной мягкую клавишу Save, если она ранее не была
сделана недоступной.
Когда функция получает уведомление от мягкой клавиши
Save, она использует функцию GetDlgItemText для получения имени
.
Windows 3.0/pg/2#3 = 74 =
файла из редактируемого блока управления, а затем контролирует
правильность имени файла, используя локально определенную
функцию CheckFileName. Эта функция контролирует имя файла так,
чтобы оно не содержало разделителей маршрута или универсальных
символов. Затем проверяется, существовал ли уже такой файл;
если это так, то CheckFileName использует функцию MessageBox
для запроса у пользователя, должен ли файл быть перезаписан.
Наконец, функция диалога использует функцию SeparateFile для
копирования имени файла в переменные DefSpec и DefPath.
10.8.9 Добавление вспомогательных функций.
Для поддержки программы EditFile необходимо добавить
несколько функций к исходному С-файлу. Это следующие функции:
Функция Описание
---------------------------------------------------------------
CheckFileName Контролирует имя файла на универсальные
символы, добавляет расширение имени
файла по умолчанию, если оно необходи-
мо, и проверяет существование файла.
SaveFile Сохраняет в файле содержимое буфера ре-
дактирования.
QuerySaveFile Подсказывает пользователю о необходи-
мости сохранить изменения.
SetNewBuffer Освобождает существующий буфер редакти-
рования и заменяет его новым.
---------------------------------------------------------------
Функция CheckFileName проверяет, не пусто ли имя файла и
не содержит ли оно универсальные символы. Она также
контролирует, существует ли уже файл, используя функцию
OpenFile и параметр OF_EXIST. Если файл существует, функция
CheckFileName подсказывает пользователю посмотреть, надо ли его
перезаписать. Добавьте следующие операторы:
BOOL CheckFileName(hWnd, pDest, pSrc)
HWND hWnd;
PSTR pDest, pSrc;
{
PSTR pTmp;
if (!pSrc[0])
return (FALSE); /* показывает, что не указано имя
файла */
pTmp = pSrc;
while (*pTmp) { /* поиск в строке универсальных
символов */
switch (*pTmp++) {
case '*':
.
Windows 3.0/pg/2#3 = 75 =
case '?':
MessageBox(hWnd,
"Универсальные символы не допускаются",
NULL, MB_OK | MB_ICONEXCLAMATION);
return (FALSE);
}
}
AddExt(pSrc, DefExt); /* добавление в случае необходи-
мости расширения имени файла
по умолчанию */
if (OpenFile(pSrc, (LPOFSTRUCT) &OfStruct, OF_EXIST >= 0)
{
sprintf(str, "Заменить существующий %s?", pSrc);
if (MessageBox(hWnd, str, "EditFile",
MB_OKCANCEL | MB_ICONHAND) == IDCANCEL);
return (FALSE);
}
strcpy(pDest, pSrc);
return (TRUE);
}
Функция SaveFile использует параметр OF_CREATE функции
OpenFile для того, чтобы открыть файл для записи. Этот параметр
побуждает функцию OpenFile удалить текущее содержимое файла.
Затем функция SaveFile получает дескриптор буфера файла из
редактируемого блока управления, блокирует буфер и копирует
содержимое в файл. Добавьте следующие операторы:
BOOL SaveFile(hWnd)
HWND hWnd;
{
BOOL bSuccess;
int IOStatus; /* результат записи файла */
if ((hFile = OpenFile(FileName, &OfStruct,
OF_PROMPT | OF_CANCEL | OF_CREATE)) < 0) {
sprintf(str, "Нельзя записать в файл %s.", FileName);
MessageBox(hWnd, str, NULL, MB_OK | MB_ICONQUESTION);
return (FALSE);
}
hEditBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
pEditBuffer = LocalLock(hEditBuffer);
hSaveCursor = SetCursor(hHourGlass);
IOStatus = write(hFile,pEditBuffer, strlen(pEditBuffer));
close(hFile);
SetCursor(hSaveCursor);
if (IOStatus != strLen(pEditBuffer)) {
sprintf(str, " Ошибка записи в %s.", FileName);
MessageBox(hWnd, str, NULL, MB_OK | MB_ICONHAND);
bSuccess = FALSE;
}
else {
.
Windows 3.0/pg/2#3 = 76 =
bSuccess = TRUE; /* файл сохранен */
bChanges = FALSE; /* изменения сохранены */
}
LocalUnlock(hEditBuffer);
return (bSuccess);
}
Сообщение EM_GETHANDLE, посланное с помощью функции
SendMessage, побуждает редактируемый блок управления возвратить
дескриптор его буфера редактирования. Этот буфер располагается
в локальной памяти, так что он может быть заблокирован с
помощью функции LocalLock. После блокировки содержимое
записывается в файл с помощью функции C write. Функция
SetCursor индицирует курсор в виде песочных часов для указания
длительной операции. Если функция write не может записать все
байты, функция SaveFile индицирует панель сообщений. Функция
LocalUnlock освобождает буфер редактирования перед возвратом
функции SaveFile.
Функция QuerySaveFile контролирует наличие изменений в
файле и подсказывает пользователю, сохранить или удалить эти
изменения, или отменить операцию. Если пользователь хочет
сохранить изменения, функция запрашивает у пользователя имя
файла, используя панель диалога SaveAs. Добавьте следующие
операторы:
BOOL QuerySaveFile(hWnd);
HWND hWnd;
{
int Response;
FARPROC lpSaveAsDlg;
if (bChanges) {
sprintf(str, "Сохранение текущих изменений: %s",
FileName);
Response = MessageBox(hWnd, str,
"EditFile", MB_YESNOCANCEL | MB_ICONQUESTION);
if (Response == IDYES) {
check_name:
if (!FileName[0]) {
lpSaveAsDlg = MakeProcInstance(SaveAsDlg,
hInst);
Response = DialogBox(hInst, "SaveAs",
hWnd, lpSaveAsDlg);
FreeProcInstance(lpSaveAsDlg);
if (Response == IDOK)
goto check_name;
else
return (FALSE);
}
SaveFile(hWnd);
}
else if (Response == IDCANCEL)
return (FALSE);
.
Windows 3.0/pg/2#3 = 77 =
}
else
return (TRUE);
}
Функция SetNewBuffer находит и освобождает буфер
редактирования перед размещением и установкой нового буфера
редактирования. Затем она корректирует редактируемый блок
управления. Добавьте следующие операторы к исходному С-файлу:
void SetNewBuffer(hWnd, hNewBuffer, Title)
HWND hWnd;
HANDLE hNewBuffer;
PSRT Title;
{
HANDLE hOldBuffer;
hOldBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
LocalFree(hOldBuffer);
if (!hNewBuffer) /* размещение нового буфера */
hNewBuffer = LocalAlloc(LMEM_MOVAABLE |
LMEM_ZEROINIT, 1);
SendMessage(hEditWnd, EM_SETHANDLE, hNewBuffer, 0L);
InvalidateRect(hEditWnd, NULL, TRUE); /* корректировка
буфера */
UpdateWindow(hEditWnd);
SetWindowText(hWnd, Title);
SetFocus(hEditWnd);
bChanges = FALSE;
}
Новый текст не будет индицирован до тех пор, пока
редактируемый блок управления не перерисует свою область
пользователя. Функция InvalidateRect освобождает для
перерисовки часть области пользователя в редактируемом блоке
управления. Аргумент NULL означает, что должен быть перерисован
весь блок управления; TRUE означает, что перед перерисовкой
должен быть стерт фон. В любом случае блок управления
подготавливается для перерисовки. Функция UpdateWindow
побуждает ПОЛИФЕЙС сразу же послать редактируемому блоку
управления сообщение WM_PAINT.
10.8.10 Экспорт функции диалога SaveAsDlg.
Необходимо экспортировать функцию диалога SaveAsDlg.
Добавьте следующую строку к оператору EXPORTS в файле
определения модуля:
SaveAsDlg @4
10.8.11 Расширение динамической области памяти.
Необходимо добавить дополнительный объем памяти к
.
Windows 3.0/pg/2#3 = 78 =
локальной динамической области. Эта память требуется для
поддержки редактируемого блока управления, который использует
память из динамической области для хранения текущего текста.
Сделайте следующие изменения в файле определения модуля:
HEAPSIZE 0xAFFF
Этот оператор задает максимально возможный буфер
редактируемого блока управления, немного меньший 32707 (32К-1)
байтов. Файлы больше указанных не могут быть открыты.
10.8.12 Трансляция и компоновка.
В файле make изменения не нужны. Оттранслируйте и
скомпонуйте прикладную программу, запустите Windows и затем
программу EditFile. Затем выберите команду Open, выберите файл,
и программа EditFile будет считывать и отображать файл. Если
файл полностью не может вместиться в окне, используйте клавиши
стрелок для прокрутки влево и вправо или вверх и вниз.
10.9 Заключение.
В данной главе описано, как работать с файлами в среде
Windows, и приводится набор правил, которыми необходимо
пользоваться при работе с файлами.
Т.к. Windows многозадачная система, то ваша программа
должна работать с файлами таким образом, чтобы исключить
возможность конфликтов с другими прикладными программами. Для
создания, открытия, закрытия и других операций вы используете
функцию OpenFile. При выполнении ввода и вывода в файл вы
используете функции С низкого уровня, кроме функций С,
осуществляющих потоковый ввод/вывод.
Дополнительную информацию по поводу файлов вы можете
получить в:
Раздел Руководство
---------------------------------------------------------------
Сравнение среды Руководство программиста. Глава 1 "ОБзор
Windows и стандарт- среды Windows".
ного окружения С
Использование языка Руководство программиста. Глава 14 "Язык
С и языка ассемблера С и язык ассемблера".
в среде Windows
Сообщение OpenFile Справочное руководство, том 1, Глава 3
"Системные функции" и Глава 4 "Список
функций".
Глава 11. Растровые карты.
.
Windows 3.0/pg/2#3 = 79 =
Для вывода изображений, которые слишком сложны, чтобы
рисовать их с помощью функций GDI, ваша программа может
использовать растровые карты. В данной главе показано, как
нарисовать и использовать растровые карты на монохромных и
цветных дисплеях.
В данной главе вы найдете следующую информацию:
- Что такое растровая карта.
- Создание растровых карт.
- Отображение растровых карт.
- Добавление цветов к монохромным растровым картам.
- Удаление растровых карт.
В данной главе описано также, как создать простую
программу, Bitmap, которая иллюстрирует концепции, обсуждаемые
здесь.
.
Windows 3.0/pg/2#3 = 80 =
11.1 Что такое растровая карта.
Термин растровая карта обозначает изображение,
сформированное в виде карты бит вместо линий. В Microsoft
Windows имеется два типа растровых карт:
- Зависимые от устройства растровые карты - это массив бит
в памяти, который может быть выведен на устройство
вывода. Поскольку имеется полное соответствие между
битами в памяти и пикселями на устройстве отображения,
то о такой растровой карте говорят, что она зависит от
устройства. Для таких растровых карт представление
изображения в памяти зависит от устройства отображения.
- Независимые от устройства растровые карты (DIB)
описывают действительный вид изображения, а не то, как
оно представляется внутри определенного устройства
отображения. Поскольку такое внешнее определение может
быть использовано с любым устройсвом, то их называют
независимыми от устройств растровыми картами.
11.2 Создание растровых карт.
Растровую карту можно создать, задав GDI размеры и формат
цвета растровой карты, а также необязательное начальное
значение битов растровой карты. GDI затем возвращает дескриптор
растровой карты. Этот дескриптор можно использовать в
последующих функциях GDI для выбора и индикации растровой
карты.
Можно создавать растровые карты следующими способами:
- Создать растровую карту с помощью Windows SDKPaint и
сохранить ее в файле. Затем добавить ее к ресурсам
прикладной программы и загрузить в прикладной
программме, используя функцию LoadBitmap.
- Создать пустую растровую карту и использовать функции
вывода GDI для рисования битов растровой карты.
- Создать пустую растровую карту, затем установить
значения ее точек с помощью битового массива.
- Ваша прикладная программа может также создать растровую
карту и инициализировать ее содержимое с помощью
существующей независимой от устройств растровой карты
(DIB).
В последующих пунктах поясняется, как использовать каждый
из этих способов для создания растровых карт.
.
Windows 3.0/pg/2#3 = 81 =
11.2.1 Создание и загрузка файлов растровых карт.
Растровые карты можно создавать с помощью SDKPaint.
SDKPaint позволяет указать размеры растровой карты, затем
заполнить ее, используя такие средства как кисть, распылитель и
даже текст. Любое из этих средств может создавать цвета из
палитры, содержащей до 28 цветов, которые вы можете сами
определять.
Для создания растровой карты с помощью данного метода
выполните следующее:
- Запустите SDKPaint и, следуя инструкциям, приведенным в
"Tools", создайте растровую карту.
- Сохраниете ее в файле с расширением .bmp.
- В файле описания ресурсов прикладной программы добавьте
ее с помощью оператора BITMAP.
Например, можно использовать следующий оператор для
загрузки растровой карты с именем "dog" из файла DOG.BMP:
dog BITMAP dog.bmp
Имя "dog" идентифицирует растровую карту, а имя файла
dog.bmp определяет содержащий ее файл.
- В прикладной программе вы можете загрузить растровую
карту, используя функцию LoadBitmap.
Функция LoadBitmap получает имя файла, содержащего
растровую карту, загружает растровую карту в память и
возвращает дескриптор растровой карты. Например,
следующий оператор загружает растровую карту с именем
"dog" и сохраняет дескриптор растровой карты в
переменной с именем hDogBitmap:
hDogBitmap = LoadBitmap(hInstance, "dog");
- Выберите растровую карту в контексте устройства с
помощью функции SelectObject.
Например, следующий оператор загружает растровую карту
определяемую hDogBitmap, в контекст устройства,
определяемый hMemoryDC:
SelectObject(hMemoryDC,hDogBitmap);
- Отобразите растровую карту с помощью функции BitBlt.
Например, следующий оператор копирует растровую карту с
контекста устройства памяти в контекст устройства,
заданный параметром hDC:
.
Windows 3.0/pg/2#3 = 82 =
BitBlt(hDC,10,10,100,150,hMemoryDC,0,0,SRCCOPY);
В этом примере отображается растровая карта с началом в
точке (10,10) устройства назначения. Растровая карта
имеет 100 элементов в ширину и 150 в высоту. Растровая
карта берется с устройства памяти, начиная с точки
(0,0). Константа SRCCOPY определяет, что Windows должна
скопировать растровую карту на устройство назначения.
11.2.2 Создание и заполнение пустых растровых карт.
Можно создать растровую карту "на лету", создав пустую
растровую карту и затем заполнив ее с помощью функций вывода
GDI. Используя этот метод вы можете снять ограничение на число
файлов с растровыми картами, ресурсов растровых карт или
растровых карт, которые вы жестко кодируете в тексте программы.
Следуйте следующему методу:
1. Создайте пустую растровую карту с помощью функций
CreateBitmap и CreateCompatibleBitmap.
2. Затем выберите растровую карту в контексте устройства
памяти с помощью функции SelectObject.
3. Нарисуйте изображение в растровой карте с помощью
функций вывода GDI.
Приведенный ниже пример создает растровую карту "звезда",
причем сначала берется растровая карта, совместимая с дисплеем,
затем она заполняется с помощью функции Polygon:
HDC hDC;
HDC hMemoryDC;
HBITMAP hBitmap;
HBITMAP hOldBitmap;
POINT Points[5] = { 32,0, 16,63, 63,16, 0,16, 48,63 };
.
.
.
(1) hDC = GetDC(hWnd);
(2) hMemoryDC = CreateCompatibleDC(hDC);
(3) hBitmap = CreateCompatibleBitmap(hDC, 64, 64);
(4) hOldBitmap = SelectObject(hMemoryDC, hBitmap);
(5) PatBlt(hMemoryDC, 0, 0, 64, 64, WHITENESS);
(6) Polygon(hMemoryDC, Points, 5);
(7) BitBlt(hDC,0,0,64,hMemoryDC,0,0,SRCCOPY);
(8) SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hMemoryDC);
(9) ReleaseDC(hWnd, hDC);
.
Windows 3.0/pg/2#3 = 83 =
1) Функция GetDC возвращает дескриптор контекста отображения.
Растровая карта должна быть совместима с дисплеем. При
создании растровой карты, совместимой с некоторым другим
устройством, необходимо использовать функцию CreateDC для
получения дескриптора этого устройства.
2) Функция CreateCompatibleDC создает контекст устройства
памяти, в котором будет нарисован образ растровой карты.
3) Функция CreateCompatibleBitmap создает пустую растровую
карту. Размер растровой карты устанавливается 64 на 64
пикселя. Фактическое число битов растровой карты зависит
от цветового формата дисплея. Если дисплей является
цветным, растровая карта будет цветной и может иметь
несколько бит на каждый пиксель.
4) Функция SelectObject выбирает растровую карту в контексте
устройства памяти и подготавливает ее для рисования.
Дескриптор ранее выбранной растровой карты сохраняется в
переменной hOldBitmap.
5) Функция PatBlt очищает растровую карту и устанавливает все
пиксели в белый цвет. Необходимо первоначально
использовать эту или аналогичную ей функцию, поскольку
образ пустой растровой карты не определен. Вы можете
рисовать в растровой карте независимо от того, пустая она
или нет.
6) Функция Polygon рисует звезду, используя конечные точки ее
лучей, определенные в массиве структур Points.
7) Функция BitBlt копирует растровую карту из памяти на экран.
8) Последовательно используемые функции SelectObject и
DeleteDC восстанавливают прежнюю растровую карту и удаляют
контекст устройства памяти (как только растровая карта
нарисована, контекст устройства памяти больше становится
не нужен). Ошибкой является попытка удалить контекст
устройства, когда выбрана растровая карта, которая не
является первоначальной растровой картой контекста.
9) Наконец, функция ReleaseDC освобождает контекст
отображения. Теперь дескриптор растровой карты hBitmap
может быть использован в последующих функциях GDI.
11.2.3 Создание растровых карт с помощью фиксированных
кодов.
Можно создать растровую карту и установить ее
первоначальный образ с помощью массива бит растровой карты,
используя функцию CreateBitmap. Функция создает растровую
.
Windows 3.0/pg/2#3 = 84 =
карту, имеющую явно заданный размер и цветовой формат, затем
инициализирует образ растровой карты транслируя образ
независимой от устройства растровой карты, в зависимый от
устройства формат, требуемый устройством отображения, и копируя
в эту растровую карту. Этот метод обычно применяется при
создании небольших растровых карт для использования с кистями
шаблона, но может использоваться также для создания больших и
растровых карт.
Примечание: Если растровая карта не монохромная (т.е.
имеющая один план цветов и один бит на пиксель), то создаваемая
функцией CreateBitmap растровая карта является зависимой от
устройства, и т.о. не может использоваться на разных дисплеях.
В приведенном ниже примере инициализируется монохромная
растровая карта 64 х 32 пикселя с помощью битов массива Square:
HBITMAP hBitmap;
HDC hDC;
BYTE Square[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
.
Windows 3.0/pg/2#3 = 85 =
.
.
.
HANDLE hDibInfo;
PBITMAPINFO pDibInfo;
if(pDibInfo = (PBITMAPINFO) LocalAlloc(LMEM_FIXED,
sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUARD)))
{
HBRUSH hOldBrush,hBrush;
pDibInfo -> bmiHeader.biSize =
(long)sizeof(BITMAPINFOHEADER);
pDibInfo -> bmiHeader.biWidth = 64L;
pDibInfo -> bmiHeader.biHeight = 32;
pDibInfo -> bmiHeader.biPlanes = 1;
pDibInfo -> bmiHeader.biBitCount = 1;
pDibInfo -> bmiHeader.biCompression = 0L;
pDibInfo -> bmiHeader.biSizeImage = 0L;
pDibInfo -> bmiHeader.biXPelsPerMeter = 0L;
pDibInfo -> bmiHeader.biYPelsPerMeter = 0L;
pDibInfo -> bmiHeader.biClrUsed = 0L;
pDibInfo -> bmiHeader.biClrImportant = 0L;
pDibInfo -> bmiColors[0].rgbRed = 0;
pDibInfo -> bmiColors[0].rgbGreen = 0;
pDibInfo -> bmiColors[0].rgbBlue = 0;
pDibInfo -> bmiColors[1].rgbRed = 0xff;
pDibInfo -> bmiColors[1].rgbGreen = 0xff;
pDibInfo -> bmiColors[1].rgbBlue = 0xff;
hDC = GetDC(hWnd);
hBitmap = CreateDIBitmap(hDC,
(LPBITMAPINFOHEADER)&(pDibInfo->bmiHeader),
CBM_INIT,
(LPSTR)Square,
(LPBITMAPINFO)pDibInfo,
DIB_RGB_COLORS);
ReleaseDC(hWnd,hDC);
DeleteObject(hBitmap);
LocalFree((HANDLE)pDibInfo);
}
Функция CreateDIBitmap создает и инициализирует растровую
карту перед тем, как возвратить ее дескриптор. Ширина и высота
растровой карты - 64 и 32 пикcеля соответственно. Растровая
карта имеет один цветовой план и один бит на каждый пиксель.
Это означает, что растровая карта монохромная.
Массив Square содержит биты, используемые для
инициализации растровой карты. Структура BITMAPINFO определяет,
как будет интерпретироваться массив бит. Она определяет высоту
и ширину растровой карты, сколько бит (1,4,8,24) используются
для представления каждого пикселя в массиве, и таблицу цветов
для пикселей. Поскольку массив Square определяет монохромную
растровую карту, то на каждый пиксель отводится по одному биту
.
Windows 3.0/pg/2#3 = 86 =
и таблица цветов содержит только два элемента, для белого и
черного. Если бит в массиве равен 0, то GDI рисует черный
пиксель, иначе белый.
Поскольку Square определяет монохромную растровую карту,
то вы можете для создания растровой карты использовать функцию
CreateBitmap:
hBitmap = CreateBitmap(64,32,1,1,(LPSTR)Square);
Это так, поскольку все монохромные растровые карты
аппаратно-независимые. Однако для цветных растровых карт
CreateBitmap использует другие спецификации битов растровой
карты по сравнению с CreateDIBitmap.
После создания и инициализации растровой карты можно
использовать ее дескриптор в последующих функциях GDI. Если
необходимо изменить растровую карту, то можно рисовать в ней,
выбрав ее в контексте устройства памяти, как описано в пункте
11.2.2 "Создание и заполнение пустых растровых карт". Если
необходимо заменить образ всей или части растровой карты
другим, можно использовать функцию SetDIBits для копирования
другого массива бит в растровую карту. Например, указанный ниже
вызов функции может заменить текущий образ растровой карты
битами из массива Circle:
BYTE Circle[ ]={
.
.
.
};
SetDIBits(hDC,hBitmap,0,32,(LPSTR)Circle,
(LPBITMAPINFO)&myDIBInfo,DIB_RGB_COLORS);
Функция SetDIBits копирует биты массива Circle в растровую
карту, определяемую переменной hBitmap. Массив содержит 32
строки, представляющих образ монохромной растровой карты
размером 64 х 32 пикселя. Если необходимо сохранить текущие
биты растровой карты перед их заменой, можно использовать
функцию GetDIBits. Она копирует указанное число строк из
растровой карты в аппаратнонезависимую спецификацию растровай
карты. Сохранить монохромную растровую карту можно также
с помощью функции GetBitmapBits.
Аналогично, поскольку массив Circle определяет монохромную
растровую карту, вы можете для изменения растровой карты
вызвать SetBitmapBits:
SetBitmapBits(hBitmap,256,(LPSTR)Circle);
В предшествующем примере показано, как создавать и
изменять небольшие растровые карты. Обычно большые растровые
карты не кодируют вручную. Вместо этого вы можете создать файл
.
Windows 3.0/pg/2#3 = 87 =
с аппаратно-независимой растровой картой с помощью SDKPaint или
другими средствами. Файл с аппаратно-независимой растровой
картой состоит из структуры типа BITMAPFILEHEADER, за которой
следуют структура BITMAPINFO и массив байт, которые вместе
определяют растровую карту.
Пример программы ShiwDIB показывает, как выводить
аппаратно-независимые растровые карты с цветами, которыми
управляет палитра. Программа ShowDIB поставляется на диске
"Saple Source Code Disk" вместе с SDK. Дополнительную
информацию вы найдете в Главе 19 "Палитры цветов".
11.2.4 Рисование цветных растровых карт.
Поскольку фиксированное кодирование цветных растровых карт
зависит от устройства и может потребовать значительных усилий,
более подходящий способ создать цветную растровую карту -
создание совместимой растровой карты и рисование в ней.
Например, для того, чтобы создать цветную растровую карту, в
которой основные цвета - красный, зеленый и синий, можно просто
создать пустую растровую карту и использовать функцию PatBlt
для рисования с помощью красной, зеленой и синей кистей. Этот
метод имеет преимущество при генерации нужной растровой карты
даже в том случае, когда дисплей является монохромным. Дело в
том, что GDI обеспечивает "псевдоцветную" кисть для
монохромного дисплея, не поддерживающего цвет. Псевдоцветная
кисть имеет уникальный набор пикселей, представляющих данный
цвет, когда он не поддерживается устройством.
Приведенный ниже фрагмент программы создает цветную
растровую карту:
# define PATORDEST 0x00FA0089L
HDC hDC;
HDC hMemoryDC;
HBITMAP hBitmap;
HBITMAP hOldBitmap;
HBRUSH hRedBrush;
HBRUSH hGreenBrush;
HBRUSH hBlueBrush;
HBRUSH hOldBrush;
.
.
.
hDC = GetDC(hWnd);
hMemoryDC = CreateCompatibleDC(hDC);
hBitmap = CreateCompatibleBitmap(hDC, 64, 32);
hOldBitmap = SelectObject(hMemoryDC, hBitmap);
hRedBrush = CreateSolidBrush(RGB(255,0,0));
hGreenBrush = CreateSolidBrush(RGB(0,255,0));
hBlueBrush = CreateSolidBrush(RGB(0,0,255));
.
Windows 3.0/pg/2#3 = 88 =
PatBlt(hMemoryDC, 0, 0, 64, 32, BLACKNESS);
hOldBrush = SelectObject(hMemoryDC, hRedBrush);
PatBlt(hMemoryDC, 0, 0, 24, 11, PATORDEST);
PatBlt(hMemoryDC, 40, 10, 24, 12, PATORDEST);
PatBlt(hMemoryDC, 24, 22, 24, 11, PATORDEST);
SelectObject(hMemoryDC, hGreenBrush);
PatBlt(hMemoryDC, 24, 0, 24, 11, PATORDEST);
PatBlt(hMemoryDC, 0, 10, 24, 12, PATORDEST);
PatBlt(hMemoryDC, 40, 22, 24, 11, PATORDEST);
SelectObject(hMemoryDC, hBlueBrush);
PatBlt(hMemoryDC, 40, 0, 24, 11, PATORDEST);
PatBlt(hMemoryDC, 24, 10, 24, 12, PATORDEST);
PatBlt(hMemoryDC, 0, 22, 24, 11, PATORDEST);
BitBlt(hDC,0,0,64,32,hMemoryDC,0,0,SRCCOPY);
SelectObject(hMemoryDC, hOldBrush);
DeleteObject(hRedBrush);
DeleteObject(hGreenBrush);
DeleteObject(hBlueBrush);
SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hMemoryDC);
ReleaseDC(hWnd, hDC);
В этом примере функция CreateSolidBrush создает красную,
зеленую и синюю кисти, необходимые для составления клеточного
шаблона. Функция SelectObject выбирает каждую кисть в контексте
устройства памяти, когда эта кисть необходима, а функция PatBlt
рисует цвета в растровой карте. Каждый цвет рисуется трижды,
всякий раз в небольшом прямоугольнике. Функция PatBlt намеренно
немного перекрывает небольшие прямоугольники различных цветов.
Поскольку задана растровая операция PATORDEST, функция PatBlt
объединяет цвет кисти с цветом, уже имеющимся в растровой
карте, используя булевскую операцию OR. Результатом является
различная цветовая окантовка каждого прямоугольника. После
завершения создания растровой карты она копируется из контекста
устройства памяти на экран с помощью функции BitBlt.
11.3 Отображение растровых карт на экране.
Растровую карту можно индицировать несколькими различными
способами:
- Вы можете использовать функцию BitBlt для копирования
растровой карты из контекста отображения памяти на
устройство отображения.
- Вы можете использовать функцию StretchBlt для
копирования растянутой или сжатой растровой карты из
контекста отображения памяти на устройство отображения.
.
Windows 3.0/pg/2#3 = 89 =
- Вы можете использовать функцию CreatePatternBrush для
создания кисти, которая объединяет растровую карту.
Последующие функции GDI, использующие эту кисть, такие
как PatBlt, могут индицировать растровую карту.
- Вы можете использовать функцию SetDIBitsToDevice для
копирования аппаратно независимой растровой карты из
контекста отображения памяти прямо на устройство
отображения.
Можно также индицировать растровую карту в меню. В этом
случае растровая карта используется как элемент меню, который
пользователь может выбрать для выполнения действия. Более
подробно смотрите главу 7 "Меню".
11.3.1. Индицирование растровой карты с помощью функции
BitBlt.
Можно индицировать растровую карту, используя функцию
BitBlt. Эта функция копирует растровую карту из исходного
контекста устройства-источника в контекст устройства
назначения. Для индицирования растровой карты с помощью BitBlt
необходимо создать контекст устройства памяти и выбрать
растровую карту в этом контексте. В приведенном ниже примере
растровая карта индицируется с помощью функции BitBlt:
HDC hDC, hMemoryDC;
.
.
.
hDC = GetDC(hWnd);
hMemoryDC = CreateCompatibleDC(hDC);
hOldBitmap = SelectObject(hMemoryDC , hBitmap);
if(hOldBitmap) {
BitBlt(hDC, 100, 30, 64, 32, hMemoryDC, 0, 0, SRCCOPY);
SelectObject(hMemoryDC, hOldBitmap);
}
DeleteDC(hMemoryDC);
ReleaseDC(hWnd, hDC);
Функция GetDC определяет контекст отображения области
пользователя окна с дескриптором hWnd. Функция
CreateCompatibleDC создает контекст устройства памяти, который
совместим с контекстом отображения. Функция SelectObject
выбирает растровую карту с дескриптором hBitмap в контексте
устройства памяти и возвращает ранее выбранную растровую карту.
Если эта функция не может выбрать растровую карту, она
возвращает нуль.
Функция BitBlt копирует растровую карту из контекста
устройства памяти в контекст отображения дисплея. Функция
.
Windows 3.0/pg/2#3 = 90 =
помещает верхний левый угол растровой карты в точку с
координатами (100, 30). Копируется вся растровая карта шириной
64 бита и высотой 32 бита. Переменные hDC и hMemoryDC
представляют дескрипторы контекста места назначения и источника
соответственно. Константа SRCCOPY определяет код растровой
операции. Она заставляет функцию BitBlt копировать растровую
карту-источник без комбинации ее с шаблонами или цветами уже
существующими в месте назначения.
Функции SelectObject, DeleteDC и ReleaseDC служат для
очистки после того, как растровая карта была индицирована.
Вообще говоря, по завершении использования контекстов
отображения и устройства памяти необходимо их как можно скорее
освободить (особенно контекст отображения), поскольку это -
ограниченный ресурс. Если программа не освободит контекст
отображения после его использования, то другие прикладные
программы при необходимости не смогут его получить. Если вы
получили контекст устройства с помощью функции GetDC то вы
должны будете его освободить функцией ReleaseDC. Если же вы
создали контекст устройства с помощью функции
CreateCompatibleDC, то вы должны будете его в дальнейшем
освободить, используя функцию DeleteDC. Функция SelectObject
необходима, поскольку нельзя удалить контекст устройства, если
выбрана растровая карта, не являющаяся первоначальной для
данного контекста.
В предыдущем примере предполагалось, что ширина и высота
растровой карты равна 64 и 32 пикселям соответственно. Другим
способом определения ширины и высоты индицируемой растровой
карты является нахождение их из самой растровой карты. Это
можно сделать, используя функцию GetObject, которая заполняет
указанную структуру размерами заданного объекта. Например, для
нахождения ширины и высоты растровой карты можно использовать
следующие операторы:
BITMAP Bitmap;
.
.
.
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
В следующем примере ширина и высота растровой карты
копируются в поля bmWidth и bmHeight структуры Bitmap. Можно
использовать эти значения в функции BitBlt следующим образом:
BitBlt (hDC, 100, 30, Bitmap.bmWidth, Bitmap.bmHeight,
hMemoryDC, 0, 0, SRCCOPY);
Функция BitBlt может индицировать монохромную и цветную
растровые карты. Не требуется специальных действий для
индикации растровых карт различных форматов. Однако, необходимо
помнить, что функция BitBlt может преобразовать растровую
карту, если ее цветной формат не совпадает с устройством
.
Windows 3.0/pg/2#3 = 91 =
назначения. Например, при индикации цветной растровой карты на
монохромном дисплее функция BitBlt преобразует пиксели, имеющие
цвет фона, в белый цвет, а все другие - в черный.
11.3.2 Растяжение растровых карт.
Растровые карты не ограничиваются их первоначальными
размерами. Их можно растягивать или сжимать, используя функцию
StretchBlt вместо функции BitBlt. Например, можно удвоить
размер растровой карты 64 х 32 пикселя, используя следующий
оператор:
StretchBlt(hDC, 100, 30, 128, 64, hMemoryDC, 0, 0, 64,
32, SRCCOPY);
Функция StretchBlt имеет два дополнительных параметра,
которых нет у BitBlt. В частности, StretchBlt определяет ширину
и высоту исходной растровой карты. Первая упомянутая ширина и
высота, заданная значениями 128 и 64 пикселей, применяется
только к окончательному размеру растровой карты в контексте
устройства-получателя.
Для сжатия растровой карты функция StretchBlt удаляет
пиксели из скопированных растровых карт. Это означает, что
некоторая часть информации в ней теряется при индицировании.
Для того, чтобы минимизировать потери, можно установить текущий
режим растяжения, чтобы заставить функцию StretchBlt попытаться
сохранить некоторую часть информации, комбинируя ее с
пикселями, которые будут индицироваться. Возможные режимы
растяжения:
Режим Описание
----------------------------------------------------------
WHITEONBLACK используется, если необходимо
сохранить белые пиксели при избытке
черных; например, если имеется
белый текст на черном фоне.
BLACKONWHITE используется, если необходимо
сохранить черные пиксели при
избытке белых; например, если
имеется белый текст на черном фоне.
COLORONCOLOR используется для цветных растровых
карт. Попытка объединять цвета
может привести к нежелательному
эффекту.
Режим растяжения устанавливается функцией
SetStrechBltMode. В приведенном ниже примере функция
SetStretchBltMode устанавливает режим растяжения в значение
.
Windows 3.0/pg/2#3 = 92 =
WHITEONBLACK:
SetStretchBltMode(hDC, WHITEONBLACK);
11.3.3 Использование растровых карт в кисти шаблона.
Можно использовать растровые карты с кистью, применяя
кисть шаблона. Как только кисть шаблона создана, можно выбирать
кисть в контексте устройства и использовать функцию PatBlt для
копирования ее на экран; Rectangle, Ellipse и другие функции
рисования могут использовать кисть для заполнения созданных
фигур. Когда Windows рисует с помощью кисти шаблона, она
заполняет указанную область, повторно копируя растровую карту
по горизонтали и вертикали так, как это необходимо. При этом
размер растровой карты не изменяется, как это делает функция
StretchBlt.
Если растровая карта используется с кистью шаблона, она
должна иметь размер по крайней мере 8 х 8; это размер шаблона
по умолчанию, используемый большинством драйверов дисплеев.
Можно применять и большие растровые карты, но будет
использоваться только левый верхний ее угол размером 8 х 8
пикселей. Можно жестко задать растровую карту кодами или
загрузить ее как ресурс. В любом случае, если имеется
дескриптор растровой карты, можно создавать кисть шаблона,
используя функцию CreatePatternBrush. В приведенном ниже
примере растровая карта загружается и используется для создания
кисти шаблона:
hBitmap=LoadBitmap(hInstance, "checks");
hBrush=CreatePatternBrush(hBitmap);
Как только кисть шаблона создана, можно выбирать ее в
контексте устройства с помощью функции SelectObject:
hOldBrush=SelectObject(hDC, hBrush);
Поскольку растровая карта является частью кисти, этот
вызов функции SelectObject не воздействует на выбранную
растровую карту с контекстом устройства.
После выбора кисти можно использовать функцию PatBlt для
заполнения указанной области растровой картой. Например,
приведенный ниже оператор заполняет верхний левый угол окна
растровой картой:
PatBlt(hDC, 0, 0, 100, 100, PATCOPY);
Растровая операция PATCOPY заставляет функцию PatBlt
полностью заменить образ места назначения кистью шаблона.
Можно также использовать кисть шаблона в качестве кисти
фона данного окна. Для этого просто необходимо присвоить
.
Windows 3.0/pg/2#3 = 93 =
дескриптор кисти полю hbrBackground структуры класса окна:
pWndClass->hbrBackground=CreatePatternBrush(hBitmap);
С этого момента Windows использует кисть шаблона при
стирании фона окна. Можно также изменить текущую кисть фона для
класса окон, используя функцию SetClassWord. Например, если
необходимо использовать новую кисть шаблона после того, как
окно создано, можно использовать следующий оператор:
SetClassWord(hWnd, GCW_HBRBACKGROUND, hBrush);
Необходимо помнить, что этот оператор изменяет кисть фона
для всех окон данного класса. Если необходимо изменить фон
только для одного окна, нужно явно обработать сообщения
WM_ERASEBKGND, которые получает окно. Приведенный ниже пример
иллюстрирует, как это сделать:
RECT Rect;
HBRUSH hOldBrush;
.
.
.
case WM_ERASEBKGND:
UnrealizeObject(hMyBkgndBrush);
hOldBrush = SelectObject(wParam, hMyBkgndBrush);
GetUpdateRect(wParam, &Rect, FALSE);
PatBlt(wParam, Rect.left, Rect.top,
Rect.right - Rect.left, Rect.bottom - Rect.top,
PATCOPY);
SelectObject(wParam, hOldBrush);
break;
Сообщение WM_ERASEBKGND передает дескриптор контекста
отображения в параметр wParam. Функция SelectObject выбирает
нужную кисть фона в контексте отображения. Функция
GetUpdateRect находит область, которую необходимо стереть (это
не всегда вся область пользователя). Функция PatBlt копирует
шаблон, затирая все, что находится в корректируемом
прямоугольнике. Наконец, функция SelectObject восстанавливает
прежнюю кисть контекста отображения.
В предыдущем примере использовалась функция
UnrealizeObject. Каждый раз, когда программа или пользователь
перемещает окно, в котором использовалась или будет
использоваться кисть шаблона, необходимо настроить ее на новую
позицию с помощью функции UnrealizeObject. Эта функция
сбрасывает область рисования кисти так, что может отображаться
любой шаблон после перемещения, совпадающий с шаблоном, который
использовался перед ним.
Можно использовать функцию DeleteObject для удаления кисти
шаблона, если она больше не нужна. Однако, эта функция не
.
Windows 3.0/pg/2#3 = 94 =
удаляет вместе с кистью растровую карту. Для удаления растровой
карты необходимо снова использовать функцию DeleteObject и
указать в ней дескриптор растровой карты.
11.3.4 Индицирование аппаратно-независимых растровых карт.
Одним из удобств аппаратно-независимых растровых карт
является возможность отображения их прямо на устройстве без
использования промежуточных контекстов устройства памяти.
Функция SetDIBitsToDevice посылает всю или часть
аппаратно-независимой растровой карты прямо на устройство
вывода, значительно уменьшая требуемую для отображения
растровой карты память. Когда вы вызываете для вывода растровой
карты функцию SetDIBitsToDevice, вы передаете ей следующую
информацию:
- Контекст устройства назначения.
- Точку в контексте устройства, в которую должна
выводиться растровая карта.
- Размер растровой карты на устройстве отображения.
- Число строк в исходном буфере растровой карты, из
которого она копируется.
- Первый пиксель, который будет копироваться из исходной
растровой карты на устройство отображения.
- Структуру описания аппаратно-независимой растровой карты
и буфер в котором содержится сама растровая карта.
- Что содержит таблица цветов описания аппаратно
независимой растровой карты - отдельные RGB значения или
индексы в палитре цветов.
Примечание. Точкой привязки аппаратно-независимой
растровой карты служит нижний левый угол растровой карты, а не
левый верхний угол, как при остальных графических операциях.
Следующий пример демонстрирует вызов функции
SetDIBitsToDevice:
SetDIBitsToDevice(hDC,0,0,lpbi->bmciHeader.bcWidth,
lpbi->bmciHeader.bcHeight,0,0,0,
lpbi->bmciHeader.bcHeight,
pBuf,(LPBITMAPINFO)lpbi,
DIB_RGB_COLORS);
В данном примере hDC определяет контекст устройства
отображения. SetDIBitsToDevice использует эту информацию для
определения поверхности устройства и для определения
допустимого формата цветов для растровой карты устройства.
.
Windows 3.0/pg/2#3 = 95 =
Следующие два параметра определяют точку на устройстве,
откуда SetDIBitsToDevice должна начать рисовать растровую
карту. В данном случае - это начало контекста устройства.
Следующие два параметра определяют ширину и высоту растровой
карты.
Шестой и седьмой параметры, в нашем примере равны 0,
определяют первый пиксель из исходной растровой карты, который
посылается на устройство. Поскольку они равны 0, то растровая
карта выводится с первого пикселя.
Следующие два параметра используются для целей
объединения. Первый из них установлен в 0, определяя, что
начальная строка отображения должна быть в буфере первой.
Второй параметр, равный высоте растровой карты, говорит о том,
что вся растровая карта будет отображаться на экране как одно
целое.
Сама растровая карта содержится в буфере pBuf, а параметр
lpbi определяет структуру BITMAPINFO, которая содержит
информацию о формате цветов исходной растровой карты.
Последний параметр - это флаг, который определяет, что
таблица цветов растровой карты содержит действительные RGB
значения или индексы в текущей логической палитре. Значение
DIB_RGB_COLORS говорит о том, что таблица содержит
действительные RGB значения.
11.3.5 Использование растровой карты в качестве элемента
меню.
Вы можете использовать растровую карту в качестве элемента
меню. Для этого замените текст элемента меню, определяемого в
файле описания ресурсов прикладной программы растровой картой.
(Вы не можете ее указать сразу в файле описания ресурсов.)
Дополнительную информацию вы найдете в главе 7 "Меню".
11.4 Добавление цвета к монохромной растровой карте.
Если ваш компьютер имеет цветной дисплей, можно добавить
цвета к монохромной растровой карте, установив цвета фона и
переднего плана контекста отображения. Цвета фона и переднего
плана специфицируют, как окрасить белые и черные биты растровой
карты при ее отображении. Цвета фона и переднего плана
устанавливаются с помощью функций SetTextColor и SetBkColor. В
приведенном ниже примере показано, как установить цвет
переднего плана в красный, а цвет фона - в зеленый:
SetTextColor(hDC, RGB(255, 0, 0));
SetBkColor(hDC, RGB(0, 255, 0));
Переменная hDC содержит дескриптор контекста отображения.
.
Windows 3.0/pg/2#3 = 96 =
Функция SetTextColor устанавливает цвет переднего плана в
красный. Функция SetBkColor устанавливает цвет фона в зеленый.
Служебная команда RGB создает цветовое значение RGB, используя
три указанных значения. Каждое из них представляет
интенсивность одного из основных цветов - красного, зеленого и
синего, причем 255 - это максимальная интенсивность, а 0 -
минимальная. Можно формировать другие цвета, задавая любую их
интенсивность в указанных пределах и объединяя их. Например,
приведенный ниже оператор создает желтое значение RGB:
RGB(255, 255, 0);
Как только будут установлены цвета переднего плана и фона,
можно производить дальнейшие действия. Можно индицировать
растровую карту (как описано ранее), и Windows будет
автоматически добавлять цвета фона и переднего плана. Цвет
переднего плана применяется к белым битам (установленным в 1),
а цвет фона - к черным битам (установленным в 0). Заметим, что
режим фона, как он определен функцией SetBkMode, не применим к
растровым картам. Кроме того, цвета фона и переднего плана не
применимы к цветным растровым картам.
При индицировании в цвете растровая карта с именем "dog"
будет красной, фон - зеленым.
11.5 Удаление растровых карт.
Растровая карта подобно другим ресурсам пока используется
занимает память. Как только растровая карта становится больше
не нужна (или перед завершением программы), необходимо ее
удалить, чтобы освободить память для других программ. Для
удаления растровой карты сначала нужно удалить ее из контекста
устройства, в котором она была выбрана, а затем окончательно
удалить ее с помощью функции DeleteObject.
В приведенном ниже примере растровая карта,
идентифицированная параметром hBitmap, удаляется после
уничтожения ее как текущей растровой карты, выбранной в
контексте устройства памяти, идентифицированном параметром
hMemoryDC:
SelectObject(hMemoryDC, hOldBitmap);
DeleteObject(hBitmap);
Функция SelectObject удаляет растровую карту из выборки,
заменяя ее предыдущей растровой картой, идентифицированной
параметром hOldBitmap. Функция DeleteObject окончательно
удаляет растровую карту. С этого момента дескриптор растровой
карты hBitmap становится некорректным и не должен больше
использоваться.
.
Windows 3.0/pg/2#3 = 97 =
11.6 Пример прикладной программы Bitmap.
В этом примере показано, как подсоединить ряд операций над
растровыми картами к прикладной программе. В частности,
показано как:
- Загрузить и индицировать монохромную растровую карту.
- Создать и индицировать цветную растровую карту.
- Растянуть или сжать растровую карту с помощью мыши.
- Установить режим растяжения/сжатия.
- Создать и использовать кисть шаблона.
- Использовать кисть шаблона для фона окна.
В этой программе пользователь специфицирует (с помощью
мыши), где и как будет индицироваться растровая карта. Если
пользователь перемещает мышь, удерживая нажатой левую кнопку, и
затем ее отпускает, прикладная программа использует функцию
StretchBlt для заполнения выбранного прямоугольника текущей
растровой картой. Если пользователь нажимает правую кнопку,
программа использует функцию BitBlt для индицирования растровой
карты.
Для создания прикладной программы Bitmap скопируйте и
переименуйте исходные файлы программы Generic, затем выполните
следующие изменения:
1. Добавьте определения констант и объявления функций во
включаемый файл.
2. Добавьте две монохромные растровые карты, созданные с
помощью SDKPaint, к файлу описания ресурсов.
3. Добавьте меню Bitmap, Pattern и Mode к файлу описания
ресурсов.
4. Добавьте глобальные и локальные переменные.
5. Добавьте фрагмент WM_CREATE к функции окна для создания
растровых карт и добавления растровых карт к меню.
6. Модифицируйте фрагмент WM_DESTROY в функции окна для
удаления растровых карт.
7. Добавьте фрагменты WM_LBUTTONUP, WM_MOUSEMOVE и
WM_LBUTTONDOWN к функции окна для создания
прямоугольника выбора и индицирования растровых карт.
8. Добавьте фрагмент WM_RBUTTONUP к функции окна для
отображения растровых карт.
.
Windows 3.0/pg/2#3 = 98 =
9. Добавьте фрагмент WM_ERASEBKGND к функции окна для
стирания области пользователя.
10. Добавьте фрагмент WM_COMMAND для поддержки меню.
11. Модифицируйте командную строку link в файле make для
включения библиотечного файла select.lib.
12. Оттранслируйте и скомпонуйте прикладную программу.
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
В следующих разделах эти шаги описаны более подробно.
11.6.1 Модификация включаемого файла.
Во включаемый файл необходимо добавить следующие
объявления функций и определения констант:
#define IDM_BITMAP1 200
#define IDM_BITMAP2 201
#define IDM_BITMAP3 202
#define IDM_PATTERN1 300
#define IDM_PATTERN2 301
#define IDM_PATTERN3 302
#define IDM_PATTERN4 303
#define IDM_BLACKONWHITE 400
#define IDM_WHITEONBLACK 401
#define IDM_COLORONCOLOR 402
#define PATORDEST 0x00FA0089L
HBITMAP MakeColorBitmap(HWND);
11.6.2 Добавление ресурсов растровой карты.
Необходимо добавить операторы BITMAP к файлу описания
ресурсов. Следующие два оператора добавляют растровые карты
"dog" и "cat" к ресурсам прикладной программы:
dog BITMAP dog.bmp
cat BITMAP cat.bmp
Растровая карта "dog" - это белое очертание собаки на
черном фоне. Растровая карта "cat" - это черное очертание
кота на белом фоне.
.
Windows 3.0/pg/2#3 = 99 =
11.6.3 Добавление меню Bitmap, Pattern и Mode.
Необходимо добавить оператор MENU к файлу описания
ресурсов. Этот оператор определяет меню Bitmap, Pattern и Mode,
используемые для выбора различных растровых карт и режимов,
используемых в прикладной программе. Добавьте следующие
операторы к файлу описания ресурсов:
BitmapMenu MENU
BEGIN
POPUP "&Bitmap"
BEGIN
MENUITEM "", IDM_BITMAP1
END
POPUP "&Pattern"
BEGIN
MENUITEM "", IDM_PATTERN1
END
POPUP "&Mode"
BEGIN
MENUITEM "&WhiteOnBlack", IDM_WHITEONBLACK, CHECKED
MENUITEM "&BlackOnWhite", IDM_BLACKONWHITE
MENUITEM "&ColorOnColor", IDM_COLORONCOLOR
END
END
Меню Bitmap и Pattern содержат единственный оператор
MENUITEM. Этот оператор определяет команду, которая только
резервирует место для команд меню. Прикладная программа будет
добавлять конкретные команды для использования их в меню с
помощью функции ChangeMenu.
11.6.4 Добавление глобальных и локальных переменных.
Необходимо объявить массивы шаблонов, дескрипторы
растровых карт, дескрипторы контекстов и другие переменные,
используемые для создания и индицирования растровых карт.
Добавьте следующие операторы в начало файла описания ресурсов:
BYTE White[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF };
BYTE Black[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00 };
BYTE Zigzag[] = { 0xFF, 0xF7, 0xEB, 0xDD, 0xBE, 0x7F,
0xFF, 0xFF };
BYTE CrossHatch[] = { 0xEF, 0xEF, 0xEF, 0xEF, 0x00,
0xEF, 0xEF, 0xEF };
HBITMAP hPattern1;
HBITMAP hPattern2;
HBITMAP hPattern3;
HBITMAP hPattern4;
.
Windows 3.0/pg/2#3 = 100 =
HBITMAP hBitmap1;
HBITMAP hBitmap2;
HBITMAP hBitmap3;
HBITMAP hMenuBitmap1;
HBITMAP hMenuBitmap2;
HBITMAP hMenuBitmap3;
HBITMAP hBitmap;
HBITMAP hOldBitmap;
HBRUSH hBrush; /* дескриптор кисти */
WORD fStretchMode; /* тип используемого режима
растяжения/сжатия */
HDC hDC; /* дескриптор контекста устройства */
HDC hMemoryDC; /* дескриптор контекста устройства
памяти */
BITMAP Bitmap; /* создание растровой карты */
BOOL bTrack = FALSE; /* TRUE, если пользователь выб-
рал область */
RECT Rect;
WORD wPrevBitmap = IDM_BITMAP1;
WORD wPrevPattern = IDM_PATTERN1;
WORD wPrevMode = IDM_WHITEONBLACK;
WORD wPrevItem;
int Shape = SL_BLOCK; /* то, что используется в прямо-
угольнике выборки */
Массивы шаблонов White, Black, Zigzag и CrossHatch
содержат биты, определяющие образы растровой карты размером 8 х
8. Переменные hPattern1, hPattern2, hPattern3 и hPattern4
содержат дескрипторы растровых карт кистей шаблонов. Переменные
hBitmap1, hBitmap2 и hBitmap3 содержат дескрипторы растровых
карт, которые будут индицироваться. Переменные hMenuBitmap1,
hMenuBitmap2 и hMenuBitmap3 содержат дескрипторы растровых
карт, которые будут индицироваться в меню Bitmap. Переменные
hBrush, hBitmap и fStretchMode содержат текущие кисть фона,
растровую карту и режим растяжения. Переменные hDC, hMemoryDC и
hOldBitmap содержат дескрипторы, используемые с контекстом
устройства памяти. Структура Bitmap содержит размеры текущей
растровой карты. Переменная bTrack используется для индикации
того, что происходит выборка. Структура Rect содержит текущий
прямоугольник выборки. Переменные wPrevBitmap, wPrevPattern,
wPrevItem и wPrevMode содержат ID ранее выбранных растровых
карт, шаблонов и режимов растяжения. Они используются для
помещения и удаления контрольных отметок в меню.
Добавьте следующие переменные в функцию MainWndProc:
HMENU hMenu;
HBRUSH hOldBrush;
HBITMAP hOurBitmap;
.
Windows 3.0/pg/2#3 = 101 =
11.6.5 Добавление фрагмента WM_CREATE.
Для создания или загрузки растровых карт и установки меню
необходимо иметь фрагмент WM_CREATE, переменные поддержки и
объявления функций. Фрагмент WM_CREATE создает четыре
монохромные растровые карты размером 8 х 8 пикселей, которые
будут использоваться как кисти шаблонов для фона окна. Он также
создает или загружает три растровые карты размером 64 х 32
пикселя, которые будут индицироваться в окне. Для того, чтобы
разрешить пользователю выбрать для просмотра растровую карту
или шаблон, фрагмент WM_CREATE добавляет их к меню Bitmap и
Pattern, используя функции ChangeMenu. Наконец, фрагмент
устанавливает начальные значения кисти, растровой карты и
режимов растяжения и создает контекст устройства памяти, из
которого копируются растровые карты.
Фрагмент WM_CREATE создает четыре шаблона, используя
функцию CreateBitmap. Он загружает две растровые карты "dog" и
"cat" и создает третью, используя функцию MakeColorBitmap,
определенную в программе. После создания шаблонов и растровых
карт фрагмент WM_CREATE создает накладываемые меню,
присоединяет к ним шаблоны и растровые карты и заменяет
существующие меню Bitmap и Pattern новыми накладываемыми меню.
Затем переменные hBrush, hBitmap и fStretchMode устанавливаются
в начальные значения кисти фона, растровой карты и режимов
растяжения. Наконец, фрагмент создает контекст устройства
памяти, из которого растровые карты копируются на дисплей.
Добавьте следующие операторы к функции окна:
case WM_CREATE: /* сообщение: создать окно */
hPattern1 = CreateBitmap(8, 8, 1, 1, (LPSTR) White);
hPattern2 = CreateBitmap(8, 8, 1, 1, (LPSTR) Black);
hPattern3 = CreateBitmap(8, 8, 1, 1, (LPSTR) Zigzag);
hPattern4 = CreateBitmap(8, 8, 1, 1, (LPSTR) CrossHatch);
hBitmap1 = LoadBitmap(hInst, "dog");
hBitmap2 = LoadBitmap(hInst, "cat");
hBitmap3 = MakeColorBitmap(hWnd);
hMenuBitmap1 = LoadBitmap(hInst, "dog");
hMenuBitmap2 = LoadBitmap(hInst, "cat");
hMenuBitmap3 = MakeColorBitmap(hWnd);
hMenu = CreateMenu();
AppendMenu(hMenu, MF_STRING | MF_CHECKED, IDM_PATTERN1, "&White");
AppendMenu(hMenu, MF_STRING, IDM_PATTERN2, "&Black");
AppendMenu(hMenu, MF_BITMAP, IDM_PATTERN3,
(LPSTR)(LONG)hPattern3);
AppendMenu(hMenu, MF_BITMAP, IDM_PATTERN4,
(LPSTR)(LONG)hPattern4);
ModifyMenu(GetMenu(hWnd), 1, MF_POPUP | MF_BYPOSITION,
(WORD)hMenu, "&Pattern");
.
Windows 3.0/pg/2#3 = 102 =
hMenu = CreateMenu();
/* Использовать в качестве элементов меню растровые карты */
AppendMenu(hMenu, MF_BITMAP | MF_CHECKED, IDM_BITMAP1,
(LPSTR)(LONG) hMenuBitmap1);
AppendMenu(hMenu, MF_BITMAP, IDM_BITMAP2,
(LPSTR)(LONG) hMenuBitmap2);
AppendMenu(hMenu, MF_BITMAP, IDM_BITMAP3,
(LPSTR)(LONG) hMenuBitmap3);
ModifyMenu(GetMenu(hWnd), 0, MF_POPUP | MF_BYPOSITION,
(WORD) hMenu, "&Bitmap");
hBrush = CreatePatternBrush(hPattern1);
fStretchMode = IDM_BLACKONWHITE;
hDC = GetDC(hWnd);
hMemoryDC = CreateCompatibleDC(hDC);
ReleaseDC(hWnd, hDC);
hOldBitmap = SelectObject(hMemoryDC, hBitmap1);
GetObject(hBitmap1, 16, (LPSTR) &Bitmap);
break;
Функции CreateBitmap и LoadBitmap работают, как описано в
начале данной главы. Функция MakeColorBitmap создана для данной
прикладной программы. Она создает и рисует цветную растровую
карту, используя метод, описанный в пункте 11.2.2 "Создание и
заполнение пустой растровой карты". Операторы этой функции
описаны ниже. Заметим, что каждая растровая карта загружается
или создается дважды. Это необходимо, поскольку никакой
единственный дескриптор растровой карты не может быть выбран в
двух контекстах устройства одновременно. Для индицирования в
области меню требуется выборка, как и для индицирования в
области пользователя.
Функция CreateMenu создает пустое меню и возвращает его
дескриптор. Функция ChangeMenu, определяющая дескрипторы
шаблонов, добавляет шаблоны в качестве элементов к новому меню.
MF_BITMAP указывает, что должна быть добавлена растровая карта.
Функция CheckMenuItem помещает контрольную отметку рядом с
текущим элементом меню, а последняя функция ChangeMenu заменяет
существующее меню Шаблон. Те же самые шаги затем повторяются
для меню Bitmap.
Функция CreateCompatibleDC создает контекст устройства
памяти, который совместим с дисплеем. Функция SelectObject
выбирает текущую растровую карту в контексте устройства памяти,
так что она готова для копирования на дисплей. Функция
GetObject копирует размеры растровой карты в структуру Bitmap.
Затем эта структура может быть использована в последующих
функциях BitBlt и StretchBlt для определения ширины и высоты
растровой карты.
.
Windows 3.0/pg/2#3 = 103 =
Функция MakeColorBitmap создает цветную растровую карту с
помощью растровой карты, совместимой с дисплеем, затем затем
рисует клеточный цветной шаблон, используя красную, зеленую и
синюю кисти и функцию PatBlt. В конец исходного файла
добавляется следующее описание функции:
HBITMAP MakeColorBitmap(hWnd);
HWND hWnd;
{
HDC hDC;
HDC hMemoryDC;
HBITMAP hBitmap;
HBITMAP hOldBitmap;
HBRUSH hRedBrush;
HBRUSH hGreenBrush;
HBRUSH hBlueBrush;
HBRUSH hOldBrush;
hDC = GetDC(hWnd);
hMemoryDC = CreateCompatibleDC(hDC);
hBitmap = CreateCompatibleBitmap(hDC, 64, 32);
hOldBitmap = SelectObject(hMemoryDC, hBitmap);
hRedBrush = CreateSolidBrush(RGB(255,0,0));
hGreenBrush = CreateSolidBrush(RGB(0,255,0));
hBlueBrush = CreateSolidBrush(RGB(0,0,255));
PatBlt(hMemoryDC, 0, 0, 64, 32, BLACKNESS);
hOldBrush = SelectObject(hMemoryDC, hRedBrush);
PatBlt(hMemoryDC, 0, 0, 24, 11, PATORDEST);
PatBlt(hMemoryDC, 40, 10, 24, 12, PATORDEST);
PatBlt(hMemoryDC, 20, 21, 24, 11, PATORDEST);
hOldBrush = SelectObject(hMemoryDC, hGreenBrush);
PatBlt(hMemoryDC, 20, 0, 24, 11, PATORDEST);
PatBlt(hMemoryDC, 0, 10, 24, 12, PATORDEST);
PatBlt(hMemoryDC, 40, 21, 24, 11, PATORDEST);
hOldBrush = SelectObject(hMemoryDC, hBlueBrush);
PatBlt(hMemoryDC, 40, 0, 24, 11, PATORDEST);
PatBlt(hMemoryDC, 20, 10, 24, 12, PATORDEST);
PatBlt(hMemoryDC, 0, 21, 24, 11, PATORDEST);
SelectObject(hMemoryDC, hOldBrush);
DeleteObject(hRedBrush);
DeleteObject(hGreenBrush);
DeleteObject(hBlueBrush);
SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hMemoryDC);
ReleaseDC(hWnd, hDC);
return (hBitmap);
}
Эта функция выполняет те же самые шаги, которые описаны в
разделе 11.2.3 "Создание растровых карт с помощью фиксированных
кодов".
.
Windows 3.0/pg/2#3 = 104 =
11.6.6 Модификация фрагмента WM_DESTROY.
Перед завершением прикладной программы необходимо удалить
созданные растровые карты, шаблоны, кисти и контекст устройства
памяти. Растровые карты, шаблоны и кисти удаляются с помощью
функции DeleteObject. Контекст устройства памяти удаляется с
помощью функции DeleteDC. Модифицируйте фрагмент WM_DESTROY
так, чтобы он выглядел следующим образом:
case WM_DESTROY: /* сообщение: разрушить окно */
SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hMemoryDC);
DeleteObject(hBrush);
DeleteObject(hPattern1);
DeleteObject(hPattern2);
DeleteObject(hPattern3);
DeleteObject(hPattern4);
DeleteObject(hBitmap1);
DeleteObject(hBitmap2);
DeleteObject(hBitmap3);
DeleteObject(hMenuBitmap1);
DeleteObject(hMenuBitmap2);
DeleteObject(hMenuBitmap3);
PostQuitMessage(0);
break;
11.6.7 Добавление фрагментов WM_LBUTTONUP, WM_MOUSEMOVE
и WM_LBUTTONDOWN.
Необходимо добавить фрагменты WM_LBUTTONUP, WM_MOUSEMOVE и
WM_LBUTTONDOWN к функции окна, чтобы дать возможность
пользователю выбрать прямоугольник, в который будет скопирована
текущая растровая карта. Эти фрагменты используют функции
выбора (описанные в главе 20 "Динамически подключаемые
библиотеки") для создания прямоугольника выбора и осуществления
обратной связи с пользователем. Фрагмент WM_LBUTTONUP затем
использует функцию StretchBlt для заполнения прямоугольника.
Добавьте следующие операторы к функции окна:
case WM_LBUTTONDOWN: /* сообщение: нажата левая кнопка
"мыши" */
bTrack = TRUE;
SetRectEmpty((LPRECT)&Rect);
StartSelection(hWnd, MAKEPOINT(lParam), (LPRECT) &Rect,
(wParam & MF_SHIFT) ? (SL_EXTEND | Shape) : Shape);
break;
case WM_MOUSEMOVE: /* сообщение: перемещение "мыши" */
if (bTrack)
.
Windows 3.0/pg/2#3 = 105 =
UpdateSelection(hWnd, MAKEPOINT(lParam), (LPRECT)&Rect,
Shape);
break;
case WM_LBUTTONUP: /* сообщение: отпущена левая кнопка
"мыши" */
bTrack = FALSE;
EndSelection(MAKEPOINT(lParam), (LPRECT)&Rect);
ClearSelection(hWnd, (LPRECT)&Rect, Shape);
hDC = GetDC(hWnd);
SetStretchBltMode(hDC, fStretchMode);
StretchBlt(hDC, Rect.left, Rect.top,
Rect.right - Rect.left, Rect.bottom - Rect.top,
hMemoryDC, 0, 0,
hBitmap.bmWidth, Bitmap.bmHeight, SRCCOPY);
ReleaseDC(hWnd, hDC);
break;
Для использования этих функций также необходимо включить
файл select.h (определенный в главе 20 "Динамически
подключаемые библиотеки"). Добавьте следующий оператор в начало
файла ресурсов:
#include "Select.h"
11.6.8 Добавление фрагмента WM_RBUTTONUP.
Необходимо добавить фрагмент WM_RBUTTONUP для
индицирования текущей растровой карты с помощью функции BitBlt.
Добавьте следующие операторы к функции окна:
case WM_RBUTTONUP: /* сообщение: освобождена правая
кнопка "мыши" */
hDC = GetDC(hWnd);
BitBlt(hDC, LOWORD(lParam), HIWORD(lParam),
Bitmap.bmWidth, Bitmap.bmHeight,
hMemoryDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hDC);
break;
11.6.9 Добавление фрагмента WM_ERASEBKGND.
Необходимо добавить фрагмент WM_ERASEBKGND для уверенности
в том, что используется выбранная кисть фона. Добавьте
следующие операторы к функции окна:
case WM_ERASEBKGND: /* сообщение: очистить фон */
UnrealizeObject(hBrush);
hOldBrush = SelectObject(wParam, hBrush);
GetClientRect(hWnd, (LPRECT)&Rect);
PatBlt(wParam, Rect.left, Rect.top,
.
Windows 3.0/pg/2#3 = 106 =
Rect.right - Rect.left, Rect.bottom - Rect.top,
PATCOPY);
SelectObject(wParam, hOldBrush);
return TRUE;
Переменная hOldBrush объявлена как локальная переменная.
Функция UnrealizeObject устанавливает выравнивание шаблона,
если окно перемещалось. Функция SelectObject устанавливает
кисть фона, а функция GetClientRect определяет, какая часть
области пользователя должна быть стерта. Функция PatBlt
копирует шаблон в корректируемый прямоугольник. Наконец,
функция SelectObject восстанавливает предыдущую кисть.
11.6.10 Добавление фрагмента WM_COMMAND.
Необходимо изменить фрагмент WM_COMMAND для поддержки меню
Bitmap, Pattern и Mode. Добавьте следующие операторы к функции
окна:
case WM_COMMAND: /* сообщение: команда Windows */
switch (wParam) {
case IDM_ABOUT:
lpProcAbout = MakeProcInstance(About, hInst);
DialogBox(hInst,
"AboutBox",
hWnd,
lpProcAbout);
FreeProcInstance(lpProcAbout);
break;
case IDM_BITMAP1:
wPrevItem = wPrevBitmap;
wPrevBitmap = wParam;
GetObject(hBitmap1, 16, (LPSTR) &Bitmap);
SelectObject(hMemoryDC, hBitmap1);
break;
case IDM_BITMAP2:
wPrevItem = wPrevBitmap;
wPrevBitmap = wParam;
GetObject(hBitmap2, 16, (LPSTR) &Bitmap);
SelectObject(hMemoryDC, hBitmap2);
break;
case IDM_BITMAP3:
wPrevItem = wPrevBitmap;
wPrevBitmap = wParam;
GetObject(hBitmap3, 16, (LPSTR) &Bitmap);
hOurBitmap = SelectObject(hMemoryDC, hBitmap3);
break;
case IDM_PATTERN1:
wPrevItem = wPrevPattern;
.
Windows 3.0/pg/2#3 = 107 =
wPrevPattern = wParam;
DeleteObject(hBrush);
hBrush = CreatePatternBrush(hPattern1);
InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
UpdateWindow(hWnd);
break;
case IDM_PATTERN2:
wPrevItem = wPrevPattern;
wPrevPattern = wParam;
DeleteObject(hBrush);
hBrush = CreatePatternBrush(hPattern2);
InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
UpdateWindow(hWnd);
break;
case IDM_PATTERN3:
wPrevItem = wPrevPattern;
wPrevPattern = wParam;
DeleteObject(hBrush);
hBrush = CreatePatternBrush(hPattern3);
InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
UpdateWindow(hWnd);
break;
case IDM_PATTERN4:
wPrevItem = wPrevPattern;
wPrevPattern = wParam;
DeleteObject(hBrush);
hBrush = CreatePatternBrush(hPattern4);
InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
UpdateWindow(hWnd);
break;
case IDM_BLACKONWHITE:
wPrevItem = wPrevMode;
wPrevMode = wParam;
fStretchMode = BLACKONWHITE;
break;
case IDM_WHITEONBLACK:
wPrevItem = wPrevMode;
wPrevMode = wParam;
fStretchMode = WHITEONBLACK;
break;
case IDM_COLORONCOLOR:
wPrevItem = wPrevMode;
wPrevMode = wParam;
fStretchMode = COLORONCOLOR;
break;
}
CheckMenuItem(GetMenu(hWnd), wPrevItem, MF_UNCHECKED);
.
Windows 3.0/pg/2#3 = 108 =
CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
break;
11.6.11 Модификация make-файла.
Файл ресурсов BITMAP.RES зависит от файлов DOG.BMP и
CAT.BMP. Для того, чтобы быть уверенными в том, что компилятор
ресурсов модифицирует файл ресурсов в случае изменения файла
CAT.BMP или DOG.BMP, добавьте следующий оператор к make-файлу:
BITMAP.RES: BITMAP.RC BITMAP.H DOG.BMP CAT.BMP
RC -r BITMAP.RC
Необходимо модифицировать командную строку link в файле
make для включения библиотечного файла select.lib. Этот файл
содержит объявления импортируемых функций для выборки, которые
используются с фрагментами WM_LBUTTONUP, WM_MOUSEMOVE и
WM_LBUTTONDOWN. Библиотека создается так, как это описано в
главе 20 "Динамически подключаемые библиотеки". Новая командная
строка link должна выглядеть так:
link /NOD bitmap, , , slibcew libw select.lib, bitmap.def
11.6.12 Трансляция и компоновка.
После внесения необходимых изменений оттранслируйте и
скомпонуйте прикладную программу Bitmap. Запустите Windows, а
затем Bitmap.
Если переместить мышь, нажимая левую кнопку, а затем ее
отпустить, то получим прямоугольник в окне, в который и будет
выведена растровая карта.
Используйте меню для изменения фона и режима растяжения.
Действие режима растяжения наблюдайте на примере растровых карт
"dog" и "cat".
11.7 Заключение.
В данной главе описано, как создавать и использовать
монохромные и цветные растровые каты. В Windows имеется два
типа растровых карт: аппаратно-зависимые и
аппаратно-независимые. Проще всего для использования растровой
карты нарисовать ее с помощью SDKPaint, добавить в файл
описания ресурсов прикладной программы и затем загрузить ее с
помощью функции LoadBitmap. Имеется несколько методов, которые
вы можете использовать в вашей прикладной программе для
создания и отображения растровых карт во время выполнения
программы. Прикладные программы могут использовать функции GDI
для вывода рисования каждого бита. Кроме этого можно
инициализировать растровые карты массивом бит или используя
существующую аппаратно-зависимую растровую карту.
.
Windows 3.0/pg/2#3 = 109 =
Windows предоставляет несколько функций для отображения и
манипулирования растровыми картами. Вы можете использовать
растровые карты в качестве элементов меню или в качестве
контрольных отметок в меню.
Дополнительную информацию по поводу растровых карт вы
можете получить в:
Раздел Руководство
---------------------------------------------------------------
Функции выборки Руководство программиста: глава 6, "Курсор,
мышь и клавиатура".
Руководство программиста: глава 20, "Дина-
мически подключаемые библиотеки".
Использование раст- Руководство программиста: глава 7, "Меню".
ровых карт в меню
Функции растровых Справочное руководство, том 1: глава 2,
карт "Функции интерфейса графических устройств",
глава 4, "Список функций".
Использование "Tools": Глава 4, "Создание изображений:
SDKPaint SDKPaint".
.
Windows 3.0/pg/2#3 = 110 =
Глава 12. Вывод на печать.
----------------------------------------------------------------
Большинство прикладных программ предоставляет
пользователям возможность получения на принтере копий ваших
данных. В большинстве программных сред выша прикладная
программа должна иметь дело с различными возможностями и
требованиями различных принтеров. В Microsoft Windows ваша
прикладная программа не должна использовать специфичную для
данного принтера информацию, она проста печатает на текущем
принтере. Windows, драйверы принтеров Windows преобразуют
запросы на вывод на принтер вашей прикладной программы в тот
вид, который может использовать данный принтер.
В данной главе вы найдете следующую информацию:
- Процесс печати в среде Windows.
- Получение информации о принтере.
- Печать строки текста.
- Печать растровой карты.
- Обработка ошибок печати.
- Отмена операций печати.
- Печать графических изображений порциями.
В данной главе также описан пример прикладной программы,
PrintFile, которая использует многое из описанного в данной
главе.
12.1 Печать в среде Windows.
В Windows ваша прикладная программа не взаимодействует
напрямую с принтером. Вместо этого вы печатаете, посылая
информацию контексту устройства печати. Это означает, что вашей
программе нет необходимости самой разбираться в специфике
конкретных принтеров.
Печатью в Windows управляют функции GDI. В общем, печать в
Windows аналогична выводу на экран: вы получаете дескриптор
контекста устройства и затем посылаете информацию этому
контексту. Для печати прикладная программа обычно выполняет
следующие шаги:
1. Прикладная программа получает информацию о текущем
принтере, такую как его тип, драйвер устройства и порт
принтера, из файла инициализации WIN.INI.
Эта информация необходима для создания контекста
устройства текущего принтера.
.
Windows 3.0/pg/2#3 = 111 =
2. Когда вы посылаете информацию контексту устройства
принтера, Windows активизирует спуллер печати, ожидая
выполнения вашего запроса на печать.
3. Прикладная программа использует для взаимодействия с
драйвером принтера управляющие последовательности.
12.1.1 Использование управляющих последовательностей.
Управляющие последовательности используются для
взаимодействия с драйвером принтера. Эти последовательности
сообщают драйверу устройства, что нужно делать, и получают
информацию о принтере, такую как размер страницы. Для посылки
управляющей последовательности драйверу устройства прикладная
программа может использовать функцию Escape.
Например, чтобы сообщить драйверу принтера, что необходимо
начать печать, нужно с помощью функции Escape послать
управляющую последовательность STARTDOC. Следующий пример
посылает управляющую последовательность STARTDOC контексту
принтера, определяемому параметром hPrinterDC. Эта управляющая
последовательность запускает запрос на вывод с именем "My Print
Request".
Escape(hPrinterDC, STARTDOC, 0, (LPSTR)"My Print Request",
0L);
При выводе на печать вы следуете тем же основным правилам,
что и при осуществлении другого вывода с использованием GDI.
Если вы печатаете текст или примитивы типа прямоугольников,
дуг, окружностей, вы можете посылать их прямо контексту
устройства печати. Вы можете также посылать текст и примитивы
контексту устройства памяти. Это позволяет создавать сложное
изображение и затем напечатать его.
12.2 Получение информации о текущем принтере.
Для создания контекста устройства печати необходима
информация о нем, такая как его тип и порт, к которому он
подключен. Программа среды Windows ControlPanel добавляет
информацию о текущем принтере к полю "device=" в разделе
[windows] файла win.ini. Прикладная программа может получить
эту информацию с помощью функции GetProfileString. Информация
затем может быть использована в функции CreateDC для создания
контекста устройства печати для конкретного принтера на
конкретном порту компьютера.
Информация о принтере в файле win.ini состоит из трех
полей:
- Тип текущего принтера (например, "EPSON").
.
Windows 3.0/pg/2#3 = 112 =
- Имя драйвера текущего принтера (например, "EPSON
FX-80").
- Порт, к которому подключен принтер (например, LPT1:).
В приведенном ниже примере показано, как получить
информацию о принтере и разделить поля на отдельные строки:
char pPrintInfo[80];
PSTR lpTemp;
PSTR lpPrintType;
PSTR lpPrintDriver;
PSTR lpPrintPort;
.
.
.
(1) GetProfileString("windows",
"device",
pPrintInfo,
(LPSTR) NULL, 80);
lpTemp = lpPrintType = lpPrintInfo;
lpPrintDriver = lpPrintPort = 0;
(2) while (*lpTemp) {
(3) if (*lpTemp == ',') {
*lpTemp++ = 0;
(4) while (*lpTemp == ' ')
lpTemp++;
if (!lpPrintDriver)
lpPrintDriver = lpTemp;
else {
lpPrintPort = lpTemp;
break;
}
}
else
lpTemp==AnsiNext(lpText);
}
(5) hPr = CreateDC(lpPrintDriver, lpPrintType, lpPrintPort,
(LPSTR) NULL);
.
.
.
}
В этом примере:
1) Функция GetProfileString получает содержимое поля device=
из раздела [windows] файла win.ini. Затем функция копирует
строку в массив lpPrintInfo.
2) Оператор while используется для разделения строки на три
отдельных поля: тип принтера, имя драйвера печати и порт
принтера.
.
Windows 3.0/pg/2#3 = 113 =
3) Поля разделяются запятыми (используется оператор if для
проверки на запятые и замену их нулями, завершающими
поле).
4) Другой оператор, while, пропускает все впередистоящие
пробелы в следующем поле.
Каждый указатель - pPrintType, pPrintDriver и pPrintPort -
получает адрес начала соответствующего поля.
5) Эти указатели затем используются в функции CreateDC для
создания контекста устройства печати для выбранного в
данный момент принтера.
12.3 Печать строки текста.
Для того, чтобы отпечатать одну строку текста, необходимо
выполнить следующие шаги:
1. Создать контекст устройства для принтера.
2. Сделать запрос на печать.
3. Напечатать строку.
4. Начать новую страницу.
5. Завершить запрос на печать.
6. Удалить контекст устройства.
В приведенном ниже примере показано, как отпечатать
единственную строку текста на печатающем устройстве Epson
FX-80, который присоединен к порту принтера lpt1:
(1) hPr = CreateDC("EPSON",
"EPSON FX-80",
"LPT1:",
(LPSTR) NULL);
if (hPr != NULL) {
(2) Escape(hPr, STARTDOC, 4, (LPSTR) "Test", 0L);
(3) TextOut(hPr, 10, 10, "Единственная строка текста.", 22);
(4) Escape(hPr, NEWFRAME, 0, 0L, 0L);
(5) Escape(hPr, ENDDOC, 0, 0L, 0L);
(6) DeleteDC(hPr);
}
В данном примере:
1) Функция CreateDC создает контекст устройства для принтера
.
Windows 3.0/pg/2#3 = 114 =
и возвращает его дескриптор. В данном примере этот
дескриптор сохраняется в переменной hPr. При вызове
функции CreateDC программа должна определить первые три
параметра, четвертый должен быть равен 0. В данном примере
программа определила следующие параметры:
- Первый параметр определяет имя драйвера устройства
"EPSON".
- Второй параметр определяет имя драйвера устройства
"EPSON FX-80".
- Третий параметр определяет порт принтера "LPT1:".
- Последний параметр определяет, как принтер должен быть
инициализирован; NULL специфицирует инициализацию по
умолчанию. (В главе 17 "Параметры принтера" описано как
установить другие параметры.)
2) Параметр STARTDOC, используемый в функции Escape, делает
запрос на печать. Имя "Test" используется программой
буферизации для идентификации запроса. Другие параметры не
используются, так что они устанавливаются в нуль.
3) Функция TextOut копирует строку текста на печать. Строка
будет размещаться, начиная с координат (10, 10) на бумаге
печатающего устройства (координаты принтера всегда берутся
относительно верхнего левого угла бумаги). Единицы
измерения по умолчанию - пиксели принтера.
4) Параметр NEWFRAME заканчивает страницу и сигнализирует
печатающему устройству продвинуться к следующей странице.
5) Параметр ENDDOC сигнализирует о завершении запроса на
печать.
6) Функция DeleteDC удаляет контекст устройства печати.
Примечание: Нельзя ожидать, что строка текста будет
отпечатана немедленно. Программа буферизации соберет всю
выводимую на принтер информацию перед ее посылкой на печать,
так что фактически печать не начнется до обработки последнего
ENDDOC.
12.4 Печать растровой карты.
Печать растровой карты аналогична печати строки текста.
Для вывода на печать растровой карты необходимо выполнить
следующее:
1. Создать контекст устройства памяти, который совместим с
растровой картой.
.
Windows 3.0/pg/2#3 = 115 =
2. Загрузить растровую карту и выбрать ее в контексте
устройства памяти.
3. Запустить запрос на печать.
4. Использовать функцию BitBlt для копирования растровой
карты из контекста устройства памяти на печать.
5. Завершить запрос на печать.
6. Удалить растровую карту из выборки контекста устройства
памяти и удалить контекст устройства.
В приведенном ниже примере показано, как отпечатать
растровую карту с именем "dog", которая была добавлена к файлу
ресурсов:
HDC hDC;
HDC hMemoryDC;
HDC hPr;
BITMAP Bitmap;
.
.
.
(1) hDC = GetDC(hWnd);
hMemoryDC = CreateCompatibleDC(hDC);
ReleaseDC(hWnd, hDC);
(2) hBitmap = LoadBitmap(hInstance, "dog");
(3) GetObject(hBitmap, (LPBITMAP) &Bitmap);
(4) hOldBitmap = SelectObject(hMemoryDC, hBitmap);
(5) hPr = CreateDC("EPSON",
"EPSON FX-80",
"LPT1:",
(LPSTR) NULL);
if (hPr != NULL) {
Escape(hPr, STARTDOC, 4, (LPSTR) "Dog", 0L);
(6) BitBlt(hPr, 10, 30,
Bitmap.bmWidth,
Bitmap.bmHeigt,
hMemDC, 0, 0, SRCCOPY);
(7) Escape(hPr, NEWFRAME, 0, 0L, 0L);
Escape(hPr, ENDDOC, 0, 0L, 0L);
DeleteDC(hPr);
}
(8) SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hMemoryDC);
DeleteObject(hBitmap);
1) В данном примере функция CreateCompatibleDC создает
.
Windows 3.0/pg/2#3 = 116 =
контекст устройства памяти, который совместим с контекстом
отображения текущего окна. Функции GetDC и ReleaseDC
служат для получения и освобождения контекста отображения.
2) Функция LoadBitmap загружает растровую карту из файла
ресурсов.
3) Функция GetObject находит информацию о растровой карте,
такую, например, как ее высота и ширина. Эти значения
используются позже в функции BitBlt.
4) Функция SelectObject выбирает растровую карту в контексте
устройства памяти.
5) Операторы запроса на печать идентичны таковым при запросе
на печать одной строки текста.
6) Для вывода растровой карты на принтер используется функция
BitBlt. Функция BitBlt копирует растровую карту из
контекста устройства памяти на печать, помещая растровую
карту, начиная с координаты (10, 30). Функция BitBlt
используется вместо функции TextOut в предыдущем примере.
7) Операторы, посылающие управляющие последовательности
NEWFRAME и ENDDOC, идентичны используемым в предыдущем
примере.
8) После завершения запроса на печать функция SelectObject и
DeleteDC используются для удаления растровой карты из
выборки и удаления контекста устройства памяти. Поскольку
растровая карта больше не нужна, функция DeleteObject
удаляет ее из памяти.
12.5 Обработка ошибок во время печати.
Хотя GDI и программа буферизации пытаются сообщить
пользователю о всех ошибках печати, необходимо подготовить
прикладную программу для приема сообщений о переполнении
дисковой или оперативной памяти. Когда происходит ошибка во
время обработки отдельной управляющей последовательности, такой
как STARTDOC или NEWFRAME, функция Escape возвращает значение,
меньшее нуля. Ошибки выхода за пределы дисковой или оперативной
памяти обычно происходят при работе управляющей
последовательности NEWFRAME. В этом случае возвращаемое
значение включает бит SP_NOTREPORTED. Если бит очищен, значит
GDI уже уведомило пользователя. Если бит установлен, программа
должна уведомить пользователя. Обычно бит устанавливается при
общей ошибке выхода за пределы пространства дисковой или
оперативной памяти.
В приведенном ниже примере показано, как следует
обрабатывать те ошибки во время печати, о которых не
сообщается:
.
Windows 3.0/pg/2#3 = 117 =
int status;
.
.
.
status = Escape(hPrDC, NEWFRAME, 0, 0L, 0L);
(1) if (status < 0) { /* ошибка, о которой
не сообщается? */
if (status) { /* да */
(2) switch (status) {
case SP_OUTOFDISK:
/* информировать пользователя о
возникшей ситуации и выполнить
необходимые действия */
break;
case SP_OUTOFMEMORY:
/* информировать пользователя о
возникшей ситуации и выполнить
необходимые действия */
break;
default:
/* информировать пользователя о
возникшей ситуации и выполнить
необходимые действия */
break;
}
}
(3) else /* сообщается, что небходимо дальнейшее дейст-
вие */
switch (status | SP_NOTREPORTED) {
case SP_OUTOFDISK:
/* выполнить необходимую обработку */
break;
case SP_OUTOFMEMORY:
/* выполнить необходимую обработку */
break;
}
}
В этом примере:
1) Первый оператор if проверяет возвращаемое значение, меньше
ли оно 0 и установлен ли бит SP_NOTREPORTED. (Если бит
SP_NOTREPORTED установлен, то пользователь не был
проинформирован об ошибке. Если бит SP_NOTREPORTED не
установлен, то пользователь получил сообщение об ошибке.)
2) В данном примере оператор switch используется для
выполнения специальных действий на ошибки SP_OUTOFDISK и
SPOUTOFMEMORY. Для всех остальных несообщаяемых ошибок
выполняется обычное сообщение об ошибке.
.
Windows 3.0/pg/2#3 = 118 =
3) Если бит SP_NOREPORTED равен 0, то Windows уже
проинформировала пользователя о возникшей ошибке. В этом
случае прикладная программа может просто выполнить
необходимую обработку ошибки.
В большинстве случаев правильным ответом на несообщаемую
ошибку будет индицирование панели сообщений, поясняющей смысл
ошибки, и завершение запроса на печать. Если об ошибке уже
сообщалось, можно завершить запрос, а затем повторно выдать его
после отведения дополнительного места на дисковой или
оперативной памяти.
12.6 Отмена операции печати.
Прикладная программа всегда должна дать пользователю шанс
отменить длительную операцию печати. Для этого программа может
создать немодальную панель диалога Abort, когда начинается
операция печати. Программа также должна определить функцию
прерывания, которая будет обрабатывать сообщения для прикладной
программы во время выполнения операции печати.
Для создания панели диалога, которая позволит пользователю
прервать операцию печати, необходимо выполнить следующие шаги:
1. Добавьте определение шаблона панели диалога Abort к
файлу описания ресурсов прикладной программы.
2. В тексте прикладной программы создайте функцию
управоления панелью диалога AbortDlg.
3. В тексте прикладной программы создайте функцию Abort
для обработки сообщений панели диалога Abort.
4. Модифицируйте процедуру печати таким образом, чтобы она
выводила панель диалога AbortDlg и обрабатывала
сообщения.
В следующих разделах эти шаги описаны более подробно.
12.6.1 Определение панели диалога.
В файл описания ресурсов прикладной программы добавьте
шаблон панели диалога AbortDlg. Например:
AbortDlg DIALOG 20, 20, 90, 64
STYLE WS_NODALFRAME | WS_SYSMENU | WS_CAPTION
CAPTION "Print File"
BEGIN
DefPushButton "Cancel" IDCANCEL 29, 44, 32, 14,
WS_GROUP
Ctext "Sending", -1, 0, 8, 90, 8
Ctext "text", ID_FILENAME 0, 18, 90, 8
Ctext "to printer spooler", -1, 0, 28, 90, 8
END
.
Windows 3.0/pg/2#3 = 119 =
12.6.2 Определение функции панели диалога.
В текст вашей программы добавьте функцию Abort для
обработки сообщений панели диалога AbortDlg. Функция панели
диалога Abort должна обрабатывать сообщения WM_INITDIALOG и
WM_COMMAND. Для того, чтобы дать возможность пользователю
выбрать клавишу Отменить, функция захватывает ввод при
инициализации панели диалога. Затем она игнорирует все
сообщения до тех пор, пока не встретится сообщение WM_COMMAND.
Ввод команды заставляет функцию разрушить окно и установить
флаг сброса в значение TRUE. В приведенном ниже примере
показаны операторы, требуемые для функции диалога:
BOOL bAbort = FALSE;
.
.
.
int FAR PASCAL AbortDlg(hWnd, msg, wParam, lParam)
HWND hWnd;
unsigned msg;
WORD wParam;
LONG lParam;
{
/* отслеживание мягкой клавиши Cancel и клавиш
ESCAPE, RETURN и SPACE BAR */
if (msg == WM_COMMAND) {
/* пользователь прервал операцию */
bAbort = TRUE;
/* разрушение панели диалога Abort */
DestroyWindow(hWnd);
return (TRUE);
}
else if (msg == WM_INITDIALOG) {
/* необходим захват ввода для информации, вводимой
пользователем */
SetFocus(hWnd);
return (TRUE);
}
return (FALSE);
}
12.6.3 Функция прерывания печати.
В тексте прикладной программы определите функцию обработки
сообщений для панели диалога Abort.
Функция прерывания получает сообщения из очереди
прикладной программы и обрабатывает их, если они направлены
панели диалога Abort. Функция продолжает цикл до тех пор, пока
она не получит сообщение WM_DESTROY, или до завершения операции
.
Windows 3.0/pg/2#3 = 120 =
печати.
Необходимо, чтобы программа, выполняющая длительные
операции печати, передала функцию прерывания в GDI для
управления необычными ситуациями, появляющимися во время
операций печати. Обычно возникает ситуация, когда операция
печати заполняет доступное дисковое пространство прежде, чем
программа буферизации может скопировать данные на печать.
Поскольку программа буферизации может продолжать печатать даже
в том случае, когда дисковое пространство заполнено, GDI
вызывает функцию прерывания для проверки, хочет ли прикладная
программа отменить операцию печати или просто ожидает, пока
освободится место на диске.
Для определения функции прерывания печати используйте
функцию MakeProcInstance для создания адреса экземпляра
процедуры для функции:
lpAbortProc=MakeProcInstance(AbortProc, hInst);
Затем вызовите функцию Escape, передав ей параметры
SETABORTPROC и адрес функции Abort:
Escape (hDC, SETABORTPROC, 0, lpAbortProc, 0L)
Затем GDI будет вызывать функцию прерывания во время
буферизации. Функция должна иметь следующее описание:
int FAR PASCAL AbortProc(hPr, Code)
(1) HDC hPr;
(2) int Code;
где:
1) hPr - это дескриптор контекста устройства печати.
2) Code определяет природу вызова. Он может принимать одно из
следующих значений:
Значение Оеписание
----------------------------------------------------------
SP_OUTOFDISK Программа буферизации исчерпала весь
запас дискового пространства во время
буферизации файла данных. Операция пе-
чати будет продолжена, если программа
дождется освобождения пространства на
диске.
0 Программа буферизации продолжается без
ошибки.
----------------------------------------------------------
Как только функция прерывания вызвана, она может
.
Windows 3.0/pg/2#3 = 121 =
возвратить TRUE для немедленного продолжения операции
буферизации или FALSE для отмены операции печати. Большинство
функций прерывания вызывают функцию PeekMessage для временной
передачи управления, а затем возвращают TRUE для продолжения
операции печати. Передача управления обычно представляет
программе буферизации достаточно времени для освобождения
некоторого пространства на диске.
Если функция прерывания возвратит FALSE, операция печати
отменяется, и при следующем вызове функции Escape из прикладной
программы будет возвращено значение ошибки.
Важно. Если программа встречает ошибку печати или операцию
по прерыванию печати, она не должна пытаться завершить
операцию, используя функцию Escape с указанием ENDDOC или
ABORTDOC. GDI автоматически завершает операцию перед возвратом
значения ошибки.
Ниже приведен примерный вид функции прерывания печати:
int FAR PASCAL AbortProc(hPr, Code)
HDC hPr; /*для множества контекстов отображения принтера*/
int Code; /*для состояния печати */
{
MSG msg;
/* обработка сообщений, предназначенных для панели диалога
прерывания */
while (PeekMessage((LPMSG) &msg, NULL, NULL, NULL,
PM_REMOVE))
if (!IsDialogMessage(hAbortDlgWnd, (LPMSG) &msg)) {
TranslateMessage((LPMSG) &msg);
DispatchMessage((LPMSG) &msg);
}
/* bAbort - TRUE, если пользователь прервал операцию */
return (!Abort);
}
12.6.4 Выполнение прерываемых операций печати.
Для того, чтобы дать пользователю возможность прерывать
операцию печати, ваша программа перед запуском операции печати
должна выполнить следующие действия:
1. Определить функцию прерывания, как показано в
предшествующем разделе.
2. С помощью функции MakeProcInstance получить адрес
экземпляра процедуры для функции прерывания.
3. С помощью функции EnableWindow необходимо сделать
недоступным родительское окно.
4. Запустите обычную операцию печати, но контролируйте
.
Windows 3.0/pg/2#3 = 122 =
возвращаемое функцией Escape значение после каждого
вызова NEWFRAME. Если значение меньше нуля,
пользователь отменил операцию или произошла ошибка.
5. Используйте функцию DestroyWindow для разрушения панели
диалога Abort, если это необходимо. (Windows разрушает
панель автоматически, если пользователь отменил
операцию печати.)
6. С помощью функции EnableWindow необходимо сделать
доступным родительское окно.
Смотрите пример программы PrntFile, которая иллюстрирует
как прикладная программа может выполнять эти действия.
12.6.5 Отмена операции печати с помощью ABORTDOC.
Можно использовать параметр ABORTDOC для отмены операции
печати, даже если отсутствует функция прерывания или панель
диалога Abort. В прикладных программах, которые не имеют
функцию прерывания, параметр ABORTDOC может быть использован
только перед первым вызовом управляющей функции с NEWFRAME или
NEXTBAND.
12.7 Печать порциями.
Разбивка на порции - такая техника печати, при которой
печатаемый образ разбивается на несколько порций (или вырезок),
и каждая порция посылается на печать самостоятельно. Разбивка
на порции дает возможность прикладной программе печатать
сложные графические образы без первоначального формирования
полного образа в памяти. Это может уменьшить требования к
памяти для печати и улучшить функционирование системы во время
выполнения операции печати. Однако, разбивка на порции может
использоваться только для принтеров, имеющих эту возможность.
Для печати изображения с разбивкой на порции необходимо
выполнить следующие шаги:
1. Использовать функцию CreateDC для получения контекста
устройства печати.
2. Использовать функцию GetDeviceCaps для проверки того,
что принтер позволяет разбивку на порции:
if(GetDeviceCaps(hPrinterDC, RASTERCAPS) & RC_BANDING);
3. Использовать функцию Escape с параметром NEXTBAND для
получения координат порции:
Escape(hPrinterDC,NEXTBAND,0,(LPSTR)NULL,(LPRECT)&rcRect);
Функция заносит в структуру rcRect координаты текущей
.
Windows 3.0/pg/2#3 = 123 =
порции. Координаты задаются в единицах устройства и все
последующие вызовы GDI отсекаются до размеров данного
прямоугольника.
4. Проверить структуру rcRect (пуст ли прямоугольник).
Пустой прямоугольник отмечает конец операции разбивки
на порции. Если он пуст, закончить операцию по разбивке
на порции.
5. Использовать функцию DPtoLP для преобразования точек
rcRect из единиц устройства в логические единицы:
DPtoLP (hPr, (LPRECT) & rcRect, 2);
6. Использовать функции вывода GDI или другие функции для
рисования внутри порции. Для экономии времени
прикладная программа должна выполнить только те вызовы
GDI, которые влияют на текущую порцию. Если ПП не хочет
экономить время, GDI будет отсекать весь вывод, который
не встречается в данной порции, так что не требуется
специальных действий.
7. Повторить шаги с 4 по 6.
После окончания операции разбивки на порции используйте
функцию DeleteDC для удаления контекста устройства печати.
В приведенном ниже примере показано, как печатать с
помощью разбивки на порции:
hPr = CreateDC("EPSON",
"EPSON FX-80",
"LPT1:",
(LPSTR) NULL);
if (hPr != NULL) {
if (GetDeviceCaps(hPr, RASTERCAPS) & RC_BANDING) {
Escape(hPr, STARTDOC, 4, (LPSTR) "Dog", (LPSTR)NULL);
Escape(hPr, NEXTBAND, 0, (LPSTR)NULL, (LPSTR) &rcRect);
while (!IsRectEmpty(&rcRect)) {
DPtoLP(hPr, (LPRECT) &rcRect, 2);
/* поместите здесь свою функцию вывода. Для эко-
номии времени используйте rcRect для опреде-
ления, какие функции должны быть вызваны для
этой порции */
Escape(hPr, NEXTBAND, 0, (LPSTR)NULL, (LPRECT) &rcRect);
}
Escape(hPr, NEWFRAME, 0, (LPSTR)NULL, (LPSTR)NULL);
Escape(hPr, ENDDOC, 0, (LPSTR)NULL, (LPSTR)NULL);
}
DeleteDC(hPr);
}
.
Windows 3.0/pg/2#3 = 124 =
12.8 Пример прикладной программы PrntFile.
В данном разделе описано, как можно добавить возможность
печати к программе EditFile, описанной в главе 10 "Ввод и вывод
в файл", скопировав текущий текст из редактируемого блока
управления и отпечатав его с помощью способов, описанных в
данной главе. Для того, чтобы добавить печать, скопируйте и
переименуйте программу EditFile в PrntFile, а затем
модифицируйте эту программу следующим образом:
1. Добавьте шаблон панели диалога AbortDlg в файл описания
ресурсов.
2. Добавьте новые переменные, необходимые при печати.
3. Добавьте в фрагмент WM_COMMAND операторы для обработки
IDM_PRINT.
4. Создайте функцию диалога AbortDlg и функцию прерывания
AbortProc.
5. Добавьте функцию GetPrinterDC.
6. Экспортируйте функцию AbortDlg и AbortProc.
7. Оттранслируйте и скомпонуйте прикладную программу.
Этот пример показывает, как распечатать содержимое
редактируемого блока управления, и включает операторы,
необходимые для поддержки функции прерывания и функции панели
диалога Abort.
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
12.8.1 Добавление панели диалога AbortDlg.
Для поддержки печати необходима новая панель диалога.
Панель диалога AbortDlg дает возможность пользователю отменить
операцию печати, выбрав клавишу Cancel. Добавьте следующий
оператор DIALOG к файлу ресурсов:
AbortDlg DIALOG 20, 20, 90, 64
STYLE WS_NODALFRAME | WS_SYSMENU | WS_CAPTION
CAPTION "Print File"
BEGIN
DefPushButton "Cancel" IDCANCEL 29, 44, 32, 14,
WS_GROUP
Ctext "Sending", -1, 0, 8, 90, 8
Ctext "text", ID_FILENAME 0, 18, 90, 8
Ctext "to printer spooler", -1, 0, 28, 90, 8
END
.
Windows 3.0/pg/2#3 = 125 =
12.8.2 Добавление переменных для печати.
Для поддержки печати необходимо объявить новые переменные.
Добавьте следующие объявления в начало файла ресурсов:
HDC hPr; /* дескриптор КО принтера */
int LineSpace; /* расстояние между строками */
int LinesPerPage; /* число строк на странице */
int CurrentLine; /* текущая строка */
int LineLenght; /* длина строки */
DWORD dwLines; /* число печатаемых строк */
DWORD dwIndex; /* индекс печатаемой строки */
char pLine[128]; /* буфер для хранения строк перед
печатью */
TEXTMETRIC TextMetric; /* информация о размере символа */
POINT PhysPageSize; /* информация о странице */
BOOL bAbort; /* FALSE, если пользователь отменил
операцию печати */
HWND hAbortDlgWnd;
FARPROC lpAbortDlg, lpAbortProc;
Переменная hPr - это дескриптор контекста устройства
печати. Она получает значение, возвращаемое функции CreateDC.
Переменные LineSpace и LinesPerPage содержат, соответственно,
интервал между строками и число строк, которое может быть
напечатано на странице. Переменная CurrentLine - это счетчик,
хранящий номер строки на текущей странице. Строки текста
печатаются по одной. Переменная dwLines содержит число строк в
редактируемом блоке управления. Структура TextMetric получает
информацию о шрифте, используемом для печати. В данном примере
используются только поля tmHeight и tmExternalLeading этой
структуры. Структура PhysPageSize получает физические ширину и
высоту бумаги, используемой при печати. Высота используется для
определения числа строк, которые могут быть отпечатаны на
странице.
12.8.3 Добавление фрагмента IDM_PRINT.
Для выполнения операции печати необходимо добавить
фрагмент IDM_PRINT к обработке WM_COMMAND в основной функции
окна. Добавьте следующие операторы:
case IDM_PRINT:
hPr = GetPrinterDC();
if (!hPr) {
sprintf(str, "Нельзя распечатать &s.", FileName);
MessageBox(hWnd, str, NULL, MB_OK | MB_ICONHAND);
break;
}
lpAbortDlg = MakeProcInstance(AbortDlg, hInst);
lpAbortProc = MakeProcInstance(AbortProc, hInst);
Escape(hPr, SETABORTPROC, NULL,
.
Windows 3.0/pg/2#3 = 126 =
(LPSTR) (long) lpAbortProc, (LPSTR) NULL);
if (Escape(hPr, STARTDOC, 15, "Текст PrntFile",
(LPSTR) NULL < 0) {
MessageBox(hWnd, "Невозможно начать печать",
NULL, MB_OK | MB_ICONHAND);
FreeProcInstance(AbortDlg);
FreeProcInstance(AbortProc);
DeleteDC(hPr);
return (NULL);
}
bAbort = FALSE; /* сбросить флаг прерывания */
hAbortDlgWnd = CreateDialog(hInst, "AbortDlg", hWnd,
lpAbortDlg);
ShowWindow(hAbortDlgWnd,SW_NORMAL);
UpdateWindow(hAbortDlgWnd);
EnableWindow(hWnd, FALSE);
GetTextMetrics(hPr, &TextMetrics);
LineSpace = TextMetric.tmHeight
+ TextMetric.tmExternalLeading;
Escape(hPr, GETPHYSPAGESIZE, NULL, (LPSTR) NULL,
(LPSTR) &PhysPageSize);
LinesPerPage = PhysPageSize.y / LineSpace;
dwLines = SendMessage(hEditWnd, EM_GETLINECOUNT, 0, 0L);
CurrentLine = 1;
for (dwindex = IOStatus = 0; dwIndex < dwLines;
dwIndex++) {
pLine[0] = 128; /* максимальный размер буфера */
pLine[1] = 0;
LineLength = SendMessage(hEditWnd, EM_GETLINE,
(WORD) dwIndex, (LONG) ((LPSTR) pLine));
TextOut(hPr, 0, CurrentLine*LineSpace,
(LPSTR) pLine, LineLength);
if (++CurrentLine > LinesPerPage) {
Escape(hPr,NEWFRAME,0,0L,0L);
CurrentLine = 1;
IOStatus = Escape(hPr, NEWFRAME, 0, 0L, 0L);
if (IOStatus < 0) || bAbort)
break;
}
}
if (IOStatus >= 0 && !bAbort) {
Escape(hPr, NEWFRAME, 0, 0L, 0L);
Escape(hPr, ENDDOC, 0, 0L, 0L);
}
EnableWindow(hWnd, TRUE);
DestroyWindow(hAbortDlgWnd);
FreeProcInstance(AbortDlg);
FreeProcInstance(AbortProc);
DeleteDC(hPr);
break;
.
Windows 3.0/pg/2#3 = 127 =
Локально определенная функция GetPrinterDC проверяет файл
win.ini на наличие данных о текущем принтере и создает контекст
устройства печати. Если текущего принтера не существует, или
контекст устройства не может быть создан, функция возвращает
NULL и обработка заканчивается с выдачей предупреждения. В
противном случае функция MakeProcInstance создает адреса
экземпляра процедуры для функции диалога AbortDlg и функции
AbortProc. Параметр SETABORTPROC, используемый в функции
Escape, устанавливает функцию прерывания. Параметр STARTDOC
означает начало операции печати и устанавливает печатаемый
заголовок (отображается в программе PrintManager). Если при
этом происходит ошибка, функция FreeProcInstance освобождает
экземпляры процедур AbortDlg и AbortProc, а функция DeleteDC
удаляет контекст устройства перед окончанием обработки.
Функция CreateDialog создает панель диалога AbortDlg, а
функция Enable Window делает недоступным основное окно. Это
удерживает пользователя от попыток работать в основном окне во
время печати. Однако, он может продолжать работать с некоторой
другой прикладной программой.
Поскольку редактируемый блок управления может содержать
более одной строки, важно обеспечить соответствующий интервал
между строками, чтобы одна строка не наползала на другую.
Функция GetTextMetrics получает информацию о текущем шрифте
такую, например, как высота и внешний разрыв, которая может
быть использована для вычисления правильного интервала между
строками. Высота - это максимальная высота символа шрифта.
Внешний разрыв - это рекомендуемый промежуток, дополняющий
высоту и служащий для разделения строк текста, которые
используют данный шрифт. Межстрочный интервал, величину
которого содержит переменная LineSpace, - это сумма полей
высоты и внешнего разрыва (TextMetric.tmHeight и
TextMetric.tmExternalLeading).
Поскольку редактируемый блок управления может содержать
больше строк, чем вмещается на одной странице, важно
определить, сколько строк может размещаться на странице. Когда
это количество строк будет напечатано, произойдет переход к
следующей странице. Управляющая функция с параметром
GETPHYSPAGESIZE получает физические размеры страницы и копирует
их в структуру PhysPageSize, содержащую ширину и высоту
страницы. Число строк на странице, присвоенное переменной
LinesPerPage, - это частное от деления физической высоты
страницы PhysPageSize.y на межстрочный интервал LineSpace.
Функция TextOut может печатать за одно обращение только
одну строку, так что для печати нескольких строк необходимо
организовать цикл с оператором for. Сообщение EM_GETLINECOUNT,
посланное редактируемому блоку управления с помощью функции
SendMessage, возвращает число строк, которое должно быть
отпечатано, и определяет число прохождений цикла. При каждом
выполнении цикла сообщение EM_GETLINE копирует содержание
.
Windows 3.0/pg/2#3 = 128 =
строки из редактируемого блока управления в буфер pLine.
Счетчик цикла index используется с сообщением EM_GETLINE для
определения строки, которая должна быть получена из
редактируемого блока управления. Сообщение EM_GETLINE также
заставляет функцию SendMessage вернуть длину строки. Длина
присваивается переменной LineLength.
После того, как строка скопирована из редактируемого блока
управления, она печатается с помощью функции TextOut.
Произведение переменных CurrentLine и LineSpacing определяет
координату строки на странице по оси у. Координата Х
устанавливается в нуль. После вывода строки значение переменной
CurrentLine увеличивается на 1. Если величина CurrentLine
становится больше LinesPerPage, необходимо перейти к следующей
странице. Текст, отпечатанный за физической границей страницы,
обрезается. Автоматическое продвижение страницы не
поддерживается, так что важно хранить число отпечатанных строк
на странице и использовать управляющую функцию с параметром
NEWFRAME для продвижения к новой странице, когда это
необходимо. Если во время печати произойдет ошибка, управляющая
функция с параметром NEWFRAME возвратит номер ошибки и
обработка закончится.
После того, как строки редактируемого блока управления
отпечатаны, управляющая функция с NEWFRAME продвинет последнюю
страницу, а управляющая функция с указанием ENDDOC завершит
запрос на печать. Функция DeleteDC удалит контекст устройства
печати, поскольку он больше не нужен, а функция DestroyWindow
разрушит панель диалога AbortDlg.
12.8.4 Создание функций AbortDlg и AbortProc.
Для поддержки процесса печати необходимо создать функции
AbortDlg и AbortProc. Функция диалога AbortDlg обеспечивает
поддержку панели диалога AbortDlg, которая появляется, когда
начинается печать. Панель диалога дает возможность пользователю
отменить операцию печати, если это необходимо. Функция
AbortProc обрабатывает сообщения, предназначенные для панели
диалога AbortDlg, и завершает операцию печати, если
пользователь запросил ее отмену.
Функция диалога AbortDlg устанавливает захват ввода и имя
печатаемого файла. Она также устанавливает переменную bAbort в
значение TRUE, если пользователь выбирает клавишу Cancel.
Добавьте следующие операторы к исходному С-файлу:
int FAR PASCAL AbortDlg(hDlg, msg, wParam, lParam)
HWND hDlg;
unsigned msg;
WORD wParam;
LONG lParam;
{
switch (msg) {
.
Windows 3.0/pg/2#3 = 129 =
case WM_COMMAND:
return (bAbort = TRUE);
case WM_INITDIALOG:
SetFocus(GetDlgItem(hDlg, IDCANCEL));
SetDlgItemText(hDlg, ID_FILENAME, FileName);
return (TRUE);
}
return (FALSE);
}
Функция AbortProc проверяет наличие сообщений в очереди
прикладной программы и направляет их функции диалога AbortDlg
или другим окнам программы. Если одно из этих сообщений
указывает функции диалога AbortDlg установить переменную bAbort
в TRUE, функция AbortProc возвращает это значение, заставляя
Windows остановить функцию печати. Добавьте следующие операторы
к исходному Си-файлу:
int FAR PASCAL AbortProc(hPr, Code)
HDC hPr; /* для множества КО принтера */
int Code; /* для состояния печати */
{
MSG msg;
while (!bAbort && PeekMessage(&msg, NULL, NULL, NULL,
TRUE))
if (!IsDialogMessage(hAbortDlgWnd, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (!bAbort);
}
12.8.5 Добавление функции GetPrinterDC.
Для поддержки операции печати необходимо добавить эту
функцию к исходному С-файлу. Функция GetPrinterDC получает
содержимое поля "device" из раздела [windows] файла win.ini,
разделяет это содержимое на отдельные компоненты и затем
создает контекст устройства печати, используя имя устройства и
тип принтера, заданный в содержимом поля. Добавьте следующие
операторы к исходному С-файлу:
HANDLE GetPrinterDC()
{
char pPrintInfo[80];
LPSTR lpTemp;
LPSTR lpPrintType;
LPSTR lpPrintDriver;
LPSTR lpPrintPort;
if (!GetProfileString("windows", "device",
.
Windows 3.0/pg/2#3 = 130 =
(LPSTR) "", pPrintInfo, 80))
return (NULL);
lpTemp = lpPrintType = lpPrintInfo;
lpPrintDriver = lpPrintPort = 0;
while (*lpTemp) {
if (*lpTemp == ',') {
*lpTemp++ = 0;
while (*lpTemp == ' ')
lpTemp = AnsiNext(lpTemp);
if (!lpPrintDriver)
lpPrintDriver = lpTemp;
else {
lpPrintPort = lpTemp;
break;
}
}
else
lpTemp = AnsiNext(lpTemp);
}
return (CreateDC(lpPrintDriver, lpPrintType,
lpPrintPort, (LPSTR) NULL));
}
Для разделения поля "device" на три компоненты, функция
AnsiNext выбирает из него по одному символу за обращение.
12.8.6 Экспортирование функции AbortDlg и AbortProc.
Необходимо в файле определения модуля вашей программы
описать функции AbortDlg и AbortProc как экспортируемые. Для
этого добавьте в файл определения модуля следующие строки в
раздел EXPORTS:
AbortDlg @5; Вызывается, когда пользователь преры-
; вает операцию печати.
AbortProc @6; Обрабатывает сообщения, предназначенные
; для панели диалога Abort.
12.6.7 Компиляция и компоновка.
В файле make не требуется проводить изменений.
Оттранслируйте и скомпонуйте прикладную программу, запустите
Windows и программу PrntFile, вы увидите, что к меню File
добавлена команда Print. Вы можете печатать сначала открыв
файл или просто набрать текст с клавиатуры и затем выбрать
команду Print.
12.9 Заключение.
В данной главе описано, как осуществлять вывод на печать
при работе в среде Windows. В среде Windows ваша программа не
имеет дело с самим принтером. Печать осуществляется путем
.
Windows 3.0/pg/2#3 = 131 =
посылки информации контексту устройства печати. Прикладная
программа взаимодействует с принтером с помощью управляющих
последовательностей.
Дополнительную информацию относительно печати вы найдете
в:
Раздел Руководство
---------------------------------------------------------------
Контекст устройства Руководство программиста, глава 3, "Вывод
в окно".
Управление парамет- Руководство программиста, глава 17, "Пара-
рами принтера метры принтера".
Использование Руководство программиста, глава 18,
шрифтов "Шрифты".
Функции работы с Справочное руководство, том 1: Глава 2,
контекстами "Функции интерфейса графических устройств"
устройств и глава 4, "Список функций".
.
Windows 3.0/pg/2#3 = 132 =
Глава 13. Системный буфер.
----------------------------------------------------------------
Системный буфер представляет собой средство обмена данными
в среде Windows. Это общая область для хранения дескрипторов
данных, через которую прикладные программы могут обмениваться
форматированными данными. Системный буфер содержит некоторое
число форматов данных и соответствующих дескрипторов,
представляющих эти данные, но в различных форматах,
поддерживаемых прикладной программой. Например, круговая
диаграмма может храниться в системном буфере как в виде
изображения метафайла, так и в виде растровой карты. Прикладная
программа, включающая круговую диаграмму, должна определить тип
представления. Общим правилом является использование
представления с большей информативностью, если прикладная
программа поддерживает такой формат.
В данном разделе поясняется, как использовать системный
буфер для того, чтобы:
- Скопировать текст в системный буфер.
- Вставить текст из системного буфера.
- Скопировать растровую карту в системный буфер.
- Вставить растровую карту из системного буфера.
- Использовать цепочку наблюдения за системным буфером.
В данной главе также описан пример прикладной программы,
ClipText, которая иллюстрирует использование описанных в данной
главе методов.
13.1 Использование системного буфера.
Для копирования данных в системный буфер необходимо
сформатировать выбранные данные, используя или встроенный, или
собственный формат. Для большинства форматов необходимо отвести
глобальную память и скопировать в нее данные. Затем нужно
скопировать дескриптор памяти в системный буфер, используя
функцию SetClipboardData.
В прикладных программах Windows копирование и вставка
осуществляются с помощью команд меню Edit. Можно добавить меню
Edit к программе так, как это описано в главе 7 "Меню".
Windows поддерживает несколько встроенных форматов данных,
используемых при обмене. Основные форматы:
.
Windows 3.0/pg/2#3 = 133 =
Формат Содержимое
--------------------------------------------------------------
CF_TEXT Оканчивающийся нулем текст.
CF_OEMTEXT Оканчивающийся нулем текст в наборе сим-
волов OEM.
CF_METAFILEPICT Структура изображения метафайла.
CF_BITMAP Аппаратно-зависимая растровая карта.
CF_DIB Аппаратно-независимая растровая карта.
CF_SYLK Стандартный формат обмена данными SYLK.
CF_DIF Стандартный формат обмена данными DIF.
CF_TIFF Стандартный формат обмена данными TIFF.
--------------------------------------------------------------
Когда данные вставляются из системного буфера с помощью
функции GetClipboardData, нужно специфицировать добавляемый
формат. Системный буфер предоставляет данные только в том
случае, если они были скопированы в этом формате.
Windows поддерживает два формата для текста - CF_TEXT и
CF_OEMTEXT. Формат CF_TEXT используется по умолчанию. Windows
использует формат CF_OEMTEXT для текста из прикладных программ,
не предназначенных для Windows. Если вы вызываете функцию
GetClipboardData для получения данных в одном формате, в то
время как доступен только другой текстовый формат, то Windows
автоматически преобразует текст в формат, необходимый вашей
прикладной программе.
Замечание: Объекты данных системного буфера могут быть
любого размера. Ваша программа способна работать с объектами
размером больше 64К. Дополнительную информацию по поводу работы
с большими объектами вы найдете в главе 16, "Еще об управлении
памятью".
13.1.1 Копирование текста в системный буфер.
В системный буфер можно скопировать небольшую строку
текста. Для этого необходимо:
1. Скопировать строку в глобальную память.
2. Открыть системный буфер.
3. Очистить системный буфер.
4. Поместить в системный буфер дескриптор глобальной
.
Windows 3.0/pg/2#3 = 134 =
памяти.
5. Закрыть системный буфер.
Копирование текста в системный буфер происходит в ответ на
выбор команды Copy из меню Edit. Для обработки выбранной
команды и копирования строки текста в системный буфер
необходимо добавить фрагмент WM_COMMAND к функции окна.
Добавьте следующие операторы:
case WM_COMMAND:
switch (wParam) {
case IDM_COPY:
if(!(hData = GlobalAlloc(GMEM_MOVEABLE,
GlobalSize(hText)))) {
}
if(!(lpData = GlobalLock(hData))) {
GlobalFree(hData);
OutOfMemory();
return(TRUE);
}
if(!(lpszText = GlobalLock(hText))) {
OutOfMemory();
return(TRUE);
}
lstrcpy(lpData,lpszText);
GlobalUnlock(hData);
GlobalUnlock(hText);
/* очистить системный буфер и установить дескриптор
на новую строку */
if (OpenClipboard(hWnd)) {
EmptyClipboard();
SetClipboardData(CF_TEXT, hData);
CloseClipboard();
}
hData = 0;
break;
}
Функция GlobalAlloc отводит необходимый для размещения
строки объем памяти. Флаг GMEM_MOVEABLE специфицирует
перемещаемую память. Системный буфер может использовать или
фиксированную или перемещаемую память, но не следует ему
задавать сбрасываемую память. Наиболее эффективно использование
перемещаемой памяти.
Примечание: Следует всегда контролировать возвращаемое
значение при выделении или блокировании памяти. Значение,
равное 0, говорит о том, что не хватает памяти.
Если используется перемещаемая память, ее необходимо
.
Windows 3.0/pg/2#3 = 135 =
заблокировать для нахождения адреса памяти. Вместо функции
исполняющей системы С strcpy используется локально определенная
функция lstrcpy, поскольку первая не может работать со
смешанными указателями (string - короткий указатель, а lpData -
длинный указатель). Для системного буфера необходимо, чтобы
строка оканчивалась нулевым символом. Наконец, память должна
быть разблокирована перед копированием ее содержимого в
системный буфер.
Каждый раз при копировании строки в системный буфер будет
отводиться новый блок глобальной памяти. Причина заключается в
том, что если однажды дескриптор данных был передан в системный
буфер, он становится его собственностью. Это означает, что
нельзя больше использовать дескриптор (кроме просмотра его
содержимого), и не следует пытаться освободить дескриптор или
изменить его содержимое.
Для копирования дескриптора глобальной памяти в системный
буфер необходимо выполнить следующие действия:
1. Открыть системный буфер.
2. Очистить системный буфер.
3. Установить дескриптор данных.
4. Закрыть системный буфер.
Приведенные ниже операторы выполняют эти шаги:
(1) if (OpenClipboard(hWnd)) {
(2) EmptyClipboard();
(3) SetClipboardData(CF_TEXT, hData);
CloseClipboard();
}
(4) hData = NULL;
1) Функция OpenClipboard открывает системный буфер для
указанного окна. Если другое окно уже открыло системный
буфер, то эта функция может закончится неудачно.
2) Функция EmptyClipboard очищает существующие в системном
буфере дескрипторы и делает владельцем системного буфера
окно, которое его открыло. Прикладная программа должна
очистить системный буфер перед копированием в него данных.
3) Функция SetClipboardData копирует дескриптор памяти в
системный буфер и идентифицирует формат данных CF_TEXT.
Затем системный буфер закрывается с помощью функции
CloseClipboard.
4) Поскольку системный буфер теперь является владельцем
глобальной памяти, идентифицированной hData, удобно
.
Windows 3.0/pg/2#3 = 136 =
установить эту память в нуль для предотвращения попыток
освободить или изменить ее.
13.1.2 Вставка текста из системного буфера.
Можно вставить текст из системного буфера в область
пользователя; т.е. можно получить дескриптор текста из
системного буфера и индицировать его в области пользователя,
используя функцию TextOut. Необходимо сделать следующее:
1. Открыть системный буфер.
2. Получить дескриптор данных, связанный с CF_TEXT или с
CF_OEMTEXT.
3. Закрыть системный буфер.
Эта операция разрешена только в том случае, если в
системном буфере существует текст. Если текста не существует,
то для предотвращения попыток его вставить можно проверить
системный буфер перед тем, как Windows индицирует меню
Edit, обработав сообщение WM_INITMENU. Если системный буфер
пуст, можно сделать недоступной команду Paste; если текст
имеется, можно сделать ее доступной. Добавьте следующие
операторы к функции окна:
case WM_INITMENU:
(1) if (wParam == GetMenu(hWnd)) {
if(OpenClipboard(hWnd)) {
(2) if(IsClipboardFormatAvailable(CF_TEXT) |
IsClipboardFormatAvailable(CF_OEMTEXT))
(3) EnableMenuItem(wParam, ID_PASTE, MF_ENABLED);
else
EnableMenuItem(wParam, ID_PASTE, MF_GRAYED);
CloseClipboard();
return(TRUE);
}
else
return(FALSE);
}
1) Поскольку многие прикладные программы имеют по крайней
мере два меню (включая системное меню), важно быть
уверенным, что сообщение относится к меню Edit. Это можно
сделать, использовав функцию GetMenu и сравнив wParam с
дескриптором, который возвращает эта функция.
2) Два вызова функции IsClipboardFormatAvailable проверяют
доступность форматов CF_TEXT и CF_OEMTEXT.
3) Функция EnableMenuItem делает доступной или недоступной
команду Paste, основываясь на том, найден ли формат
CF_TEXT.
.
Windows 3.0/pg/2#3 = 137 =
Можно вставить данные из системного буфера, когда
пользователь выбирает команду Paste из меню Edit. Для обработки
команды меню и получения текста из системного буфера необходимо
добавить фрагмент IDM_PASTE к фрагменту WM_COMMAND в функции
окна. Добавьте следующие операторы сразу после фрагмента
IDM_COPY:
case IDM_PASTE:
(1) if (OpenClipboard(hWnd)) {
/* взять текст из системного буфера */
(2) hClipData = GetClipboardData(CF_TEXT);
CloseClipboard();
break;
}
(3) if(!(lpClipData = GlobalLock(hClipData))) {
OutOfMemory();
CloseClipboard();
break;
}
(4) hDC = GetDC(hWnd);
TextOut(hDC, 10, 10, lpClipData);
GlobalUnlock(hClipData);
ReleaseDC(hWnd, hDC);
CloseClipboard();
}
break;
В этом примере:
1) Функция OpenClipboard открывает системный буфер для
указанного окна, если он еще не открыт.
2) Функция GetClipboardData возвращает дескриптор данных для
текста или нуль, если таких данных нет. Этот дескриптор
следует проверять перед использованием.
3) Функция GetClipboardData возвращает дескриптор глобальной
памяти. Поскольку используется формат CF_TEXT, то
предполагается, что глобальная память содержит
оканчивающуюся нулем ANSI-строку. Это означает, что
глобальная память может быть заблокирована с помощью
функции GlobalLock, а содержимое может быть индицировано в
области пользователя с помощью функции TextOut.
4) Для того, чтобы иметь возможность видеть, что программа
скопировала содержимое системного буфера, функция TextOut
записывает, начиная с точки с координатами (10,10) области
пользователя. Для использования функции TextOut необходим
контекст отображения и, следовательно, нужно использовать
GetDC. Поскольку необходимо освободить контекст
отображения сразу после его использования, требуется
функция ReleaseDC.
.
Windows 3.0/pg/2#3 = 138 =
Данный способ индицирования текста в области пользователя
применен только для иллюстрации. Поскольку содержимое строки
прикладной программой не сохраняется, не существует способа
перерисовать текст, если будет стерт фон области пользователя,
как это происходит во время обработки сообщения WM_PAINT.
(Пример программы ClipText, описаннный далее в данной главе,
демонстрирует, как можно сохранить текст, с тем, чтобы в
дальнейшем можно было осуществлять перерисовку окна.)
Нельзя модифицировать или удалить данные, полученные из
системного буфера. Можно проверить их или сделать копию, но
нельзя их изменить. Для проверки данных необходимо
заблокировать дескриптор, как в нашем примере, но оставлять
дескриптор данных заблокированным нельзя. Разблокируйте его
сразу после использования.
Дескрипторы данных, возвращаемые функцией
GetClipboardData, предназначены только для временного
использования. Дескрипторы принадлежат системному буферу, а не
программе, запросившей данные. Соответственно, дескрипторы не
должны освобождаться и должны быть разблокированы сразу после
использования. Прикладная программа должна скопировать данные,
связанные с дескриптором, а затем освободить его без изменений.
Функция CloseClipboard закрывает системный буфер; вы
должны закрыть системный буфер сразу же после его
использования, т.к. необходимо дать возможность воспользоваться
системным буфером другим прикладным программам. Перед тем, как
закрыть системный буфер, необходимо разблокировать данные,
полученные функцией GetClipboardData.
13.1.3 Вставка растровых карт из системного буфера.
Из системного буфера можно вставить в область пользователя
не только текст. Растровую карту также можно получить из
системного буфера и индицировать ее в области пользователя. Для
получения и индицирования растровой карты необходимо
использовать тот же способ, что и при вставке текста, но
применительно к растровым картам нужно сделать небольшие
изменения.
Сначала необходимо модифицировать фрагмент WM_INITMENU в
функции окна, чтобы он распознавал формат CF_BITMAP вместо
CF_TEXT. После его изменения фрагмент WM_INITMENU будет
выглядеть так:
case WM_INITMENU:
if (wParam == GetMenu(hWnd)) {
if(OpenClipboard(hWnd)) {
if(IsClipboardFormatAvailable(CF_BITMAP))
EnableMenuItem(wParam, ID_PASTE, MF_ENABLED);
else
.
Windows 3.0/pg/2#3 = 139 =
EnableMenuItem(wParam, ID_PASTE, MF_GRAYED);
CloseClipboard();
return(TRUE);
}
else
return(FALSE);
}
Получить растровую карту из системного буфера также легко,
как и текст, но индицирование растровой карты требует большей
работы. В общем, необходимо сделать следующее:
1. Получить дескриптор данных растровой карты из
системного буфера. Дескрипторы данных растровой карты
из системного буфера - это дескрипторы растровой карты
GDI (созданные с помощью функций, например, таких как
CreateBitmap).
2. Создать совместимый контекст отображения и выбрать в
нем дескриптор данных.
3. Использовать функцию BitBlt для копирования растровой
карты в область пользователя.
4. Освободить дескриптор растровой карты из текущей
выборки.
После изменения фрагмент IDM_PASTE должен выглядеть так:
case IDM_PASTE:
if (OpenClipboard(hWnd)) {
/* взять текст из системного буфера */
hClipData = GetClipboardData(CF_BITMAP);
CloseClipboard();
break;
}
if(!(lpClipData = GlobalLock(hClipData))) {
OutOfMemory();
CloseClipboard();
break;
}
hDC = GetDC(hWnd);
(1) hMemoryDC = CreateCompatibleDC(hDC);
if (hMemoryDC != NULL) {
(2) GetObject(hClipData, sizeof(BITMAP),
(LPSTR) &PasteBitmap);
(3) hOldBitmap = SelectObject(hMemoryDC, hClipData);
if (hOldBitmap != NULL)
BitBlt(hDC, 10, 10,
PasteBitmap.bmWidth,
PasteBitmap.bmHeight,
.
Windows 3.0/pg/2#3 = 140 =
hMemoryDC, 0, 0, SRCCOPY);
SelectObject(hMemoryDC, hOldBitmap);
}
(4) DeleteDC(hMemoryDC);
}
ReleaseDC(hWnd, hDC);
GlobalUnlock(hClipData);
CloseClipboard();
GlobalUnlock(hText);
}
break;
В данном примере:
1) Функция CreateCompatableDC возвращает дескриптор контекста
отображения в памяти, который совместим с дисплеем вашего
компьютера. Это означает, что растровые карты, выбранные в
данном контексте отображения, могут быть скопированы
непосредственно в область пользователя. Если функция
CreateCompatibleDC заканчивается неудачно (возвращает
NULL), растровая карта не может быть индицирована.
2) Функция GetObject возвращает ширину и высоту растровой
карты, а также описание формата растровой карты. Она
копирует эту информацию в структуру PasteBitmap, размер
которой определяется функцией sizeof. В данном примере
используются только ширина и высота и только в функции
BitBlt.
3) Функция SelectObject выбирает растровую карту в
совместимом контексте отображения. Если она заканчивается
неудачно (возвращает NULL), растровая карта не может быть
индицирована. Функция SelectObject может закончиться
неудачно, если растровая карта имеет формат, отличающийся
от совместимого контекста отображения. Это может
случиться, например, если растровая карта была создана для
индикации на другом компьютере.
4) Функция DeleteDC удаляет совместимый контекст отображения.
Перед удалением его первоначальная растровая карта должна
быть восстановлена с помощью функции SelectObject.
13.1.4 Инструментальная программа Clipboard.
Инструментальная программа Clipboard - clipbrd.exe дает
пользователю способ просмотреть содержимое системного буфера;
по этой причине она также называется "Программа просмотра
системного буфера". Она перечисляет имена всех форматов, для
которых в системном буфере существует дескриптор (или NULL), и
индицирует содержимое системного буфера в одном из этих
форматов.
Программа просмотра системного буфера может индицировать
.
Windows 3.0/pg/2#3 = 141 =
все стандартные форматы данных. Если существуют дескрипторы
более, чем для одного стандартного формата данных, программа
индицирует только один формат, выбирая из приведенного ниже
списка в порядке уменьшения приоритета: CF_TEXT,
CF_OEMTEXT, CF_METAFILEPICT, CF_BITMAP, CF_SYLK и CF_DIF.
Дополнительную информацию о форматах системного буфера вы
можете получить в Справочном руководстве, том 1.
13.2 Использование специальных возможностей системного
буфера.
Системный буфер обеспечивает ряд специальных возможностей,
которые позволяют уменьшить необходимое количесво работы и
сделать работу более удобной. Это следующие возможности:
- Системный буфер дает возможность прикладным программам
отложить форматирование данных, передаваемых в системный
буфер, до тех пор, пока эти данные не потребуются. Если
используется сложный формат данных и другие программы
возможно не будут его использовать, прикладная программа
может сохранить время, не форматируя эти данные до тех
пор, пока в них не возникнет необходимость.
- Позволяет рисовать внутри области пользователя
программы Clipboard. Рисование в окне программы
Clipboard дает возможность индицировать форматы данных,
которые эта программа не знает как отображать.
В последующих подразделах эти свойства описываются более
подробно.
13.2.1 Представление данных по запросу.
Если прикладная программа использует много форматов
данных, она может сберечь время форматирования, передавая
нулевые дескрипторы данных функции SetClipboardData вместо
того, чтобы генерировать все дескрипторы данных при
использовании команд Cut и Copy. Прикладная программа
фактически не должна генерировать дескриптор данных до тех пор,
пока другая прикладная программа не запросит дескриптор, вызвав
функцию GetClipboardData.
Когда вызвана функция GetClipboardData с запросом на
формат, для которого был установлен нулевой дескриптор данных,
владельцу системного буфера посылается сообщение
WM_RENDERFORMAT. Когда прикладная программа получает это
сообщение, она должна выполнить следующее:
1. Отформатировать данные, которые были скопированы
последними в системный буфер (значение wParam сообщения
WM_RENDERFORMAT специфицирует запрошенный формат).
.
Windows 3.0/pg/2#3 = 142 =
2. Отвести блок глобальной памяти и скопировать в него
сформатированные данные.
3. Передать дескриптор глобальной памяти и номер формата в
системный буфер, используя функцию SetClipboardData.
Для выполнения указанных шагов прикладная программа должна
хранить запись о последних данных, скопированных в системный
буфер. Программа может избавиться от этой информации, когда она
получает сообщение WM_DESTROYCLIPBOARD, которое посылается
владельцу системного буфера каждый раз, когда он очищается с
помощью вызова функции EmptyClipboard.
13.2.2 Представление форматов перед завершением работы.
При разрушении прикладной программы разрушаются также
сведения о представлении данных при их копировании в системный
буфер. Соответственно, когда разрушается прикладная программа,
владеющая системым буфером, посылается специальное сообщение
WM_RENDERALLFORMATS. При получении этого сообщения программа
должна следовать шагам, описанным в предыдущем разделе.
13.2.3 Регистрация собственных форматов.
Кроме того, программа может создать и использовать
собственные форматы или даже новые форматы общего
использования. Для создания и использования новых форматов
обмена данными прикладная программа должна выполнить следующее:
1. Вызвать функцию RegisterClipboardFormat для регистрации
имени нового формата.
2. Использовать значение, возвращенное этой функцией, как
код нового формата при вызове функции SetClipboardData.
Регистрация имени формата дает уверенность в том, что
программа использует уникальный номер формата. Кроме того, он
позволяет программе Clipboard индицировать правильное имя
данных, хранящихся в системном буфере. Более подробно об
индикации собственных типов данных см. раздел 13.2.4
"Управление отображением данных в Clipboard".
Если несколько программ зарегистрировали форматы с одним и
тем же именем, все они получат один и тот же код формата. Это
дает возможность прикладным программам создать собственные
общие типы данных. Например, если несколько программ
зарегистрировали формат с именем WORKSHEET, все они будут иметь
один и тот же номер формата при вызове функций SetClipboardData
и GetClipboardData и общий базис для передачи данных типа
WORKSHEET между ними.
.
Windows 3.0/pg/2#3 = 143 =
13.2.4 Управление отображением данных Clipboard.
Существуют две причины, по которым прикладной программе
может потребоваться управлять выводом информации в Clipboard:
- Прикладная программа может иметь собственные типы
данных, которые трудно или невозможно индицировать
разумным способом.
- Программа может иметь собственные типы данных, которые
требуют специальных знаний для их отображения на экране.
Использование формата отображения для собственных данных.
Можно использовать "формат отображения" для представления
собственного формата данных, который в противном случае трудно
или невозможно индицировать. Данные, связанные с форматом
отображения, - это текст, растровая карта или изображения
метафайла, которые программа просмотра системного буфера может
индицировать как заменители соответствующих собственных данных.
Для использования формата отображения необходимо скопировать в
системный буфер как собственные, так и индицируемые данные.
Когда программа просмотра системного буфера выбирает формат для
индицирования, она выбирает формат отображения вместо
собственых данных.
Существуют три формата отображения: CF_DSPTEXT,
CF_DSPBITMAP и CF_DSPMETAFILEPICT. Данные, связанные с этими
форматами, идентичны форматам текста, растровой карты и
изображения метафайла. Поскольку указанные форматы являются
также стандартными форматами, программа просмотра системного
буфера может индицировать их без помощи прикладной программы.
Последующее описание предполагает, что прикладная
программа следует шагам, описанным в пункте 13.1.1 "Копирование
текста в системный буфер" для установки владельца системного
буфера и дескрипторов данных.
Для форсирования отображения на экране собственных типов
данных в стандартном формате данных прикладная программа должна
выполнить следующие шаги:
1. Открыть системный буфер для изменений, вызвав функцию
OpenClipboard.
2. Создать глобальный дескриптор, который содержит текст,
растровую карту или изображение метафайла, определив
информацию, которая должна быть отображена в программе
просмотра системного буфера.
3. Установить дескриптор системного буфера, вызвав функцию
SetClipboardData. Переданный код формата должен быть
CF_DSPTEXT для дескриптора текста, CF_DSPBITMAP - для
дескриптора растровой карты и CF_DSPMETAFILEPICT - для
.
Windows 3.0/pg/2#3 = 144 =
дескриптора изображения метафайла.
4. Сообщить, что изменения в системный буфер уже внесены,
вызвав функцию CloseClipboard.
Полное управление индицированием в программе просмотра
системного буфера.
Прикладная программа может полностью управлять
отображением и прокруткой информации в программе просмотра
системного буфера. Такое управление полезно, когда прикладная
программа имеет сложные собственные типы данных, которые только
она знает, как индицировать. Microsoft Write использует это
свойство для отображения сформатированного текста.
Последующее описание предполагает, что прикладная
программа следует шагам, описанным в пункте 13.1.1 "Копирование
текста в системный буфер" для установки владельца системного
буфера и дескрипторов данных.
Для управления индицированием информации в программе
просмотра системного буфера прикладная программа должна
выполнить следующие шаги:
1. Открыть системный буфер для изменений, вызвав функ-
цию OpenClipboard.
2. Вызвать функцию SetClipboardData, используя в качестве
формата данных CF_OWNERDISPLAY с нулевым дескриптором.
3. Сообщить, что выполнено изменение системного буфера,
вызвав функцию CloseClipboard.
Затем владелец системного буфера будет получать
специальные сообщения, связанные с индицированием информации в
программе просмотра системного буфера. Это следующие сообщения:
Сообщение Действие
---------------------------------------------------------------
WM_PAINTCLIPBOARD Нарисовать указанную часть окна.
WM_SIZECLIPBOARD Сообщить об изменении размера окна.
WM_VSCROLLCLIPBOARD Прокрутить окно в вертикальном направ-
лении.
WM_HSCROLLCLIPBOARD Прокрутить окно в горизонтальном
направлении.
WM_ASKCBFORMATNAME Задать имя индицируемого формата
---------------------------------------------------------------
Полное описание этих сообщений можно найти в Справочном
.
Windows 3.0/pg/2#3 = 145 =
руководстве, том 1.
Использование цепочки наблюдения за системным буфером.
Использование этой цепочки и окон просмотра системного
буфера обеспечивают способ уведомления прикладных программ об
изменениях в системном буфере. Уведомление в виде сообщения
WM_DRAWCLIPBOARD передается вниз по цепочке наблюдения каждый
раз, когда вызывается функция CloseClipboard. Получатель этого
сообщения должен определить природу изменений (пусто,
установлен и т.д.) вызвав функции GetClipboardData,
EnumClipboardFormats и другие в случае необходимости.
Любое окно, которое обеспечило себе связь в цепочке
наблюдения, должно быть готово сделать следующее:
1. Удалить себя из цепочки перед разрушением.
2. Передать сообщения WM_DRAWCLIPBOARD следующей связи в
цепочке.
Программа для этих действий выглядит так:
case WM_DESTROY:
ChangeClipboardChain(hWnd, my_save_next);
/* остальные операторы для обработки WM_DESTROY */
break;
case WM_DRAWCLIPBOARD:
if (my_save_next != NULL)
SendMessage(my_save_next, WM_DRAWCLIPBOARD,
wParam, lParam);
/* остальные операторы для обработки WM_DRAWCLIPBOARD */
break;
Строка my_save_next - это значение, возвращенное функцией
SetClipboardViewer. Эти действия над цепочкой наблюдения за
системным буфером должны быть первыми шагами, выполняемыми
ветвями оператора switch, которые обрабатывают сообщения
WM_DESTROY и WM_DRAWCLIPBOARD.
13.3. Пример прикладной программы ClipText.
Этот пример иллюстрирует как копировать и вставлять данные
с помощью системного буфера. Для создания программы ClipText
скопируйте и переименуйте исходные файлы прикладной программы
EditMenu, а затем сделайте следующие изменения:
1. Добавьте новые переменные.
.
Windows 3.0/pg/2#3 = 146 =
2. Модифицируйте функцию инициализации экземпляра.
3. Добавьте фрагмент WM_INITMENU.
4. Модифицируйте фрагмент WM_COMMAND для обработки
IDM_CUT, IDM_COPY и IDM_PASTE.
5. Добавьте фрагмент WM_PAINT.
6. Добавьте функцию OutOfMemory.
7. Оттранслируйте и скомпонуйте программу.
В этом примере используется глобальная память для
запоминания копируемого текста. Более подробную информацию о
глобальной памяти смотрите в главе 15, "Управление памятью".
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
13.3.1 Добавление новых переменных.
Необходимо добавить несколько новых глобальных переменных
для хранения дескрипторов, используемых в операциях копирования
и вставки, а также для хранения адресов строк текста. Добавьте
следующие операторы в начало исходного С-файла:
HANDLE hText = NULL;
char szInitialClientAreaText[] = "This Program demonstrates..."
HANDLE hData, hClipData; /* дескрипторы для вырезания данных */
LPSTR lpData, lpClipData; /* указатели на вырезаемые данные */
Необходимо также добавить переменные для рисования и
манипуляции с системным буфером. Добавьте следующие переменные
в начало функции основного окна MainWndProc:
HDC hDC;
PAINTSTRUCT ps;
RECT rectClient;
LPSTR lpszText;
13.3.2 Модификация программы инициализации экземпляра.
При запуске экземпляра программы ClipText, она должна
выделить область в глобальной памяти и заполнить ее данными из
строки. Добавьте в функцию инициализации экземпляра следующие
операторы:
if (!(hText = GlobalAlloc(GMEM_MOVEABLE,
(DWORD)sizeof(szInitialClientAreaText)))) {
OutOfMemory();
.
Windows 3.0/pg/2#3 = 147 =
return (FALSE);
}
if (!(lpszText = GlobalLock(hText))) {
OutOfMemory();
return (FALSE);
}
lstrcpy(lpszText, szInitialClientAreaText);
GlobalUnlock(hText);
13.3.3 Добавление фрагмента WM_INITMENU.
Необходимо добавить фрагмент WM_INITMENU к функции окна с
целью подготовки меню Edit к вставке. Вообще говоря, команда
Paste не должна быть доступна до тех пор, пока в системном
буфере не существует выбранный текст, подлежащий вставке.
Добавьте следующие операторы к функции окна:
case WM_INITMENU:
if (wParam == GetMenu(hWnd)) {
if (OpenClipboard(hWnd)) {
if (IsClipboardFormatAvailable(CF_TEXT)
|| IsClipboardFormatAvailable(CF_OEMTEXT))
EnableMenuItem(wParam, IDM_PASTE, MF_ENABLED);
else
EnableMenuItem(wParam, IDM_PASTE, MF_GRAYED);
CloseClipboard();
return (TRUE);
}
else /* Системный буфер недоступен */
return (FALSE);
}
Эти операторы обрабатывают сообщение WM_INITMENU только в
том случае, если указанное меню - это меню-строка. Функция
IsClipboardFormatAvailable определяет, есть ли текст в
системном буфере. Если есть, функция EnableMenuItem делает
команду доступной. В противном случае команда Paste недоступна.
13.3.4 Модификация фрагмента WM_COMMAND.
Необходимо модифицировать фрагменты IDM_CUT, IDM_COPY и
IDM_PASTE в фрагменте WM_COMMAND для обработки команд меню
Edit. По IDM_COPY и IDM_CUT необходимо создать блок глобальной
памяти, заполнить его текстом и скопировать дескриптор блока в
системный буфер, а в IDM_CUT необходимо удалить имеющийся в
прикладной программе текст. Фрагмент IDM_PASTE должен получить
из системного буфера дескриптор и записать текст в область
пользователя.
Замените существующий оператор IDM_COPY следующими
.
Windows 3.0/pg/2#3 = 148 =
операторами:
case IDM_CUT:
case IDM_COPY:
if (hText != NULL) {
/* Выделить память и скопировать в нее строку. */
if (!(hData
= GlobalAlloc(GMEM_MOVEABLE, GlobalSize (hText)))) {
OutOfMemory();
return (TRUE);
}
if (!(lpData = GlobalLock(hData))) {
OutOfMemory();
return (TRUE);
}
if (!(lpszText = GlobalLock (hText))) {
OutOfMemory();
return (TRUE);
}
lstrcpy(lpData, lpszText);
GlobalUnlock(hData);
GlobalUnlock (hText);
/* Очистить текущее содержимое системного буфера и
* установить дескриптор данных на новую строку. */
if (OpenClipboard(hWnd)) {
EmptyClipboard();
SetClipboardData(CF_TEXT, hData);
CloseClipboard();
}
hData = NULL;
if (wParam == IDM_CUT) {
GlobalFree (hText);
hText = NULL;
EnableMenuItem(GetMenu (hWnd), IDM_CUT, MF_GRAYED);
EnableMenuItem(GetMenu(hWnd), IDM_COPY, MF_GRAYED);
InvalidateRect (hWnd, NULL, TRUE);
UpdateWindow (hWnd);
}
}
return (TRUE);
Функция GlobalAlloc отводит блок глобальной памяти,
используемый для запоминания строки текста. Функция lstrcpy
копирует строку из области пользователя окна в блок после того,
как дескриптор был заблокирован функцией GlobalLock. Дескриптор
должен быть разблокирован перед его копированием в системный
.
Windows 3.0/pg/2#3 = 149 =
буфер. Функция EmptyClipboard используется для удаления любых
существующих данных из системного буфера.
Замените оператор IDM_PASTE следующими операторами:
case IDM_PASTE:
if (OpenClipboard(hWnd)) {
/* get text from the clipboard */
if (!(hClipData = GetClipboardData(CF_TEXT))) {
CloseClipboard();
break;
}
if (hText != NULL) {
GlobalFree(hText);
}
if (!(hText = GlobalAlloc(GMEM_MOVEABLE
, GlobalSize(hClipData)))) {
OutOfMemory();
CloseClipboard();
break;
}
if (!(lpClipData = GlobalLock(hClipData))) {
OutOfMemory();
CloseClipboard();
break;
}
if (!(lpszText = GlobalLock(hText))) {
OutOfMemory();
CloseClipboard();
break;
}
lstrcpy(lpszText, lpClipData);
GlobalUnlock(hClipData);
CloseClipboard();
GlobalUnlock(hText);
EnableMenuItem(GetMenu(hWnd), IDM_CUT, MF_ENABLED);
EnableMenuItem(GetMenu(hWnd), IDM_COPY, MF_ENABLED);
/* копирование текста в окно прикладной программы */
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
return (TRUE);
}
else
return (FALSE);
}
break;
Функция GetClipboardData возвращает дескриптор блока
.
Windows 3.0/pg/2#3 = 150 =
глобальной памяти. Функция GlobalLock блокирует этот
дескриптор, возвращая адрес блока, который используется в
функции TextOut для записи текста.
13.3.5 Добавление фрагмента WM_PAINT.
Фрагмент WM_PAINT необходим для вывода текущего текста на
экран при изменениях состояния окна. Добавьте следующие
операторы к функции окна:
case WM_PAINT:
hDC = BeginPaint (hWnd, &ps);
if (hText != NULL) {
if (!(lpszText = GlobalLock (hText))) {
OutOfMemory();
} else {
GetClientRect (hWnd, &rectClient);
DrawText (hDC, lpszText, -1, &rectClient
,DT_EXTERNALLEADING | DT_NOPREFIX |
DT_WORDBREAK);
GlobalUnlock (hText);
}
}
EndPaint (hWnd, &ps);
break;
13.3.6 Добавление функции OutOfMemory.
Необходимо добавить функцию, которая будет отображать
панель сообщения, когда прикладной программе не будет хватать
памяти. Добавьте следующую функцию в файл с исходным текстом
прикладной программы:
void OutOfMemory(void)
{
MessageBox(
GetFocus(),
"Out of Memory",
NULL,
MB_ICONHAND | MB_SYSTEMMODAL);
return;
}
Во включаемый файал добавьте ее предварительное описание:
void OutOfMemory(void);
13.3.7 Трансляция и компоновка.
Для перетрансляции и перекомпоновки ПП ClipText не
требуется вносить каких-либо изменений в файл make. После
трансляции и компоновки запустите Windows, программу Clipboard
и программу ClipText. Затем выберите команду Copy в меню
.
Windows 3.0/pg/2#3 = 151 =
Edit. На экране появится картинка, представленная на рис.13.1.
Рисунок 13.1 Вставка текста в ClipText из Clipboard.
1. Текст в системном буфере (Clipboard).
2. Текст, вставленный в ClipText из системного буфера.
13.4 Заключение.
В данной главе описано, как использовать системный буфер
для обмена данными между прикладными программами. Системный
буфер - это область памяти, в которой прикладная программа
может сохранить дескрипторы данных, другая прикладная программа
может затем получить данные, связанные с этим дескриптором.
Прикладная программа может поместить в системный буфер
различнфе данные одновременно, при условии, если они используют
разные форматы. Это обеспечивает уверенность в том, что данные
будут поддерживаться различными прикладными программами.
Простое использование системного буфера состоит в копировании
текста или растровых карт в и из прикладных программ. Более
сложное использование включает управление отображением данных и
регистрацию собственных форматов данных.
Дополнительную информацию относительно системного буфера
вы найдете в:
Раздел Руководство
---------------------------------------------------------------
Контекст отображения Руководство программиста, глава 3, "Вывод в
окно".
Работа с растровыми Руководство программиста, глава 11, "Раст-
картами ровые карты".
Управление памятью Руководство программиста, глава 15, "Управ-
ление памятью", и глава 16, "Еще об управ-
лении памятью".
Обмен данными с ис- Руководство программиста, глава 22, "Дина-
пользованием прото- мический обмен данными".
кола Windows DDE
вместо системного
буфера
Функции управления Справочное руководство, том 1, глава 1,
системным буфером "Функции интерфейса с устройством управ-
ления окнами".
Форматы системного Справочное руководство, том 1, глава 4,
буфера "Список функций".
Форматы файлов сис- Справочное руководство, том 2, глава 9,
темного буфера "Форматы файлов".
.
Windows 3.0/pg/2#3 = 152 =
ЧАСТЬ 3. БОЛЕЕ СЛОЖНЫЕ РАЗДЕЛЫ.
----------------------------------------------------------------
Microsoft Windows предоставляет много средства, которые
позволяют писать красивые и удобные прикладные программы.
Однако, отличие между хорошей прикладной программой и
прекрасной прикладной программой состоит в том, что хорошая
прикладная программа просто работает, а прекрасная прикладная
программа работает быстро и эффективно, имеет дополнительные
возможености интерфейса с пользователем, такие как цветные и
красивые шрифты, и предоставляет пользователю мощные и гибкие
средства для решения сложных или больших задач.
Главы из второй части дают вам хорошую базу, чтобы вы
могли начать разрабатывать свои собственные программы. Однако,
если вы уже программировали в Windows, то вы, возможно,
захотите расширить или улучшить свои программы, например,
добиться более эффективного использования памяти или
предоставить более мощные средства для печати.
В третьей части мы расскажем о некоторых более сложных
вопросах программирования в среде Windows. Мы предполагаем, что
вы прочли части 1 и 2 данного руководства и знакомы со средой
Windows. В каждой главе затрагивается одна тема. Поскольку
примеры программ из глав третьей части более сложные, чем во
второй, то в главах части 3 не приводятся полные тексты
программ. Тексты всех программ вы найдете на диске "SDK Sample
Source Disk", который поставляется вместе с SDK.
.
Windows 3.0/pg/2#3 = 153 =
Глава 14. Язык С и язык Ассемблера.
----------------------------------------------------------------
В первой и второй частях вводятся функции Microsoft
Windows, которые вы используете в программах на С или
ассемблере, при создании прикладных программ для Windows. В
этих частях основное внимание уделялось элементам прикладных
программ, которые относятся к Windows.
Однако завершенные программы для Windows не всегда
используют только функции Windows. Вместо этого ваша программа,
возможно, использует функции стандартных библиотек С и ваши
собственные функции, которые в свою очередь вызываются Windows
или другими модулями вашей программы. Однако важно, чтобы вы
знали как включать эти процедуры в вашу программу.
В данной главе обсуждаются следующие вопросы:
- Выбор модели памяти.
- Использование NULL.
- Использование аргументов командной строки DOS.
- Написание экспортируемых функций.
- Использование стандартных функций исполняющей системы С.
- Написание программ на ассемблере.
14.1 Выбор модели памяти.
Как и обычные программы DOS, программы в среде Windows
могут содержать один или несколько сегментов кода и один или
несколько сегментов данных, в зависимости от используемой при
компиляции модели памяти. В главе 16 "Еще об управлении
памятью" обсуждаются доступные параметры моделей памяти.
Выбираемая вами модель памяти влияет на эффективность
работы программы в среде Windows. В большинстве случаев лучшей
является смешанная. При использовании смешанной модели вы
компилируете модули с используемыми по умолчанию малой или
средней моделями памяти и именами сегментов. Затем вы с помощью
явных вызовов FAR (в модулях с малой моделью памяти) или NEAR
(в модулях со средней моделью памяти) изменяете эти параметры
при вызове функций из других сегментов.
Преимущества смешанной модели памяти:
- Ближние вызовы уменьшают объем кода, генерируемого
компилятором, и такой вызов функции выполняется быстрее.
- Компилирование модулей с названными именами сегментов
разделяет сегменты на небольшие части, которые делают
.
Windows 3.0/pg/2#3 = 154 =
проще для Windows процесс перемещения сегментов в
памяти.
Для создания прикладной программы, использующей смешанную
модель (с малой моделью по умолчанию), выполните следующее:
1. Создайте прототипы для всех функций, которые вызываются
из вне кодового сегмента, в котором она определена. Для
удобства эти описания можно поместить во включаемый
файл. Вы должны описать как дальние (используя ключевое
слово FAR) все вызовы функций, которые совершаются из
других сегментов. Ниже приводится пример такого
описания:
int FAR MyCalculation(int,int);
2. Откомпилируйте модули на С с ключем -AS для создания
прикладной программы в малой модели памяти.
3. Откомпилируйте модули на С с ключем -NT для указания
имен сегментов.
Ключи компилятора и многое другое описаны в "Tools".
Создание прикладной программы в смешанной модели памяти,
используя среднюю модель по умолчанию, аналогично
вышеописанному за исключением того, что вы должны явно описать
как NEAR функции, которые вызываются только внутри того
сегмента, в котором они определяются. Компилировать такие
модули надо с помощью ключа -AM для установки средней модели
памяти по умолчанию.
14.2 Использование NULL.
Символическая константа NULL по разному определена в
Windows и в Microsoft C Compiller версии 6.0. В Windows
константа NULL определена в файле windows.h следующий образом:
#define NULL 0
С другой стороны, во включаемых файлах библиотек С 6.0
(таком как STDDEF.H) константа определяется следующим образом:
#ifndef NULL
#define NULL ((void *)0)
#endif
Для исключения сообщений компилятора лучше использовать
NULL только для указателей, таких как параметры функций Windows
с типом LPSTR. Вы не должны использовать NULL для переменных,
которые объявлены с основным типом, таким как int, WORD,
HANDLE, и т.п. HANDLE определен в WINDOWS.H как WORD.
.
Windows 3.0/pg/2#3 = 155 =
Вы можете исключить выдачу сообщений, поставив директиву
включения Windows.H перед включением всех остальных включаемых
файлов, которые определяют NULL, как это показано ниже:
#include
#include
Поскольку включаемые файлы в стандартных библиотеках С не
определяют NULL, если он уже определен, препроцессор не изменит
начального определения в WINDOWS.H.
14.3 Использование аргументов командной строки и
переменных среды DOS.
В прикладной программе можно получить аргументы командной
строки, которые были использованы при запуске прикладной
программы так же, как и переменные среды DOS.
При запуске прикладной программы в среде Windows стартовая
процедура Windows копирует аргументы командной строки в
переменные _argc и _argv. Как и соответствующие переменные в
стандартных программах С, эти переменные представляют собой
число аргументов и массив строк, содержащий сами эти аргументы.
Кроме этого, переменная environ получает указатель на массив
строк, содержащих значения переменных среды DOS, на момент
запуска прикладной программы.
Чтобы воспользоваться этими переменными вы должны объявить
их как внешние для вашей прикладной программы, как показано
ниже:
extern int _argc;
extern char * _argv[];
extern char * _environ[];
Если хотите, то можно проверить параметр lpCommandLine,
передаваемый в функцию WinMain Windows.
Если в прикладной программе не требуется доступ к
аргументам командной строки или к переменным среды DOS, вы
можете уменьшить объем памяти "кучи" и объем кода вашей
программы исключив из нее код инициализации. Этот процесс
описан в разделе 14.5.10, "Исключение стартового кода С".
Динамически подключаемые библиотеки (DLL) не имеют доступа
к параметрам _argc, _argv и environ. Вместо этого для получения
аргументов командной строки они могут проверить параметр
lpCommandLine, передаваемый Windows функции LibEntry. Смотрите
главу 20, "Динамически подключаемые библиотеки".
Поскольку динамически подключаемые библиотеки не имеют
доступа к переменной -environ, то для получения значений
переменных среды DOS они должны использовать функцию
GetDOSEnvironment.
.
Windows 3.0/pg/2#3 = 156 =
14.4 Написание экспортируемых функций.
Как правило, функции, которые вы определяете в вашей
программе, не требуют специальной обработки. Однако имеется два
исключения из этого правила:
- Функции в DLL, которые вызываются из вне библиотеки.
- Функции многократного вызова.
Информацию о функциях DLL вы найдете в главе 20,
"Динамически подключаемые библиотеки".
Функции многократного вызова - это функции вашей
прикладной программы, которые вызываются не из программы, а из
Windows. Ниже приведен список основных типов функций
многократного вызова:
- Функция WinMain. Это точка входа вашей прикладной
программы.
- Функции окон прикладной программы. Эти функции
обрабатывают сообщения, посылаемые окну.
- Функции диалога. Эти функции обрабатывают сообщения,
посылаемые панели диалога.
- Функции перечисления. Эти функции обрабатывают
результаты функций перечисления Windows.
- Функции уведомления об освобождении памяти. Эти функции
вызываются Windows для уведомления вашей прикладной
программы, что необходимо сбросить блок памяти.
- Функции ловушек (фильтры). Эти функции обрабатывают
сообщения, посылаемые окнами другим прикладным программам.
Большинство функций ловушек должны располагаться в бибдтотеках.
14.4.1 Создание функции многократного вызова.
Для всех функций многократного вызова вы должны следовать
следующим шагам:
1. Определить функцию многократного вызова с помощью
ключевого слова PASCAL. Это приведет к тому, что
параметры функции будут помещаться в стек "справа
налево", а не как в стандартных функциях С.
2. Определить функцию многократного вызова с
использоаванием ключевого слова FAR. Это позволяет
осуществлять вызов функции из вне сегмента, в котором
.
Windows 3.0/pg/2#3 = 157 =
она определена. Это правило не применяется только к
функции WinMain.
3. Откомпилируйте модули, содержащие функции многократного
вызова с ключем -Gw (не путайте с -GW). Этот ключ
указывает компилятору, что необходимо добавить к
функции код "пролога" и "эпилога", которые заставляют
использовать правильный сегмент данных при вызове
функции.
4. Перечислите функции многократного вызова в операторе
EXPORTS файла определения модуля (.DEF). Это позволит
определить значение и атрибуты функций многократного
вызова.
Для всех функций многократного вызова, исключая WinMain,
ваша программа передает Windows адрес экземпляра процедуры
функции многократного вызова, когда Windows вызывает функцию
многократного вызова. Например, когда вы создаете панель
диалога, одним из параметров функции, создающей панель диалога,
является адрес экземпляра процедуры для функции, которая будет
обрабатывать сообщения, посылаемые панелью диалога.
Для создания адреса экземпляра процедуры используется
функция MakeProcInstance. Эта функция возвращает адрес
экземпляра процедуры, который указывает на код пролога, который
выполняется перед вызовом самой функции. Этот пролог подключает
сегмент данных экземпляра прикладной программы к функции
многократного вызова. Таким образом, при вызове функции она
получает доступ к переменным и данным сегмента данных
экземпляра процедуры. Вам не нужно создавать адрес экземпляра
процедуры для функции WinMain или для любой функции окна, чей
класс ваша программ зарегистрировала с помощью функции
RegisterClass.
После того, как функция многократного вызова больше не
нужна (это происходит, когда вы знаете, что она не будет
вызвана), вы можете отключить функцию от сегмента данных с
помощью функции FreeProcInstance.
14.4.2 Создание функции WinMain.
Каждая прикладная программа Windows должна иметь функцию
WinMain. Как и функция main в стандартном С, функция WinMain
выполняет роль точки входа в программу. Она содержит операторы
и вызовы функций Windows, которые создают окна и считывают и
обрабатывают сообщения ввода, предназначенные данной прикладной
программе. Объявление функции выглядит следующим образом:
int PASCAL WinMain(hInst,hPrevInst,lpCmdLine,nCmdShow);
HANDLE hInst;
HANDLE hPrevInst;
LPSTR lpCmdLine;
.
Windows 3.0/pg/2#3 = 158 =
int nCmdShow;
{
.
.
.
}
Как и все функции Windows, функция WinMain объявлена с
ключевым словом PASCAL. В результате, ваше объявление функции
WinMain должно содержать четыре параметра, даже если в
программе они не используются.
Хотя Windows вызывает функцию WinMain напрямую, она не
должны быть объявлена как FAR или как экспортируемая в файле
описания модуля, поскольку она вызывается из стартового кода,
который добавляется компилятором к сегменту данных. WinMain
неявно объявляется NEAR или FAR в зависимости от используемой
при компиляции модуля, содержащего WinMain, модели памяти.
14.5 Использование функций исполняющей системы С.
SDK содержит специальные версии функций библиотек
исполняющей системы С, которые отличаются от функций,
поставляемых с компилятором С. В следующих разделах описаны
различные отличия функций исполняющей системы С в Windows и в
компиляторе С.
14.5.1 Использование библиотек С.
Вы можете пользоваться библиотеками функций, поставляемых
с Microsoftt C Compiller версии 5.1 и выше. Специальные версии
этих библиотек адаптированы к среде Windows. Код пролога и
эпилога Windows добавляется ко всем функциям исполняющей
системы С, которым это требуется. Это предотвращает проблемы,
которые могут возникнуть при перемещении кодовых сегментов в
памяти, в ситуации, когда ее не хватает. Многие функции были
переписаны, чтобы исключить предположение, что DS равен SS, что
не верно для Windows DLL. Обсуждение того, как вызывать из DLL
функции, которые подразумевают, что DS равен SS, вы найдете в
главе 20, "Динамичеески подключаемые библиотеки".
SDK содержит два набора библиотек. Один набор используется
при компиляции прикладных программ, а другой при компиляции
Windows DLL. Эти бабалиотеки содержат стартовый код DLL или
прикладной программы, функции исполняющей системы С, включая
функции заменители зависимости от модели памяти. В результате
SDK требует только одну импортируемую библиотеку, LIBW.LIB. Эта
библиотека не зависит от модели памяти.
Программа SDK 3.0 Install обычно присваивает имена
библиотекам, содержащим версии для Windows стандартных функций
С в соответствии со следующим соглавшением:
.
Windows 3.0/pg/2#3 = 159 =
{ S | M | C | L }{ LIB | DLL } C { A | E }W.LIB
Буквы S, M, C и L представляют соответственно малую,
среднюю, компактную и большую модели памяти. LIB и DLL
определяют, что должны подключаться библиотеки или модули DLL.
A и E определяют альтернативную библиотеку или бибилиотеку
эмулятора. Используя это соглашение вы должны явно указать
версии Windows стандартных библиотек С при компоновке вашей
программы. Ниже приведен пример командной строки DOS для
компоновки прикладной программы Windows со стандартными
библиотеками С:
LINK GENERIC,,, /NOD SLIBCEW LIBW, GENERIC.DEF
Ключ /NOD (не использовать поиск директорий по умолчанию)
рекомендуется для исключения того, что компоновщик найдет и
подключит библиотеки С, а не версии этих библиотек для Windows.
Если вы укажете этот ключ, то не будут компоноваться прикладные
программы, которые используют функции С, не поддерживаемые
библиотеками Windows.
SDK также содержит специальные версии для Windows
включаемых файлов. Эти файлы помогут вам определить, что вы
случайно вызываете в своей программе функции С, которые не
поддерживаются в среде Windows. Для выполнения такой проверки
добавьте следующую директиву во включаемый файл вашего модуля
перед всеми директивами включения файла:
#define _WINDOWS
Набор функций исполняющей системы С, которые поддерживают
вызов из прикладных программ Windows, включает подмножество
функций, которые поддерживают вызов из Windows DLL. Включаемые
файлы Windows распознают это подмножество. Если вы создаете
DLL, вы должны включить обе директивы перед директивами
включения файлов заголовков исполняющей системы С:
#define _WINDOWS
#define _WINDLL
14.5.2 Выделение памяти.
Хотя версии для Windows библиотек исполняющей системы С
поддерживают заменители для функиций выделения памяти, таких
как malloc и free, лучше все же пользоваться функциями Windows.
Например, функция malloc позволяет вам выделить фиксированный
блок в локальной "куче", а функция LocalAlloc позволяет вам
определить перемещаемый блок памяти.
14.5.3 Работа со строками.
Для работы со строками вы можете использовать функции
исполняющей системы С. Однако в малой и средней моделях памяти
.
Windows 3.0/pg/2#3 = 160 =
эти функции не могут работать со строками, объявленными как
дальние указатели или массивы, такими как созданные в
глобальной области памяти с помощью функции GlobalAlloc. На
функции работы с буферами (такими как memcpy и memset)
накладываются те же ограничения при работе в малой и средней
модели памяти.
Windows предоставляет для работы со дальними строками
следующие функции:
- lstrcat
- lstrcmp
- lstrcmpi
- lstrcpy
- lstrlen
Для сравнения или проверки символов из набора символов
ANSI лучше использовать следующие функции вместо функций
исполняющей системы С:
- AnsiLower
- AnsiLowerBuff
- AnsiNext
- AnsiPrev
- AnsiUpperBuff
- IsCharAlpha
- IsCharAlfaNumeric
- IsCharLower
- IsCharUpper
Windows также содержит функции wsprintf и wvsprintf для
замены функций С sprintf и vsprintf. Они имеют следующие
преимущества:
- Версии для Windows используют дальние буфера вместо
ближних.
- Версии для Windows намного меньше.
- Версии для Windows позволяют вам исключить из программы
стартовый код С, если вы не используете функции исполняющей
.
Windows 3.0/pg/2#3 = 161 =
системы С. Смотрите раздел 14.5.10, "Исключение стартового кода
С".
Однако версии этих функций для Windows поддерживают не
полный набор спецификаций формата. В частности, формат с
плавающей точкой, указателя и восьмеричных чисел.
Важно. Если вы используете вместо sprintf или vsprintf
соответствующие функции Windows убедитесь, что все строки,
передаваемые в качестве аргументов к спецификатору формата %s,
были FAR.
char buffer[100];
char *str1; /* ближний указатель в малой или средней
модели памяти */
.
.
.
sprintf(buffer,"Str1 = %s",str1); /* допустимо */
wsprintf(buffer,"Str1 = %s",(LPSTR)str1); /* допустимо */
wsprintf(buffer,"Str1 = %s",str1); /* неверно */
14.5.4 Использование функций файлового ввода/вывода.
Для создания, открытия, повторного открытия и удаления
используйте функцию OpenFile. Функция OpenFile возвращает
дескриптор файла DOS, который вы можете использовать с такими
функциями исполняющей системы С как read, write, lseek и close.
Если вы компилируете модуль в малой или средней модели памяти,
параметр buffer функций read и write является ближним
указателем (char near *). Если вы хотите считывать или
записывать данные в буфер, объявленный в вашей программе как
дальний указатель или массив, используйте функции Windows
_lread и _lwrite. Они также используются для записи или чтения
из буферов, динамически выделенных в глобальной области памяти.
Вы можете также использовать буферизованный ввод и вывод в
файл, т.е. функции fopen, fread и fwrite.
Вы можете также использовать функции Windows _lopen и
_lcreat для открытия и создания файлов.
Поскольку Windows - мультизадачная среда, другие программы
могут попытаться получить доступ к тому файлу, в который
записывает или из которого читает ваша прикладная программа. Вы
можете управлять доступом других программ к файлу с помощью
бита разделения в параметре wStyle при открытии файла.
Оставляйте файлы открытыми только когда вы производите операции
чтения или записи, если ваша программ не должна управлять
доступом к файлу в другое время.
Примечание: Если DLL открывает файл, дескриптор файла
принадлежит прикладной программе, которая вызвала DLL. Если DLL
открывает не один файл и разделяется несколькими прикладными
.
Windows 3.0/pg/2#3 = 162 =
программами, то возможна ситуация, когда один и тот же
дескриптор будет связан DOS несколько раз.
14.5.5 Использование функций ввода и вывода на консоль.
Ваша программа должна разделять системную консоль с
другими прикладными программами. Следовательно, версии Windows
библиотек исполняющей системы С не содержат функций ввода и
вывода на консоль:
- cgets
- cprintf
- cputs
- getch
- getche
- kbhit
- putch
- ungetc
Вместо этого, ваша программа должны получать ввод с
консоли через сообщения WM_KEYDOWN, WM_KEYUP и WM_CHAR. Если
требуется более сложная техника, вы можете с помощью функции
PeekMessage заглянуть вперед на ввод клавиатуры, или вы можете
установить функцию ловушки в DLL с помощью функции
SetWindowHook.
14.5.6 Использование графических функций.
Интерфейс графических устройств Windows (GDI)
предоставляет аппаратно-независимые графические функции.
Следовательно, графические функции библиотек исполняющей
системы С не включаются в версию библиотек Windows.
14.5.7 Использование вычислений с плавающей точкой.
Если в вашей программе используются переменные с плавающей
точкой вы должны компоновать вашу программу с ключами: -FPi,
-FPc или FPa.
Прикладные программы, скомпонованные с ключем -FPi,
используют арифметический сопроцессор 80х87, если в момент
выполнения он установлен. В противном случае, прикладная
программа использует эмулятор.
Программы, скомпилированные с ключем -FPc, аналогичны
программам, скомпилированным с ключем -FPi, за исключением
.
Windows 3.0/pg/2#3 = 163 =
того, что при необходимости они могут быть скомпонованы с
альтернативной математической библиотекой.
Программы, скомпилированные с ключем -FPa, используют
альтернативную математическую библиотеку, если в момент
выполнения сопроцессор не установлен. Это наиболее маленькая и
быстрая опция, доступная без сопроцессора, однако она приносит
в жертву скорости точность по сравнению с библиотекой
эмулятора.
Если вы используете ключи -FPi или -FPc, вы должны
подключить библиотеку WIN87EM.LIB в командной строке LINK:
LINK SAMPLE,,,SLIBCEW WIN87EM LIBW, SAMPLE.DEF
В Windows 3.0 программа SETUP автоматически помещает
WIN87EM.DLL в системную директорию Windows.
Вы можете использовать параметр SIGFPE (ошибка вычислений
с плавающей точкой) функции исполняющей системы С для
отслеживания ошибок вычислений с плавающей точкой, таких как
переполнение или деление на 0. Для этого адрес процедуры
обработки ошибок необходимо подготовить с помощью функции
MakeProcInstance.
В прикладных программах Windows вместо функций setjmp и
longjmp вы должны использовать функции Catch и Throw.
14.5.8 Запуск других прикладных программ.
Для запуска прикладных программ Windows содержит функции
WinExec и LoadModule. LoadModule запускает только прикладные
программы Windows, а WinExec позволяет запускать как прикладные
программы Windows, так и обычные программы. Вы должны
использовать эти функции вместо функций исполняющей системы С
exec и spawn. Как и функции семейства spawn функции LoadModule
и WinExec позволяют вашей программе продолжать работу во время
выполнения запущенной ими программы.
WinExec предоставляет простой интерфейс для запуска
дочернего процесса. LoadModule более сложна, поскольку требует
от вашей программы блок параметров, однако позволяет управлять
средой, в которой будет запущен дочерний процесс.
14.5.9 Использование функций интерфейса с MS-DOS и BIOS.
Не используейте функции интерфейса с BIOS при работе в
Windows.
Вы можете использовать функции исполняющей системы С для
работы с прерыванием 21H, такими как intdos, intdosx и другими
фунциями _dos, например, _dos_getdrive. Вы можете также
использовать функции int86 и int86x для вызова прерываний,
.
Windows 3.0/pg/2#3 = 164 =
отличных от 21H. Однако вы должны использовать прерывания с
исключительной осторожностью и только тогда, когда они
необходимы.
14.5.10 Исключение стартового кода С.
Обычно при компоновке прикладных программ Windows или DLL
компоновщик добавляет к кодовому сегменту _TEXT стартовый код
С. Для прикладных программ Windows (но не для DLL) этот код в
свою очередь выделяет место в автоматической области памяти
программы для переменных времени выполнения.
SDK версии 3.0 позволяет вам убрать этот код и данные,
требуемые для библиотек С. Вы можете это сделать в следующих
случаях:
- Ваша прикладная программа или DLL не содержит явного
вызова стандартных функций С.
- В вашей программе не используются аргументы командной
строки _argc и _argv или переменная _environ. Смотрите
раздел 14.3 "Использование аргументов командной строки
DOS", в котором описано, как можно получить командную
строку и окружение DOS. В DLL переменные _argc, _argv и
_environ не должны использоваться никаким образом.
- В вашей программе или DLL не вызываюся явно функции
библиотек С, такие как проверка стека или деление
длинных целых. По умолчанию проверка стека включена, но
вы можете ее отключить с помощью ключа -Gs в командной
строке компилятора.
Исключение стартового кода С из прикладных программ
Windows.
Для того, чтобы исключить стартовый код С из прикладной
программы, компонуйте программу с библиотекой с именем
xNOCRT.LIB вместо обычных библиотек C xLIBCAW.LIB или
xLIBCEW.LIB (вместо x необходимо подставить требуемую модель
памяти: S, M, C, L).
В следующем примере приведена командная строка
компоновщика для прикладной программы SAMPLE, в которой не
выполняются явные или неявные вызовы стандартных функций С:
link /nod sample,,, snocrt libw sample.def
Библиотека SNOCRT.LIB включает стартовый код Windows,
который вызывает функцию WinMain.
Если вы компонуете программу с библиотекой xNOCRT.LIB
вместо xLIBCAW или xLIBCEW, и компоновщик сообщает об
обнаружении неразрешимых внешних символов, которые не
.
Windows 3.0/pg/2#3 = 165 =
принадлежат к вашей программе, то, возможно, вы неявно
вызываете библиотеки исполняющей системы С. В этом случае вы
можете исключить стартовый код С, требуемый для явных вызовов
функций С и использования переменных _argv, _argc и _environ.
Для этого подключите библиотеку xNOCRT.LIB перед, а не вместо
библиотеки xLIBCEW.LIB или xLIBCAW.LIB. Необходимо также
указать в командной строке компоновщика ключ /NOE.
В следующем примере показана командная строка компоновщика
для программы SAMPLE, которая неявно выполняет вызовы функций
С, но не выполняет явных вызовов:
link /nod /noe sample,,, snocrt slibcew libw, sample.def
Исключение стартового кода С из Windows DLL.
Для исключения стартового кода С из Windows DLL подключите
вместо библиотеки xDLLCEW.LIB или xDLLCAV.LIB статическую
библиотеку xNOCRT.LIB.
В следующем примере показана командная строка компоновщика
для DLL с именем SAMPDLL, которая не выполняет явно или неявно
вызовы функций С:
link /nod sampdll libentry, sampdll.dll,, snocrtd libw,
sampdll.def
Библиотека xNOCRTD.LIB включает стартовый код Windows,
который вызывает функцию LibMain библиотеки DLL.
Как и в случае с прикладными программами, в случае
присутствия в библиотеке явного или неявного вызова
библиотечных функций С компоновщик сообщает о появлении
неразрешимых ссылок, которые не принадлежат вашей DLL. В этом
случае вы можете исключить стартовый код С, требуемый для явных
вызовов библиотечных функций, подключив библиотеку xNOCRTD.LIB
вместе с xDLLCAW.LIB или xDLLCEW.LIB. Например:
link /nod /noe sampdll libentry, sampdll.dll,, snocrt
sdllcew, sampdll.def
Не забудьте указать ключ /NOE.
14.6 Написание программ на языке ассемблера.
Программы для Windows, написанные на ассемблере, - это
высокоструктурированные программы, использующие соглашения по
вызову высокоуровневых языков, а также функции, типы данных и
соглашения Windows. Хотя вы ассемблируете свои программы с
помощью Microsoft Macro Assempler, ваша цель состоит в том,
чтобы получить объектный файл, аналогичный генерируемому
компилятором С. Ниже приведены некоторые советы, которые
помогут вам в достижении этой цели и в создании прикладных
.
Windows 3.0/pg/2#3 = 166 =
программ с использованием ассемблера:
1. Включите в исходных файл CMACROS.INC. Этот файл
содержит макрокоманды языка высокого уровня,
определяющие сегменты, программные модели, интерфейс с
функциями и типы данных, необходимые для создания
прикладных программ для Windows. Эти макрокоманды
описаны во втором томе Справочного руководства.
2. Определите модель памяти, установив в единицу один из
ключей memS, memMm, memC или memL. Этот ключ должен
быть установлен перед оператором включения файла
CMACROS.INC.
3. Установите ключ ?PLM равным 1, чтобы указать, что должно
использоваться соглашение по вызову языка Pascal. Этот
ключ должен быть установлен перед оператором включения
файла CMACROS.INC. Соглашение по вызову языка Pascal
требуется только для функций, которые вызывает Windows.
4. Установите ключ ?WIN равным 1, чтобы указать, что
необходимо добавлять код пролога и эпилога Windows.
Этот ключ должен быть установлен перед оператором
включения файла CMACROS.INC. Этот ключ требуется только
для функций многократного вызова (или для
экспортируемых функций для библиотек Windows).
5. Создайте точку входа в вашу программу, WinMain, и
убедитесь, что она объявлена как PUBLIC. Она должны
иметь следующий вид:
cProc WinMain, ,
parmW hInstance
parmW hPrevInstance
parmD lpCmdLine
parmW nCmdShow
cBegin WinMain
.
.
.
cEnd WinMain
Функция WinMain должна быть определена внутри
стандартного кодового сегмента CODE.
6. Убедитесь, что ваши функции многократного вызова
объявлены следующим образом:
cProc TestWndProc, ,
parmW hWnd
parmW message
parmW wParam
parmD lParam
.
Windows 3.0/pg/2#3 = 167 =
cBegin TestWndProc
.
.
.
cEnd TestWndProc
Функции многократного вызова должны определяться в
кодовом сегменте.
7. Скомпонуйте вашу программу вместе с соответствующими
библиотеками Windows для С и с библиотеками исполняющей
системы С. Для правильной компоновки вы должны в
исходный файл добавить внешнее определение символа
__acrtuses.
Примечание: Функции Windows разрушают содержимое всех
регистров, за исключением DI, SI, BP и DS.
14.6.1 Изменение состояния флага прерывания.
Когда Windows работает в расширенном режиме процессора
386, то она выполняется на уровне I/O Privilege Level 0
(IOPL0). На этом уровне команды POPF и IRET не изменяют
состояние флага прерываний. (Другие флаги сохраняются и
восстанавливаются.) Это означает, что после завершения данного
примера прерывания останутся запрещены:
pushf;
cli
.
.
.
popf; прерывания остались запрещенными
На IOPL0 изменить состояние флага можно только с помощью
команд STI и CLI. Перед выходом из критической части, в которой
вы запретили прерывания, вы не можете полагаться на то, что
команда POPF восстановит состояние флага прерываний. Вместо
этого вы должны явно установить состояние флага в соответствии
с сохраненным предыдущей командой PUSHF состоянием. Ниже
приводится пример того, как это можно сделать:
pushf; иллюстрирует допустимый метод
cli
.
.
.
pop ax
test ah,2
jz SkipSTI
sti
SkipSTI:
.
.
Windows 3.0/pg/2#3 = 168 =
.
.
Если вы установили ловушку на прерывание, которая вызывает
следующую программу обработки прерывания в цепочке, вы также не
можете полагаться на то, что команда IRET восстановит состояние
флага. Ниже приведен неправильный код:
My_SW_Int_Hook; неверный код
sti;
.
.
.
pushf ; эмулирует вызов прерывания
cli ; и cli
call [следующую программу обработки прерывания]
; команда IRET следующей программы обработки
; прерывания может не восстановить флаг прерываний, и
; он останется сброшенным (прерывания будут
; запрещены).
.
.
.
iret
Для исключения такой возможности можно поместить STI
стразу же после вызова следующей программы обработки
прерывания, чтобы разрешить прерывания в случае, если следующая
в цепочке программа обработки прерываний оставит флаг
сброшенным. Ниже приводится пример, иллюстрирующий правильный
метод:
My_SW_Int_Hook; ниже верно
sti;
.
.
.
pushf ; эмулирует вызов прерывания
cli ; и cli
call [следующую программу обработки прерывания]
sti ; разрешить прерывания, даже если следующая прог-
; рама обработки прерывания оставила его сброшенным.
.
.
.
iret
14.6.2 Как на ассемблере написать экспортируемую функцию.
Когда вы пишете на ассемблере экспортируемую функцию, не
надо начинать ее с:
mov ax,xxxx
.
Windows 3.0/pg/2#3 = 169 =
В данном примере xxxx обозначает константу.
Этот код в начале экспортируемой функции идентичен началу
кодового сегмента библиотеки, который Windows помещает в
расширенную память при работе в реальном режиме. Когда Windows
пытается перезагрузить кодовый сегмент, она подразумевает что
константа содержит адрес сегмента данных библиотеки и фиксирует
это значение в качестве нового адреса сегмента данных
библиотеки.
Чтобы исключить такую ситуацию, просто поставте перед
командой MOV команду NOP, как показано ниже:
nop
mov ax,xxxx
14.6.3 Использование регистра ES.
При использовании регистра ES в программах на языке
ассемблера необходимо проявлять особую осторожность. В
некоторых условиях, если данный регистр содержит указатель
сброшенный объект, то это может вызвать неустранимую ошибку
защиты памяти (general-protection failure GP) при работе в
стандартном режиме или в расширенном режиме 386 процессора. При
некотором редком стечении обстоятельств это может привести к
тому, что Windows войдет в бесконечный цикл.
Ошибка GP возникает, когда программа вынимает из стека
значение в ES и это значение ссылается на сегмент, который был
сброшен.
Например, в следующем примере ES ссылается на динамически
выделенный в глобальной области памяти объект. Освобождение
объекта, когда его селектор временно помещается в стек,
приводит к тому, что он становится неверным:
push es
.
.
.
cCall GlobalFree
.
.
.
pop es
Ваша программа не должна явно сбрасывать сегмент.
Следующий пример показывает, как это может быть сделано неявно:
push es; ES определяет сбрасываемый сегмент
.
.
.
Windows 3.0/pg/2#3 = 170 =
.
call far Proc1 ; Proc1 явно или неявно сбрасывает объект,
; определяемый ES
.
.
.
pop es
Windows обрабатывает ошибки кодового сегмента в
стандартном и в расширенном режиме 386 процессора. Однако она
не обрабатывает ошибок сегментов данных, поэтому оба примера
приведут к возникновению ошибки GP.
Ситуация, которая может привести к тому, что Windows
войдет в бесконечный цикл, возникает, если ES содержит
указатель на сбрасываемый сегмент. В таких случаяв вы должны
очистить его, перед тем, как совершать вызов одного
сбрасываемого сегмента из другого.
Ниже покащано, как это сделать:
mov es, _CODESEG1 ; сбрасываемый сегмент
.
.
.
xor ax, ax ; В данном примере ES очищается перед
mov es, ax ; осуществлением вызова сбрасываемого
call far Proc1 ; сегмета из сбрасываемого сегмента
Если в такой ситуации вы не очистите ES, то программа
управления сегментами Windows войдет в бесконечный цикл,
сбрасывая и загружая три сбрасываемых кодовых сегмента, в
случае надостатка памяти. Во время этого процесса ES будет
помещаться и выниматься из стека, что будет приводить к
безсмысленной перезагрузке сегмента CODESEG1, когда имеется
место только для двух других кодовых сегментов.
14.7 Заключение.
В данной главе обсуждалось, как писать прикладные
программы для Windows используя С и язык ассемблера. Также
описывается, как выбирать модель памяти, использовать в
программе константу NULL и как получить в программе аргументы
командной строки DOS и значения переменных среды DOS. Кроме
этого, в данной главе описано, как писать экспортируемые
функции, и приведены специальные правила, которые помогают
писать программы с использованием программ из библиотек
исполняющей системы С и программ на ассемблере.
Дополнительную информацию относительно создания прикладных
программ Windows на С и ассемблере вы найдете в:
.
Windows 3.0/pg/2#3 = 171 =
Раздел Руководство
---------------------------------------------------------------
Упраление памятью Руководство программиста, глава 15, "Управ-
ление памятью", и глава 16, "Еще об управ-
лении памятью".
Создание динамически Руководство программиста, глава 20, "Дина-
подключаемых библио- мически подключаемые библиотеки".
тек
Макрокоманды ассемб- Справочное руководство, том 2, глава 14,
лера для Windows "Список макрокоманд ассемблера", и глава
13, "Обзор макрокоманд ассемблера".
Компиляция и компо- "Tools", глава 1, "Компиляция прикладных
новка программ программ: компилятор С", и глава 2, "Ком-
поновка прикладных программ: компоновщик".
.
Windows 3.0/pg/2#3 = 172 =
Глава 15. Управление памятью.
----------------------------------------------------------------
Все прикладные программы для работы должны использовать
память. Поскольку Microsoft Windows - многозадачная среда, то в
ней несколько прикладных программ могут одновременно
использовать память. Windows управляет использованием памяти
для того, чтобы дать возможность всем прикладным программам
использовать ее, а также для того, чтобы сделать это
использование наиболее эффективным.
В данной главе дается короткое введение в систему
управления памятью Windows.
Приводится следующая информация:
- Использование памяти в среде Windows.
- Эффективное использование сегментов с кодом и данными.
Кроме этого, в данной главе приводится описание простой
программы MEMORY, которая иллюстрирует использование памяти
Windows.
15.1 Использование памяти.
Система управления памятью Windows позволяет отводить
блоки памяти для их использования в прикладной программе. Блоки
памяти можно отводить из глобальной или локальной динамической
области памяти. Глобальная динамическая область памяти - это
пул свободной памяти, доступный для всех прикладных программ.
Локальная динамическая область памяти - это пул свободной
памяти, доступный только данной программе. Windows также
управляет сегментами кода и сегментами данных вашей программы.
В некоторых системах управления памятью отведенная память
остается фиксированной в определенном месте до тех пор, пока не
будет освобождена. В Windows отводимая память может быть
перемещаемой, сбрасываемой, а также фиксированной. Блок
перемещаемой памяти не имеет фиксированного адреса; Windows
может переместить его в любой момент по новому адресу. Блоки
перемещаемой памяти дают возможность Windows лучше использовать
свободную память. Например, если перемещаемый блок памяти
разделяет два свободных блока памяти, Windows может передвинуть
его на новое место, чтобы объединить два свободных блока в один
непрерывный. Блок сбрасываемой памяти аналогичен перемещаемой
памяти в том, что Windows может переместить его. Кроме того,
его можно повторно выделить с нулевой длиной, если необходимо
место для удовлетворения запроса на отведение памяти. Повторное
выделение блока памяти с нулевой длиной разрушает данные,
которые содержит блок, но прикладная программа всегда имеет
возможность повторно загрузить сброшенные данные при
необходимости.
.
Windows 3.0/pg/2#3 = 173 =
При отведении блока памяти выдается дескриптор этого
блока, а не указатель. Дескриптор памяти идентифицирует
отведенный блок. Он используется для получения текущего адреса
блока, когда необходимо иметь доступ к памяти.
Для доступа к блоку памяти необходимо заблокировать
дескриптор памяти. При этом блок памяти временно фиксируется и
возвращается указатель на его начало. Пока дескриптор памяти
заблокирован, Windows не может переместить или сбросить блок.
Следовательно, после окончания использования блока необходимо
сразу же разблокировать дескриптор. Сохранение дескриптора
памяти заблокированным приводит к потере эффективности работы
системы управления памятью и может вызвать сбой при последующих
запросах на выделение памяти.
Windows позволяет более компактно использовать память.
Объединяя свободные участки памяти между отведенными блоками
Windows формирует непрерывный блок свободной памяти, из
которого могут быть отведены дополнительные блоки памяти. Это
объединение является результатом перемещения и (если
необходимо) сброса блоков памяти. Windows также позволяет
сбросить отдельные блоки памяти, если временно в них нет нужды.
15.1.1 Использование глобальной динамической области памяти.
Глобальная динамическая область памяти содержит всю память
среды. При запуске Windows отводит память, необходимую для
программ и данных, из этой области. Остающаяся свободная память
в глобальной динамической области памяти доступна прикладным
программам и библиотекам Windows.
Прикладные программы обычно используют глобальную
динамическую область памяти для выделения больших блоков
(больше килобайта). Хотя в глобальной области памяти можно
выделять больше памяти, однако есть одно неудобство: поскольку
проще работать с данными в локальной динамической области
памяти, чем с глобальной памятью, то программе удобнее
пользоваться локальными данными.
Из глобальной динамической области памяти можно получить
блок памяти любого размера. Программа обычно отводит память
большими блоками. Эти блоки могут превышать 64 К, если
прикладной программе необходим большой непрерывный объем
памяти. Windows предоставляет специальные средства для доступа
к данным далее 64K. Их использование описано в главе 16, "Еще
об управлении памятью".
Можно отвести блок глобальной памяти, используя функцию
GlobalAlloc. Необходимо указать размер и тип памяти
(фиксированная, перемещаемая или сбрасываемая) и функция
GlobalAlloc возвратит дескриптор блока. Перед использованием
блока памяти необходимо заблокировать его с помощью функции
GlobalLock, которая возвратит полный 32-битовый адрес первого
.
Windows 3.0/pg/2#3 = 174 =
байта блока памяти. Затем можно использовать этот длинный
указатель для доступа к блоку.
В приведенном примере функция GlobalAlloc отводит 4096
байт перемещаемой памяти, а функция GlobalLock блокирует ее,
так что первые 256 байт могут быть установлены в значение 0xFF:
HANDLE hMem;
LPSTR lpMem;
int i;
if ((hMem = GlobalAlloc(GMEM_MOVEABLE, 4096)) != NULL){
if ((lpMem = GlobalLock(hMem)) != (LPSTR) NULL) {
for (i = 0; i < 256; i++)
lpMem[i] = 0xFF;
GlobalUnlock(hMem);
}
}
В данном примере программа разблокирует дескриптор памяти
сразу же после ее использования при помощи функции
GlobalUnlock. При блокировании перемещаемой или сбрасываемой
памяти Windows гарантирует, что блок остается фиксированным в
памяти до разблокирования. Это означает, что пока память
заблокирована, адрес остается корректным. Следует учесть, что
при таком способе налагаются ограничения на лучшее
использование памяти в случае других запросов на ее выделение.
Функция GlobalAlloc возвращает NULL, если запрос на память
не выполнен. Необходимо всегда контролировать возвращаемое
значение для уверенности в том что корректный дескриптор
существует. При необходимости можно проверить, сколько памяти
осталось в глобальной динамической области, используя функцию
GlobalCompact. Эта функция возвращает число байт в наибольшем
непрерывном свободном блоке памяти.
Необходимо также проверить адрес, возвращаемый функцией
GlobalLock. Эта функция возвращает нулевой указатель, если
дескриптор памяти некорректен, или содержимое блока памяти
сброшено.
Можно освободить ненужную больше глобальную память,
использовав функцию GlobalFree. Вообще говоря, необходимо
освобождать память сразу же, как только она становится больше
не нужна, с тем чтобы другие прикладные программы могли ее
использовать. Вы должны всегда освобождать глобальную память
перед завершением выполнения программы.
15.1.2 Использование локальной динамической области памяти.
Локальная динамическая область содержит свободную память,
которая может быть отведена прикладной программой для
собственных нужд. Локальная динамическая область находится в
.
Windows 3.0/pg/2#3 = 175 =
сегменте данных прикладной программы и, следовательно, доступна
только определенному экземпляру прикладной программы. Из
локальной динамической области можно получать память блоками до
64 К (память может быть по запросу фиксированной, перемещаемой
или сбрасываемой).
Прикладная программа не получает локальной динамической
области автоматически. Ее необходимо определить для данной
программы, используя оператор HEAPSIZE в файле определения
модуля. Этот оператор устанавливает первоначальный размер в
байтах локальной динамической области. (Описание операторов
файла определения модуля вы найдете в Справочном руководстве,
том 2). Если эта область расположена в фиксированном сегменте
данных, можно отводить память не свыше заданного в операторе
размера. Если эта область расположена в перемещаемом сегменте
данных, можно отводить память сверх заявленного размера, но до
64 К (поскольку Windows автоматически отводит локальной
динамической области память до тех пор, пока сегмент данных не
достигнет 64 К). Однако, надо помнить, что в этом случае
Windows может переместить сегмент данных, и длинные указатели
на локальную память могут стать неправильными.
Максимальный размер локальной динамической области памяти
зависит от размера стека программы, а также от размера
статических и глобальных данных. Локальная динамическая область
разделяет сегмент данных со стеком и указанными данными, но
поскольку сегмент данных не может быть больше, чем 64 К,
локальная динамическая область может быть не больше, чем 64 К
минус размер стека и размер глобальных и статических данных.
Размер стека прикладной программы определяется оператором
STACKSIZE, задаваемым в файле определения модуля. (Описание
операторов файла определения модуля вы найдете в Справочном
руководстве, том 2). Размер глобальных и статических данных
зависит от того, сколько в программе объявлено строк, а также
глобальных и статических переменных. Windows отводит под стек
не меньше 5К. Если в файле определения модуля указана меньшая
величина, Windows все равно укажет 5К.
Локальную память можно отвести с помощью функции
LocalAlloc. Функция отводит блок памяти в локальной
динамической области и возвращает дескриптор памяти. Локальный
блок памяти блокируется с помощью функции LocalLock. Она
возвращает 16-битовое смещение на первый байт в блоке памяти.
Смещение берется относительно начала сегмента данных. В данном
примере функция LocalAlloc отводит 256 байт перемещаемой
памяти, а функция LocalLock блокирует его, так что первые 256
байт могут быть установлены в значение 0xFF:
HANDLE hMem;
PSTR pMem;
int i;
if ((hMem = LocalAlloc(LMEM_MOVEABLE, 256)) != NULL) {
if (pMem = LocalLock(hMem)) != NULL) {
.
Windows 3.0/pg/2#3 = 176 =
for ( i = 0; i < 256; i++)
pMem[1] = 0xFF;
LocalUnlock(hMem);
}
}
В этом примере прикладная программа разблокирует
дескриптор памяти, используя функцию LocalUnlock, сразу же
после доступа к блоку памяти. При блокировании перемещаемой или
сбрасываемой памяти Windows гарантирует, что блок остается
фиксированным в памяти до разблокирования. Это означает, что
пока память заблокирована, адрес остается корректным. Следует
учесть, что при таком способе налагаются ограничения на лучшее
использование памяти в случае других запросов на ее выделение.
Если необходимо быть уверенным, что локальная динамическая
область памяти будет эффективно использоваться, не нужно
забывать разблокировать память после ее использования.
Функция LocalAlloc возвращает NULL, если запрос на память
не выполнен. Необходимо всегда контролировать возвращаемое
значение для уверенности в том, что корректный дескриптор
существует. При необходимости можно проверить, сколько памяти
осталось в локальной динамической области, используя функцию
LocalCompact. Эта функция возвращает число байт в наибольшем
непрерывном свободном блоке памяти.
Необходимо также проверить адрес, возвращаемый функцией
LocalLock. Эта функция возвращает нулевой указатель, если
дескриптор памяти недопустим или содержимое блока памяти
сброшено.
15.1.3 Работа со сбрасываемой памятью.
Блок сбрасываемой памяти создается с одновременным
использованием двух параметров - GMEM_DISCARDABLE и
GMEM_MOVEABLE. Этот блок будет перемещен в случае необходимости
для освобождения места под другие запросы памяти или, если нет
достаточного объема памяти для удовлетворения запроса, блок
памяти будет сброшен. В приведенном ниже примере происходит
выделение сбрасываемого блока из глобальной памяти:
hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DISCARDABLE, 4096L);
Windows сбрасывает блок памяти посредством повторного
выделения его с новым размером в нуль байт. Содержимое блока
теряется, но его дескриптор остается корректным. Однако, любая
попытка заблокировать дескриптор и получить доступ к блоку
будет неудачной.
Windows определяет, какой из блоков памяти сбросить, с
помощью алгоритма "последний наименее используемый" (LRU).
Блоки памяти будут сбрасываться до тех пор, пока не будет
достаточно места для удовлетворения запроса на выделение
.
Windows 3.0/pg/2#3 = 177 =
памяти. В общем, если в течение некоторого времени к
сбрасываемому блоку памяти не было доступа, он является
кандидатом на сбрасывание. Заблокированный блок не может быть
сброшен.
Можно сбрасывать свои собственные блоки памяти,
используя функцию GlobalDiscard. Эта функция освобождает блок,
но сохраняет дескриптор памяти. Можно также сбросить блоки
памяти других прикладных программ, используя функцию
GlobalCompact. Эта функция перемещает и сбрасывает блоки памяти
до тех пор, пока не будет доступен указанный или максимально
возможный объем памяти. Один из способов сбросить все
сбрасываемые блоки памяти - задать в качестве аргумента -1. Это
запрос на всю память. Хотя запрос не будет удовлетворен, он
освободит всю сбрасываемую память и сформирует максимально
возможный блок свободной памяти.
Поскольку дескриптор сбрасываемого блока памяти остается
корректным, можно получить информацию о блоке, используя
функцию GlobalFlags. Это полезно тогда, когда надо проверить,
что блок действительно был сброшен. Функция GlobalFlags
устанавливает в возвращаемом значении бит GMEM_DISCARDED, если
указанный блок памяти сброшен. Следовательно, если была сделана
попытка заблокировать сбрасываемый блок и она закончилась
неудачно, можно проконтролировать состояние блока, используя
GlobalFlags.
Как только сбрасываемый блок был сброшен, его содержимое
теряется. Если необходимо использовать блок снова, нужно
повторно отвести для него место соответствующего размера и
заполнить данными, которые он содержал прежде. Повторное
выделение памяти осуществляется с помощью функции
GlobalReAlloc. В данном примере контролируется состояние блока
и затем, если он был сброшен, заполняется данными:
lpMem = GlobalLock(hMem);
if (lpMem = (LPSTR) NULL) {
if (GlobalFlags(hMem) & GMEM_DISCARDED) {
hMem = GlobalReAlloc(hMem, 4096L,
GMEM_MOVEABLE | GMEM_DISCARDABLE);
lpMem = GlobalLock(hMem);
/* заполнение данными */
GlobalUnlock(hMem);
}
}
Можно сделать сбрасываемый объект несбрасываемым и
наоборот, используя функцию GlobalReAlloc и флаг GMEM_MODIFY,
как показано в приведенном ниже примере, в котором перемещаемый
блок, идентифицированный параметром hMem, изменяется на
сбрасываемый блок.
.
Windows 3.0/pg/2#3 = 178 =
hMem = GlobalReAlloc(hMem, 4096L,
GMEM_MODIFY | GMEM_MOVEABLE);
В следующем примере сбрасываемый блок становится
несбрасываемым:
hMem = GlobalReAlloc(hMem, 0, GMEM_MODIFY);
15.2 Использование сегментов.
Одним из главных свойств Windows является то, что она дает
возможность пользователю одновременно работать более, чем с
одной прикладной программой. Это накладывает дополнительные
требования на написание программ, работающих в этой среде. Хотя
многие компьютеры имеют по крайней мере 640 К памяти, эта
память становится ограниченным ресурсом, как только
пользователь загрузит для работы несколько прикладных программ.
Необходимо сознательно использовать память и стараться
минимизировать объем, занимаемый прикладной программой в любой
заданный момент.
Для того, чтобы помочь управлять использованием памяти в
прикладной программе, Windows применяет для кодовых сегментов и
сегментов данных ту же систему управления памятью, что
использована в программе для выделения блоков глобальной памити
и манипулирования ими. При запуске прикладной программы Windows
отводит место под кодовые сегменты и сегменты данных в
глобальной памяти, а затем копирует сегменты из выполняемого
файла в память. Эти сегменты могут быть фиксированными,
перемещаемыми и даже сбрасываемыми. Необходимо определить их
атрибуты в файле определения модуля.
Можно уменьшить влияние прикладных программ на память,
используя перемещаемые кодовые сегменты и сегменты данных.
Использование перемещаемых сегментов позволяет Windows по
крайней мере, перемещать сегменты при необходимости для того,
чтобы воспользоваться свободной памятью, когда она становится
доступной.
Можно минимизировать влияние прикладных программ на
память, используя сбрасываемые кодовые сегменты. Если кодовый
сегмент - сбрасываемый, то Windows, при необходимости,
сбрасывает его для удовлетворения запросов на глобальную
память. В отличие от обычных блоков памяти, которые может
выделять сам пользователь, сбрасываемые кодовые сегменты
управляются Windows которая автоматически перезагружает их,
если прикладная программа попытается выполнить код, находящийся
внутри этого сегмента. Это означает, что кодовые сегменты
прикладной программы находятся в памяти только тогда, когда они
необходимы.
Сбрасывание сегмента разрушает его содержимое. Windows не
.
Windows 3.0/pg/2#3 = 179 =
сохраняет текущее содержимое сбрасываемого сегмента. Вместо
этого предполагается, что сегмент не отличается от того,
который был первоначально загружен, и, при необходимости, он
будет загружаться непосредственно из выполняемого файла.
15.2.1 Использование кодовых сегментов.
Кодовый сегмент - это некоторое количество байт машинных
команд. Кодовый сегмент не может быть больше 64 К.
Важное замечание: Нельзя хранить записываемые данные в
кодовом сегменте. При попытке записи данных в кодовый сегмент
возникает неустранимая ошибка защиты памяти при работе Windows
в защищенном режиме. Однако, Windows позволяет хранить в
кодовом сегменте данные, предназначенные только для чтения, это
может быть например таблица переходов. Дополнительную
информацию о работе Windows в защищенном режиме вы найдете в
главе 16, "Еще об управлении памятью".
Каждая ПП имеет, по крайней мере, один кодовый сегмент.
Например, описанные в предыдущих разделах примеры программ
имеют только один кодовый сегмент. Также можно создавать
прикладные программы, которые имеют несколько кодовых
сегментов. В самом деле, большинство прикладных программ,
работающих в среде Windows, имеют несколько кодовых сегментов.
Сегментирование позволяет уменьшить размер любого заданного
кодового сегмента ровно до такого числа инструкций, которые
необходимы для выполнения определенной задачи. Если при этом
сегменты могут быть сбрасываемыми, это значительно снизит
требования к памяти.
При использовании средних или больших моделей памяти
создаются прикладные программы, использующие несколько кодовых
сегментов. Обычно они имеют один или несколько исходных файлов
для каждого сегмента. Необходимо отдельно скомпилировать каждый
исходный файл и явно задать имя сегмента, которому будет
принадлежать скомпилированный код. Затем необходимо
скомпоновать прикладную программу, поименовав сегменты и
определив их атрибуты в файле определения модуля.
Для определения атрибутов сегмента используйте оператор
SEGMENTS в файле определения модуля. В приведенном ниже примере
показаны определения трех сегментов:
SEGMENTS
PAINT_TEXT MOVEABLE DISCARDABLE
INIT_TEXT MOVEABLE DISCARDABLE
WNDPROC_TEXT MOVEABLE DISCARDABLE
Можно также использовать оператор CODE в файле определения
модуля для определения атрибутов всех кодовых сегментов по
умолчанию. Оператор CODE определяет также атрибуты сегментов,
которые не были явно указаны в операторе SEGMENTS. В
.
Windows 3.0/pg/2#3 = 180 =
приведенном ниже примере показано, как сделать все не
определенные в операторе SEGMENTS сегменты сбрасываемыми:
CODE MOVEABLE DISCARDABLE
При использовании в прикладной программе сбрасываемых
кодовых сегментов необходимо привести в соответствие свойство
сбрасываемости с частотой доступа к сегменту. Например, нельзя,
видимо, делать сбрасываемым сегмент, содержащий основную
функцию окна, поскольку она часто вызывается Windows. Т. к.
сбрасываемый сегмент должен, при необходимости, загружаться с
диска, экономия памяти от сбрасываемых сегментов может
перевешиваться потерями от многочисленных загрузок с диска.
Примечание: В библиотеке все кодовые сегменты должны быть
перемещаемыми и сбрасываемыми. Если в файле определения модуля
библиотеки вы укажете, что сегмент является только
перемещаемым, Windows все равно сделает его сбрасываемым.
15.2.2 Сегменты данных.
Каждая прикладная программа имеет сегмент DATA. Сегмент
DATA содержит стек программы, локальную динамическую область
памяти, а также статические и глобальные данные. Как и кодовый
сегмент, сегмент DATA не может быть больше 64 К.
Сегмент DATA может быть только фиксированным или
перемещаемым, но не сбрасываемым. Если сегмент DATA -
перемещаемый, Windows автоматически блокирует его, когда
передает управление прикладной программе. В противном случае
перемещаемый сегмент DATA может переместиться, если прикладная
программа отвела глобальную память или пытается выделить больше
памяти, чем имеется в локальной динамической области. По этой
причине важно не сохранять длинные указатели на переменные,
находящиеся в сегменте DATA.
Можно определить атрибуты сегмента DATA, используя
оператор DATA в файле определения модуля. Атрибуты по умолчанию
- перемещаемый и множимый. Атрибут множимости побуждает
Windows создавать копию сегмента данных для каждого экземпляра
прикладной программы. Это означает, что содержимое сегмента
DATA уникально для каждого экземпляра программы.
Прикладные программы большой модели памяти могут иметь
несколько дополнительных сегментов данных, но только один
сегмент DATA.
Описание файла определения модуля вы найдете в Справочном
руководстве, том 2.
15.3 Пример прикладной программы Memory.
Этот пример прикладной программы иллюстрирует, как создать
.
Windows 3.0/pg/2#3 = 181 =
прикладную программу Windows, использующую среднюю модель
памяти, и которая использует сбрасываемые кодовые сегменты. Для
создания программы Memory скопируйте и переименуйте исходные
файлы программы Generic, а затем внесите следующие изменения:
1. Разделите исходный С-файл на черыре отдельных файла.
2. Модифицируйте включаемый файл.
3. Добавьте определения новых сегментов к файлу
определения модуля.
4. Модифицируйте файл make.
5. Оттранслируйте и скомпонуйте программу.
Ниже эти шаги описаны более подробно.
Примечание: Вместо того, чтобы вводить тексты, приведенные
в следующих разделах, возможно вам будет удобнее просто
переписать исходные тексты из SDK.
15.3.1 Разделение исходного С-файла.
Необходимо разделить исходный С-файл на отдельные файлы
т.о., чтобы функции внутри файла компилировались как отдельные
сегменты. Можно разделить исходный файл этой программы на 4
части:
.
Windows 3.0/pg/2#3 = 182 =
Исходный файл Содержимое
---------------------------------------------------------------
memory1.c Содержит функцию WinMain. Поскольку Windows
выполняет эту функцию довольно часто,
сегмент, созданный для данного исходного
файла, не сбрасываемый, и поэтому не может
возникнуть ситуация, когда сегмент часто
загружается с диска. Поскольку WinMain
относительно невелика, хранение ее в памяти
существенно не влияет на доступный объем
глобальной памяти.
memory2.c
Содержит функцию MemoryInit. Поскольку эта
функция используется только при запуске
прикладной программы, сегмент, созданный из
этого исходного файла, может быть
сбрасываемым.
memory3.c Содержит функцию MemoryWndProc. Хотя
сегмент, созданный для этого исходного
файла, может быть сбрасываемым, функция
MemoryWndProc, вероятно, будет вызываться по
крайней мере так же часто, как функция
WinMain. В этом случае сегмент -
перемещаемый, а не сбрасываемый.
memory4.c Содержит функцию About. Поскольку эта
функция вызывается редко (только при
индикации панели диалога About), кодовый
сегмент, созданный для этого исходного
файла, может быть сбрасываемым.
---------------------------------------------------------------
В каждый исходный файл необходимо включить файлы windows.h
и memory.h.
15.3.2 Модификация включаемого файла.
Необходимо переместить объявление переменной hInst в файл
memory.h. Это даст уверенность в том, что переменная будет
доступна во всех сегментах. Переменная hInst используется в
функциях WinMain и MemoryWndProc.
15.3.3 Добавление новых определений сегментов.
Необходимо добавить определения сегментов в файл описания
модуля для определения атрибутов каждого кодового сегмента. Это
означает, что необходимо добавить оператор SEGMENTS в файл и
указать каждый сегмент по имени в прикладной программе. После
изменений файл определения модуля будет выглядеть так:
.
Windows 3.0/pg/2#3 = 183 =
NAME Memory
DESCRIPTION 'Пример прикладной программы'
STUB 'WINSTUB.EXE'
(1) SEGMENTS
MEMORY_MAIN PRELOAD MOVEABLE
MEMORY_INIT LOADONCALL MOVEABLE DISCARDABLE
MEMORY_WNDPROC LOADONCALL MOVEABLE
MEMORY_ABOUT LOADONCALL MOVEABLE DISCARDABLE
(2) CODE MOVEABLE
DATA MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8182
EXPORTS
MemoryWndProc @1
About @2
В этом файле определения модуля:
1) Оператор SEGMENTS определяет атрибуты каждого сегмента:
- Сегмент MEMORY_MAIN содержит WinMain.
- Сегмент MEMORY_INIT содержит функцию MemoryInit.
- Сегмент MEMORY_WNDPROC содержит функцию окна.
- Сегмент MEMORY_ABOUT содержит функцию диалога.
Каждый сегмент имеет атрибут MOVEABLE, а сегменты
MEMORY_INIT и MEMORY_ABOUT имеют атрибут DISCARDABLE.
При запуске программы загружается только сегмент
MEMORY_MAIN. Другие сегменты имеют атрибут LOADONCALL,
означающий, что они загружаются по необходимости.
2) Хотя каждый сегмент явно определен, все же задается
оператор CODE. Этот оператор определяет атрибуты любых
дополнительных сегментов, которые компоновщик может
добавить к прикладной программе: например, сегменты,
содержащие функции исполняющей системы С, вызываемые в
исходных файлах прикладной программы.
15.3.4 Модификация файла make.
Необходимо модифицировать файл make для отдельной
.
Windows 3.0/pg/2#3 = 184 =
трансляции новых исходных С-программ. Поскольку эта программа
соответствует средней модели, необходимо использовать ключ -AM
при трансляции. Для ясности необходимо также именовать каждый
сегмент, используя ключ -NT при трансляции.
Необходимо также изменить команду LINK для использования
библиотеки средней модели памяти MLIBCEW.LIB вместо библиотеки
малой модели памяти SLIBCEW.LIB.
После изменений файл make должен выглядеть так:
memory.res: memory.rc memory.h
rc -r memory.rc
memory1.obj: memory1.c memory.h
cl -c -AM -Gsw -Zp -NT MEMORY_MAIN memory1.c
memory2.obj: memory2.c memory.h
cl -c -AM -Gsw -Zp -NT MEMORY_INIT memory2.c
memory3.obj: memory3.c memory.h
cl -c -AM -Gsw -Zp -NT MEMORY_WNDPROC memory3.c
memory4.obj: memory4.c memory.h
cl -c -AM -Gsw -Zp -NT MEMORY_ABOUT memory4.c
memory.exe: memory1.obj memory2.obj memory3.obj
memory4.obj memory.def
link4 memory1 memory2 memory3 memory4,memory.exe,,
mlibcew libw,memory.def
rc memory.res
memory.exe: memory.res
rc memory.res
15.3.5 Трансляция и компоновка.
После трансляции и компоновки программы Memory запустите
Windows, программу Heapwalker (входит в состав SDK) и программу
Memory. Используйте Heapwalker для просмотра различных
сегментов прикладной программы Memory.
15.4 Заключение.
В данной главе описано, как выделять и использовать память
в среде Windows. Поскольку Windows - это многозадачная среда,
необходимо, чтобы ваша программа корректно использовала память
совместно с другими программами.
Кроме этого, в данной главе описано, как использовать в
программе сегменты данных и кода.
.
Windows 3.0/pg/2#3 = 185 =
Дополнительную информацию относительно управления памятью
вы найдете в:
Раздел Руководство
---------------------------------------------------------------
Управление памятью Руководство программиста, глава 16, "Еще об
управлении памятью".
Функции управления Справочное руководство, том 1, глава 4,
памятью "Список функций".
Операторы файла оп- Справочное руководство, том 2: глава 10,
ределения модуля "Операторы файла определения модуля".
.
|