Глава 18. Более подробно о ресурсах

             Программы Windows очень легко использовать, т.к. они предос-
        тавляют пользователю стандартный интерфейс. Например, большинство
        программ Windows используют меню для реализации команд программы,
        и курсор,  который позволяет применять "мышь" в качестве управле-
        ния такими инструментальными  средствами,  как  указатель  "мыши"
        стрелка или кисть для рисования.

             Меню и курсоры - это два примера ресурсов программы Windows.
        Ресурсы это данные,  хранимые в выполняемом (.EXE) файле програм-
        мы,  но они располагаются отдельно от  обычного  сегмента  данных
        программы.  Ресурсы  разрабатываются  и  специфицируются вне кода
        программы,  затем добавляются к  скомпилированному коду программы
        для создания выполняемого файла программы.

             Следующие ресурсы  вы будете создавать и использовать наибо-
        лее часто:

             - Меню.
             - Блоки диалога.
             - Пиктограммы.
             - Курсоры.
             - Оперативные клавиши.
             - Графические изображения.
             - Строки символов.

             Обычно при загрузке приложения в  память  Windows  оставляет
        ресурсы на диске и загружает в случае необходимости отдельные ре-
        сурсы в процессе выполнения программы.  За исключением  побитовых
        отображений (графических образов),  Windows удаляет ресурс из па-
        мяти после окончания его использования. Если вы хотите  загрузить
        ресурс  при загрузке программы или не хотите, чтобы Windows имела
        возможность удалить ресурс из памяти, вы можете сменить его атри-
        буты.  Детальное  описание создания и модификации ресурсов содер-
        жится в руководстве пользователя по пакету разработчика ресурсов.

                                  Создание ресурсов
        -----------------------------------------------------------------

             Вы можете создавать ресурсы, используя редактор ресурсов или
        компилятор ресурсов. Паскаль поддерживает оба эти метода, поэтому
        вы сами можете выбрать,  какой их них является более  удобным.  В
        большинстве случаев проще использовать редактор ресурсов и созда-
        вать ваши ресурсы визуально.  Однако, иногда более удобно исполь-
        зовать компилятор ресурсов для компиляции файла описания ресурса,
        который может вам встретиться в книге или в  журнале.  Независимо
        от выбранного вами подхода вы обычно создаете файл ресурса (.RES)
        для каждого приложения.  Этот файл ресурса будет содержать двоич-
        ную информацию о всех меню,  диалогах, графических образах и дру-
        гих ресурсах, используемых вашим приложением.

             Двоичный файл  ресурса (.RES) добавляется к вашему исполняе-
        мому файлу (.EXE) в процессе компиляции с использованием директи-
        вы компилятора $R, как это описано в данной главе. Вы также долж-
        ны написать код,  который будет загружать ресурсы в  память.  Это
        придаст вам дополнительную гибкость, поскольку ваша программа бу-
        дет использовать память лишь для ресурсов, которые используются в
        данный момент. Загрузка ресурсов в память также рассматривается в
        данной главе.

                     Добавление ресурсов к выполняемой программе
        -----------------------------------------------------------------

             Ресурсы хранятся  в  двоичном формате в файле .RES,  поэтому
        они должны быть добавлены к выполняемому файлу приложения (.EXE).
        Результатом будет файл,  который наряду со скомпилированным кодом
        приложения будет содержать и его ресурсы.

             Есть три способа добавления ресурсов к выполняемому файлу:

             - Можно  использовать  редактор ресурсов для копирования ре-
               сурсов из файла .RES в уже скомпилированный файл программы
               .EXE. Инструкции по этой операции содержатся в руководстве
               пользователя по пакету разработчика ресурсов.

             - Можно задать директиву в исходном  коде  файла.  Например,
               эта программа на языке Паскаль:

                program SampleProgram;
                {$r SAMPLE.RES}
                  .
                  .
                  .

               добавит файл ресурсов  SAMPLE.RES  к  выполняемому  файлу.
               Каждая программа  на языке Паскаль может иметь только один
               файл ресурсов (хотя этот файл ресурсов может включать дру-
               гие файлы  ресурсов).  Все  эти  файлы должны быть файлами
               .RES и хранить ресурсы в двоичном формате.  Директива ком-
               пилятора $R позволяет вам задать отдельный файл .RES.

             - Использовать компилятор ресурсов.

                            Загрузка ресурсов в приложение
        -----------------------------------------------------------------

             После добавления  ресурса  к  выполняемому файлу,  он должен
        быть явно загружен приложением до своего использования.  Конкрет-
        ный способ загрузки ресурса зависит от типа ресурса.

                                    Загрузка меню
        -----------------------------------------------------------------

             Меню окна является одним из атрибутов его создания.  Другими
        словами  это  характеристика окна,  которая должна быть задана до
        создания  соответствующего  элемента  меню  (с   помощью   метода
        Create). Следовательно, меню может быть задано в типе конструкто-
        ра Init или вскоре после конструирования. Ресурсы меню загружают-
        ся вызовом функции Windows LoadMenu со строкой идентификатора ме-
        ню при конструировании нового объекта окна. Например:

             constructor SampleMainWindow.Init(AParent: PWindowsObject;
                                               ATitle: PChar);
             begin
               TWindow.Init(AParent, ATitle);
               Attr.Menu:=LoadMenu(HInstance, PChar(100));
               .
               .
             end;

             Код PChar(100) переводит целое значение  100  в  тип  PChar,
        совместимый с Windows тип строки.  LoadMenu загружает ресурс меню
        с идентификатором 100 в новый объект  окна.  Ресурс  может  иметь
        символьное имя (строку),  например,  'SampleMenu',  а не числовой
        идентификатор.  В этом случае предыдущий код будет выглядеть сле-
        дующим образом:

             constructor SampleMainWindow.Init(AParent: PWindowsObject;
                                               ATitle: PChar);
             begin
               TWindow.Init(AParent, ATitle);
               Attr.Menu:=LoadMenu(HInstance, 'SampleMenu');
               .
               .
             end;

             Дополнительная информация  по  созданию объектов окна содер-
        жится в Главе 10, "Объекты окна".

             Для обработки выбора варианта меню просто определяется метод
        для окна, которое владеет этим меню, используя специальное расши-
        рение заголовка определения метода идентификатором cm_First:

             procedure HandleMenu101(var Msg: TMessage);
                                                 virtual cm_First+101;

             Обратите внимание на то,  что 101 представляет собой выбран-
        ного варианта меню (а не самого ресурса меню).  Каждый пункт меню
        имеет уникальный целочисленный идентификатор. В этом методе зада-
        ча состоит в организации соответствующей реакции на выбор  пункта
        меню. Обычно вы будете определять символьные константы для команд
        вашего меню.

                             Загрузка оперативных клавиш
        -----------------------------------------------------------------

             Оперативные клавиши  -  это  активные клавиши или комбинации
        клавиш, которые используются для задания команд приложения. Обыч-
        но  оперативные клавиши определяются как эквиваленты выбора пунк-
        тов меню.  Например,  клавиша Del - это  стандартная  оперативная
        клавиша, которую можно использовать как альтернативу выбора пунк-
        та Delete в меню Edit. Однако, оперативные клавиши могут реализо-
        вывать команды, которые не соответствуют элементам меню.

             Ресурсы оперативных клавиш хранятся  в  таблице  оперативных
        клавиш.  Для  загрузки  таблицы  оперативных  клавиш используется
        функция Windows LoadAccelerators,  которая просто возвращает опи-
        сатель таблицы. В отличие от ресурса меню, который связан с конк-
        ретным окном, ресурс оперативной клавиши принадлежит всему прило-
        жению.  Каждое приложение может иметь только один  такой  ресурс.
        Объекты приложения резервируют одно поле объекта,  HAccTable, для
        хранения описателя ресурса оперативных клавиш.  Обычно вы  будете
        загружать  ресурс  оперативных клавиш в методе объекта приложения
        InitInstance:

             procedure SampleApplication.InitInstance;
             begin
                TApplication.InitInstance;
                HAccTable := LoadAccelerators(HInstance,
                                              'SampleAccelerators');
             end;

             Часто вы  будете определять оперативные клавиши для быстрого
        выбора вариантов меню.  Например,  Shift+Ins обычно  используется
        для быстрого выбора команды Paste. Оперативные клавиши генерируют
        основанные на команде сообщения,  которые  идентичны  сообщениям,
        генерируемым выбором пункта меню.  Для привязки метода реакции на
        выбор в меню с соответствующей оперативной  клавишей  нужно  убе-
        диться  в  том,  что  определенное в ресурсе значение оперативной
        клавиши идентично идентификатору элемента меню.

                               Загрузка блоков диалога
        -----------------------------------------------------------------

             Блоки диалога  являются единственным типом ресурса,  который
        непосредственно соответствует типу объекта ObjectWindows. TDialog
        и  его производные типы,  включая TDlgWindow,  определяют объекты
        интерфейса, которые используют ресурсы блока диалога. Каждый объ-
        ект  блока  диалога обычно связан с одним ресурсом блока диалога,
        который задает его размер, местоположение и ассортимент управляю-
        щих элементов (таких, как командные кнопки и блоки списка).

             При конструировании  блока  диалога  задайте  ресурс объекта
        блока диалога. Как ресурсы меню и командных клавиш, ресурс диало-
        га  может  иметь  символьное имя или целочисленный идентификатор.
        Например:

             Adlg := New(PSampleDialog, Init(@Self, 'AboutBox'));
        или
             Adlg := New(PSampleDialog, Init(@Self, PChar(120)));

             Дополнительная информация по созданию объектов  диалога  со-
        держится в Главе 11, "Объекты диалоговых блоков".

                            Загрузка курсоров и пиктограмм
        -----------------------------------------------------------------
             Каждый тип объекта окна имеет специальные атрибуты, называе-
        мые атрибутами регистрации. Среди этих атрибутов есть курсор окна
        и пиктограмма.  Для установки этих атрибутов  для  типа  окна  вы
        должны определить метод GetWindowClass (как и GetClassName).

             Например, вы  создаете  курсор  для выбора элементов в блоке
        списка. Курсор имеет вид указательного пальца и хранится в ресур-
        се курсора с именем 'Finger'. Кроме того, вы создаете ресурс пик-
        тограммы с именем 'SampleIcon',  который выглядит как улыбающееся
        лицо. Вы должны написать метод GetWindowClass следующим образом:

             procedure SampleWindow.GetWindowClass(var AWndClass:
                                                   TWndClass);
             begin
                TWindow.GetWindowClass(AWndClass);
                AWndClass.hCursor:=LoadCursor(HInstance, 'Finger');
                AWndClass.hIcon:=LoadIcon(HInstance, 'SampleIcon');
             end;

             Однако, между курсором и пиктограммой имеется одно  отличие.
        Оно состоит в том, что курсор задается для одного окна, а пиктог-
        рамма представляет все приложение. Следовательно, пиктограмма ус-
        танавливается в  типе объекта только для основного окна.  У этого
        правила имеется одно исключение:  для приложений, которые следуют
        правилам многодокументального  интерфейса (MDI),  каждое дочернее
        окно MDI имеет свою собственную пиктограмму.
             Для использования одного из уже имеющихся курсоров или  пик-
        тограмм  Windows,  передайте 0 в HInstance и используйте значение
        idc_ (например, idc_IBeam) для курсора и значение idi_ (например,
        idi_Hand) для пиктограммы. Например:

             procedure SampleWindow.GetWindowClass(var AWndClass:
                                                   TWndClass);
             begin
              TWindow.GetWindowClass(AWndClass);
              AWndClass.hCursor := LoadCursor(HInstance, idc_IBeam);
              AWndClass.hIcon := LoadIcon(HInstance, idi_Hand);
             end;

             Дополнительную информацию  по регистрационным атрибутам окна
        можно найти в Главе 10, "Объекты окна".

                             Загрузка строковых ресурсов
        -----------------------------------------------------------------
             Принципиальная причина выделения строк приложения в качестве
        ресурсов состоит в облегчении процесса  настройки  приложения  на
        конкретное  применение  или  возможности  перевода  приложения на
        иностранный язык.  Если строки определены в исходном коде, то для
        их изменения  или  перевода  нужно иметь доступ к исходному коду.
        Если же  они определены как ресурсы,  то хранятся в таблице строк
        выполняемого файла приложения.  Вы можете  использовать  редактор
        строк для перевода строк в таблице,  без изменения и даже доступа
        к исходному коду. Каждый выполняемый файл может иметь только одну
        таблицу строк.

             Для загрузки строки из таблицы в буфер сегмента данных ваше-
        го   приложения   используется   функция   LoadString.  Синтаксис
        LoadString следующий:

             LoadString(HInstance, StringID, @TextItem,SizeOf(TextItem));

             * Параметр  StringID - это номер идентификатора строки (нап-
               ример,  601) в таблице строк.  Это  число  можно  заменить
               константой.

             * Параметр  @TextItem  -  это  указатель  на массив символов
               (PChar), который принимает строку.

             * Параметр SizeOf(TextItem) - это максимальное число  симво-
               лов, передаваемых в @TextItem. Максимальный размер ресурса
               строки 255 символов, поэтому передача буфера из 256 симво-
               лов гарантирует полную передачу строки.

             LoadString возвращает  число скопированных в буфер символов,
        или ноль, если ресурс не существует.

             Вы можете  использовать  ресурс  строки  для вывода текста в
        блоке сообщения. Например, вы можете вывести сообщение об ошибке.
        В  данном  примере  вы определяете строку 'Program unavailable' в
        таблице строк  и  определяете  константу  ids_NoProgrm в качестве
        идентификатора строки.  Для использования этого ресурса строки  в
        блоке сообщения об ошибке,  вы можете написать следующую процеду-
        ру:

             procedure TestDialog.RunErrorBox(ErrorNumber:
                                              Integer); virtual;
             var
               TextItem: array[0255] of Char;
             begin
               LoadString(HInstance, ids_NoPrgrm, @TextItem, 20);
               MessageBox(HWindow, @TextItem, 'Error', mb_OK or
                          mb_IconExclamation);
             end;

             Данный пример загружает отдельную строку в блок сообщения об
        ошибке.  Для  загрузки  списка  строк  в  блок  списка вызывается
        LoadString для загрузки каждой строки, затем вызывается AddString
        для добавления ее в блок списка.

             Другое использование ресурса строк применяется для элементов
        меню,  которые добавляются в меню вашего исходного кода.  В  этом
        случае сначала получается ресурс строки с помощью LoadString. За-
        тем эта строка передается как параметр в  вызовы  функций  Window
        CreateMenu и AppendMenu. Например:

             procedure SampleWindow.Init(AParent: PWindpwsObject; ATitle:
                                         PChar);
             var
              TextItem: array[0255] of Char;
             begin
                TWindow.Init(AParent, ATitle);
                Attr.Menu := LoadMenu(HInstance, PChar(100));
                LoadString(HInstance, 301, @TextItem, 20);
                AppendMenu(Attr.Menu, mf_String ormf_Enabled, 501,
                           @TextItem);
             end;

                           Загрузка графических изображений
        -----------------------------------------------------------------

             Функция Windows  LoadBitmap  загружает  ресурсы  графических
        изображений (битовых отображений). LoadBitmap загружает побитовое
        распределение в память и возвращает его описатель. Например:

             HMyBit:=LoadBitmap(HInstance, PChar(501));

        загружает ресурс побитового отображения с идентификатором  501  и
        записывает его описатель в переменную HMyBit.  После загрузки по-
        битового отображения оно останется в памяти до его явного  удале-
        ния вами. В отличие от других ресурсов, оно остается в памяти да-
        же после закрытия пользователем вашего приложения.

             В Windows имеется ряд заранее определенных графических изоб-
        ражений,  которые  используются как часть графического интерфейса
        Windows.  Ваше приложение может загружать эти изображения (напри-
        мер,  obm_DnArrow,  obm_Close и obm_Zoom). Как и предопределенные
        пиктограммы и курсоры,  предопределенные графические  изображения
        могут  быть загружены,  если в вызове LoadBitmap вместо HInstance
        задать ноль:

             HMyBit:=LoadBitmap(0,  PChar(obm_Close));

             После загрузки графического образа ваше приложение может ис-
        пользовать его разными способами:

             * Для рисования картинки на экране. Например, вы можете заг-
               рузить побитовое распределение в блок информации о  прило-
               жении в качестве заставки.

             * Для создания кисти, которую вы можете использовать для за-
               полнения областей экрана или для создания фона окна.  Соз-
               дав  кисть по графическому образу,  вы можете закрасить ей
               область фона.

             * Для отображения картинок вместо текста  в  элементах  меню
               или  элементах  блока списка.  Например,  вы можете вместо
               слова 'Arrow' (стрелка) в пункте меню поместить  изображе-
               ние стрелки.

             Дополнительная информация относительно использования графики
        с побитовым отображением содержится в Главе 17.

             Если побитовое отображение не  используется,  то  его  нужно
        удалить из памяти.  В противном случае занимаемая им память будет
        недоступна другим приложениям. Даже если вы не удаляете его после
        использования  приложением,  вы обязательно должны удалить его до
        прекращения работы приложения. Графический образ удаляется из па-
        мяти с помощью функции Windows DeleteObject:

             if DeleteObject(HMyBit) then         { успешно };

             После удаления графического изображения его описатель стано-
        вится некорректным, и его нельзя использовать.


               Использование побитовых отображений для создания кистей
        -----------------------------------------------------------------

             Вы можете  использовать графические образы для создания кис-
        тей, которые могут закрашивать области экрана. Область может быть
        закрашена сплошным цветом или в виде заданного образца. Минималь-
        ный размер используемого в кисти графического образа составляет 8
        на 8 элементов изображения.  Если вы применяете большее графичес-
        кое изображение, то в кисти используется только его левый верхний
        угол 8 на 8.  Предположим, что вы хотите заполнить область полос-
        ками, как это показано на Рис. 18.1.

        +-----------------------------------------------------------+-+-+
        |#=#XXXXXXXXXXXXXXXXXBitmap drawing testXXXXXXXXXXXXXXXXXXXX|^|v|
        +-----------------------------------------------------------+-+-|
        | File    Help                                                  |
        +---------------------------------------------------------------|
        |                                                               |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |    #X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X     |
        |                                                               |
        +---------------------------------------------------------------+

             Рис. 18.1. Заполнение области экрана полосками.

             При заполнении области на Рис. 18.1 Windows циклически копи-
        рует кисть. Действительный размер побитового распределения - лишь
        8 на 8 элементов изображения,  но кистью можно закрасить весь эк-
        ран.

             XXXXXX######
             XXXXXX######
             XXXXXX######

             Рис. 18.2. Ресурс графического изображения для создания кис-
        ти по образцу Рис. 18.1.

             Следующий код помещает образец графического образа в кисть:

             procedure SampleWindow.MakeBrush;
             var
               MyLogBrush: TLogBrush;
             begin
               HMyBit := LoadBitmap(HInstance, PChar(502));
               MyLogBrush.lbStyle := bs_Pattern;
               MyLogBrush.lbHatch := HMyBit;
               TheBrush := CreateBrushInderect(@MyLogBrush);
             end;

             Для проверки образца, отобразим его в прямоугольнике:

             procedure MyWindow.Paint(PaintDC: HDC;
                                      var PaintInfo: TPaintStruct);
             begin
               SelectObject(PaintDC, TheBrush);
               Rectangle(PaintDC, 20, 20, 200, 200);
             end;

             После использования кисти вы должны удалить и кисть,  и гра-
        фическое изображение:

             DeleteObject(HMyBit);
             DeleteObject(TheBrush);

                      Отображение графических изображений в меню
        -----------------------------------------------------------------

             Для отображение  графических  образов  в  меню  используется
        функция ModifyMenu.  Она меняет существующий элемент  меню  таким
        образом, что выводит побитовое отображение вместо текста варианта
        меню,  определенного в редакторе меню. Например, этот конструктор
        Init добавляет и модифицирует меню окна:

             type
              MyLong = record
               case Integer of
                0: (TheLong: Longint);
                1: (Lo: Word;
                    Hi: Word);
              end;

             constructor SampleWindow.Init(AParent: PWindowsObject;
                                           ATitle: PChar);
             var
                ALong: MyLong;
             begin
                TWindow.Init(AParent, ATitle);
                Attr.Menu := LoadMenu(HInstance, PChar(100));
                ALong.Lo := LoadBitmap(HInstance, PChar(503));
                ModifyMenu(Attr.Menu, 111, mf_ByCommand or mf_Bitmap, 211,
                           PChar(ALong.TheLong));
                  .
                  .
                  .
             end;

             В приведенном выше коде 111 - это идентификатор команды  ва-
        рианта меню,  который изменяется, а 211 это его новый идентифика-
        тор. Однако, вы можете использовать один и тот же идентификатор в
        обоих случаях.

        +-----------------------------------------------------------+-+-+
        |#=#XXXXXXXXXXXXXXXXXXXXStepsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|^|v|
        +-----------------------------------------------------------+-+-|
        |XXFileXXX  Help                                                |
        +--------------+------------------------------------------------|
        |XXXXNewXXXXXXX|                                                |
        |    pick      |                                                |
        |        me    |                                                |
        |    Save      |                                                |
        |    Save As   |                                                |
        +--------------+                                                |
        |                                                               |
        |                                                               |
        +---------------------------------------------------------------+

             Рис. 18.3. Меню, где в качестве одного из пунктов выбора ис-
        пользовано графическое изображение.
                              Назад | Содержание | Вперед