Глава 3. Двоичные ресурсы

Ресурсы — это двоичные данные добавленные в конец исполнимого файла или библиотеки. Ресурсы могут быть как стандартными, так и определяемыми программистом. Стандартные ресурсы — это пиктограммы, курсоры, растровые изображения, метафайлы, шрифты, таблицы строк, таблицы акселераторов, шаблоны меню, шаблоны диалоговых окон и «информация о версии». Ресурсы, определяемые программистом содержат любую информацию, которую программа должна использовать и не должна изменять.

При разработке приложений на Delphi вам не нужно заботиться о ресурсах, так как Delphi берет их поддержку на себя. Однако полезно уметь манипулировать с ресурсами на этапе разработки программы. В давние времена программисты вынуждены были разрабатывать ресурсы, пользуясь только Блокнотом windows, позже появились специальные программы разработки ресурсов. Результатом работы такой программы обычно является так называемый файл проекта ресурса (resource script), который затем компилируется в двоичный файл с расширением .res. После этого этот двоичный файл должен быть подключен к вашей программе при помощи директивы компилятора {$R} следующим образом :

{$R filename.res}


где filename.res имя подключаемого файла ресурсов. Когда ресурсы подключены, вы можете извлекать их во время работы программа при помощи функций, описываемых ниже.

Вы можете редактировать .res файл при помощи Image Editor, входящего в состав Delphi, однако в этом случае вы будете способны редактировать только графические ресурсы. Чтобы иметь возможность создавать и редактировать все типы ресурсов, вы должны обзавестись специализированным редактором ресурсов, например Resource Workshop фирмы Borland (вы можете скачать его отсюда). При помощи этой программы вы сможете создавать свои проекты ресусов, редактировать уже существующие, редактировать ресурсы прямо в исполнимых файлах. Вы также можете использовать для создания и редактирования ресурсов редакторы, входящие в среду разработки «Visual Studio» или «Borland C++ 5»

Наконец вы создали проект ресурсов в виде текстового файла .rc (я вам настоятельно советую не создавать .res файл прямо в редакторе ресурсов, лучше сохраняйте проект в виде .rc файла. Кроме того, новый проект начинайте всегда в пустом каталоге и никогда не удаляйте файлы, созданные редактором). Теперь нужно откомпилировать rc файл, для чего используется входящая в состав Delphi программа BRCC32.EXE. Это консольная программа с интерфейсом командной строки, требующая в качестве параметра имя компилируемого rc файла. По умолчанию создается .res файл с именем, совпадающим с именем проекта ресурсов. Кроме того, НЕ создавайте проект ресурса (.rc файл) c тем же именем, что и проект Delphi, например, если ваш проект Delphi называется project.dpr .rc-файл НЕ должен называться project.rc, иначе Delphi просто перезапишет ваш .res файл на свой.

Наверх

Теперь подробнее о создании и использовании стандартных ресурсов

Пиктограммы (icon)

    Если у вас есть готовая пиктогамма в .ico файле, то вы можете добавить её в файл проекта ресурсов следующим образом:
120 ICON filename1.ico
MAINICON ICON filename2.ico

где ключевое слово ICON сигнализирует, что в проект добавляется именно пиктограмма. Перед словом ICON ставится идентификатор, по которому эта пиктограмма будет затем "опознана" прграммой. В качестве идентификатора могут применяться либо целочисленные константы, либо строковые константы. При компиляции проекта файл пиктограмы должен находиться в том же катклоге, что и файл проекта.

Когда ресурсы подключкны к проекту Delphi, вы можете загружать пиктограмму при помощи функции LoadIcon. В нашем случае это делается так:
var icon1, icon2 : THandle
...
icon1:=LoadIcon(hInstance, PChar(120));
//Эквивалентный вызов:
//icon1 := LoadIcon(hInstance, '#120');
icon2:=LoadIcon(hInstance,'MAINICON');

