Сайт о телевидении

Сайт о телевидении

» » Создание DLL файла ресурсов для Partial CUI файла. Создание и использование только ресурсов в DLL

Создание DLL файла ресурсов для Partial CUI файла. Создание и использование только ресурсов в DLL

Resource Tuner Console (RTC) разработан для решения проблемы, с которой часто сталкиваются разработчики программного обеспечения. Продукт выполняет задачу редактирования ресурсов 32- и 64-битных исполняемых файлов в автоматическом режиме, по заранее написанному сценарию, из командной строки .

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

Редактирование Portable Executable (PE) файлов

  • Работает с 32- и 64-битными PE файлами - такими, как.EXE, .DLL, компилированные файлы ресурсов (.RES), драйвера устройств (.SYS, .ACM), элементы ActiveX (.OCX), библиотеки Borland (.DPL и.BPL), апплеты контрольной панели (.CPL), скринсейверы (.SCR) и другие.
  • Вычисление и коррекция контрольной суммы файла.
  • Изменение адреса Image Base .
  • Автоматический распаковщик UPX .
  • Возможно подключение пользовательских плагинов для обработки файлов при открытии.

Кроме функций редактирования ресурсов, Resource Tuner Console так же содержит ряд методов для изменения структуры PE файлов: обновление контрольной суммы, ребазирование DLL, обновление штампа времени и даты, создание или удаление пустой секции ресурсов.

При помощи Resource Tuner Console вы можете:

  • Добавлять или изменять File Version и Product Version Information
  • Добавлять или заменять Icons и Cursors
  • Добавлять или заменять графические ресурсы (BMP, GIF, JPEG, PNG, AVI)
  • Добавлять, заменять или удалять строковые ресурсы в исполняемых файлах
  • Добавлять или заменять манифест приложения
  • Добавлять секцию ресурсов в файлы, скомпилированные без ресурсов
  • Обрабатывать много файлов одновременно

Редактирование ресурсов по сценарию

Resource Tuner Console получает управление из файла скрипта. Использование сценария в скрипте - это единственный способ, позволяющий с достаточной гибкостью описать всё многообразие возможных операций с ресурсами различных типов и в большом наборе файлов. Сценарий также позволяет повторно использовать одни и те же операции много раз подряд без ошибок и легко адаптируется к изменению условий.

Для того, чтобы избежать сложностей с установкой и настройкой, в качестве скриптового языка по умолчанию используется VBScript , входящий в стандартную конфигурацию Windows всех версий.

  • Resource Tuner Console поддерживает любой установленный активный скриптовый язык, который работает с Windows Scripting Host
  • Resource Tuner Console поддерживает Unicode

Скачайте бесплатную ознакомительную версию!


Resource Tuner Console работает на всех версиях Windows
от Windows 2000 и XP до 7, 8 и 10.

Минимальные системные требования:
Процессор Intel Pentium® или AMD K5 166 MHz
16 MB RAM

Resource Tuner Console не устанавливает никаких дополнительных DLLs или ActiveX в системные папки Windows. Единственные папки, куда инсталлятор помещает файлы - это папка программы и специальная папка Windows для Application Data.

В состав Resource Tuner Console входят примеры, объясняющие основную функциональность программы и демонстрирующие самые популярные возможности. Ознакомьтесь с библиотекой скриптов , чтобы получить представление о том, как Resource Tuner Console может вам помочь.

Создание DLL, содержащей одни лишь ресурсы

Файл ресурсов надо скомпилировать - "rc MyResDll.rc" - и преобразовать линкером в DLL, обязательно указав флаг "/NOENTRY", т. к. эта динамическая библиотека содержит исключительно одни ресурсы и ни строки кода: "link MyRedDll.res /DLL /NOENTRY".

В Visual Studio это сделать еще проще - достаточно кликнуть по папке "Resourses" окна "File View" и добавить новый файл ресурса, который затем можно будет модифицировать визуальным редактором по своему усмотрению.

