Смена иконки стороннего приложения средствами Win API

аватар: POZiTRON
Звание: Энтузиаст
Сообщений: 278

Приветствую!

Ранее, я уже поднимал на этом форуме тему смены ресурсов версии и иконки у стороннего приложения, достиг определенных успехов в то время. Но свой проект установочной программы я так и не доделал, не срослось кое что в то время.

Сейчас решил поднять идею проекта, возродить так сказать. Ибо кое чего в Smart Install Maker (который я использую ооочень долгое время уже) - не хватает, а остальные средства слишком "тугие" или громоздкие.

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

Версию exe-шника, я меняю, что называется "с пол пинка". Имеется на руках, сделанный давно модуль, позволяющий элементарно парсить и компиллировать ресурс версии.
Этот самый ресурс, обновляется буквально в 3 строчки - BeginUpdateResource, UpdateResource, EndIpdateResource. Прекрасно работает и в х32 и в х64.

Иконка
Доставила проблемы... Просто так ее не обновишь. Есть модуль acWorkRes, который обсуждался тогда давно. Он работает, но только в х32 и только с х32 экзешниками. При компилляции в х64 выскакивает несравнимо большое количество ошибок. При работе с х64 екзешниками, сейчас уже не помню толи ошибка, толи файл бьется.

Где можно почитать, как вообще правильно устанавливать иконку и о структуре ресурсов?
Суть в чем, на самом деле есть два типа ресурса: RT_ICON и RT_ICON_GROUP. Так вот, толи это на самом деле один ресурс, толи еще что - но при удалении любого из них, во всяком случае в Res Hackere так - удаляется и другой.

acWorkRes как-то напрямую работает с файлом, модифицирует какие то указатели и еще что-то. Разобрать сложно. Но каким то образом, если ICO файл содержить несколько иконок разных размеров - добавляет сколько надо ресурсов типа RT_ICON.

Как вообще быть в этом случае?

аватар: jimmyjonezz
Звание: Мастер
Сообщений: 2470

Я одно время писал статьи по теме ресурсов... попробуй поискать подшивки статей!

Может наведет на мысль...
UPD: 1

__________________

welcome to stravaganza.ru

аватар: POZiTRON
Звание: Энтузиаст
Сообщений: 278

Приветствую!

Все таки разобрался, как устроены ресурсы, отвечающие за иконку в PE файлах. Параллельно понял устройство и *.ico файлов, ибо это практически одно и то-же. Разница в 2 байтах заголовка сущности.

Это RT_GROUP_ICON с именем `MAINICON` и один или несколько ресурсов RT_ICON с целочисленным именем, скажем от 1 до n.

Сделал свой класс. Который, идеально читает как *.ico файл, так и PE. Отображает количество сущностей, данные и т.д.

Идеально сохраняет в *.ico файл, с любым количеством сущностей. Хоть 1, хоть 10.
Контрольная сумма совпадает полностью, с оригинальной *.ico иконкой (которую, я потом поместил в PE-Донора, при компилляции).

Вот с сохранением в ресурсы PE, собственно ради чего все это и писалось, проблема. Да будь одна неладна...

Иконку из одной сущности, маленького размера, взятая либо с другого PE файла, либо с иконки - записывает.

Иконку из 10 сущностей, красивую, Vista Ready... Не записывает.
ResourceHacker отказывается показывать RT_GROUP_ICON и RT_ICON с 6 по 10.
При этом, если не записывать иконки - ресурс RT_GROUP_ICON записывается отлично, побайтно (все контрольные суммы совпадают) равен аналогичному ресурсу в PE-Доноре иконке.
При этом, иконки с 1 по 5 - записываются хорошо, правда не дошли руки проверить контрольные суммы.

Собственно код:
Второй вариант, первый был запутанный, вызывающий общие процедуры, использующиеся при сохранении в *.ico файл, и работающие 100% в том случае.

procedure TdsIconResource.SaveToResource(aFileName: WideString; aLanguage: LANGID);
var
mainheader: TdsIconHeader;
entityheader: TdsIconEntityHeader;
entityid: Word;

aStream: TMemoryStream;

hUpdate: THandle;

i: Integer;

entity: TdsIconResourceEntity;

procedure SpecialWrite(const Buffer; Count: Longint);
var
Writed: LongInt;
begin
Writed:=aStream.Write(Buffer, Count);

if Writed <> Count then
raise Exception.Create(Format('Writed fail, %d\%d', [writed, count]));
end;
begin
hUpdate:=BeginUpdateResourceW(PWideChar(aFileName), False);

aStream:=TMemoryStream.Create;

