Загрузка...
 
Печать

Работа с двоичными ресурсами (.rc) в MS Visual C++ 2010 Express


Ресурсы - это бинарные данные, добавляемые в исполняемый файл при компоновке программы.1
В бесплатной MS Visual C++ 2010 Express редактор двоичных ресурсов отсутствует. Но это не беда, ведь ресурсы (и скрипты для них) можно создавать в сторонних редакторах. После этого их останется только импортировать в MSVC++ . Хороший пример такого редактора - программа Resource Editor by Anders Melander. О ней мы и расскажем в данной статье.

Содержание



Структура исполняемого PE-модуля (теория)

Image
Рис.1 Структура PE-файла

Сокращение PE расшифровывается как Portable Executable (от англ. "переносной исполняемый"). Данный формат пришёл из UNIX, где аналогичный формат назывался COFF (Common Object File Format - Стандартный формат объектных файлов). Microsoft подвергла COFF-формат значительной переработке и в теперешнем виде исполняемые PE-файлы используются в среде Windows повсеместно. В группу PE-файлов входят:
  • Исполняемые файлы приложений Windows (.exe);
  • Динамически подсоединяемые библиотеки (Dynamic Link Library - DLL);
  • Объектные модули (obj);
  • Драйверы режима ядра.
Главной особенностью PE-модуля является простота его загрузки в память, для чего он не требует какой-либо дополнительной настройки. По сути, PE-модуль содержит слепок участка оперативной памяти. По своей структуре PE-файл чем-то напоминает слоёный пирог (см. Рис. 1). На Рис. 1 изображена общая схема PE-формата исполняемого модуля. В самом первом разделе (на рисунке он изображён сверху) разработчики отдали дань совместимости с операционной системой MS-DOS. Данный раздел давно ушёл в прошлое вместе с самой MS-DOS, но именно с него по сей день начинается любой исполняемый файл Win32. Два первых байта (MZ) - это т.н. сигнатура, которая подтверждает, что перед нами исполняемый модуль ОС MS-DOS.
Закрыть
noteЛюбопытный факт

Сокращение MZ - это инициалы сотрудника компании Microsoft Марка Збиковски (Mark Zbikowscki), разработчика структуры исполняемых модулей в ОС MS-DOS.

После сигнатуры MZ в PE-модуле идёт стандартный заголовок MS-DOS, а далее - маленькая программа-заглушка, которая обычно выводит на экран консоли сообщение: "This programm cannot be run in DOS mode." ("Эта программа не может выполняться в MS-DOS."), после чего приложение завершает свою работу. Данный кусок кода часто называют stub (от англ. "заглушка").
Сразу после заголовков MZ и PE располагается таблица секций, состоящая из структур по 40 байтов каждая. Вот наиболее часто встречающиеся секции, генерируемые различными компиляторами:2
СЕКЦИЯ ОПИСАНИЕ
.text Секция содержит исполняемый код (Microsoft).
CODE Секция содержит исполняемый код (Borland).
.data Секция, содержащая инициализированные глобальные переменные (Microsoft).
.bss Все неинициализированные данные. Размер секции в файле равен нулю.
.CRT Секция, содержащая инициализированные данные (Microsoft).
CRT Секция, содержащая инициализированные данные (Borland).
.rdata Данные доступны только для чтения (константы, отладочная информация).

По таблице секций можно вычислить положение секции в файле, её размер и, тем самым, получить возможность посмотреть, что в ней хранится.

Двоичные ресурсы исполняемых PE-файлов (тоже теория)

Форматы исполняемых файлов, используемых в Windows, позволяют хранить внутри них особые типы данных - ресурсы. К стандартным ресурсам Windows относятся:
  • Диалоговые окна (Dialog)
  • Значки (Icons)
  • Определения меню (Menu)
  • Определения панелей инструментов (Toolbar)
  • Растровые изображения (Bitmap)
  • Курсоры мыши (Cursor)
Кроме стандартных, программист может сам определить свои собственные ресурсы.
Помимо редактора ресурсов, MS Visual C++ также включает в себя интерпретатор встроенного языка описания ресурсов. Его директивы можно увидеть, если открыть в текстовом редакторе, предварительно созданный в MS Visual C++, шаблон ресурсов (файл с расширением .rc).
Ресурсы можно создавать двумя способами:
  • Описать ресурсы программы в текстовом ASCII файле с расширением RC. (Этот способ практически не используется.)
  • Создать ресурс при помощи специализированного редактора ресурсов в режиме WYSIWYG (What You See Is What You Get - "Что видишь, то и получаешь").
