Интеграция собственных продуктов с уже существующими системами и их окружением рождает иногда интересные решения. С задачей такого рода я столкнулся производя локализацию разрабатываемого приложения.
Вообще говоря, стояла задача по написанию утилиты для существующей системы, которая бы располагалась в той же файлово-каталожной структуре и максимально дружественно вписывалась в окружение. Существующая система, львиную долю которой составляет неуправляемый код, имеет специально выделенную папку, в которой содержатся ресурсы. Каждый конкретный набор ресурсов, характерный выбранной локали, помещён в папку с названием этой локали. Например, вся английская локаль лежит в папке en, русская в ru, немецкая в de и т.д. При этом локали могут меняться в момент выполнения основного приложения, информация о выбранном языке хранится в специальном ключе реестра. В этом нет ничего необычного, просто я сразу отвечаю на вопросы, о невозможности создании локализованной сборки заранее.
Далее, поскольку единственный путь, которым вызывается моя утилита, пункт меню в основной программе, логично было бы подгружать необходимый набор ресурсов в момент запуска. Плюс сборка всех модулей, в том числе и моего, выполняется скриптом, менять код которого у меня полномочий нет. Таким образом задача делится на две: во-первых, инструментировать код своей утилиты таким образом, чтобы он подгружал необходимый ресурс в момент запуска; во-вторых, необходимо создать инфраструктуру, позволяющую создавать наборы локализованных ресурсов с учетом принятой политики размещения и используемый build-скриптов.
Начнём с инструментации кода. Создадим класс, который будет нашим менеджером ресурсов. Далее создадим в нём функцию загрузки ресурсов из файла:
// Создаем корневое имя ресурса string resourcesBase = "MyApplication"; // Создаем путь к папке с ресурсами string location = Assembly.GetExecutingAssembly().Location; location = Path.GetDirectoryName(location); DirectoryInfo dInfo = Directory.GetParent(location); // Читаем из реестра текущую локаль и добавляем ее в путь resourcesPath += GetMainProgramLocale(); // Создаем сам ResourceManager m_ResManager = ResourceManager.CreateFileBasedResourceManager(resourcesBase, dInfo.FullName + resourcesPath, null);
Для этого вопспользуемся CreateFileBasedResourceManager, которая подгрузит из папки указанный набор ресурсов и вернет ResourceManager.
В этом же классе создадим функцию, которая будет возвращать выбранный ресурс, например строку:
public string GetString(string strID) { try { // В самом простом виде, например, так return m_ResManager.GetString(strID); } catch (MissingManifestResourceException e) { // Обработка исключительной ситуации } catch (InvalidOperationException e) { // Обработка исключительной ситуации } // и т.д. }
Далее, для удобства билда, создадим bat-файл, который будет упаковывать .resx фалы в бинарные .resource, с помощью утилиты resgen и помещать их в нужный каталог.
call "%VS80COMNTOOLS%..\..\VC\bin\vcvars32.bat" rem На вский случай вызываем vcvars для корректного вызова resgen resgen MyAppl\Localization\en\MyAppl.en-US.resx MainProgram\loc\en\MyAppl.resources resgen MyAppl\Localization\ru\MyAppl.ru-RU.resx MainProgram\loc\ru\MyAppl.resources
А запуск этого bat-файла поместим в Post Build Event.
Вот и все. Скрипт отвечающий за сборку менять не надо – у нас есть Post Build Event, а собственный менеджер ресурсов вернет нужный ресурс, подгружая необходимый набор в момент запуска приложения.




.png)