Статья
Версия для печати
Обсудить на форуме (2)
Разделение ресурсов в MS Visual С++ .NET проекте.


Автор: Джон.
"Товарищи! Я, конечно, не лектор. Укротитель я." (c)

Недавним темам на форуме, всколыхнувшим
воспоминания, посвящается. ;-)


Конечно, неплохо было бы размусолить кучу примеров когда это нужно, а когда нет, но при наличии отсутствия, как любил говаривать один литературный герой, кучи свободного времени буду краток. Предлагаемый способ не претендует на окончательное и 100% решение данной проблемы и, если не даёт 100% автоматизацию, то во всяком случае позволяет сильно облегчить ручной труд.


Как всё начиналось. (можно пропустить)

Есть большой проект Visual C++ .NET 2003 с кучей ресурсов (rc-файл 300 КБ, ~7200 строчек, компилированная версия ресурсов весит почти 4 МБ, exe - 10M), который ведётся на протяжении нескольких лет. В разработке участвовала целая куча программеров, из которых "иных уж нет, а те далече".

Задание.
На основе существующего кода и ресурсов создать облегчённую версию с ограниченными функциями: выкинуть пару дюжин диалогов, битмапов, как основных пожирателей места, кнопок, меню и тд; добавить пару диалогов и меню.
При этом проект (код) должен оставаться в единственном, насколько это возможно, уникальном экземпляре. Причём разработка продолжается и все изменения, затрагивающие общие объекты, должны находить отображение в обоих версиях. "Фи, да как нефиг делать", - воскликнут додиезники. Но тут им обломается - на выходе должен быть единственный ЕХЕ-файл. К сожалению, разбить на модули (даже если бы это было возможно практически!) нельзя. Да и гибкости такой у сиплюплюснутых всё равно нет.

Ну, с кодом вроде всё понятно: расставил где надо #ifdef да #ifndef, и готово. А вот как с ресурсами быть?


"Ближе к телу." (с)

Переходим к пошаговой инструкции (мне так проще излагать, а вам, наверное, читать) на примере небольшого проекта. В нём мы создадим базу для раздельных ресурсов и удалим несколько кнопок из ToolBar.

1. Создаём проект (или используем готовый), типа MDI - для пущей сложности, называем его TestPrj.

2. Добавляем необходимые конфигурации. Обычно стандартный мастер создаёт две конфигурации Debug и Release. Создадим новые Debug LT и Release LT на их основе.

  • Меню -> Build -> Configuration Manager.
  • Из списка Active Solution Configuration: выбираем <New...>
  • В поле Solution Configuration Name: впечатываем Debug LT
  • В списке Copy Settings from выбираем Debug
  • Подтверждаем Ок
  • Повторяем действия и создаём конфигурацию Release LT на основе Release

3. Теперь изменим эти две новые конфигурации.
Внимание! Перечисленные ниже действия необходимо проделать последовательно для обеих конфигураций: Debug LT и Release LT.
   
  • Меню -> Project -> Properties
  • В С/С++ -> Preprocessor -> Preprocessor Definitions добавляем LT_VERSION
  • Изменим имя выходного файла для LT конфигурации
  • Linker -> General -> Output File: изменим $(OutDir)/$(ProjectName).exe на $(OutDir)/$(ProjectName)_LT.exe

Добавим ключик для ресурсов

  • В Resources -> General -> Preprocessor Definitions добавляем LT_VERSION

И поменяем имя файла для компилированных ресурсов
   
  • Resources -> General -> Resource File Name изменим $(IntDir)/$(InputName).res на $(IntDir)/$(InputName)_LT.res

Собственно, ничего нового и служит только логическим началом. Теперь начинаем разделять ресурсы.

4. Идём в папку проекта и делаем копии файлов.

  • TestPrj.rc -> TestPrj_LT.rc
  • Resource.h -> Resource_LT.h

5. Редактируем файлы в любом доступном текстовом редакторе.
В Resource_LT.h в блоке

Код:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TestPrj.rc
//

Меняем TestPrj.rc на TestPrj_LT.rc .
Вот так он должен выглядеть.

Код:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TestPrj_LT.rc
//

Сохраняем файл. Несмотря на то, что эта строчка закомментирована, она читается парсером редактора ресурсов, посредством этой строчки обеспечивается обратная связь: .h -> .rc .
Изменения в TestPrj_LT.rc. Ищем в тексте включения resource.h и меняем их на resource_LT.h. Обычно таких включений два:

Код:
#include "resource.h"
Код:
#include "resource_LT.h"

Код:
1 TEXTINCLUDE  
BEGIN
"resource.h\0"
END
Код:
1 TEXTINCLUDE  
BEGIN
"resource_LT.h\0"
END

Сохраняем файл.

6. Добавим новые файлы в проект.

7. В Solution Explorer кликаем правой кнопкой на TestPrj_LT.rc и в контекстном меню выбираем Properties.

  • В списке Configuration: выбираем Multiple Configurations.
  • Выделяем в списке всё, кроме LT-конфигураций (в реальном проекте их может быть больше), в нашем случае Debug и Release -> OK
  • General -> Excluded From Build -> Yes

Таким образом, мы исключили LT-ресурсы из "НЕ"-LT-компиляций.

8. Аналогично исключаем TestPrj.rc из LT-компиляций. Для этого в Multiple Configurations выделяем LT-конфигурации.
Внимание! Обычно при добавлении файлов в проект Excluded From Build у них установлен в No. По косвенным сведениям, это не всегда так. Поэтому проверьте эту опцию для нужных конфигураций.
В принципе всё. Разделение ресурсов закончено. Нормальная сборка и сборка LT дадут два одинаковых файла.
Теперь их можно раздельно редактировать. Добавим последний штрих.

9. Проблема заключается в том, что ToolBar используют картинки прозрачно для пользователя. Редактор ресурсов динамически создаёт и изменяет готовый битмап. Это же относится и к другим ресурсам, использующим "внешние" файлы (bmp, ico, cur). Если предусматриваются изменения этих ресурсов в различных конфигурациях, то их тоже нужно клонировать.
На данном этапе у нас есть два ресурса, которые работают с одним и тем же битмапом. Оба ToolBar, и в нормальной версии, и в LT работают с Toolbar.bmp. Сделаем копию этого файла и назовём её Toolbar_LT.bmp. Добавим её в проект.
Теперь идём в редактор ресурсов Resource View и в LT-ресурсах (TestPrj_LT.rc) -> ToolBar переопределяем файл для IDR_MAINFRAME:

  • Filename: res\Toolbar.bmp -> res\Toolbar_LT.bmp
   
Всё. Теперь их можно изменять абсолютно независимо друг от друга. Удалим несколько кнопок и откомпилируем проект. Выбирая ту или иную компиляцию, получим разные ресурсы.
На уровне кода используются проверки #ifndef LT_VERSION или #ifdef LT_VERSION. Если надо исключить полностью .cpp файлы - лучше воспользоваться опцией Excluded From Build, как мы это проделали с rc-файлами. Её можно устанавливать сразу у нескольких выделенных файлов.
Редактирование текстовых ресурсов, меню и диалогов не требует никаких дополнительных действий. Если что-то случайно удалили - всегда можно скопировать этот ресурс из другой компиляции. Копирование можно использовать и при добавлении общих ресурсов. Например, можно полностью добавить диалог, или только его часть. Вроде, всё... Ничего не забыл?

Copyright 2008. Все права принадлежат Клубу программистов "Весельчак У"
Версия для печати
Обсудить на форуме (2)