В состав MS Visual C++ всех версий (кроме бесплатных редакций Express) входит редактор ресурсов, позволяющий визуально создавать и редактировать стандартные виды ресурсов. На самом деле это не один, а несколько редакторов (для каждого вида ресурсов). Но в литературе их часто рассматривают как один. После компиляции Проекта все задействованные в нём ресурсы размещаются (встраиваются) в результирующий исполняемый файл.
Существуют также редакторы ресурсов от сторонних производителей (ResEdit, PE-Editor, Resource Workshop и др.). Все они имеют схожий функционал и пользовательский интерфейс, не требующий много времени на освоение.
В практической части данного урока мы создадим базовое (простейшее) оконное приложение и встроим в него двоичные ресурсы:
  • Стандартный (заменим значок на тулбаре);
  • Кастомный (опишем и разместим внутри exe-шника рандомный двоичный файл).
Но сперва создадим и скомпилируем само приложение.

Создаём базовое оконное приложение Windows

  • В MS Visual C++ 2010 Express создай Проект базового оконного приложения WinTest01.
Весь процесс подробно описан в статье Создание приложений Сpp Win32, где мы создаём пустое Решение/Проект WinTest01, добавляем в него файл исходного кода WinMain.cpp, вводим исходный код базового оконного приложения и отправляем Проект на компиляцию.
О том, где взять и как правильно установить MS Visual C++ 2010 Express, читай здесь: MS Visual Cpp 2010 Express. Установка и указание путей к DirectX SDK.
Закрыть
noteОбрати внимание

Напомним, что создаваемый проект изначально должен быть пустым, для чего ставится соответствующая галка в мастере (См. Рис.2).

Image
Рис.2 Создаём пустой проект Windows-приложения


Добавляем в Проект файл исходного кода WinMain.cpp

  • Добавь в Проект WinTest01 файл исходного кода WinMain.cpp.
Добавленный файл сразу откроется в MSVC++. Сейчас он пуст.
WinMain.cpp
// Файл: WinMain.cpp
// Описание: Шаблон оконного приложения Windows. Простейшее (базовое) приложение Windows,
// выводящее на экран окно.
// www.igrocoder.ru 2015
// При использовании просьба указывать ссылку на источник.

#define STRICT
#define WIN32_LEAN_AND_MEAN	// Уменьшаем кол-во используемых компонентов в программе.

#include <windows.h>

// Прототип функции WinMain
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow); 

// Объявление оконной процедуры
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

// Реализация главной функции программы
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND hWnd;			// Дескриптор окна
	MSG msg;			// Структура сообщения

	WNDCLASSEX wndClass;	// Объявляем экземпляр класса программы на базе WNDCLASSEX

   // Заполняем структуру оконного класса
	wndClass.cbSize=sizeof(wndClass);
	wndClass.style=CS_CLASSDC;	// Поочерёдный доступ к контексту устройства (Device Context)
	wndClass.lpfnWndProc=WindowProc;		// Оконная процедура
	wndClass.cbClsExtra=0;
	wndClass.cbWndExtra=0;
	wndClass.hInstance=hInstance;			// Экземпляр окна
	wndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);		// Пиктограмма приложения
	wndClass.hIconSm=LoadIcon(NULL, IDI_APPLICATION);	// Малая пиктограмма приложения
	wndClass.hCursor=NULL;		// Курсор при наведении на окно
	wndClass.hbrBackground=NULL;	// Закрашиваем окно белым цветом
	wndClass.lpszMenuName=NULL;	// Дескриптор Главного меню окна. Сейчас оно не нужно
	wndClass.lpszClassName="GameClass"; // Обзываем оконный класс.
	
	// Если класс не зарегистрируется, досрочно прерываем выполнение программы
	if(!RegisterClassEx(&wndClass)) return FALSE;

	// Создание окна на основе зарегистрированного класса
	hWnd=CreateWindow(
		"GameClass",		// Класс окна.
		"My game title",		// Текст заголовка (на верхнем тулбаре).
		WS_OVERLAPPEDWINDOW,	// Обычное окно с кнопками в правом верхнем углу.
		0, 0,				// Координаты X и Y
		GetSystemMetrics(SM_CXSCREEN),	// Ширина окна
		GetSystemMetrics(SM_CYSCREEN),	// Высота окна
		NULL,							// Дескриптор родительского окна
		NULL,							// Дескриптор меню
		hInstance,						// Дескриптор экземпляра приложения
		NULL);							// Дополнительные данные

	if(hWnd==NULL) return(FALSE);

	// Собственно, показываем окно
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	// Очищаем структуру сообщения
	ZeroMemory(&msg, sizeof(MSG));

	// Процедура выборки сообщений
	// Продолжаем бесконечно, пока не получим сообщение WM_QUIT (завершение).
	while(msg.message != WM_QUIT)
	{	// Просмотрим очередь: вдруг там ожидает сообщение...
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		// Есть сообщение! Обработаем его как обычно...
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
		else
		{
			// Нет сообщений, ожидающих обработки.
			// Значит приступим к выполнению критичных ко времени операций.
			// Например, займёмся рендерингом.
		}
	}

	//Удаляем регистрацию класса
	UnregisterClass("GameClass", hInstance);
	
	// Выходим из приложения
	return (msg.wParam);
}