Для загрузки ресурса из DLL - в принципе, можно воспользоваться уже знакомой нам функцией LoadLibray, и передавать возращенный ею дескриптор LoadString или другой функции, работающей с ресурсами. Однако загрузку динамической библиотеки можно значительно ускорить, если "объяснить" системе, что эта DLL не содержит ничего, кроме ресурсов, и нам достаточно лишь спроецировать ее на адресное пространство процесса, а обо всем остальном мы сумеем позаботиться и самостоятельно.

Вот тут-то и пригодится функция LoadLibraryEx: ее первый аргумент, как и у коллеги LoadLibrary, задает имя динамической библиотеки для загрузки, второй - зарезервирован и должен быть равен нулю, а третий, будучи равным LOAD_LIBRARY_AS_DATAFILE, заставляет функцию делать именно то, что нам нужно - загружать DLL как базу данных (если динамическая библиотека содержит помимо ресурсов еще и код, то загрузка с этим ключом проходит все равно успешно, но функции загруженной DLL не будут доступны - только ресурсы):

#include

#include

h=LoadLibraryEx("MyResDll.dll",0,

LOAD_LIBRARY_AS_DATAFILE);

LoadString(h,1,&buf,99);

printf("%s\n",&buf);

Демонстрация оптимизированной загрузки DLL, не содержащей ничего кроме ресурсов

Эта программа компилируются точно так же, как и предыдущие примеры явной компоновки - и после запуска победно выводит на экране "Hello, Word!", подтверждая, что ресурс "строка" из динамической библиотеки был успешно загружен! Аналогичным способом можно загружать ресурсы из исполняемых файлов; с этой точки зрения они ничем не отличаются от динамических библиотек.

ООП и DLL

#include "stdafx.h"

DWORD ul_reason_for_call,

LPVOID lpReserved

//Наш код.

//Добавляем функцию Add.

if(a>b) res = a; else res = b;

Мастер нам сделал заготовку для нашей dll. Будем развивать ее. Для этого добавим 1 функцию и один класс с методом. Вот код:

#include "stdafx.h"