Вы видите, что при использовании целочисленных констант необходимо явное приведение типов ( Integer -> PChar), при использовании строковых идентификаторов явное приведение типов не требуется.

Наверх



Растровые изображения (bitmaps)

Если у вас есть растровое изображение в формате .bmp, вы можете добавить его в файл проекта ресурсов следующим образом:
130 BITMAP bitmap1.bmp
new_bitmap BITMAP bitmap2.bmp

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

Когда ресурсы подключены, вы можете загружать свои растровые изображения при помощи функции LoadBitmap, параметры которой точно такие же как и у LoadIcon

Наверх



Блоки диалога (Диалоговые окна) (dialog box)

Простейшим примером диалогового окна является окно «About Program», находящееся в меню почти каждой Windows-программы; это окно обычно имеет только одну кнопку.

Кроме того, на основе ресурсов диалоговых окон можно создавать полноценные приложения, в том числе и в Delphi. В этом случае значительно сокращается время на написание интерфейсной части win32api-приложения.

Конечно, можно создавать диалоговые окна и при помощи функции CreateWindow, но тогда вам придется точно указывать размера и координаты всех дочерних элементов управления вашего окна. Однако всего этого можно избежать, если воспользоваться предлагаемым Windows способом создания диалоговых окон из ресурсов определенного вида — ресурсов диалоговых окон (dialog box resources). Шаблон диалогового окна создается в специализированном редакторе ресурсов так же как и в редакторе форм Delphi (мышкой перетаскиваете элементы управления из палитры на форму).

Преимущество такого способа создания диалоговых окон заключается в том, что Windows берет на себя обеспечение интерфейса диалоговых окон, к которому привык пользователь — кнопки по умолчанию (срабатывающия, когда пользователь нажмет <Enter> или <Esc>); возможность переключения между дочерними элементами управления при помощи клавиши <Tab> и т.п.

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

Замена
1539 DIALOG  LOADONCALL MOVEABLE DISCARDABLE 37, 34, 300, 125
STYLE DS_MODALFRAME | 0x6004L | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Настройка принтера"
FONT 8, "Helv"
{
 CONTROL "Принтер", 1074, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 4, 6, 230, 67
 CONTROL "(Принтер не указан)", 1088, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 18, 30, 208, 9
 CONTROL "&Используемый по умолчани\xFE", 1058, "BUTTON", BS_RADIOBUTTON | WS_CHILD | 
		  WS_VISIBLE | WS_GROUP | WS_TABSTOP, 8, 16, 218, 12
 CONTROL "&Другой принтер:", 1059, "BUTTON", BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE, 8, 42, 218, 12
 CONTROL "", 1136, "COMBOBOX", CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_BORDER | 
		  WS_VSCROLL | WS_GROUP, 18, 56, 208, 80
 CONTROL "Ориентаци\xFF", 1072, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 4, 74, 92, 46
 CONTROL "", 1084, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 12, 92, 18, 20
 CONTROL "&Книжна\xFF", 1056, "BUTTON", BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | 
		  WS_TABSTOP, 40, 86, 53, 12
 CONTROL "&Альбомна\xFF", 1057, "BUTTON", BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE, 40, 102, 53, 12
 CONTROL "Бумага", 1073, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE | WS_GROUP, 101, 74, 133, 46
 CONTROL "&Размер:", 1089, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 
		  106, 88, 29, 9
 CONTROL "", 1137, "COMBOBOX", CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
		| WS_TABSTOP, 137, 86, 92, 80
 CONTROL "&Подача:", 1090, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 106, 104, 28, 9
 CONTROL "", 1138, "COMBOBOX", CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_BORDER | 
		  WS_VSCROLL | WS_TABSTOP, 137, 102, 92, 80
 CONTROL "ОК", 1, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP,
		  240, 4, 55, 14
 CONTROL "Отмена", 2, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  240, 20, 55, 14
 CONTROL "&Настройка...", 1024, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  240, 40, 55, 14
 CONTROL "&Справка", 1038, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  240, 56, 55, 14
}

