
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.9.173] |
![]() |
|
Сообщ.
#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 ); |
Сообщ.
#16
,
|
|
|
в QT есть что-нибудь подобное ?
|
Сообщ.
#17
,
|
|
|
У меня выводится только тип 1го ресурса
![]() ![]() BOOL WINAPI EnumTypesFunc(HMODULE hModule,LPTSTR lpType, LONG lParam) { Form1->Memo1->Lines->Add(lpType); return(true); } .................................................................................................................. HMODULE lib=LoadLibraryW(OpenDialog1->FileName.c_str()); if(lib==NULL){ShowMessage("PE load fail");return;} EnumResourceTypes(lib,(ENUMRESTYPEPROC)EnumTypesFunc,0); .................................................................................................................. Что я делаю не так? |
Сообщ.
#18
,
|
|
|
Пропущена тема информации о версии. Вчера пришлось вспоминать, что к чему. А msdn хоть и дал описание но краткое. А пример, откровенно выдран из контекста. Надо объявлять собственные макросы. В прочем, это лучше, чем ничего: http://msdn.microsoft.com/en-us/library/wi...8(v=vs.85).aspx
Информацию о версии, можно посмотреть в свойствах файла. Кое-что всплывает прямо в эксплорере. И дополнительно высвечивается в диспетчере задач. Фишка вообщем полезная. А вот как считывать эту информацию, не помню. Придётся опять вспоминать! Вообщем, надо дополнить! |
![]() |
Сообщ.
#19
,
|
|
как это связано с ресурсами?
да и найти можно, по крайней мере я своё найду ![]() Определить версию dll, exe |
Сообщ.
#20
,
|
|
|
Информация о версии, это ресурсы бинарного файла. Прописываются в ресурсах, компилятся утилитой rc.exe и всё такое.
Я нашел, но исключительно в плане прописывания. А вот как считывать эти ресурсы, когда-то делал, сейчас не помню и даже не искал. |
Сообщ.
#21
,
|
|
|
У меня вопрос насчет sdkpaint. Никогда не понимал, как им пользоваться.
Нашел тут один единственный тред: Что это за ошибка Цитата trainer @ Если верить MSDN, то файл старого формата. Требуется его сконвертировать в текущий каким-нибудь графическим редактором Цитата An old format Device Independent Bitmap was found in the specified file. It should be converted to the current format. The SDKPAINT application provided in the Windows 3.0 SDK, or an equivalent application, can be used to do this. Это происходит при попытке добавить в ресурс бинаря иконку в хорошем качестве (16 bit/pixel depth, к примеру). Типа, её надо "пропустить" через sdkpaint. Как это сделать я не понимаю и гугл не помогает. Кто знает? Как мне засунуть хорошую иконку в exe/dll в виде ресурса? |
Сообщ.
#22
,
|
|
|
Цитата reinterpret_alexey @ У меня вопрос насчет sdkpaint. Никогда не понимал, как им пользоваться. Нашел тут один единственный тред: Что это за ошибка Цитата trainer @ Если верить MSDN, то файл старого формата. Требуется его сконвертировать в текущий каким-нибудь графическим редактором Цитата An old format Device Independent Bitmap was found in the specified file. It should be converted to the current format. The SDKPAINT application provided in the Windows 3.0 SDK, or an equivalent application, can be used to do this. Это происходит при попытке добавить в ресурс бинаря иконку в хорошем качестве (16 bit/pixel depth, к примеру). Типа, её надо "пропустить" через sdkpaint. Как это сделать я не понимаю и гугл не помогает. Кто знает? Как мне засунуть хорошую иконку в exe/dll в виде ресурса? Может быть это поможет? |