
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.9.173] |
![]() |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Работа с ресурсами исполняемых файлов.
Содержание: 1. Описание вопроса: Трудность при работе с ресурсами исполняемых файлов заключаются в том, что явно обратиться к ним мы не можем, т.к. ресурсы не находятся в нашей программе. При работе с ними нам следует рассмотреть два случая: Следует отметить, что разница между этими двумя случаями невелика, но во втором случае возникают некие сложности – нам не известны идентификаторы и типы ресурсов, и нам нужно их получить. В первом же случае всё проще – мы знаем, какие ресурсы нам нужны и можем свободно их использовать, зная о них всё необходимое. Итак, рассмотрим проблему, отвечая на вопросы, обсуждение которых неоднократно поднималось на форуме: 2. Извлечение ресурсов: Q: Как достать файл из ресурсов другого приложения? A: Делается это достаточно просто, рассмотрим следующий пример: Данное консольное приложение работает вместе с файлом “simple.dll” (далее “Исполняемым файлом”), который на момент запуска должен находиться в той же директории. Исполняемый файл имеет ресурс IDR_JPEG1 (простой jpg файл). В файле resource.h: ![]() ![]() #define IDR_JPEG1 1001 В файле ресурсов simple.rc: ![]() ![]() ///////////////////////////////////////////////////////////////////////////// // // JPEGs // IDR_JPEGS1 JPEGs DISCARDABLE "Image1.jpg" Как добавлять ресурсы в проект я думаю, вы сможете найти сами, поэтому этого я объяснять не стану. Зная всё вышеописанное, и имея скомпилированный файл simple.dll с указанным ресурсом, мы можем извлечь его при помощи простой программы: ![]() ![]() // Хидеры программы #include <windows.h> #include <iostream.h> // 1001 - число идентификатора в исполняемом файле (как и в resource.h у проекта // исполняемого файла simple.dll) #define IDR_JPEG1 1001 // Ф-ция для извлечения ресурса bool ExtractMyJpeg() { // Инициализируем переменные HRSRC hRes = 0; HGLOBAL hData = 0; LPVOID pData; // Загружаем исполняемый файл (в данном случае dll) HMODULE hModule = LoadLibrary("simple.dll"); // Если не удалось загрузить исполняемый файл, то выходим if(hModule == NULL) return false; // Находим ресурс в исполняемом файле, указав идентификатор и тип ресурса // (в примере это "JPEGs"), hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_JPEG1), "JPEGs"); // Если ресурс не найден, то выходим if(hRes == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return false; } // Получаем размер ресурса для того, чтобы сохранить его в файл DWORD dwSize = SizeofResource(hModule,hRes); // Если не смогли получить размер, то выходим if(dwSize == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return false; } // Загружаем ресурс hData = LoadResource(hModule, hRes); // Если не смогли загрузить, то выходим if(hData == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return false; } // Фиксируем ресурс в памяти и получаем указатель на первый байт ресурса pData = LockResource(hData); // Если не удалось зафиксировать ресурс, то выходим if(pData == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return false; } // Создаём файл, в который будем писать HANDLE File = CreateFile("data.jpg",GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_ALWAYS,0,0); // Если не удалось создать файл, то выходим if(File == INVALID_HANDLE_VALUE) { // Освобождаем исполняемый файл FreeLibrary(hModule); return false; } // Переменная для ф-ции записи в файл DWORD Written=0; // Записываем весь ресурс в файл if(WriteFile(File,pData,dwSize,&Written,0)==NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); // Закрываем хендл файла CloseHandle(File); return false; } // Закрываем хендл файла CloseHandle(File); // Освобождаем исполняемый файл FreeLibrary(hModule); return true; } int main() { // Если не удалось извлечь ресурс, то сообщаем об ошибке if(!ExtractMyJpeg()) { // Сообщаем об ошибке cout << "Error No " << GetLastError() << endl; } return 0; } В данном примере использовалась функция FindResource (подробное описание данной ф-ции доступно в (MSDN), последним параметром которой является тип ресурса (в примере это "JPEGs"), хочу отметить, что для стандартных ресурсов типы описаны в MSDN. (Типы ресурсов) 3. Загрузка ресурсов: Q: Как загрузить картинку, иконку или другой ресурс из исполняемого файла? A: Делается это стандартными функциями, при помощи которых вы обычно работаете с собственными ресурсами. Для загрузки битмапа: ![]() ![]() HBITMAP LoadExBitmap(int value) { // Загружаем исполняемый файл HMODULE hModule = LoadLibrary("simple.dll"); // Проверка на валидность if (hModule == NULL) return 0; // Загружаем битмап стандартным способом, только в качестве // первого параметра передаём hModule. HBITMAP map = LoadBitmap(hModule, MAKEINTRESOURCE(value)); // Возвращаем битмапу если она загружена if(map!=NULL) return map; // Освобождаем исполняемый файл FreeLibrary(hModule); return NULL; } Для иконок, String-table’ов, меню, акселераторов и других ресурсов принцип такой же, только ф-ции LoadIcon, LoadString и LoadMenu… Подробное описание использования этих ф-ций вы можете найти в MSDN: 4. Получение списка ресурсов: Q: Можно ли получить список ресурсов исполняемого файла? Если да, то как? A: Теперь следует рассмотреть случай, когда мы не знаем ни тип, ни идентификатор ресурса. Для этого нужно получить список типов и идентификаторов ресурсов в них, с чем вы и сможете в результате работать. Данная программа перечисляет все типы ресурсов, а потом, используя полученный тип, перечисляет все идентификаторы для исполняемого файла. Работает по такому же принципу – исполняемый файл находится в той же директории. ![]() ![]() // Хидеры программы #include <windows.h> #include <iostream.h> // К этой ф-ции будет переходить управление при получении нового // идентификатора из списка. BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam) { // Следует проверять идентификатор, т.к. он можен представлять // собой, как число, так и строку if((ULONG)lpName & 0xFFFF0000) { // Если это строка, то и выводим её как строку cout << lpName; }else{ // Иначе выводим как число cout << (USHORT)lpName; } cout << endl; return TRUE; } // К этой ф-ции переходит управление при получении нового типа ресурсов BOOL EnumTypesFunc(HMODULE hModule,LPTSTR lpType,LONG lParam) { // Проверка как и в прошлой ф-ции if ((ULONG)lpType & 0xFFFF0000) { cout << "Type: " << lpType << endl; }else{ cout << "Type: " << (USHORT)lpType << endl; } // Перечисляем идентификаторы по получению нового типа ресурсов EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc,0); return TRUE; } int main() { // Загружаем исполняемый файл HMODULE hModule = LoadLibrary("simple.dll"); // Проверка на валидность if (hModule == NULL) return 0; // Перечисляем типы ресурсов EnumResourceTypes(hModule,(ENUMRESTYPEPROC)EnumTypesFunc,0); // Освобождаем исполняемый файл FreeLibrary(hModule); return 0; } Теперь, рассмотрим редактирование, добавление и удаление ресурсов… 5. Замена ресурсов одного исполняемого файла на ресурсы другого: Q: При попытке перезаписать ресурс стандартными методами для работы с файлами (CreateFile, WriteFile) получил испорченный исполняемый файл. Но ведь редакторы ресурсов как-то это делают! Но вот как?! A: Таким способом редактировать ресурсы слишком сложно, и делается это не совсем так, потому, что сами вы не сможете узнать (а если сможете, то скорее пожалеете, что взялись за это дело ![]() Следующий код показывает, как можно заменить ресурс одного приложения на ресурс другого: ![]() ![]() BOOL ReplaceIcon(WORD Number) { // Переменные для работы с ресурсами 2-х исполняемых файлов HGLOBAL hResLoad; HMODULE hModule; HRSRC hRes; HANDLE hUpdateRes; LPVOID lpResLock; BOOL result; // Загружаем исполняемый файл из которого будем копировать ресурс hModule = LoadLibrary("first.exe"); // Если загрузить не удалось, то выходим if(hModule == NULL) return FALSE; // Ищем ресурс в памяти исполняемого файла hRes = FindResource(hModule, MAKEINTRESOURCE(1), RT_ICON); // Если найти ресурс не удалось, то выходим if(hRes == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return FALSE; } // Загружаем ресурс hResLoad = LoadResource(hModule, hRes); // Если загрузить ресурс не удалось, то выходим if(hResLoad == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return FALSE; } // Фиксируем ресурс lpResLock = LockResource(hResLoad); // Если не удалось зафиксировать ресурс, то выходим if(lpResLock==NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return FALSE; } // Пытаемся начать обновлять ресурс второго файла hUpdateRes = BeginUpdateResource("second.exe", false); // Если не удалось начать обновление, то выходим if (hUpdateRes == NULL) { // Освобождаем исполняемый файл FreeLibrary(hModule); return FALSE; } // Собственно тут и происходит обновление ресурса result = UpdateResource(hUpdateRes,RT_ICON,MAKEINTRESOURCE(Number), MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),lpResLock,SizeofResource(hModule, hRes)); // Если не удалось обновить ресурс, то выходим if (result == FALSE) { // Освобождаем исполняемый файл FreeLibrary(hModule); return FALSE; } // Завершаем обновление if(EndUpdateResource(hUpdateRes, FALSE) == NULL){ // Освобождаем исполняемый файл FreeLibrary(hModule); return FALSE; } // Освобождаем загруженый исполняемый файл if(FreeLibrary(hModule) == NULL) return FALSE; return TRUE; } 6. Замена ресурсов из файлов: Данный код применим не только для иконок и замены ресурсов, которые читаются из исполняемого файла. Следующий код показывает, как можно заменить ресурс – битмап, прочитав его из памяти: ![]() ![]() bool ReplaceBitmap(WORD Number) { // Переменные для чтения и добавления ресурса DWORD dwResSize, dwRead; HANDLE File,hUpdateRes; // Открываем файл для дальнейшего чтения (файл new.bmp) File = CreateFile("new.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // Если открыть не удалось, то выходим if(File == INVALID_HANDLE_VALUE) return FALSE; // Сохраняем его размер исключив BITMAPFILEHEADER (для остальных ресурсов, исключать размер заголовка не нужно) dwResSize = GetFileSize(File, NULL)-sizeof(BITMAPFILEHEADER); // Если не удалось получить размер, то выходим if(dwResSize <= NULL) { // Закрываем хендл файла CloseHandle(File); return FALSE; } // Создаём массив для чтения файла char *pRes=new char[dwResSize]; // Устанавливаем позицию чтения файла, чтобы не читать заголовок битмапа (для остальных ресурсов следует читать с начала файла и не смещать позицию чтения) SetFilePointer(File,sizeof(BITMAPFILEHEADER),0,0); // Читаем файл if(ReadFile(File, (LPVOID)pRes, dwResSize, &dwRead, NULL) == NULL) { // Закрываем хендл файла CloseHandle(File); return FALSE; } // Начинаем обновлять ресурсы hUpdateRes = BeginUpdateResource("ResourceKeeper.exe", FALSE); if(hUpdateRes == NULL) { // Закрываем хендл файла CloseHandle(File); return FALSE; } // Добавляем ресурс if(UpdateResource(hUpdateRes, RT_BITMAP, MAKEINTRESOURCE(Number), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPVOID)pRes, dwResSize) == NULL) { // Закрываем хендл файла CloseHandle(File); return FALSE; } // Завершаем обновление if(EndUpdateResource(hUpdateRes, FALSE) == NULL) { // Закрываем хендл файла CloseHandle(File); return FALSE; } // Закрываем хендл файла if(CloseHandle(File) == NULL) return FALSE; return TRUE; } 7. Особенность работы с ресурсом Bitmap: Следует отметить, что при перезаписи битмапа из файла, следует исключить заголовок - BITMAPFILEHEADER. Также при записи ресурса в файл нужно создать этот заголовок, используя например вот эту ф-цию: ![]() ![]() #include <atlimage.h> void StoreBitmap(HBITMAP map, char *PathName){ CImage image; image.Attach(map); image.Save(PathName); } 8. Функции для работы с ресурсами: 9. Ссылки: Ключевые слова: LoadLibrary, LoadLibraryEx, FindResource, SizeofResource, MAKEINTRESOURCE, LoadResource, LockResource, FreeLibrary, LoadBitmap, LoadIcon, LoadMenu, LoadString, EnumResourceNames, EnumResourceTypes, BeginUpdateResource, UpdateResource, EndUpdateResource. 10. Примеры программ: В качестве примера приводятся две программы: Первая является "держателем" ресурсов и способна распаковать свой ресурс - битмап в файл, а вторая меняет иконку для первой, а также переписывает ресурс – битмап, читая его из файла. Прикреплённый файл ![]() |
Сообщ.
#2
,
|
|
|
Цитата SimBiOd, 22.04.05, 22:52:14, 692209 ![]() ![]() // Открываем файл для дальнейшего чтения (файл 1.bmp) File = CreateFile("1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(File == INVALID_HANDLE_VALUE) return FALSE; // Сохраняем его размер dwResSize = GetFileSize(File, NULL); if(dwResSize == NULL) return FALSE; // Создаём массив для чтения файла char *pRes=new char[dwResSize]; // Читаем файл if(ReadFile(File, (LPVOID)pRes, dwResSize, &dwRead, NULL) == NULL) return FALSE; // Закрываем его хендл if(CloseHandle(File) == NULL) return FALSE; // Начинаем обновлять ресурсы hUpdateRes = BeginUpdateResource("2.exe", FALSE); if(hUpdateRes == NULL) return FALSE; // Добавляем ресурс if(UpdateResource(hUpdateRes, RT_BITMAP, MAKEINTRESOURCE(Number), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPVOID)pRes, dwResSize) == NULL) return FALSE; Небольшая поправка: при добавлении битмапа из файла.bmp в ресурсы надо отбрасывать BITMAPFILEHEADER, иначе получится не RT_BITMAP, а что-то другое... "Unknown bitmap format", как говорит редактор ресурсов VC++ ![]() Рабочий пример -> Смена bmp (сообщение #604775) |
Сообщ.
#3
,
|
|
|
CBP, спасибо, что помогаешь, но я с этим уже разобрался, а это просто набросок, я знаю, что для записи в ресурс из файла нужно исключить BITMAPFILEHEADER, а для записи в файл дописывать.
![]() P.S прошу не писать в эту тему пока я не закончу, пока это просто мысли в слух. P.P.S 2 CBP ещё раз спасибо за попытку помочь. |
Сообщ.
#4
,
|
|
|
HMODULE LoadLibraryEx(
LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags ); dwFlags LOAD_LIBRARY_AS_DATAFILE If this value is used, the system maps the file into the virtual address space of the calling process as if it were a data file. Nothing is done to execute or prepare to execute the mapped file. Use this flag when you want to load a DLL only to extract messages or resources from it. You can use the resulting module handle with any Win32 functions that operate on resources. DONT_RESOLVE_DLL_REFERENCES is implied. |
Сообщ.
#5
,
|
|
|
Цитата SimBiOd @ Так может наоборот, лучше писАть, пока не закончил - меньше переделывать придется? P.S прошу не писать в эту тему пока я не закончу, пока это просто мысли в слух. ![]() У меня вот такое замечание - в кодах примеров, в нескольких местах, если после загрузки библиотеки или открытия/создания файла происходит ошибка (например, не найден ресурс или ошибка чтения/записи), то часто используется return FALSE - без всякого освобождения библиотеки или закрытия открытого хэндла файла - нехорошо это - чему мы так научим подрастающее поколение? ![]() Раз уж не хочется (это я понимаю) подробно расписывать, то лучше уж тогда в примерах ошибки вообще не обрабатывать, а просто указать, что в реальном коде это надо делать. |
Сообщ.
#6
,
|
|
|
Adil, я согласен, это можно и даже нужно поправить...
![]() |
![]() |
Сообщ.
#7
,
|
|
SimBiOd, всё хорошо, но мне не нравятся пара-тройка вещей
![]() - красный цвет в названиях разделов - курсив - отсутствие отступов в коде |
Сообщ.
#8
,
|
|
|
Господа модераторы, так что с топиком, редактировать ещё чё нужно?
![]() |
Сообщ.
#9
,
|
|
|
можно ещё в ссылки добавить заковыристые темы "Запись в StringTable" и "Проигрывание миди из ресурса (без извлечения на HDD!)"
![]() ресурсы (сообщение #525711) Воспроизведение Mid файлов из ресурса (сообщение #639315) |
Сообщ.
#10
,
|
|
|
Допустим есть exeшник в котором в виде ресурса имеется нпример другой ехешник (или картинка, неважно). Можно ли, не распаковывая этот ресурс (ехе или картинку) в отдельный файл, запустить его?
|
Сообщ.
#11
,
|
|
|
Цитата GRIENDERS @ Допустим есть exeшник в котором в виде ресурса имеется нпример другой ехешник (или картинка, неважно). Можно ли, не распаковывая этот ресурс (ехе или картинку) в отдельный файл, запустить его? в случае с исполняемым модулем - нельзя. а вот если картинка, то можно использовать протокол res: для обращения. например, для тестирования, можешь создать html-ку на ХР и вписать туда ![]() ![]() <img src="res://C:\Windows\System32\user32.dll/#2/36.bmp"> и увидишь картинку. но таким образом нельзя использовать CreateFile. Но запись типа ShellExecute( NULL, NULL, "res://C:\Windows\System32\user32.dll/#2/36.bmp", NULL, NULL, SW_SHOW ); вполне сработает. более подробно смотри тут: Pluggable Protocols. |
Сообщ.
#12
,
|
|
|
alexander.stoyan,спасибо.
Один вопрос: "Я делаю электронную книгу - на форме через элемент activex "microsoft web browser" можно просматривать HTML, которые у меня в проге в виде ресурсов. Будет ли работать прога с элементом activex "microsoft web browser" на всех виндах? " |
Сообщ.
#13
,
|
|
|
Дело не в винде, а в версии IE. В твоём случае нужне IE от 4.0.. учитывая, что 4.0 по дефолту идёт с NT4, а в W2K - IE 5.0, в XP - 5.5-6.0, в Vista - 7.0, то не о чем беспокоиться
![]() |
Сообщ.
#14
,
|
|
|
И еще одно дополнение:
Извлечение ресурсов dll функцией самой dll. Облазил форум, и очень часто встречал темы где жалуются на ошибку 1813 при работе с ресурсами в длл. Нельзя вызывать GetModuleHandle с параметром NULL - это возвратит handle на главный exe, а не на нашу dll. ![]() ![]() HMODULE hModule = GetModuleHandle (NULL); Обязательно необходимо получить HMODULE dll самой себя ![]() ![]() HMODULE hModule = GetModuleHandle ("название_dll.dll"); и в дальнейшем подставлять во все функции работающие с ресурсом. ![]() ![]() HRSRC hres = FindResource (hModule, MAKEINTRESOURCE (IDR_USER1), "USER"); |
Сообщ.
#15
,
|
|
|
Цитата zatomant @ Обязательно необходимо получить HMODULE dll самой себя ![]() ![]() HMODULE hModule = GetModuleHandle ("название_dll.dll"); учти, что ддл-ка может переименоваться. а hModule приходит в DllMain ![]() ![]() BOOL WINAPI DllMain( HANDLE hinstDLL, // вот его и надо использовать, и больше никакой DWORD dwReason, LPVOID lpvReserved ); |