mainheader.wReserved:=0;
mainheader.wType:=FType;
mainheader.wCount:=FCount;

SpecialWrite(mainheader, SdsIconHeader);

for I := 0 to FCount - 1 do
begin
entity:=TdsIconResourceEntity(FEntities.Objects);

entityheader.bWidth:=entity.FWidth;
entityheader.bHeight:=entity.FHeight;
entityheader.bColorCount:=entity.FColorCount;
entityheader.bReserved:=0;
entityheader.wPlanes:=entity.FPlanes;
entityheader.wBitCount:=entity.FBitCount;
entityheader.dwBytesInRes:=entity.FData.Size;

SpecialWrite(entityheader, SdsIconEntityHeader);

entityid:=i+1;

SpecialWrite(entityid, SizeOf(Word));

entity.FData.Position:=0;

if not UpdateResourceW(hUpdate, RT_ICON, MAKEINTRESOURCE(entityid), aLanguage, entity.FData.Memory, entity.FData.Size) then
raise Exception.Create('not UpdateResourceW~Icon~, '+IntToStr(GetLastError));
end;

aStream.Position:=0;

if not UpdateResourceW(hUpdate, RT_GROUP_ICON, 'MAINICON', aLanguage, aStream.Memory, aStream.Size) then
raise Exception.Create('not UpdateResourceW~Group~, '+IntToStr(GetLastError));

if not EndUpdateResourceW(hUpdate, False) then
raise Exception.Create('not EndUpdateResourceW, '+IntToStr(GetLastError));

FreeAndNil(aStream);
end;

В чем может быть проблема?
[I]Ошибок никаких не возникает, значит все функции возвращают истину!

Есть ли какие то ограничения у BeginUpdateResource, UpdateResource, EndUpdateResource?
Я ведь понимаю правильно, что можно UpdateResource - вызывать столько раз, сколько нужно?
Пробовал каждый раз вызывать эту "святую троицу", так вообще - все ресурсы затерлись к чертовой бабушке! Были только иконки с 6 по 10!
(Что кстати странно...)

аватар: Va-Bank
Звание: Гуру
Сообщений: 8261

Оффтоп.
Интересует вопрос. Я может чего-то не допонял, но для чего все это. В чем и где это может пригодиться?

__________________

Используя трассировку кода, можно избежать ламерских вопросов!

]]>]]>

аватар: POZiTRON
Звание: Энтузиаст
Сообщений: 278

Va-Bank написал(а):

Оффтоп.
Интересует вопрос. Я может чего-то не допонял, но для чего все это. В чем и где это может пригодиться?

В 1 сообщении написано.

аватар: Va-Bank
Звание: Гуру
Сообщений: 8261

Да естественно я прочитал. Все что я понял, так это то что ты меняешь иконку у стороннего приложения, только для чего это нужно? Smile

__________________

Используя трассировку кода, можно избежать ламерских вопросов!

]]>]]>

аватар: POZiTRON
Звание: Энтузиаст
Сообщений: 278

Va-Bank написал(а):

Да естественно я прочитал. Все что я понял, так это то что ты меняешь иконку у стороннего приложения, только для чего это нужно? Smile

Решил возродить старую идею о написании своего инсталлятора.

аватар: POZiTRON
Звание: Энтузиаст
Сообщений: 278

Кажется нашел...
Причину, по которой код отказывался правильно сохранять иконку (точнее ее потроха) по разным, нужным, ресурсам PE файла.

Все дело было, в сущности
Размером 256х256 (Vista Ready Icon)!

По каким-то странным причинам, именно из-за этого бьется ресурс(ы). Более того, при попытке отобразить эту иконку (Build Single Ico -> Stream -> TIcon -> TImage) - выдается AV!

С другими разрешениями, вплоть до х128 включительно - работает как часы.

Вот небольшая тестовая программка:

]]>Скачать]]>

Все кнопки без исключения работают, но на мой взгляд - то ради чего, все делалось - это последняя кнопка.
Удаляет старый *.exe файл (с именем "Test.exe"), копирует на его место новый (Источником служить Bolvanka_Z.exe) и заменяет иконку!

Но что делать с х256?
Какие могут быть идет по этому поводу? Опять-же, Delphi отлично компиллирует это дело. Пример, как раз в IconResource.exe, иконка с индексом 6!

Ага! 6 иконка! Вот после записи ее и слетало все)

P.S.
Если отображает количество цветов, равное 0 - это значит, что больше 8 миллионов цветов.
Если отображает размер 0 - это 256.
В архив входят несколько иконок, в том числе многослойные.