Работа с ресурсами в Borland C++ BuilderМатериалы к лекциям. Ресурсы, которые может использовать приложениеЛюбая программа в Windows это не только код, но еще и набор данных типа картинок, строк, значков, диалоговых окон и так далее. Все это хранится внутри .exe модуля программы в виде ресурсов. То есть, ресурсы в Windows можно рассматривать как вид данных, которые уже инициализированы. Ресурсы не загружаются в память вместе со всей программой, а находятся на диске до тех пор, пока их не вызовет программа. Это позволяет не плодить на диске лишние микрофайлы и экономить оперативную память при выполнении приложения. Ресурсы возможно помещать в отдельные DLL библиотеки. Например если в DLL хранятся диалоговые окна, меню, строковые сообщения и т.п., то при необходимости их изменения не понадобится изменять сами программы. Ресурсы можно разделить на два типа: двоичные и текстовые. Двоичные ресурсы это обычно растровые картинки, иконки, курсоры.., а текстовые ресурсы это строки, шаблоны диалоговых окон и т.п. Ресурсы в проектах приложений Borland C++ Builder хранятся раздельно. Файл проекта с расширением .DFM содержит информацию о ресурсах окон и компонентов для конструирования самой формы приложением. Он создается автоматически при создании проекта и изменяется при добавлении компонентов на форму. Другой файл с расширением .res (Project1.res) - файл графических ресурсов - хранит курсоры, иконки и рисунки. Для редактирования графических ресурсов Borland C++ Builder предусмотрен редактор Image Editor. Он позволяет редактировать битовые изображения (.bmp), пиктограммы (.ico), курсоры (.cur), а также сами файлы ресурсов (.res и .dcr). Файлы ресурсов .rc также могут быть подключены к проекту Borland C++ Builder. При компиляции проекта информация из файлов .rc и информация на которую они ссылаются будет помещена в файл с расширением .res, а ресурсы из этого файла перенесены в .exe файл и становятся доступны приложению. Каждый тип ресурсов имеет свой идентификатор. Полный список ресурсов можно посмотреть в файле winuser.h. Некоторые типы ресурсов:
Таким образом - ресурсы это составная часть файлов .exe и .dll. Практически, при создании приложения, файлы ресурсов только переписываются в двоичном виде в .exe(.dll) файлы. Составляющие ресурсы извлекаются и используются по мере выполнения программы. Для просмотра используемых приложением ресурсов можно использовать функцию WIN32 API EnumResourceTypes(). BOOL EnumResourceTypes ( HMODULE hModule, // указатель на модуль ENUMRESTYPEPROC lpEnumFunc, // функция перечисления LONG_PTR lParam // параметры определенные программистом ); Пример использования: Поместим на форму один компонент TButton и определяем внешнюю функцию f_EnumFunc(), которая будет функцией перечисления:
BOOL WINAPI
f_EnumFunc(HMODULE hModule,LPTSTR lpType,LONG lParam);
....
BOOL WINAPI
f_EnumFunc(HMODULE hModule,LPTSTR lpType,LONG lParam)
{
char vrgchBuffer[100];
if((ULONG)lpType & 0xFFFF0000)
{
wsprintf(vrgchBuffer,"%s\n",lpType);
}
else
{
wsprintf(vrgchBuffer,"%u\n",(USHORT)lpType);
}
MessageBox(0,vrgchBuffer,"Ресурс",MB_ICONEXCLAMATION);
return true;
}
В обработчике события нажатия кнопки TButton пишем код:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
HMODULE hMod=NULL;
hMod = LoadLibrary("calc.exe");
if(hMod != NULL)
{
EnumResourceTypes(hMod,(ENUMRESTYPEPROC)f_EnumFunc,0);
FreeLibrary(hMod);
}
else Label1->Caption="Нет возможности загрузить модуль программы";
}
Цифры выдаваемые программой говорят о номерах ресурсов, которые использовал программист при ее написании. .exe файл в ресурсах и его выполнениеПоставим целью спрятать в ресурсах создаваемого приложения некоторую другую программу, а в процессе выполнения приложения вызвать ее из ресурсов. Поместим на форме компонент TMemo и два компонента TButton. В обработчике Button1Click напишем код:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Clear();
Memo1->Lines->Add("MYEXE EXEFILE calc.exe");
Memo1->Lines->SaveToFile("a.rc");
}
Откомпилируем приложение, при нажатии кнопки Button1 в директории программы бедет содан файл со строчкой (которая отобразится в компоненте Memo): MYEXE EXEFILE calc.exe Это строка "Имя ресурса | Тип ресурса | Имя программы" - полностью идентифицирует ресурс, а именно - программу calc.exe, которую можно будет спрятать в ресурсах приложения. Далее, присоеденяем к проекту этот файл (Меню: Project/Add To Project.. выбираем a.rc файл). Если сейчас попробовать откомпилировать проект, то компилятор выдаст ошибку. Причина - путь к calc.exe должен быть указан полный и, хотя ресурс еще не используется, но уже формируется. Поэтому скопируем calc.exe из директории Windows в директорию приложения (в противном случае необходимо указать полный путь).. Приложение компилирутся и можно начинать использовать ресурс. Удаляем файл "calc.exe" из директории проекта. В обработчике Button2Click напишем код:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
char *pchResType = "EXEFILE";
TResourceStream* ptRes =
new TResourceStream((int)HInstance, "MYEXE",pchResType);
//Это не цель, но строка показывает, что
//TResourceStream содержит полную копию .exe файла
ptRes->SaveToFile("calc.exe");
//Запускаем калькулятор
ShellExecute(Handle,"open","calc.exe",NULL,NULL,SW_SHOWNORMAL);
delete ptRes;
}
Здесь осуществляется выбор ресурса в поток - файла calc.exe (из файл "a.res", который уже создан). TResourceStream - потоковый класс, который обеспечивает доступ к компилируемым ресурсам в приложения. Он имеет множество методов для доступа к ресурсам, в том числе и для сохранения потока. При нажатии на вторую кнопку калькулятор вызывается даже если его удалить не только из текущей директории приложения, но и из директории Windows. Это не единственный способ вызова и запуска на выполнения .exe файла из ресурсов, но наиболее простой. Другие способы извлечения ресурсов и работы с потоками будут рассмотрены ниже - там где это или целесообразно, или другим способом задача не выполнима. При желании гурманы могут поэкспериментировать с TMemoryStream, WinExecute() и.т.п. Текстовая информация в ресурсахПомещение и извлечение текстовой информации, скопированной в ресурс из файла, и получение доступа к ней мало отличается от примера, описанного выше. Создадим аналогичный проект с одним компонентом TMemo и двумя компонентами TButton. Шаги создания кода приложения будут аналогичны пройденным в предыдущем примере. Приведем лишь коды обработчиков событий нажатия кнопок.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Clear();
Memo1->Lines->Add("MYTEXT RT_RCDATA readme.txt");
Memo1->Lines->SaveToFile("a.rc");
}
Здесь одно отличие - тип ресурса вместо EXEFILE стал RT_RCDATA. После включения в проект файла "a.rc", не забудем поместить в дирректорию проекта любой файл readme.txt. Последний обработчик имеет отличие от вызова программы связанное только с тем, что информация используется в самом приложении.
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
TResourceStream* ptRes =
new TResourceStream((int)HInstance, "MYTEXT","RT_RCDATA");
//Можно и сохранить
ptRes->SaveToFile("readme.txt");
//Загружаем ресурс
Memo1->Lines->LoadFromStream(ptRes);
delete ptRes;
}
В результате при нажатии кнопки 2 файл "readme.txt" появляется в дирректории приложения (сколько его не удадяй) и отображается в компоненте Memo. Картинки в ресурсахКартинки можно поместить в ресурс как используя файл ресурсов проекта (project1.res) так и отдельный файл ресурсов, аналогично описанному выше хранению текста и программ. Для рассмотрения первой возможности создадим новый проект и поместим на форму компонент TImage и два компонента TButton. В директорию проекта поместим любой рисунок в формате bmp (например "a.bmp") и откроем его в любом графическом редакторе, например в Microsoft Paint. Не компилируя проект запустим Image Editor (меню Tools/Image Editor). Далее откроем project1.res из директории проекта(File/Open, Projects/project1.res). Выполняем правый клик в поле открывшегося окна и в контекстном меню выбираем пункт New/Bitmap. И сразу видим недостаток этого метода - максимальное число цветов для изображения - 256. Устанавливаем число цветов и размер (его определяем в Paint - меню Рисунок/Атрибуты). В файле у Contens появилась новая ветвь Bitmap с именем рисунка Bitmap1. (Можно воспользовавшись контекстным меню пунктом "Rename" и переименовать рисунок, например в MYBITMAP, но для наглядности делать этого не будем). Выполним двойной клик на имени рисунка - откроется окно редактора с пустым рисунком. Перенесем рисунок из Paint (ctrl+A, Ctrl+C) (можно и нарисовать свой рисунок в Image Editor) в окно Image Editor(ctrl+V). Закроем окно редактора рисунка и Image Editor, естественно на вопрос: "Save Changes..." кликнем "Yes". Создадим обработчик нажатия для кнопки 2 и запишем несколько вариантов кода,позволяющего отображать и сохранять рисунок:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
//Вариант 1 - загрузка функцией LoadFromResourceName()
Image1->Picture->Bitmap->LoadFromResourceName
((int)HInstance,"Bitmap1");
//Вариант 2 - загрузка функцией LoadImage()
HBITMAP hBmp=LoadImage(GetModuleHandle(NULL),
"Bitmap1",IMAGE_BITMAP,0,0,
LR_COPYFROMRESOURCE | LR_DEFAULTCOLOR |
LR_CREATEDIBSECTION);
if(hBmp == 0)
ShowMessage("Картинка не загружена");
else
Image1->Picture->Bitmap->Handle=hBmp;
//Вариант 3 - через использование TBitmap
Graphics::TBitmap* ptBitmap;
ptBitmap = new Graphics::TBitmap();
ptBitmap->LoadFromResourceName((int)HInstance,"Bitmap1");
Image1->Width=ptBitmap->Width;
Image1->Height=ptBitmap->Height;
//И отобразить несколькими способами
// a).
Image1->Picture->Bitmap->Assign((TPersistent*)ptBitmap);
// b).
BitBlt(Image1->Canvas->Handle,0,0,ptBitmap->Width,ptBitmap->Height,
ptBitmap->Canvas->Handle,0,0,SRCCOPY);
// c).
Image1->Canvas->Draw(0,0,ptBitmap);
}
Откомпилируем и загрузим приложение, при нажатии кнопки 2 изображение появится в компоненте TImage. Для рассмотрения использования отдельного файла ресурса в проект внесем некоторые изменения. В обработчиках событий ButtonClick запишем коды, приведенные ниже. После компиляции и запуска приложения нажимаем первую кнопку и закрываем приложение. Добавляем к проекту файл "a.rc". Вновь компилируем и запускаем приложение и нажимаем вторую кнопку - картинка отображается в компоненте Image.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
ptSList->Add("MYBMP RT_BITMAP \"a.bmp\"");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
TResourceStream* ptRes =
new TResourceStream((int)HInstance,"MYBMP","RT_BITMAP");
ptRes->SaveToFile("a.bmp");
Image1->Picture->LoadFromFile("b.bmp");
delete ptRes;
}
Пример полностью аналогичен предыдущему примеру, за исключением того, что для формирования файла .rc использован класс TStringList, а тип ресурса стал "RT_BITMAP". Отображение рисунка возможно выполнить и немного по другому:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
Graphics::TBitmap *ptBitmap;
ptBitmap=new Graphics::TBitmap();
char *pchResType = "RT_BITMAP";
TResourceStream* ptRes =
new TResourceStream((int)HInstance, "MYBMP",pchResType);
ptBitmap->LoadFromStream(ptRes);
Image1->Picture->Assign((TPersistent*)ptBitmap);
delete ptRes;
delete ptBitmap;
}
Редактирование картинок из ресурсов и отображение отредактированных картинок. Следующие примеры показывают как можно извлекать ресурс картинки и внести в него коррективы. Зная структуру файла картинки, например, таким образом можно сделать надписи на картинках или наложить на рисунок из ресурса другой рисунок и т.п.
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
//Тип ресурса
char *pchResType = "RT_BITMAP";
//Поиск ресурса
HRSRC hSrsrc=FindResource(NULL,"MYBMP",pchResType);
if(!hSrsrc)
{ShowMessage("Ресурс не найден"); return;}
//Загрузка ресурса в память
HGLOBAL hGlobal=LoadResource(NULL,hSrsrc);
if(!hGlobal)
{ShowMessage("Ресурс не загружен"); return;}
//Фиксация ресурса или получения указателя на ресурс
void *pvResPtr=LockResource(hGlobal);
if(!pvResPtr)
{ShowMessage("Ресурс не зафиксирован"); return;}
TMemoryStream * ptMStream=new TMemoryStream();
//Размер ресурса
unsigned long vulResourceSize=SizeofResource(NULL,hSrsrc);
//Поток бит помещаем в массив по умолчанию для Stream
ptMStream->WriteBuffer(pvResPtr,vulResourceSize);
//На этом этапе можно сохранить ресурс в памяти
ptMStream->SaveToFile("a0.bmp");
//Буфер для маневра байтами
unsigned char * puchResourceBuffer =
new unsigned char[vulResourceSize];
//Переносим весь поток в буфер
memcpy(puchResourceBuffer, pvResPtr, vulResourceSize);
//Замена битов рисунка - для примера просто чуть чуть
//испортим картинку. Учитывая, что в файле 256 цветной .bmp
//биты рисунка начинаются с 0x436(1078)го байта спортим
//в картинке несколько байт
for(int i=1078; i < 1000; i++) puchResourceBuffer[i]=i;
//Поток на начало
ptMStream->Seek(0,0);
//Сохранить в поток все с изменениями
ptMStream->Write(puchResourceBuffer,vulResourceSize);
//Поток на начало
ptMStream->Position=0;
//Опять сохраним и посмотрим, что получилось
//ptMStream->SaveToFile("a1.html");
Image1->Picture->Bitmap->LoadFromStream(ptMStream);
delete Stream;
delete puchResourceBuffer;
}
Тоже можно выполнить и чуть чуть по другому и с картинки из файла project1.res.
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
Graphics::TBitmap* ptBitmap;
ptBitmap = new Graphics::TBitmap();
//Загружаем ресурс
ptBitmap->LoadFromResourceName((int)HInstance,"Bitmap1");
Image1->Width=ptBitmap->Width;
Image1->Height=ptBitmap->Height;
//Сдесь можно отобразить изображение
//Image1->Picture->Bitmap->Assign(Bitmap);
TMemoryStream * ptMStream=new TMemoryStream();
ptBitmap->SaveToStream(ptMStream);
//Можно сохранить то, что в потоке
ptMStream->SaveToFile("a1.bmp");
//Поток на начало
ptMStream->Seek(0,0);
//Буфер для манипуляции битами
puchResourceBuffer =
new unsigned char[ptMStream->Size];
ptMStream->Read(puchResourceBuffer,ptMStream->Size);
//Замена битов рисунка - портим рисунок
for(int i=1078; i < 1000; i++) puchResourceBuffer[i]=i;
//Поток на начало
ptMStream->Seek(0,0);
//Сохранить в поток все с изменениями
ptMStream->Write(puchResourceBuffer,ptMStream->Size);
//Поток на начало
ptMStream->Position=0;
//Можно посмотреть, что получилось
Stream->SaveToFile("a1.html");
//Отобразить
Image1->Picture->Bitmap->LoadFromStream(Stream);
delete Bitmap;
delete Stream;
delete puchResourceBuffer;
}
Иконки в ресурсахХранение иконок в ресурсах ничем не отличается от хранения рисунков. Аналогично сначала используем иконку, поместив ее в ресурс через projekt1.res, а затем с использованием файла a.res. Создадим новый проект на которой для данного примера достаточно поместить всего лишь два компонента Button. Не компилируя проект откроем projeсt1.res в Image Editor. Там уже есть веточка Icon с именем иконки MAINICON. Воспользовавшись контекстным меню пункт New/Icon, создаем новую иконку и переименуем ее в MAINICON1. Двойным кликом на имене иконки откроем графический редактор и нарисуем любой рисунок. Помним, что цвет левого нижнего пикселя является цветом прозрачности. Закроем редактор сохранив изменения в файле projeсt1.res. Создадим обработчик нажатия для кнопки 2 и запишем следующий код:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
HWND h=LoadIcon(HInstance,"MAINICON1");
if(h == 0) ShowMessage("Иконка не загружена");
else
{
Application->Icon->Handle=h;
Application->Icon->SaveToFile("a.ico");
}
}
Запускаем приложение. При нажатии кнопки 2 иконка меняется не только на форме окна приложения, но и в панели задач. Следующий код показывает как можно получить информацию об иконке и как создается новая иконка, на основании структуры HICON. Кроме того здесь также показано как скопировать иконку в буфер обмена.
//Загружаем нашу иконку
HICON hIcon=LoadIcon(HInstance,"MAINICON1");
//Копируем информацию из нее в структуру HICON
GetIconInfo(hIcon,&tIconInfo);
//По информации в структуре создаем новую иконку
HICON hIcon1=CreateIconIndirect(&tIconInfo);
//Записываем иконку в буфер
BOOL fOpen = ::OpenClipboard(NULL);
if(fOpen)
{
::EmptyClipboard();
//Как ни странно а указатель hbmColor на биты иконки
::SetClipboardData(CF_BITMAP, tIconInfo.hbmColor);
::CloseClipboard();
}
//Обязательно удаляем что создано CreateIconIndirect()
DestroyCursor(hIcon1);
Структура ICONINFO общая для иконок и курсоров.
typedef struct _ICONINFO
{
BOOL fIcon;
DWORD xHotspot;
DWORD yHotspot;
HBITMAP hbmMask;
HBITMAP hbmColor;
}ICONINFO;
Для рассмотрения использования отдельного файла ресурса для работы с иконками в проект внесем некоторые изменения. В обработчиках событий ButtonClick запишем коды, приведенные ниже. После компиляции и запуска приложения нажимаем первую кнопку и закрываем приложение. Добавляем к проекту файл "a.rc". Вновь компилируем и запускаем приложение и нажимаем вторую кнопку - иконка меняется на нарисованную ранее (сохранена на предыдущем шаге) и в форме окна приложения, и в панели задач.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
ptSList->Add("MYICON RT_ICON \"a.ico\"");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
char *pchResType = "RT_ICON";
TResourceStream* ptRes =
new TResourceStream((int)HInstance, "MYICON",pchResType);
//Можно и не сохранять
ptRes->SaveToFile("a.ico");
Form1->Icon->LoadFromStream(ptRes);
Application->Icon=Form1->Icon;
delete ptRes;
}
Курсоры в ресурсахИспользование ресурсов для манипуляций с курсорами во многом аналогично работе с иконками, но здесь есть и существенное отличие, речь о котором пойдет ниже. Также как и ранее сначала создадим курсор в project1.res, а затем в файле a.res. Создадим новый проект на которой для данного примера поместим шесть компонент Button. Не компилируя проект откроем projeсt1.res в Image Editor. Так же как создавали иконку в предыдущем примере создадим курсор с именем MAINCURSOR и нарисуем его (любой рисунок, например черную стрелку). Закроем редактор сохранив изменения в файле projeсt1.res. В секции private файла Unit1.h определим функцию vLoadCursor(). Эта функция необходима только для того, чтобы не повторять один и тотже код. private: void __fastcall vLoadCursor(HCURSOR hCursor); Тело функции опишем в файле "UNIT1.cpp" следующим образом:
void __fastcall
TForm1::vLoadCursor(HCURSOR hCursor)
{
if(hCursor == 0) ShowMessage("Курсор не загружен");
else
{
Screen->Cursors[1] = hCursor;
Form1->Cursor =(TCursor)1;
}
}
Этот код показывает, что если удалось получить хэндл курсора, то его можно и подключить через массив Cursors свойств Screen. Об этом уже говорилось в разделе "Курсоры". Создадим обработчик нажатия для кнопки 2 и запишем следующий код:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
HWND hCursor=LoadCursor(GetModuleHandle(NULL),"MAINCURSOR");
vLoadCursor(hCursor);
}
Откомпилируем и запустим приложение - нарисованный курсор появляется при нажатии на кнопку 2. Для кнопки 3 создадим другой способ загрузки курсора.
void __fastcall
TForm1::Button3Click(TObject *Sender)
{
HCURSOR hCursor=LoadImage
(GetModuleHandle(NULL),
"MAINCURSOR",IMAGE_CURSOR,0,0,
LR_COPYFROMRESOURCE | LR_CREATEDIBSECTION);
vLoadCursor(hCursor);
}
Курсор загружается как и при нажатии на кнопку 2. О функции LoadImage() речь также уже шла. Перейдем к использованию отдельного .res файла. Для кнопки 1 напишем уже знакомый (новый только идентификатор ресурса RT_CURSOR) код:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
ptSList->Add("MAINCURSOR1 RT_CURSOR \"Hand-i.cur\"");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
Выполним этот код и в директории проекта будет создан файл "a.rc", содержащий строку: MyCur RT_CURSOR "Hand-i.cur" На данном этапе отличия от предыдущего примера нет. Скопируем в директорию проекта файл "Hand-i.cur" из директории "C:\Windows\Cursors\" и добавим к проекту файл "a.rc" (Project/Add to Project), а в обработчиках Button4Click напишем код:
void __fastcall
TForm1::Button4Click(TObject *Sender)
{
char *pchResType = "RT_CURSOR";
TResourceStream* ptRes =
new TResourceStream((int)HInstance, "MAINCURSOR1",pchResType);
//Можно сохранить курсор
ptRes->SaveToFile("b.cur");
delete ptRes;
//Можно загрузить из файла
HCURSOR hCursor = LoadImage(0,"b.cur",
IMAGE_CURSOR,0,0,LR_LOADFROMFILE);
//Актуализируем курсор
vLoadCursor(hCursor);
}
При запуске приложения все работает, курсор формы сменился со стрелки на руку. Но как загрузить курсор не сохраняя его в файле на диске. До этого и иконки и картинки можно было загрузить и использовать прямо из потока. Для курсоров такой возможности нет. Остается один выход - либо хранить курсоры только в Файле ресурсов приложения, либо пойти по достаточно сложному пути, который описан в обработчике события нажатия кноки пять (Button5Click).
void __fastcall
TForm1::Button5Click(TObject *Sender)
{
////////////////////////////////////////////////////////////
// В Ы Б О Р Р Е С У Р С А В Б У Ф Е Р
////////////////////////////////////////////////////////////
char *pchResType = "RT_CURSOR";
//Поиск ресурса
HRSRC hSrsrc=FindResource(NULL,"MAINCURSOR1",pchResType);
if(!hSrsrc)
{ShowMessage("Ресурс не найден"); return;}
//Загрузка ресурса в память
HGLOBAL hGlobal=LoadResource(NULL,hSrsrc);
if(!hGlobal)
{ShowMessage("Ресурс не загружен"); return;}
//Фиксация ресурса или получения указателя на ресурс
void *pvResPtr=LockResource(hGlobal);
if(!pvResPtr)
{ShowMessage("Ресурс не зафиксирован"); return;}
////////////////////////////////////////////////////////////
// К О П И Р У Е М П О Т О К В С В О Й Б У Ф Е Р
////////////////////////////////////////////////////////////
//Так как указатели на void не поддержывают арифметики
//приходится копировать биты ресурса в поток TMemoryStream
//а оттуда в свой буфер
TMemoryStream * Stream=new TMemoryStream();
unsigned long vulResourceSize=SizeofResource(NULL,hSrsrc);
//Поток бит помещаем в массив по умолчанию для Stream
Stream->WriteBuffer(pvResPtr,vulResourceSize);
//На этом этапе можно сохранить курсор и затем
//загрузить его если надо из файла.
Stream->SaveToFile("w.cur");
//Но цель сейчас обойтись без файла
// Определяем буферы байт для всего потока ресурса.
//Там всего как в любом курсоре 32*32 - 326 байт
char vrgchResourceBuffer[326];
//Можно объявить и так
//unsigned char * puchResourceBuffer =
// new unsigned char[ResourceSize];
memcpy(vrgchResourceBuffer, pvResPtr, vuliResourceSize);
////////////////////////////////////////////////////////////
// Ф О Р М И Р У Е М Г О Р Я Ч У Ю Т О Ч К У
////////////////////////////////////////////////////////////
unsigned int viHotSpootX=vrgchResourceBuffer[10]+
10*vrgchResourceBuffer[11];
unsigned int viHotSpootY=vrgchResourceBuffer[12]+
10*vrgchResourceBuffer[13];
////////////////////////////////////////////////////////////
// Ф О Р М И Р У Е М М А С С И В Ы М А С О К
////////////////////////////////////////////////////////////
// Маски начинаются с адреса 70 и 198 и содержат по 128 байт
char vrgchResourceBuffer1[128];
char vrgchResourceBuffer2[128];
for(int i=0;i < 32;i++)
{
for(int j=0;j < 4;j++)
{
vrgchResourceBuffer1[j+i*4]=vrgchResourceBuffer[198-4-i*4+j];
vrgchResourceBuffer2[j+i*4]=vrgchResourceBuffer[326-4-i*4+j];
}
}
//Можго пойти по друглму пути c применением указателей
/*
void *pvBuf=&vrgchResourceBuffer[70];
memcpy(vrgchResourceBuffer1,pvBuf,128);
pvBuf=&vrgchResourceBuffer[198];
memcpy(vrgchResourceBuffer2,pvBuf,128);
// Переворачиваем маски - хранятся по 4 байта строка
//первая строка маски есть последняя строка массива
for(int i=0;i < 16;i++)
{
for(int j=0;j < 4;j++)
{
char ch1=vrgchResourceBuffer1[j+i*4];
char ch2=vrgchResourceBuffer2[j+i*4];
vrgchResourceBuffer1[j+i*4]=vrgchResourceBuffer1[128-4-i*4+j];
vrgchResourceBuffer2[j+i*4]=vrgchResourceBuffer2[128-4-i*4+j];
vrgchResourceBuffer1[128-4-i*4+j]=ch1;
vrgchResourceBuffer2[128-4-i*4+j]=ch2;
}
}
*/
////////////////////////////////////////////////////////////
// С О З Д А Е М И З А Г Р У Ж А Е М К У Р С О Р
////////////////////////////////////////////////////////////
HCURSOR hCursor=CreateCursor(NULL,viHotSpootX,viHotSpootY,32,32,
vrgchResourceBuffer2,vrgchResourceBuffer1);
//Наконец то загрузили
vLoadCursor(hCursor);
}
Таким образом эта проблема решена. Но она к сожалению не одна, и хотя это может быть и не надо, все же попробуем сохранить курсор из Файла ресурсов приложения на диске, тоесть сохранить курсор MAINCURSOR. Причина возникшей трудности в самом файле ресурсов приложения. Если откроем "project1.res" и "a.res" в редакторе ресурсов Image Editor, то увидим суть различия в достуре к курсорам.
Строки символов в ресурсахПо аналогии с предыдущими примерами поместим на форме компонент Memo и два компонента Button. В обработчиках ButtonClick напишем код:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
ptSList->Add("MyString RT_STRING Begin \"Моя строка\" End");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
char *pchResType = "RT_STRING";
TtResourceStream* ptRes =
new TtResourceStream((int)HInstance, "MyString",pchResType);
Memo1->Lines->LoadFromStream(ptRes);
delete ptRes;
}
Выполнив ту же последовательность действий как и в предыдущих примерах получим в компоненте Memo при нажатии кнопки два строку: "Моя строка" HTML документы в ресурсахПомещение HTML документов (файлов .html, .htm ...) в ресурсы полностью аналогично с примером помещения в ресурсы и использования .exe файлов. Поэтому здесь приводятся лишь коды приложения, форма которого имеет компонент TMemo и два компонента TButton, и которое выводит в браузере документ "a.html", а текст кода документа в компоненте Memo.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
ptSList->Add("MyHTML RT_HTML \"a.html\"");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
char *pchResType = "RT_HTML";
TtResourceStream* ptRes =
new TtResourceStream((int)HInstance, "MyHTML",pchResType);
Memo1->Lines->LoadFromStream(ptRes);
Memo1->Lines->SaveToFile("b.html");
ShellExecute(0,NULL,"b.html", NULL,NULL,SW_tResTORE);
delete ptRes;
}
.wav и другие музыкальные файлы в ресурсахПо аналогии с предыдущими примерами оформим создание файла a.rc для формирования a.res в приложении, поместив на форме компонент TButton. Откомпилируем и выполним приложение, предварительно поместив в директорию приложения любой музыкальный файл, например a.wav.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
ptSList->Add("MYWAV SOUND \"mywav.wav\"");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
Заметим, что идентификатора ресурса SOUND нет в списке идентификаторов ресурсов. Это говорит о том, что в файл ресурсов можно помещать и произвольную информацию - вопрос лишь в том, как ее потом оттуда извлечь и использовать. Добавим на форму еще один компонент TButton и запишем код:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
sndPlaySound("MyWav", SND_SYNC | SND_RESOURCE);
PlaySound("MyWav",NULL, SND_SYNC | SND_RESOURCE);
}
Содержимое файла, помещенное в ресурс, воспроизводится дважды (для обеспечения этой возможности добавлен флаг SND_SYNC). Однако, если повторить все тоже, но заменив тип ресурса "SOUND", например на "SOUND1", эти функции откажутся воспроизводить ресурс. Таким образом, хотя "SOUND" не определен как тип ресурса в файле winuser.h как некоторая константа (например MAKEINTRESOURCE(x) - где x некоторое число), функции считают, что в исполняемом файле находится ресурс именно с типом "SOUND". И всетаки, хотя в этом и нет необходимлсти, для приведенных ниже кодов ресурс может иметь любой идентификатор отличный от "SOUND". Здесь используются уже знакомые по предыдущим параграфам потоки, которые позволяют найти практически произвольный ресурс и извлечь его из исполняемого модуля как двоичный код.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TStringList* ptSList = new TStringList();
ptSList->Clear();
//Заменили тип ресурса SOUND на SOUND1
ptSList->Add("MYWAV SOUND1 \"mywav.wav\"");
ptSList->SaveToFile("a.rc");
delete ptSList;
}
Используем TResourceStream.
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
char *pchResType = "SOUND1";
TResourceStream* ptRes =
new TResourceStream((int)HInstance, "MyWav",pchResType);
//На это этапе можно сохранить
ptRes->SaveToFile("MyWav1.wav");
//Здесь возможно запустить системный плеер
ShellExecute(Handle,"open","MyWav1.wav",NULL,NULL,SW_SHOWNORMAL);
//Можно воспроизвести из памяти
unsigned char * puchResourceBuffer =
new unsigned char[ptRes->Size];
ptRes->Read(puchResourceBuffer,ptRes->Size);
sndPlaySound(puchResourceBuffer, SND_SYNC | SND_MEMORY);
delete ptRes;
}
Используем TMemoryStream и функции поиска и фиксации ресурса.
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
HRSRC hRsrc=FindResource(NULL,"MyWav","SOUND1");
if(!hRsrc)
{ShowMessage("Ресурс не найден"); return;}
//Загрузка ресурса в память
HGLOBAL hGlobal=LoadResource(NULL,hRsrc);
if(!hGlobal)
{ShowMessage("Ресурс не загружен"); return;}
//Фиксация ресурса или получения указателя на ресурс
void *pvResPtr=LockResource(hGlobal);
if(!pvResPtr)
{ShowMessage("Ресурс не зафиксирован"); return;}
TMemoryStream * ptMStream=new TMemoryStream();
//Размер ресурса
unsigned long vulResourceSize=SizeofResource(NULL,hRsrc);
//Поток бит помещаем в массив по умолчанию для Stream
ptMStream->WriteBuffer(pvResPtr,vulResourceSize);
//На этом этапе можно сохранить
ptMStream->SaveToFile("mywav2.wav");
//Здесь возможно запустить системный плеер
ShellExecute(Handle,"open","MyWav2.wav",NULL,NULL,SW_SHOWNORMAL);
//Воспроизвести из файла
sndPlaySound("mywav2.wav", SND_SYNC | SND_FILENAME);
unsigned char * puchResourceBuffer =
new unsigned char[vulResourceSize];
//Переносим весь поток в буфер
memcpy(puchResourceBuffer, pvResPtr, vulResourceSize);
//Воспроизвести из памяти
sndPlaySound(puchResourceBuffer, SND_SYNC | SND_MEMORY);
}
Аналогично .wav файлам можно манипулировать и с любыми другими музыкальными файлами. |