2016-12-29 1 views
0

Мне просто интересно, есть ли API в Windows для загрузки HICON из массива байтов (буфер)? Предположим, что я загрузил файл *.ico, и у меня есть содержимое этого файла в каком-то буфере. Я хочу иметь возможность создать HICON из этого буфера.Загрузить HICON из буфера (* .ico файл)

Можно загрузить HICON с *.ico, который находится на жестком диске, поэтому я предполагаю, что должен быть такой же простой способ сделать это из буфера памяти?

До сих пор я нашел только 2 решения, но ни один из них не подходит для меня.

Первый involved ATL usage and GDI+ (Я использую Rust и у меня нет привязок к GDI +).

Второй был основан на использовании LookupIconIdFromDirectoryEx() и CreateIconFromResourceEx(). Сначала я позвонил LookupIconIdFromDirectoryEx(), чтобы получить смещение для правильного значка, а затем попытался позвонить CreateIconFromResourceEx()CreateIconFromResource()), чтобы получить HICON, но во всех случаях я получаю значение NULL, в результате возвращает 0. Мое использование этих функций было основано на this article (я попытался передать не только 0 в качестве второго параметра, но и размер буфера массива, исключая смещение, но он все равно не работает).

Единственное оставшееся решение, которое я имею в виду, состоит в том, чтобы вручную проанализировать файл *.ico, а затем извлечь из него PNG-изображения, а затем использовать описанный подход here, чтобы создать значок из изображения PNG. Но это похоже на обходное решение (Qt использует подобный подход, хотя, возможно, они не смогли найти другое решение). Существуют ли более простые методы (возможно, некоторые вызовы WinAPI), чтобы все было сделано?

UPD. Вот некоторый тестовый код, который я попробовал (у вас должен быть значок, чтобы запустить пример без сбоев).

#include <cstdio> 
#include <cstdlib> 
#include <Windows.h> 

#pragma comment(lib, "User32.lib") 

int main() 
{ 
    // Read the icon into the memory 
    FILE* f = fopen("icon.ico", "rb"); 
    fseek(f, 0, SEEK_END); 
    long fsize = ftell(f); 
    fseek(f, 0, SEEK_SET); 
    char* data = (char*)malloc(fsize + 1); 
    fread(data, fsize, 1, f); 
    fclose(f); 

    static const int icon_size = 32; 
    int offset = LookupIconIdFromDirectoryEx((PBYTE)data, TRUE, icon_size, icon_size, LR_DEFAULTCOLOR); 
    if (offset != 0) { 
     HICON hicon = CreateIconFromResourceEx((PBYTE)data + offset, 0, TRUE, 0x30000, icon_size, icon_size, LR_DEFAULTCOLOR); 
     if (hicon != NULL) { 
      printf("SUCCESS"); 
      return 0; 
     } 
    } 

    printf("FAIL %d", GetLastError()); 
    return 1; 
} 
+0

Похоже, что в вашем коде есть ошибка. Мы не можем вам помочь, если вы не предоставите [mcve]. – IInspectable

+0

Исправьте свой код, а не сдадитесь –

+0

@IInspectable, я не добавил его изначально, потому что он был в основном таким же, как и в статье, которую я связал. Но теперь я обновил описание и прикрепил туда исходный код. В моем случае он всегда печатает «FAIL 0». – ScienceSE

ответ

-2

Я нашел решение. Фактически после небольшого исследования выяснилось, что код, который я разместил внутри образца, действительно правильный.

В функции WinAPI есть ошибка LookupIconIdFromDirectoryEx(). Я заметил, что для некоторых значков я могу получить правильный значок и настроить его, но для других он терпит неудачу либо на более поздней стадии CreateIconFromResourceEx(), либо раньше на LookupIconIdFromDirectoryEx(). Я заметил, что иногда функция не находит значок, даже если значок находится внутри файла. Иногда функция возвращала одно и то же значение для разных значков внутри файла значка.

Я провел несколько раундов тестов и проанализировал формат каждого файла значка на основе format definition. Затем я сравнил фактические смещения с значениями, возвращаемыми LookupIconIdFromDirectoryEx().