// Оконная процедура
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_DESTROY:			// В случае этого сообщения...
		PostQuitMessage(0);		// Говорим Windows закрыть приложение
		break;

		// В случае любых других сообщений...
	default: return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	return 0;
}

  • Сохрани Решение/Проект (Файл -> Сохранить все).

Готовим Проект WinTest01 к компиляции

Для успешной компиляции Проекта WinTest01 изменим пару его свойств.

Устанавливаем многобайтовую кодировку (multibyte encoding)

Закрыть
noteПримечание

В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, данный Проект мы настроим под многобайтовую кодировку.

Для этого...
  • В Обозревателе решений щёлкаем по названию только что созданного Проекта WinTest01.
  • Во всплывающем меню выбираем "Свойства"
Image
  • В появившемся окне установки свойств Проекта жмём "Свойства конфигурации", в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".
Image
  • Жмём ОК.

Отключаем инкрементную компоновку (incremental linking)

Иначе при компиляции возможны ошибки вроде этой:
Error LNK1123: сбой при преобразовании в COFF: файл недопустим или поврежден

  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства.
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации -> Компоновщик -> Общие (Configuration Properties -> Linker -> General), в правой части в строке "Включить инкрементную компоновку" ставим значение "Нет (/INCREMENTAL:NO)".
  • Жмём ОК.
  • Сохрани Решение/Проект (Файл -> Сохранить все).

Удаляем файл cvtres.exe

В нашем случае (Win7 x64) полный путь до данного файла такой: C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cvtres.exe
В Win32-кодинге он особо ни на что не влияет. Зато без него линковка идёт как по маслу.

Компилируем Проект Test01

Проект готов к компиляции.
  • Скомпилируй Проект/Решение (кнопка с зелёным треугольником на Панели инструментов MSVC++ или <F5> на клавиатуре).
Image
Если весь код был введён без ошибок, после компиляции запустится приложение, отображающее окно с белым фоном.

Мутим двоичные ресурсы в MSVC+ + 2010 Express

Продолжаем работать с Проектом WinTest01.

Организуем в нашем приложении оригинальные значки (иконки)

У каждой игры есть стартовый (запускной) .exe файл, который в Проводнике Windows отображается с оригинальным значком с логотипом игры. В каждом таком приложении, как правило, используется два значка, в исходном виде представляющие собой растровые изображения с расширением .ico:
  • Значок размером 32х32 точек, отображаемый в Проводнике Windows.
  • Уменьшенная копия первого значка размером 16х16 точек, отображаемая на тулбаре (верхней полоске) окна приложения.

Готовим значки

Оба значка можно без труда создать и экспортировать в файл формата .ico в любом графическом редакторе (в идеале Photoshop). Битность может быть любая, но желательно не больше 8-битной палитры (256 цветов). Часто оба значка выглядят одинаково, но отличаются лишь размером.
В нашем случае мы нашли готовое ICO-изображение в Интернете по запросу "ico 32x32". Скачанный файл почему-то оказался больше - 128x128 пикселей.
  • Открой скачанный файл в любом графическом редакторе.