Цветами я пометил здесь различные элементы шаблона

Таким цветом помечены идентификаторы элементов управления (идентификаторы дочерних окон)

Таким цветом помечены опции памяти ресурса

Таким цветом помечены имена класов дочерних окон

Таким цветом помечен стиль окна

Таким цветом помечены размеры и координаты окон



Другой пример

1541 DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 44, 260, 95
STYLE DS_MODALFRAME | 0x6004L | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Замена"
FONT 8, "Helv"
{
 CONTROL "&Образец:", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 5, 9, 48, 8
 CONTROL "", 1152, "EDIT", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_GROUP | WS_TABSTOP, 
		  54, 7, 125, 12
 CONTROL "З&аменить на:", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 5, 26, 48, 8
 CONTROL "", 1153, "EDIT", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_GROUP | WS_TABSTOP, 
		  54, 24, 125, 12
 CONTROL "&Только слово целиком", 1040,"BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP,
		  6, 46, 104, 12
 CONTROL "С у&четом регистра", 1041, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 
		  6, 62, 91, 12
 CONTROL "&Найти далее", 1, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  193, 4, 60, 14
 CONTROL "&Заменить", 1024, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  193, 21, 60, 14
 CONTROL "Заменить &все", 1025, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  193, 38, 60, 14
 CONTROL "Отмена", 2, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 193, 55, 60, 14
 CONTROL "&Справка", 1038, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 
		  193, 75, 60, 14
}
	


Ко всему нарисованному следует добавить вот что: во-первых, все размеры задаются не в пикселах или пунктах, а в особых единицах измерения диалоговых окон, чтобы узнать об этом подробнее, читайте справку по WIN32Api.

Во-вторых, стиль элементов ws_tabstop позволяет определить этот элемент как элемент, на который можно попасть при помощи клавиши <Tab>,причем использовать этот стиль можно лишь при создании диалоговах окон таким вот способом.

В-третьих, стиль bs_pushbutton определяет простую кнопку, а стиль bs_defpushbutton кнопку по умолчанию, то есть кнопку, которая сработает, когда пользователь нажмет клавишу <Enter>.

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

На основе одного ресурса диалогового окна может быть создано как модальное, так и немодальное окно; соответственно различается и способ сопровождения диалогового окна. Далее я отдельно рассмотрю механизм создания модальных и немодальных окон.

Наверх


Создание модальных диалоговых окон

Модальные окна — это окна, при появлени которых на экране запрещается доступ к родительскому окну.

Для создания модальных диалоговых окон существует функция DialogBoxParam

function DialogBoxParam(hInstance: HINST; 
					lpTemplateName: PChar;  
						hWndParent: HWND; 
					  lpDialogFunc: TFNDlgProc; 
					   dwInitParam: LPARAM): Integer
где,
hInstance
описатель приложения
lpTemplateName
идентификатор шаблона блока диалога, по которому будет построено окно. Должен быть приведен к типу PChar
hwndParent
описатель родительского окна, которое станет недоступным после вызова этой функции, а доступным станет лишь после закрытия диалогового окна
lpDialogProc
указатель на функцию сопровождения блока диалога, она очень похожа на оконную функцию, однако есть и существенные отличия, но об этом ниже
dwInitParam
некоторое число, посылаемое блоку диалога при его создании в составе сообщения wm_initdialog (это сообщение посылается диалоговой функции после создания окна, но до его появления на экране; используется для инициализации начальных значений дочерних окон диалогового окна)

Диалоговая функция (lpDialogProc) должна иметь заголовок следующего типа:

function DialogProc(hWnd: THandle; Msg: Integer; wParam, lParam : Integer): Bool; stdcall;

