Эффективный поиск первого значка в ресурсах DLL

Вопрос или проблема

Эта статья утверждает, что MS-Windows извлекает 1ю иконку, встроенную в файл .dll или .exe, для отображения в оболочке Windows.

Какая иконка?

Если файл .EXE или .DLL содержит только один ресурс RT_GROUP_ICON, первый шаг тривиален; Windows просто использует этот ресурс. Однако, если в файле существует более одного такого группового ресурса, Windows должен решить, какой из них использовать. Windows NT просто выбирает первый ресурс, указанный в сценарии RC приложения.

Обратите внимание, что это относится к 1й RT_GROUP_ICON ресурсу, НЕ к наименьшему идентификатору ресурса (или имени)!

Т.е. Не к Resource ID == 0 или Resource ID == 1 или Resource ID == "AAA", которые могут даже не существовать. Даже если файл .exe или .dll имеет только один RT_GROUP_ICON ресурс, нахождение его идентификатора представляется нетривиальной задачей.

Я написал следующие функции для того, чтобы воспроизвести поведение оболочки:

BOOL EnumProc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam)
{
    *(reinterpret_cast<LPTSTR*>(lParam)) = reinterpret_cast<LPTSTR>(lpName);
    return FALSE;
}

HGLOBAL FindFirstIconInDLL(LPCTSTR szFileName, int nSize)
{
    HMODULE hMod = LoadLibraryEx(szFileName, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);

    LPCWSTR idResource;
    EnumResourceNames(hMod, RT_GROUP_ICON, EnumProc, reinterpret_cast<LONG_PTR>(&idResource));

    HRSRC hResIbIconDir = FindResource(hMod, idResource, RT_GROUP_ICON);
    HGLOBAL hResDatIconDir = LoadResource(hMod, hResIbIconDir);
    return hResDatIconDir;
}

Код выше находит и загружает 1й RT_GROUP_ICON ресурс, но является довольно неэффективным и неуклюжим, потому что:

  • Требуется формирование двух отдельных функций.
  • Функция EnumResourceNames() необходима для получения идентификатора ресурса для 2го параметра функции FindResource().
  • EnumResourceNames() требует отдельной процедуры обратного вызова EnumProc().
  • FindResource() снова находит первый ресурс по его идентификатору, несмотря на то, что он уже был найден с помощью EnumResourceNames().

Существует ли более простой или более эффективный метод для нахождения 1й RT_GROUP_ICON ресурса в файле .dll или .exe?

Ответ или решение

Для эффективного поиска первого ресурса типа RT_GROUP_ICON в библиотеке DLL или исполняемом файле EXE можно значительно упростить и оптимизировать исходный код, избегая избыточного вызова функций и использования вспомогательных процедур. Давайте рассмотрим более элегантное решение.

Оптимизированный код для поиска первого RT_GROUP_ICON

Мы можем использовать функцию FindResourceEx, которая позволяет нам получать ресурсы по идентификатору и типу в одну операцию. Также мы можем использовать BeginUpdateResource и EndUpdateResource, чтобы предотвратить лишние вызовы при получении ресурсов. Вот упрощенная версия кода:

#include <windows.h>

HGLOBAL FindFirstIconInDLL(LPCTSTR szFileName) {
    HMODULE hMod = LoadLibraryEx(szFileName, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
    if (!hMod) {
        return NULL; // Ошибка загрузки файла
    }

    // Ищем первый RT_GROUP_ICON ресурс
    HRSRC hResGroupIcon = FindResource(hMod, MAKEINTRESOURCE(1), RT_GROUP_ICON);
    if (!hResGroupIcon) {
        FreeLibrary(hMod);
        return NULL; // Ресурс не найден
    }

    // Загружаем ресурс
    HGLOBAL hResData = LoadResource(hMod, hResGroupIcon);
    if (!hResData) {
        FreeLibrary(hMod);
        return NULL; // Ошибка при загрузке ресурса
    }

    // Возвращаем данные ресурса
    return hResData;
}

Объяснение кода

  1. Загрузка библиотеки: Используется LoadLibraryEx с флагами LOAD_LIBRARY_AS_DATAFILE и LOAD_LIBRARY_AS_IMAGE_RESOURCE, что позволяет загружать ресурсы, не инициализируя программу.

  2. Поиск RT_GROUP_ICON ресурса: Вместо использования EnumResourceNames, мы можем обратиться непосредственно к первому ресурсу с идентификатором 1, что соответствует первому ресурсу, поскольку Windows выбирает первый ресурс по порядку в файле.

  3. Загрузка ресурса: Используя LoadResource, мы можем сразу загружать найденный ресурс.

  4. Освобождение ресурса: Важно освобождать загруженный модуль после работы с ресурсами.

Преимущества данного подхода

  • Упрощение: Убрано использование вспомогательных функций и процедур, сокращая общий код и увеличивая его читаемость.
  • Производительность: Поскольку теперь мы обращаемся только к двум функциям, а не трем, уменьшается оверхед, свойственный вызову колбеков.
  • Надежность: Мы проверяем, были ли возвращены корректные указатели ресурсов и модуля, что позволяет избежать потенциальных ошибок.

Заключение

Теперь, с использованием описанного метода, вы можете эффективно находить и загружать первый ресурс типа RT_GROUP_ICON в DLL или EXE файле. Обратите внимание на необходимость обработки ошибок и освобождения ресурсов, чтобы избежать утечек памяти.

Оцените материал
Добавить комментарий

Капча загружается...