BOOL APIENTRY DllMain(HANDLE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

//Наш код.

//Добавляем функцию Add.

Declspec(dllexport) int Add(int a, int b)

//Добавляем класс MyClass с методом MyMax.

Declspec(dllexport) int MyMax(int a, int b){

if(a>b) res = a; else res = b;

Обратите внимание, что добавляемы функции и методы, которые мы хотим вызывать извне, вы пишем с модификатором __declspec(dllexport). Таким образом мы помечаем так называемые экспортируемые функции.

Компилируем программу. В папке debug проекта должен появится файл firstdll.dll.

Теперь пишем тестовую программу.

#include

//Подключаем необходимый заголовочный файл.

#include "..\firstdll.cpp"

cout<

cout<

Запускаем программу. Как и следовало ожидать, она выведет 22 и 11.

Функция DllMain

Большинство библиотек DLL - просто коллекции практически независимых друг от друга функций, экспортируемых в приложения и используемых в них. Кроме функций, предназначенных для экспортирования, в каждой библиотеке DLL есть функция DllMain. Эта функция предназначена для инициализации и очистки DLL. Она пришла на смену функциям LibMain и WEP, применявшимся в предыдущих версиях Windows. Структура простейшей функции DllMain может выглядеть, например, так:

BOOL WINAPI DllMain (HANDLE hInst,DWORD dwReason, LPVOID IpReserved) {

BOOL bAllWentWell=TRUE;

switch (dwReason) {

case DLL_PROCESS_ATTACH: // Инициализация процесса.

case DLL_THREAD_ATTACH: // Инициализация потока.

case DLL_THREAD_DETACH: // Очистка структур потока.

case DLL_PROCESS_DETACH: // Очистка структур процесса.

if(bAllWentWell) return TRUE;

else return FALSE;

Функция DllMain вызывается в нескольких случаях. Причина ее вызова определяется параметром dwReason, который может принимать одно из следующих значений.

При первой загрузке библиотеки DLL процессом вызывается функция DllMain с dwReason, равным DLL_PROCESS_ATTACH. Каждый раз при создании процессом нового потока DllMain вызывается с dwReason, равным DLL_THREAD_ATTACH (кроме первого потока, потому что в этом случае dwReason равен DLL_PROCESS_ATTACH).

По окончании работы процесса с DLL функция DllMain вызывается с параметром dwReason, равным DLL_PROCESS_DETACH. При уничтожении потока (кроме первого) dwReason будет равен DLL_THREAD_DETACH.

Все операции по инициализации и очистке для процессов и потоков, в которых нуждается DLL, необходимо выполнять на основании значения dwReason, как было показано в предыдущем примере. Инициализация процессов обычно ограничивается выделением ресурсов, совместно используемых потоками, в частности загрузкой разделяемых файлов и инициализацией библиотек. Инициализация потоков применяется для настройки режимов, свойственных только данному потоку, например для инициализации локальной памяти.

В состав DLL могут входить ресурсы, не принадлежащие вызывающему эту библиотеку приложению. Если функции DLL работают с ресурсами DLL, было бы, очевидно, полезно сохранить где-нибудь в укромном месте дескриптор hInst и использовать его при загрузке ресурсов из DLL. Указатель IpReserved зарезервирован для внутреннего использования Windows. Следовательно, приложение не должно претендовать на него. Можно лишь проверить его значение. Если библиотека DLL была загружена динамически, оно будет равно NULL. При статической загрузке этот указатель будет ненулевым.

В случае успешного завершения функция DllMain должна возвращать TRUE. В случае возникновения ошибки возвращается FALSE, и дальнейшие действия прекращаются.

Замечание. Если не написать собственной функции DllMain(), компилятор подключит стандартную версию, которая просто возвращает TRUE.

Пример проблемы:

HANDLE hThread; DWORD dwThreadId;

switch (fdwReason)

case DLL_PROCESS_ATTACH:

// DLL проецируется на адресное пространство процесса

// создаем поток для выполнения какой-то работы

hThread = CreateThread(NULL, 0, SomeFunction, NULL, 0, &dwThreadId);

// задерживаем наш поток до завершения нового потока

WaitForSingleObject(hThread, INFINITE);

// доступ к новому потоку больше не нужен

CloseHandle(hThread);

case DLL_THREAD_ATTACH:

// создается еще один поток

case DLL_THREAD_DETACH:

// поток завершается корректно

case DLL_PROCESS_DETACH:

// DLL выгружается из адресного пространства процесса

Функции точки входа должно выполнять только простые задачи инициализации. Они не должны вызвать функцию LoadLibrary или LoadLibraryEx (или функцию, которая вызывает эти функции), потому что это может создать циклы взаимозависимости в порядке загрузки DLL. Это может стать результатом того, что DLL начнет использоваться прежде, чем система выполнит код ее инициализации. Точно так же функция точки входа не должна вызвать функцию FreeLibrary (или функцию, которая ее вызывает), потому что это может привести к тому, что DLL начнет использоваться после того, как система выполнила код завершения ее работы.

Пример от Microsoft. Пошаговое руководство. Создание и использование библиотеки DLL (C++) Visual Studio 2013

В этом пошаговом руководстве описывается создание библиотеки динамической компоновки (DLL) для использования с C++ приложением. Использование библиотеки - хороший способ повторного использования кода. Вместо повторной реализации одних и тех же процедур в каждой программе, вы создаете их один раз и затем ссылаетесь на них из других приложений. Поместив код в библиотеке DLL, вы сэкономите место в каждом приложении, которое ссылается на этод код, а так же сможете обновлять DLL без перекомпиляции всех приложений. Дополнительные сведения о библиотеках DLL см. в разделе DLL в Visual C++.

Каждый раз, как мне надо собрать новую ресурсную dll, я судорожно начинаю искать нужную информацию. Надоело! Все, что посчитаю важным, закину сюда.

С любезного разрешения Андрея Бушмана почти полностью дублирую . Скрины, взятые у Андрея, дополню (ну просто для себя) скринами с VS2015 CommunityEdition. Собственные комментарии и уточнения буду писать курсивом.
Статья написана для AutoCAD 2009, но прекрасно срабатывает и на других версиях AutoCAD.

Графические изображения, которые используются в CUI файлах, могут браться как из внешних BMP файлов, так и из неуправляемого DLL файла ресурсов, в который эти изображения помещены. Второй способ более удобен в использовании. Здесь рассматривается, как создавать такие DLL файлы и как их использовать в CUI файлах.

AutoCAD давно (начиная с версии 2010) использует формат CUIX в качестве частичных файлов меню, представляющий собой обычный архив, расширение которого переименовано с ZIP на CUIX. Однако в нашей компании используется AutoCAD 2009 SP3, который не умеет работать с CUIX, но использует более старый формат - CUI файлы, содержимое которых представлено в формате XML.

Кроме того, начиная с версии 2015, если не ошибаюсь, в AutoCAD введены два варианта оформления - темная тема и светлая тема. Сделать иконку, которая будет видна и там, и там, очень сложно. А имя картинки менять "на лету" не самое лучшее решение. Подробнее - ниже.

Создание DLL файла ресурсов

В MS Visual Studio 2012 создаём новый проект, на основе шаблона Empty Project и назначаем ему нужное имя:

Теперь в окне Solution Explorer нажимаем правой кнопкой мыши на имени проекта и в появившемся контекстном меню выбираем пункт Properties . Назначаем нужные значения свойствам Target Extension и Configuration Type :

Кроме этого, в настройках проекта нужно изменить ещё одну опцию - No Entry Point:

Произведя обозначенные выше изменения, жмём кнопку Применить и ОК . Теперь свойства нашего, пока ещё пустого проекта настроены должным образом. На вкладке Solution Explorer , из контекстного меню проекта, выбираем пункт Add -> New Item... и в появившемся диалоговом окне выбираем шаблон файла ресурсов:

Имя файлу можно назначить любое, в нашем примере оставляем Resource.rc . Жмём кнопку Add . Добавленный нами файл будет содержать в себе перечень изображений, которые будут использоваться в наших CUI(x) файлах.

Андрей рекомендует картинки, которые будут "загоняться" в dll, помещать в подкаталог \images. Вполне разумная рекомендация, хотя и не критична. Другой вопрос, что настоятельно рекомендую все же помещать ресурсы так, чтобы для них можно было "вычислить" относительный путь. Очень помогает, если разработка ведется не на одном рабочем месте.

В качестве эксперимента добавим BMP файлы разных размеров: 16x16, 24x24, 32x32, 48x48, 64x64 и 128x128. Кнопки на палитрах инструментов должны содержать изображения размером 16x16, а кнопки, размещённые на палитрах Ribbon, имеют размеры поболее. В нашем тестовом CUI файле разместим кнопки с указанными выше разрешениями и там и там, дабы посмотреть результат использования различных размеров.

Уточнение: BMP должен гарантированно подгружаться в VS. Для пользователей GIMP не самые лучшие новости: BMP приходится создавать с глубиной цвета 24 бит и сохранять без сжатия. Если сохранить без сжатия не получается, то через пакетное преобразование с помощью FastStone Image Viewer можно легко добиться нужного результата.

Можно было бы копировать и файлы в формате PNG, однако AutoCAD 2009 не умеет в CUI файлах использовать изображения такого формата.

Уточнение : можно попытаться подключать не *.bmp, а *.ico-файлы. Потребуется, конечно, некоторая дополнительная работа, но это возможно.
В старых версиях для обеспечения "прозрачности" фона следует использовать цвет RGB 192,192,192. Старые версии AutoCAD обрабатывают его как прозрачный.
И вот еще - AutoCAD2018 уже не "понимает" bmp, и требует как раз png

Теперь возвращаемся к нашей IDE: переключаемся на вкладку Resource View .

Из контекстного меню элемента Resource.rc вызываем пункт Add Resource...

В диалоговом окне Add Resource в списке Resource type выбираем элемент Bitmap , жмём кнопку Import... и указываем BMP файлы из созданного нами ранее подкаталога \images. Получаем такой результат (вид абсолютно одинаков что для 2012, что для 2015)

Теперь для каждого элемента значение свойства ID нужно обособить двойными кавычками, чтобы их можно было успешно использовать в наших CUI(x) файлах:

Всё, теперь компилируем наш DLL файл (клавиша F6 или через меню Build -> Build Solution ) .

Конец цитирования, дальнейшие действия по использованию DLL пропишу потом, если вдруг понадобится.

Теперь кое-что из собственного (и не только собственного) опыта.

Подобная технология прекрасно работает при использовании частичных файлов адаптации. Если попробовать собрать такую dll для корпоративного файла адаптации, то заменить уже имеющуюся dll не получится: сначала все пользователи должны выключить AutoCAD.

Имя dll должно совпадать с именем cui(x) файла. Крайне желательно положить dll рядом с cui(x) файлом. Теоретически можно ее просто закинуть в пути поддержки, но лично я подобным никогда не занимался - потом замучаешься искать.

По поводу тем оформления, как было обещано, на основе информации на форуме ADN-CIS и справки Autodesk :

  1. В версиях AutoCAD без темы оформления ресурсы забирались из dll, имеющей то же имя, что и файл cui(x)
  2. В версиях AutoCAD с темами оформления необходимо создать отдельный ресурс DLL для каждой темы: светлой и темной. Соответствующий DLL ресурса используется на основе того, какая тема в настоящий момент применена к пользовательскому интерфейсу. По соглашению об именовании файлов DLL требуется использовать то же имя, что и имя файла адаптации, но также необходимо добавить суффикс "_light" для файла DLL, который будет использоваться в качестве светлой темы. Например, если загружен файл CUIx под именем mymenu.cuix , AutoCAD выполняет поиск файла библиотеки ресурсов mymenu_light.dll , если используется светлая тема, и файла mymenu.dll , если используется темная тема.

Последовательность компиляции библиотек с учетом варианта темы оформления отлично описал Дмитрий Загорулькин :

CUIX-файл делал один на версии 2014-2016, проблем с этим не было. Также, сделал два C++ проекта с ресурсными иконочными DLL - один для темной схемы, второй - для светлой. В настройках проектов в разделе "Build Events - Post Build Events" можно настроить события таким образом, чтобы создаваемые dll файлы после сборки сразу копировались в нужные папки под нужным названием. В папку для 2014 версии - только "светлая" dll без суффикса, в папку для версий 2015-2016 - "светлая" с суффиксом "_light", "темная" - без суффикса. Я сперва так делал, а потом настроил то же самое но уже в проекте WIX-инсталлятора. В общем, каким образом ни делай, есть возможность один раз настроить и уже потом не думать откуда и какие файлы нужно скопировать и как назвать - все делается автоматически.
Как AutoCAD определяет, какой DLL подгружать:
- в версиях до 2014 включительно подгружается только та DLL с иконками, название которой совпадает с названием файла CIUX. К примеру, есть файл: "MyTools.cuix", иконки для этого файла ищутся в файле "MyTools.dll".
- в версиях 2015-2016 для темной схемы подгружается DLL, совпадающая с названием CUIX, для светлой - название с суффиксом "_light". Пример: "MyTools.cuix", иконки для темной схемы: "MyTools.dll", для светлой: "MyTools_light.dll".
Еще один неочевидный момент. Можно создавать один ICO файл для иконок 16х16 и 32х32. Для этого внутрь ICO нужно поместить два соответствующих изображения. В настройках кнопки указывается при этом одинаковый ID изображения для большой и маленькой кнопки. AutoCAD сам выберет иконку в зависимости от размера выводимого изображения. Этот прием позволяет сильно сократить количество ICO файлов в ресурсах C++ проекта.

К вопросу об использовании ico-файлов. Тут все просто - читаем статью Использование ресурсной dll для CUIx с прозрачными растрами . Скажу честно - я пока подобным не игрался, но поверю Диме.
===
Добавлено:
Цитата из статьи по поводу подключения ico-файлов:

Достаточно просто добавить новый ico-ресурс при помощи редактора Visual Studio и отредактировать свойства “ICO” ресурса в “RCDATA” как показано ниже: