C++ C++ C# C# ASP.NET Security ASP.NET Security ASM ASM Скачать Скачать Поиск Поиск Хостинг Хостинг  
  Программа для работы с LPT портом...
Язык: .NET — ©Alexey...
  "ASP.NET Atlas" – AJAX в исполнении Micro...
Язык: .NET — ©legigor@mail.ru...
  "Невытесняющая" Многопоточность...
Язык: C/C++ — ©...
  01.05.2010 — Update World C++: Сборник GPL QT исходников
  15.12.2007 — Весь сайт целиком можно загрузить по ссылкам из раздела Скачать
Хостинг:
Windows 2003, ASP.NET 2.0
бесплатный и от 80 руб./мес


   Отправить письмо
Кулабухов Артем, Беларусь




 Работа с ресурсами исполняемых файлов / Статьи / Visual C++

Работа с ресурсами исполняемых файлов.



Содержание:
  • 1) Описание вопроса.
  • 2) Извлечение ресурсов.
  • 3) Загрузка ресурсов.
  • 4) Получение списка ресурсов.
  • 5) Замена ресурсов одного исполняемого файла на ресурсы другого.
  • 6) Замена ресурсов из файлов.
  • 7) Особенность работы с ресурсом Bitmap.
  • 8) Функции для работы с ресурсами.
  • 9) Ссылки.
  • 10) Примеры программ.

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:
  • LoadBitmap - Ф-ция для загрузки битмапа.
  • LoadIcon - Ф-ция для загрузки иконки.
  • LoadString - Ф-ция для загрузки элемента String-table.
  • LoadMenu - Ф-ция для загрузки меню.
    и др...

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. Также при записи ресурса в файл нужно создать этот заголовок, используя например вот эту ф-цию:
BOOL StoreBitmapFile(LPCTSTR lpszFileName, HBITMAP HBM)
{
BITMAP BM; 
BITMAPFILEHEADER BFH; 
LPBITMAPINFO BIP; 
HDC DC; 
LPBYTE Buf; 
DWORD ColorSize,DataSize; 
WORD BitCount; 
HANDLE FP; 
DWORD dwTemp;
GetObject(HBM, sizeof(BITMAP), (LPSTR)&BM);
BitCount = (WORD)BM.bmPlanes * BM.bmBitsPixel;
switch (BitCount){
case 1:
case 4:
case 8: 
case 32:
ColorSize = sizeof(RGBQUAD) * (1 << BitCount); 
case 16:
case 24:
ColorSize = 0; 
}
DataSize = ((BM.bmWidth*BitCount+31) & ~31)/8*BM.bmHeight;
BIP=(LPBITMAPINFO)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(BITMAPINFOHEADER)+ColorSize);
BIP->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BIP->bmiHeader.biWidth = BM.bmWidth;
BIP->bmiHeader.biHeight = BM.bmHeight;
BIP->bmiHeader.biPlanes = 1;
BIP->bmiHeader.biBitCount = BitCount;
BIP->bmiHeader.biCompression = 0;
BIP->bmiHeader.biSizeImage = DataSize;
BIP->bmiHeader.biXPelsPerMeter = 0;
BIP->bmiHeader.biYPelsPerMeter = 0;
if (BitCount < 16) BIP->bmiHeader.biClrUsed = (1<<BitCount);
BIP->bmiHeader.biClrImportant = 0;
BFH.bfType = 0x4d42; 
BFH.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+ BIP->bmiHeader.biClrUsed * 
sizeof(RGBQUAD);
BFH.bfReserved1 = 0;
BFH.bfReserved2 = 0;
BFH.bfSize = BFH.bfOffBits + DataSize; 
Buf = (LPBYTE)GlobalAlloc(GMEM_FIXED, DataSize);
DC = GetDC(0); 
GetDIBits(DC, HBM, 0,(WORD)BM.bmHeight, Buf, BIP, DIB_RGB_COLORS); 
ReleaseDC(0, DC);
FP=CreateFile(lpszFileName,GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); 
WriteFile(FP,&BFH,sizeof(BITMAPFILEHEADER),&dwTemp,NULL); 
WriteFile(FP,BIP,sizeof(BITMAPINFOHEADER) + BIP->bmiHeader.biClrUsed * sizeof(RGBQUAD),&dwTemp,NULL); 
WriteFile(FP,Buf,DataSize,&dwTemp,NULL);
CloseHandle(FP); 
GlobalFree((HGLOBAL)Buf);
HeapFree(GetProcessHeap(),0,(LPVOID)BIP); 
return(true);
}

8. Функции для работы с ресурсами:
  • FindResource - Ф-ция для поиска ресурса.
  • SizeofResource - Ф-ция для получения размера ресурса.
  • LoadResource - Ф-ция для загрузки ресурса.
  • LockResource - Ф-ция для фиксирования ресурса в памяти.
  • EnumResourceTypes - Ф-ция для получения списка типов ресурсов.
  • EnumResourceNames - Ф-ция для получения списка имён ресурсов.
  • BeginUpdateResource - Ф-ция для обновления ресурсов.
  • UpdateResource - Ф-ция для замены ресурсов.
  • EndUpdateResource - Ф-ция для завершения обновления ресурсов.

9. Ссылки:
Ключевые слова:
LoadLibrary, LoadLibraryEx, FindResource, SizeofResource, MAKEINTRESOURCE, LoadResource, LockResource, FreeLibrary, LoadBitmap, LoadIcon, LoadMenu, LoadString, EnumResourceNames, EnumResourceTypes, BeginUpdateResource, UpdateResource, EndUpdateResource.

10. Примеры программ:
В качестве примера приводятся две программы:
Первая является "держателем" ресурсов и способна распаковать свой ресурс - битмап в файл, а вторая меняет иконку для первой, а также переписывает ресурс – битмап, читая его из файла.

Сообщение отредактировано: SimBiOd - 20.05.05, 14:51
Скачать пример Небольшая поправка: при добавлении битмапа из файла.bmp в ресурсы надо отбрасывать BITMAPFILEHEADER, иначе получится не RT_BITMAP, а что-то другое... "Unknown bitmap format", как говорит редактор ресурсов VC++.