В нашем случае это бесплатный просмотрщик XnView(external link).
  • В нём поочерёдно измени размер изображения (Изображение -> Изменение размера... ):
  • сначала до размеров 32х32, пересохранив результат как Test01.ico,
  • затем до размеров 16х16, пересохранив результат как Test01_sm.ico.
XNView также без труда открывает и пересохраняет в .ico файлы многих других форматов (jpg, png, bmp и др.). Кроме того, значки из Интернета можно также создавать и редактировать с помощью онлайн-редактора http://www.xiconeditor.com(external link) или Photoshop (если есть).
Напомним, для дальнейшей работы нам требуется 2 значка размером 16х16 и 32х32.
  • Подготовь два значка размером 16х16 и 32х32.
  • Скопируй оба значка в каталог с текущим Проектом WinTest01.
В нашем случае (Win7 x64) C:\Users\<Имя пользователя>\Documents\Visual Studio 2010\Projects\WinTest01\WinTest01 .

Создаём скрипт ресурсов. Редактор ресурсов Resource Editor by Anders Melander.


Image
Рис.3 Программа Resource Editor by AM


Просто добавить значки в Проект WinTest01 не получится. Сперва необходимо их описать в специальном скрипте ресурсов, представляющем собой обычный текстовый файл с расширением .rc . Напомним, в бесплатной MSVC++2010 Express редактор ресурсов вырезан напрочь. Поэтому для создания скрипта ресурсов воспользуемся сторонним приложением Resource Editor by Anders Melander (далее - Resource Editor by AM). Далее в этой статье будем работать только в ней. В разделе Софт нашего сайта ты найдёшь и другие редакторы двоичных ресурсов. Все они имеют во многом схожий интерфейс и поддерживают сохранение макета в файл шаблона ресусов (.rc).
Самораспаковывающийся архив (2,8 Мб) предложит распаковать Portable-версию программы в тот же каталог.
  • Перейди в каталог с программой и запусти ResourceEditor.exe .
Программа на русском языке автоматически создаст рабочий Проект (см. Рис.3).
Быстро пробежимся по её интерфейсу:
ПАНЕЛЬ ИНТЕРФЕЙСА ОПИСАНИЕ
Свойства (Properties) Похож на редактор свойств в IDE от Borland и Microsoft. Активируется сразу после создания ресурса. В двухколоночной таблице в формате Свойство - Значение. Редактируем свойства активного (=выбранного в данный момент или только что созданного) ресурса/элемента управления. У разных ресурсов разные свойства. Но есть и общие: ID, Name, Language и т.д.
Ресурсы (Resources) Древовидный список ресурсов (например, открытого .exe-файла). Ресурсы группируются по типу. При клике ЛКМ по ресурсу в панели Свойства отображаются его свойства, доступные для редактирования. Ресурсы могут быть добавлены, удалены, экспортированы, сохранены.
Варианты (Variants) Показывает различные варианты ресурса. Любой ресурс может иметь только 1 ID, но несколько вариантов на разных языках, с разной глубиной цветности или размерами.
Preview Предварительный просмотр ресурса (например, курсора). Любопытно, что Resource Editor by AM умеет отображать в этой панели видеоролики и HTML-документы (если есть).
Edit Редактор ресурсов. Здесь мышью перемещаем элементы диалогового окна или просто перерисовываем курсоры/значки. Ресурсы неизвестного типа отображаются здесь в виде HEX-кода или текста. Чтобы не уходить от темы (создание скрипта ресурсов) сам встроенный редактор значков (иконок) здесь не рассматриваем. Там всё просто. При желании разберёшься сам.

Напомним, для описания двух значков нам требуются два файла:
  • Resource01.rc
  • resource.h
Первый сгенерим с помощью Resource Editor by AM, второй создадим в текстовом редакторе по образцу (об этом ниже). В resource.h прописываются т.н. IDI (идентификационные номера) ресурсов.
  • В Resource Editor by AM На панели Ресурсы жмём правой кнопкой мыши (ПКМ) по рабочей области. В контекстном меню выбираем "Добавить из файла".
  • В диалоговом окне выбираем Test01.ico .
  • Активируй добавленный значок в окне Ресурсы ЛКМ. В панели Свойства смени ID значка на IDI_ICON1.
  • Аналогичным образом добавь Test01_sm.ico и смени его ID на IDI_ICON2.
  • Сохрани скрипт ресурса в файл Resource01.rc в каталог с Проектом WinTest01.