В теле этой функции должен стоять не совсем обычный блок обработки сообщений. Вместо сообщения ws_create блок диалога получает сообщение wm_initdialog сразу после создания, но до появления на экране; и именно в обработчике этого сообщения нужно устанавливать начальные значения управляющих элементов диалогового окна (поставить где надо галочку или записать какой-либо текст). Также диалоговая функция не получает сообщение wm_paint.

Получая сообщение wm_InitDialog диалоговая функция должна вернуть ненулевое значение (true), если она обрабатывает это сообщение и нуль (false) в противном случае. Также диалоговая процедура должна вернуть нуль в случае, если она вызывает функцию SetFocus для установки фокуса ввода на каком-либо дочернем элементе. В противном случае функция должна возвратить не нуль, в этом случае Windows установит фокус на первом элементе диалогового окна автоматически.

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

Для рассылания сообщений элементам управления блока диалога служит функция SendDlgItemMessage, она работает аналогично SendMessage, но требует в качестве параметров описатель окна блока диалога и числовой идентификатор элемента управления, которому посылается сообщение. Пример

SendDlgItemMessage(wndDialog{описатель диал. окна},100{id элемента},
   wm_enable,Integer(false),0);

Для установки текста дочернего окна блока диалога используется функция SetDlgItemText, требующая в качестве параметров описатель окна блока диалога, идентификатор элемена и строку нового текста окна элемента.

function SetDlgItemText(hDlg: HWND; nIDDlgItem: Integer; lpString: PChar): BOOL;

Для закрытия модального диалогового окна нужно использовать только функцию EndDialog

function EndDialog(hDlg: HWND; nResult: Integer): BOOL;
где,
hDlg
описатель закрываемого диалогового окна
nResult
целое число, которое будет возвращено функцией создавшей это диалоговой окно (DialgBoxParam, например)

Создание немодальных диалоговых окон

Напомню, что модальное окно — это окно, после появления которго на экране доступ к проролившему его окну запрещается. Немодальным (modeless) же называется окно, которое не запрещает доступ к ролительскому.

Сопровождение немодальных диалоговых окон несколько сложне нежели модальных. Для создания немодального окна используется функция CreateDialogParam (на самом деле имеется четыре функции предназначенные для достижения этой цели, однако все они схожи по функциональности, поэтому здесь я рассмотрю только одну из них).

function CreateDialogParam(hInstance: THandle; 
					   lpTemplateName: PChar;
						   hWndParent: HWND; 
						 lpDialogFunc: TFNDlgProc; 
						  dwInitParam: LPARAM): HWND;
где,
hInstance
описатель экземпляра процесса,из которого берется ресурс окна
lpTemplateName
имя ресурса окна
hWndParent
описатель родительского окна для создаваемого окна
lpDialogFunc
адрес диалоговой функции, функция точно такая же, как и в случае модального окна .
dwInitParam
некоторое число, которое передается в диалоговую функцию в составе сообщения wm_InitDialog

Интерфейс клавиатуры модального диалогового окна включает в себя поддержку клавиш [Tab], [Enter], [Esc], а также клавиш стрелок, они автоматически становятся доступными сразу после создания окна. Чтобы зваставить так вести немодальное окно, нужно слегка изменить цикл сбора сообщений в вашей программе (здесь всюду ведется разговор только о программах, написанных на WinAPI!!!). Перед изменением цикл сбора сообщений обычно выглядит так:

While GetMessage(Mesg,0,0,0) do
 begin
  TranslateMessage(Mesg);
  DispatchMessage(Mesg);
 end;
после изменения так:
While GetMessage(Mesg,0,0,0) do
 begin
  if wndDiaog<>0 then 
     if IsDialogMessage(wndDialog,Mesg) then continue;
  TranslateMessage(Mesg);
  DispatchMessage(Mesg);
 end;

где переменная wndDialog должна содержать описатель немодального диалогового окна и, если окно не существует, нуль.
Назад|Содержание|Вперед

Хостинг от uCoz