Побочный эффект VerQueryValue
От: Deff  
Дата: 10.02.05 18:32
Оценка:
недавно столкнулся с интересной проблемой: секция ресурсов моего exe-файла (в силу специфики задачи) содержит два экземпляра RT_VERSION, один из которых имеет стандартный идентификатор 1, а второй имеет строковое название PROD_DATA. Работать с GetFileVersionInfoSize, GetFileVersionInfo возможно только для ресурса 1, а
PROD_DATA нужно обрабатывать с помощью функций работы с ресурсами.
Работаю по схеме
FindResource -> LoadResource -> LockResource. Размер получаю с помощью SizeofResource.
Все благополучно, пока не начнется работа с VerQueryValue.
Использование VerQueryValue с указателем от LockResource приведет к исключению ACCESS_VIOLATION.
Причем исход может быть фатальным или нет, в зависимости от того, что мы хотим получить от ресурса: указатель на VS_FIXEDFILEINFO или на таблицу языков и кодировок. На опыте — попытка получить поле, любое, например OriginalFilename дает ACCESS VIOLATION и ломает программу.
При этом наблюдается запись в память (за пределами ресурса) значения поля OriginalFilename, переведенного в ANSI. Это выходит за рамки написанного в MSDN.
Возникло подозрение.. Делаю так: работаю со стандартной версией стандартными функциями, а потом функциями Resource API и выясняется, что GetFileVersionInfoSize != SizeofResource. Мне не сразу пришло в голову в чем дело, но похоже, ребята из Редмонда предположили, что за вызовом GetFileVersionInfoSize следует получение нужного куска памяти — буфера. Полагаясь на это они дают размер заведомо бОльший размера ресурса, что позволяет им использовать "лишнюю" область как временный буфер.
Некрасиво, но не мне судить. Проблема в том, что если не работать с GetFileVersionInfo, а использовать стандартные функции работы с ресурсами то выделять память (казалось бы) вообще не нужно. Но VerQueryValue ломает эти представления.
Задача моя не изменилась и мне по прежнему нужно хранить два блока RT_VERION.
А значит, чтобы сработали принятые в VerQueryValue предположения необходимо выделить отдельный блок памяти и скопировать данные ресурсы в него. Причем этот блок памяти заведомо больше размера ресурса (чтобы, повторюсь, создать буфер для VerQueryValue). Вопрос, каким же должен быть этот размер?
Вот мой вариант решения этого вопроса, я не стану объяснять деталей. Я собрал сведения от GetFileVersionInfoSize() и SizeofResource() нескольких приложений (имеющих версию, VS_VERSIONINFO).
Легко получить простую системку и получить:
GetFileVersionInfoSize == SizeofResource * 2 + sizeof(VS_FIXEDFILEINFO) — 44
Я могу ошибаться, да и трактовки могут быть разные. Правильным было бы полезть вглубь DLL и разобраться с формулой по асм-коду, но мне не хватает смелости. Я путаюсь уже при подготовке стека и обработчиков исключений, так что не судите строго.
Интересно, если кто сталкивался с этой проблемой — как она решалась, и если подобным образом, как вычислялся необходимый минимальный размер буфера.

и напоследок, при внесении описанных изменений создается достаточного размера буфер для VerQueryValue и функция отрабатывает без ACCESS_VIOLATION. А о том как она использует буфер вам больше расскажет наблюдение за дампами памяти выделенного блока (в зоне старших адресов).

Надеюсь не off-topic
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.