В нашем случае (Win7 x64): C:\Users\<Имя пользователя>\Documents\Visual Studio 2010\Projects\WinTest01\WinTest01 .
В главном меню: Файл -> Сохранить как... Укажи имя Resource01. Во всплывающем списке выбери расширение .rc и жми Сохранить.
  • Открой сохранённый файл Resource01.rc в любом текстовом редакторе. Ознакомься с его содержимым.
В скрипте указаны абсолютные (obsolete) пути к обоим значкам. Но они также могут быть относительными (relative). Сейчас это неважно.

Добавляем resource.h (Проект Engine)

В заголовочном файле resource.h перечисляются ID-идентификаторы ресурсов.
ОК, приступаем.
  • Стартуй MSVC++ 2010 и открывай Решение GameProject01 (если не сделал это раньше).
  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы" Проекта Engine.
  • Во всплывающем меню Добавить->Создать элемент... (Add->New Item...)
  • В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи "resource.h" (Да, именно с маленькой буквы! Так требует MSVC++.).
Image
Добавленный файл сразу откроется в правой части MSVC++2010.
  • В только что созданном и открытом файле resource.h набираем следующий код:
resource.h
#define IDI_ICON1    101
#define IDI_ICON2    102

Закрыть
noteНевероятно, но факт!

В конце листинга Resource.h обязательно оставь пустую строку, перенеся каретку ввода на следующую строку. В противном случае при компиляции MS VC++ выдаст ошибку! Природа подобных "Фэн-шуй наклонностей" никому неизвестна.

Как видим, нашим значкам были присвоены символьные идентификаторы IDI_ICON1 и IDI_ICON2. Они-то и нужны для загрузки значков в приложение.

В принципе всё. Скрипт ресурсов Resource01.rc и сопутствующий заголовок resource.h готовы. Осталось добавить Resource01.rc в Проект WinTest01 и кое-что прописать в коде.

Добавляем файл Resource01.rc в Проект WinTest01

  • Стартуй MSVC++2010 Express, если не сделал этого раньше. Открой Решение/Проект WinTest01, созданное выше.
  • В Обозревателе решений щёлкни правой кнопкой мыши по "папке" (Напомним, что в терминологии Microsoft это не папки, а фильтры!) "Файлы ресурсов".
  • Во всплывающем контекстном меню выбери Добавить->Существующий элемент.
  • В появившемся окне выбора файла выбери файл скрипта ресурсов Resource01.rc и нажми "Добавить".

Изменения в Resource01.rc. Прописываем #include resource.h

  • Перейди в каталог Проекта WinTest01 и открой Resource01.rc в любом текстовом редакторе.
  • Найди следующий фрагмент:
Фрагмент Resource01.rc
...
#define APSTUDIO_READONLY_SYMBOLS
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
...

  • Строкой ниже добавь директиву
#include "resource.h"

  • Сохрани изменения в Resource01.rc .

Изменения в WinMain.cpp . Прописываем #include resource.h

  • В начале листинга WinMain.cpp найди список подключаемых заголовочных файлов (include-строки):
Фрагмент WinMain.cpp
#define STRICT
#define WIN32_LEAN_AND_MEAN	// Уменьшаем кол-во используемых компонентов в программе.

#include <windows.h>
...

  • Добавь чуть ниже строку :
#include "resource.h"

  • Сохрани Решение (Файл->Сохранить всё).

Прописываем новые значки в структуре оконного класса

  • В WinMain.cpp найди следующий фрагмент:
Фрагмент WinMain.cpp
...
// Реализация главной функции программы
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND hWnd;			// Дескриптор окна
	MSG msg;			// Структура сообщения

	WNDCLASSEX wndClass;	// Объявляем экземпляр класса программы на базе WNDCLASSEX

   // Заполняем структуру оконного класса
	wndClass.cbSize=sizeof(wndClass);
	wndClass.style=CS_CLASSDC;	// Поочерёдный доступ к контексту устройства (Device Context)
	wndClass.lpfnWndProc=WindowProc;		// Оконная процедура
	wndClass.cbClsExtra=0;
	wndClass.cbWndExtra=0;
	wndClass.hInstance=hInstance;			// Экземпляр окна
	wndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);		// Пиктограмма приложения
	wndClass.hIconSm=LoadIcon(NULL, IDI_APPLICATION);	// Малая пиктограмма приложения
	wndClass.hCursor=NULL;		// Курсор при наведении на окно
	wndClass.hbrBackground=NULL;	// Закрашиваем окно чёрным цветом
	wndClass.lpszMenuName=NULL;	// Дескриптор Главного меню окна. Сейчас оно не нужно
	wndClass.lpszClassName="GameClass"; // Обзываем оконный класс.
...

  • Строку wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); замени на
wndClass.hIcon=LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));

  • Строку wndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); замени на
wndClass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON2));

  • Сохрани Решение (Файл->Сохранить всё).
Закрыть
noteОбрати внимание

Если до этого в первом параметре функции LoadIcon стояло NULL (идентификатор экземпляра окна), то теперь мы прописали текущий экземпляр (инстанс) hInstance. Без этого тоже компилируется, но значок в заголовке выводится в виде знака вопроса.

Макрос MAKEINTRESOURCE конвертирует имя ресурса в строковой указатель (string pointer), который требуют многие функции.
  • Скомпилируй Решение/Проект(F5).
Если всё сделал правильно, на экране появится окно приложения. На его тулбаре будет подготовленный нами значок 16х16. Второй значок 32х32 можно увидеть, открыв папку с исполняемым файлом приложения в Проводнике (по умолчанию: C:\Users\User1\Documents\Visual Studio 2010\Projects\WinTest01\Debug).

Работа с кастомными (пользовательскими ресурсами)

Как мы уже говорили, что помимо стандартных ресурсов (значки, меню, строки и т.д.), MSVC++ также поддерживает загрузку и использование нестандартных ресурсов, например игровых карт (level maps).

Подготовка кастомного (нестандартного) ресурса

Вновь работаем с ResourceEditor by AM. Тот же проект со значками.
Предположим, что нашим кастомным ресурсом будет игровая карта. Парсить мы её сейчас, конечно, не будем. И вообще, карты не компилируют вместе с exe-шником. Важно усвоить сам принцип.
  • Найди и скачай в Интернете по запросу "Quake 2 maps" любую карту.
В нашем случае это оказался архив с картой 187city_w.zip, взятый отсюда: https://www.lahtela.com/action-quake-2-maps(external link).
  • Открой архив, например, программой 7-Zip и распакуй из него 187city_w.bsp из подкаталога maps в каталог с Проектом WinTest01.
В нашем случае: C:\Users\<Имя пользователя>\Documents\Visual Studio 2010\Projects\WinTest01\WinTest01 .
В программе ResourceEditor by AM:
  • В панели Ресурсы щёлкни правой кнопкой мыши (ПКМ).
  • Во всплывающем меню выбери Добавить из файла.
  • В появившемся диалоге выбери файл 187city_w.bsp.
Добавленный файл появится в общем списке ресурсов + в панелях Edit и Preview появится его щестнадцатеричное представление (HEX-код).
Видим, что по умолчанию ему присвоено ID 187CITY. В идеале картовый ресурс желательно переименовать по общепринятым правилам.
В панели Ресурсы выдели ресурс карты щелчком ЛКМ.
  • В панели Свойства измени ID ресурса с 187CITY на IDR_187city. Жми OK.
  • Сохрани ResEdit-проект (Файл->Сохранить).
Строкой ниже в панели свойств видим:
Type: RCDATA
  • Смени тип на BSP_MAP.
Назвать тип можно как угодно. Главное - чтобы потом можно было без труда понять, что внутри.
Кастомный ресурс добавится в общем списке в панели Ресурсы. Resource Editor разместил его в "ветке" bsp_map, согласно указанному нами типу.
  • Сохрани Resource Editor-проект (Файл->Сохранить).

  • Открой Resource01.rc в любом текстовом редакторе.
Видим, что в обоих файлах появились записи о нашем новом картовом ресурсе.
  • Добавь недостающую директиву #include "resource.h" .
  • Сохрани изменения.
  • Добавь в resource.h строку #define BSP_MAP 103:
resource.h
#define IDI_ICON1    101
#define IDI_ICON2    102

#define BSP_MAP      103


Перекомпилируем Проект Test01