Предположим, у нас есть 2 значка: A и B.

A значок в моем случае содержал 5 изображений, записи внутри файла ICO были размещены в следующем порядке:

  1. 256x256 PNG
  2. 128x128 BMP
  3. 64x64 BMP
  4. 32х32 BMP
  5. 16x16 BMP

В B значок содержал 7 изображений, они были помещены в следующем порядке:

  1. 16x16 BMP
  2. 32x32 BMP
  3. 48x48 BMP
  4. 64x64 BMP
  5. 128x128 BMP
  6. 256x256 BMP

Результаты LookupIconIdFromDirectoryEx() fo r каждый из значков можно найти ниже.

Иконка A:

Иконка B:

  1. НЕ НАЙДЕНО (функция не удалось, и вернулся 0)
  2. НЕ НАЙДЕНО (функция не удалось, и вернулся 0)
  3. НЕ НАЙДЕНО (функция не удалось, и возвращенный 0)

Я проанализировал фактический формат, согласно definition in wikipedia (приведенные ниже таблицы содержат записи значков, каждая строка - отдельная запись, каждый столбец является полем для этой записи) для обоих файлов значков.

Фактическое расположение A является:

W  H  * * * **  SIZE  OFFSET 
------------------------------------------------ 
0  0  0 0 1 32  43253 86 
128 128 0 0 1 32  67624 43339 
48 48 0 0 1 32  9640  110963 
32 32 0 0 1 32  4264  120603 
16 16 0 0 1 32  1128  124867 

Фактическое расположение B является:

W  H  * * * **  SIZE  OFFSET 
------------------------------------------------ 
16 16 0 0 0 32  1128  102 
32 32 0 0 0 32  4264  1230 
48 48 0 0 0 32  9640  5494 
64 64 0 0 0 32  16936 15134 
128 128 0 0 0 32  67624 32070 
0  0  0 0 0 32  270376 99694 

Таким образом, вы можете ясно видеть, что в случае A только смещение для первого изображения был корректным, смещения для других изображений были неправильными и равными ... размеру третьего изображения (??), может быть, просто совпадение.

В случае второго изображения все смещения были правильными, пока мы не достигли изображения 128x128, которое даже не найдено.

Обычная вещь между этими двумя случаями заключается в том, что функция начала вести себя странно после разбора значка 128x128, и вот интересная вещь - посмотрите размер значка 128x128 и сравните его с размером других изображений. В обоих случаях размер изображения 128x128 не помещается в 2 байта. Сразу после разбора значка, в котором размер был больше 2 байтов, поведение функции не определено. Судя по этим данным, я могу предположить, что где-то в коде они ожидают, что размер значка не может превышать 2 байта. Если размер больше, поведение не определено.

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

Так что я могу определенно сказать, что есть ошибка внутри LookupIconIdFromDirectoryEx() реализация.

UPD. Иконы можно найти здесь: A icon, B icon.

+0

Очень маловероятно, что в Windows API есть ошибка. Скорее всего, вы неверно истолковываете результаты. Значки с изображениями PNG сильно отличаются от значков с изображениями BMP. Раймонд Чен имеет многосерийную серию в формате файла ICO. [Эволюция формата файла ICO, часть 4: изображения PNG] (https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473) может помочь в понимании результатов, которые вы наблюдаете. – IInspectable

+0

@Инспективный, но результаты показывают, что он работает неправильно, даже если я использую значок, который содержит только изображения BMP. Посмотрите на файл значка 'B' в моем ответе, функция не удалась, когда я попытался найти значок размером более 128x128 пикселей. – ScienceSE

+0

Сначала я спросил о способе загрузки «HICON» из файла ICO, но, похоже, нет других надежных решений (по крайней мере, я не слышал ответов от других, я также не смог найти лучшего решения себя). Единственный способ, который работал для меня, - это тот, который я описал. Затем я сделал небольшое исследование, чтобы найти причину, по которой он терпит неудачу. Странно, что мой ответ получил так много голосов, не объясняя причины для них. – ScienceSE

Смежные вопросы