В этом материале пойдет речь об использовании ресурсов программы, а точнее их подгрузки во время выполнения тех или иных сценариев или команд.
Ресурсы - двоичные данные, которые можно добавлять в исполняемый файл программы. Помимо стандартных ресурсов можно определять собственные - заказные ресурсы. Каждый ресурс идентифицируется либо строкой имени, либо числом от 1 до 65535. Во втором случае число необходимо пропустить через макрос MAKEINTRESOURCE, конвентирующий целое число в тип ресурса, совместимый с функциями управления ресурсами. Таким образом некоторые объекты можно создать заранее и затем загружать их из исполняемого файла или создавать их динамически во время выполнения программы. К стандартным ресурсам относятся: таблица акселераторов, изображение, курсор, метафайл, шрифт, иконка, меню, окно диалога, таблица строк, информация о версии программы.
Размещение ресурсов в .EXE файле имеет два основных преимущества:
- Ресурс загружается более быстро, чем если бы он грузился с диска.
- Нет необходимости переживать, что пользователь случайно удалит файл ресурса и программа будет отображаться не так, как бы хотелось автору.
Для начала нам нужно создать файл ресурса. По умолчанию расширение для этих файлов - .RES. Файл ресурса может быть создан с помощью Image Editor, который входит в поставку с Delphi. Подробнее читайте файл справки данной программы. Но мы пойдем другим путем...
Ресурс Bitmap.
Чтобы действительно использовать ресурс, Вы должны сделать несколько вызовов Windows API. BMP-изображения, курсоры и иконки загруженные в файлы RES могут быть извлечены использованием функций LoadBitmap, LoadCursor и LoadIcon соответственно.
Создайте текстовый файл, например, testres.rc и поместите в него следующее:
RIS1 BITMAP "Pic1.bmp"
В этом примере:
RIS1 - Название ресурса. BITMAP - Тип ресурса, или вернее папка, в которой он будет помещен.
Pic1.bmp - Файл изображения, или путь с именем файла.
После сохранения данного текста необходимо скомпилировать ресурс, используя программу Borland Resource Compiler (консольное приложение brcc32.exe), которая входит в поставку с Delphi.
brcc32.exe testres.rc
Будет создан полноценный файл ресурса с расширением .RES. Далее остается подключить его в программу директивой компилятора - {$R testres.res}.
Чтобы загрузить побитовое изображение, которое расположено в ресурсах программы, например, в орган управления Static, нужно выполнить следующее:
//определяем константу Static
const
ID_STATIC = 5
//определяем переменную
var
hBMP: hBitmap;
//где-то в программе загружаем изображение из ресурсов...
hBMP := LoadImage(hInstance, 'RIS1', IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
//...и помещаем изображение в Static
SendDlgItemMessage(hWin, ID_STATIC, STM_SETIMAGE, IMAGE_BITMAP, hBMP);
При этом орган управления Static должен содержать стиль SS_BITMAP.
Ресурс Icon.
Что же касается иконок, то работа с ними очень похожа на работу с BMP-изображениями. Отличие заключается в папке, в которую компилируются иконки:
ZNACHEK1 ICON "Pic1.ico"
В этом примере:
ZNACHEK1 - Название ресурса. ICON - Тип ресурса.
Pic1.ico - Файл иконки.
Наверное, нет смысла напоминать о том, что нужно скомпилировать ресурс, подключить к программе и…
//определяем константу Static
const
ID_STATIC = 5
//определяем переменную
var
hZnachek: hIco;
//где-то в программе загружаем значек из ресурсов...
hZnachek := LoadIcon(hInstance, 'ZNACHEK');
//...и помещаем его в Static
SendDlgItemMessage(hWin, ID_STATIC, STM_SETIMAGE, IMAGE_ICON, hZnachek);
При этом орган управления Static должен содержать стиль SS_ICON.
Ресурс String.
Довольно интересна работа со строковыми ресурсами. На первый взгляд здесь все просто: загружаешь готовый текст и наслаждаешся резуальтатом. Но есть и подводные камни. Например, что делать, если нужно вставить в готовый текст какие-то определенные данные, например числа или подставить другой текст? И с этим можно легко справится. В файле ресурса пишем:
STRINGTABLE
LANGUAGE LANG_RUSSIAN, 0x1
{
6, "Здесь будет %s текст."
7, "Я родился %d числа, %d года."
8, "А этот текст\nпостроен с переносами\nи \"кавычками\"!"
}
STRINGTABLE - Секция строковых ресурсов. LANGUAGE - Здесь указывается язык строковых ресурсов. В данном случае русский. 6, 7, 8 - Идентификаторы, по которым вызываються определенные строки.
Директива %s вставляет текст, а %d - числа. Директива \n перемещает текст на следующую строку, а \" позволяем поместить кавычки в ресурсный файл. Как это работает, будет рассказано немного позже.
Чтобы без особых проблем вызывать строку из ресурсов, была написана небольшая функция, которая тестировалась во всех операционных средах Windows и показала надежные результаты своей работы:
function LoadStr(ID: DWORD): String;
var
buffer: array[0..1023] of Char;
begin
LoadString(hInstance, ID, buffer, SizeOf(buffer));
result := String(buffer);
end;
Далее в программе…
//определяем константу органа управления Static
const
ID_STATIC = 5
//определяем переменную
var
Str: String;
//результатом данного примера будет вывод в компонент Static
//6 строкого ресурса с добавлением слова "мой":
//"Здесь будет мой текст."
Str := 'мой';
SendMessage(GetDlgItem(hWin, ID_STATIC), WM_SETTEXT, 0, Integer(PChar(Format(LoadStr(6), [Str]))));
//результатом данного примера будет вывод в компонент Static
//7 строкого ресурса с добавлением чисел 8 и 1975:
//"Я родился 8 числа, 1975 года."
SendMessage(GetDlgItem(hWin, ID_STATIC), WM_SETTEXT, 0, Integer(PChar(Format(LoadStr(7), [8, 1975]))));
//результатом данного примера будет вывод в компонент Static
//8 строкого ресурса с переносом и текстом в кавычках:
//"А этот текст
//построен с переносами
//и "кавычками"!"
SendMessage(GetDlgItem(hWin, ID_STATIC), WM_SETTEXT, 0, Integer(PChar(LoadStr(8)));
Лицензия из ресурсов.
При установке программы всегда приходится соглашатся с надоедливой лицензией. Следующий пример покажет, как можно загрузить текст из ресурсов в орган управления Edit:
3 BINRES "License.txt"
3 - Название ресурса. BINRES - Имя секции, в которой будет расположен ресурс. Необязательно называть секцию именно так. Вы можете назвать ее как угодно.
License.txt - Файл с текстом лицензионного соглашения.
Для загрузки текста в Edit была написана следующая функция:
function ResToString(Instance: hInst; ResName, ResType: PChar): string;
var
ResSize, HG, HI: Cardinal;
pc: PChar;
begin
Result := '';
HI := FindResource(Instance, ResName, ResType);
if HI <> 0 then begin
HG := LoadResource(Instance, HI);
if HG <> 0 then
try
ResSize := SizeOfResource(Instance, HI);
pc := LockResource(HG);
SetString(result, pc, ResSize);
except;end;end;end;
Очень важно присвоить органу управления Edit нужные стили. Иначе текст не будет отображен так как нужно. Необходимые стили: ES_MULTILINE, WS_VSCROLL, WS_HSCROLL.
//определяем константу органа управления Edit
const
ID_EDIT = 32;
//Загружаем текст из ресурсов в Edit
SetDlgItemText(hWin, ID_EDIT, @ResToString(0, MAKEINTRESOURCE(3), 'BINRES')[1]);
Ресурс WAV.
Последнее время большой популярностью у программистов пользуется озвучивание некоторых событий в приложении. А в различных напоминалках вообще без этого не обойтись. Обычно для этого применяются WAV-файлы:
ZVUK WAVE "phone.wav"
ZVUK - Имя ресурса. WAVE - Секция ресурса.
phone.wav - Название файла.
Для простого проигрывания WAV-ресурсов была написана следующая процедура:
//Вопроизведение WAVE-файла из ресурса
procedure PlayWaveFromResource(ResName, SectionName: String);
var
FindHandle, ResHandle: THandle;
ResPtr: Pointer;
begin
FindHandle := FindResource(HInstance, PChar(ResName), PChar(SectionName));
if FindHandle <> 0 then begin
ResHandle := LoadResource(HInstance, FindHandle);
if ResHandle <> 0 then begin ResPtr := LockResource(ResHandle);
if ResPtr <> nil then sndPlaySound(PChar(ResPtr), SND_ASYNC or SND_MEMORY);
UnlockResource(ResHandle);
end;
FreeResource(FindHandle);
end;end;
Теперь осталось подключить модуль MMSystem в uses программы и проиграть звук:
PlayWaveFromResource('ZVUK', 'WAVE');
Для остановки воспроизведения звука можно воспользоватся командой:
sndPlaySound(nil, SND_ASYNC or SND_MEMORY);
Ресурс Menu.
Ни один текстовый редактор не обходится без меню. А в tray-области меню становится просто необходимым:
300 MENU
LANGUAGE LANG_RUSSIAN, 0x0
{
POPUP "Context"
{
MENUITEM "Отобразить сообщение 1", 200
MENUITEM "Отобразить сообщение 2", 201
MENUITEM SEPARATOR
MENUITEM "Закрыть программу", 202
}
}
300 - Идентификатор меню. LANGUAGE - Язык меню. POPUP - Тип меню: всплывающее. MENUITEM - Пункты меню. SEPARATOR - Горизонтальная разделяющая полоса. 200, 201, 202 - Идентификаторы пунктов меню.
//определяем переменную меню
var
Menu: HMenu;
//Загружаем меню
Menu := LoadMenu(hInstance, MAKEINTRESOURCE(300));
//Наводим курсор
GetCursorPos(CPos);
//Устанавливаем положение
TrackPopupMenu(GetSubMenu(Menu, 0), TPM_LeftAlign or TPM_TopAlign, CPos.X, CPos.Y, 10, hWin, nil);
//Уничтожаем
DestroyMenu(Menu);
Сохранения ресурса на диск.
Eсли вам нужно будет извлечь какой-нибудь ресурс из программы для последующей работы, то сделать это можно так:
Вот так выглядит метод применения данной функции (извлечение значка):
ResourceToFile('ICON', 'C:\main.ico')
Хочется верить, что после публикации данного материала не будут больше возникать вопросы по поводу использования ресурсов в программах, написаных на WIN32API в DELPHI. Но кто знает, что уже завтра придумает человечество...