Обновив всего 2 файла (Resource01.rc и resource.h) с помощью Resource Editr by AM и программы Блокнот, мы можем смело отправлять Проект WinTest01 на перекомпиляцию! При этом ни один из ресурсов даже не был добавлен в MSVC++2010. Всё делалось через однажды указанный скрипт ресурсов Resource.rc .
  • Стартуй MSVC++2010 Express, если не сделал этого раньше. Открой Решение/Проект WinTest01, созданное выше.
  • Перекомпилируй Проект WinTest01 (ПКМ по названию Проекта WinTest01 -> Перестроить).
На экране появится окно приложения. Любопытно, что файл карты 187city.bsp был полностью размещён внутри исполняемого файла WinTest01.exe (по умолчанию расположен по пути C:\Users\User1\Documents\Visual Studio 2010\Projects\WinTest01\Debug), а его размер сразу увеличился с 29 до 428 Кб.

Применение кастомного ресурса в коде приложения

После включения (посредством скрипта ресурсов) кастомного ресурса в Проект Test01 (и размещения внутри исполняемого файла приложения), мы можем получить к нему доступ из исходного кода через его (ресурса) указатель, который получают с помощью одной из трёх специальных функций:3
Таблица 1. Функции для работы с ресурсами
ФУНКЦИЯ ОПИСАНИЕ
FindResource Ищет прикреплённый к исполняемому .exe-файлу ресурс и возвращает его дескриптор (handle).
LoadResource Загружает прикреплённый к исполняемому .exe-файлу ресурс в глобальную область памяти и возвращает его дескриптор (handle).
LockResource Блокирует (locks) участок памяти с размещённым в нём ресурсом и возвращает указатель (memory pointer) на него.

Функция FindResource... Вот её прототип:
Прототип функции FindResource
HRSRC FindResource(
	HMODULE hModule, // Указатель на модуль ресурсов, обозначенный в WinMain.
	LPCSTR lpName, // Имя ресурса.
	LPCSTR lpType // Тип ресурса.
	);

...находит ресурс в блоке ресурсов исполняемого .exe-файла и возвращает его дескриптор (handle), который затем использует функция LoadResource. Параметр hModule представляет собой указатель на экземпляр (instance handle), получаемый от функции WinMain. lpName - имя ресурса (например IDR_MAP1). lpType - имя типа ресурса (например GameMaps). Как мы видели в программе ResEdit ресурсы каждого типа помещаются в отдельную папку-фильтр.
Функция LoadResource загружает ресурс в память и возвращает ещё один дескриптор, в этот раз для использования в функции LockResource.
Прототип функции LoadResource
GLOBAL LoadResource(
	HMODULE hModule, // Указатель на модуль ресурсов, обозначенный в WinMain.
	HRSRC hResInfo // Дескриптор (handle) из вызова функции FindResource.
	);

Вызовы функций FindResource и LoadResource можно комбинировать в один:
HGLOBAL hResource = LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_MAP1), "GameMaps"));

Функция LockResource принимает всего 1 параметр - дескриптор (handle), полученный от функции LoadResource:
Прототип функции LockResource
LPVOID LockResource(HGLOBAL hResData);

Дескриптор, возвращаемый функцией LockResource представляет собой void-указатель, который затем можно транслировать (cast) в любой тип данных.
В качестве примера загрузки кастомного ресурса возьмём текстовый файл readme.txt и зададим ему пользовательский тип ресурсов TEXT. Допустим, имя данного ресурса IDR_TEXT1. Данный фрагмент кода загружает ресурс в память и выводит содержимое текстового файла во всплывающем окне сообщений (Message box):
HGLOBAL hResource = LoadResource(hInst, FindResource(hInst, MAKEINTRESOURCE(IDR_TEXT1), "TEXT"));
if(hResource!=NULL)
{
	char *pText = (char*)LockResource(hResource);
	MessageBox(NULL, pText, "Text", MB_OK);
}


Итоги

Урезанность бесплатной MS Visual C++ 2010 - не помеха. Раздобыв дополнительную бесплатную программу по созданию ресурсов Resource Editor by AM, мы запросто можем "подсунуть" нашей IDE практически любой готовый ресурс.

Источники


2. Пирогов В.Ю. Ассемблер и дизассемблирование. - СПб.: БХВ-Петербург, 2006
3. Adams J. Programming Role Playing Game in DirectX 8.0 - Premier Press, 2002


Последние изменения страницы Воскресенье 24 / Июль, 2022 04:54:43 MSK

Последние комментарии wiki

No records to display

Search Wiki Page

Точное совпадение

Категории

|--> C#
|--> C++