C++ Builder
Russian LinkExchange Banner Network
Реклама - двигатель сами понимаете чего...
Delphi  |  JBuilder
::Главная ->Литература ->Руководство по программированию в Windows

Содержание
::Новости
::F.A.Q.
::Форум
::Компоненты
::Исходники
::Литература
::Рассылка
::Ссылки

Клуб
::Клуб программистов
::Члены клуба
::off-форум
::off-чат

Работа
::Есть программисты
::Есть вакансии
::Программы на заказ
::Готовые программы

Другое
::О сайте
::Голосование
::Модератору

	
	                                   Оглавление                                  
       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,
        ределения модуля     "Операторы файла определения модуля".
.


Delphi  |  JBuilder
::Главная ->Литература ->Руководство по программированию в Windows
C++ Builder
Реклама - двигатель сами понимаете чего...
Russian LinkExchange Banner Network
(c) 2000 by AlmigoR
Сайт управляется системой uCoz