Работа с ресурсами в 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 файлам можно манипулировать и с любыми другими музыкальными файлами. |