Что нового

Получение информации из VERSIONINFO

Andrey_A

Продвинутый
Сообщения
325
Репутация
68
Подскажите как получить из исполняемого файла типа exe, dll ресурс "Version Info"
Вот файл для теста в архиве.
ResHacker отображает такую информацию и мне её нужно получить:
Код:
1 VERSIONINFO
FILEVERSION 10,0,0,5175
PRODUCTVERSION 10,0,2411,0
FILEOS 0x4
FILETYPE 0x2
{
BLOCK "StringFileInfo"
{
    BLOCK "040904E4"
    {
        VALUE "CompanyName", "Microsoft Corporation"
        VALUE "FileDescription", "Office10"
        VALUE "FileVersion", "10.0.5175"
        VALUE "InternalName", "mstore10"
        VALUE "LegalCopyright", "Copyright© Microsoft Corporation 1983-2001.  All rights reserved."
        VALUE "LegalTrademarks1", "Microsoft® is a registered trademark of Microsoft Corporation."
        VALUE "LegalTrademarks2", "Windows® is a registered trademark of Microsoft Corporation."
        VALUE "OriginalFilename", "Office10.MMW"
        VALUE "ProductName", "Microsoft Clip Organizer"
        VALUE "ProductVersion", "10.0.2411.0"
        VALUE "Built by", "MediaStoreDB"
    }
}

BLOCK "VarFileInfo"
{
    VALUE "Translation", 0x0419 0x04E4
}
}


Пытался получить так, но не получается:

Код:
#include <resources.au3>
$sFile='D:\test\xxxxx'
$string = _ResourceGetAsStringW("VERSIONINFO",16,1033,$sFile)
MsgBox(4096,'Переменная $string',$string)


Функцию FileGetVersion не предлагать - она работает криво.
.
Сообщение автоматически объединено:

Цель всего этого конечно одна - это получить данные файла, стал копать глубже и выяснилось что есть проблема в функциях: FileGetVersion() и _WinAPI_VerQueryValue()
А всё из-за того, что код языка не совпадает: в строке VALUE "Translation", 0x0419 0x04E4 написан код 041904E4, а реальные данные находятся в другом блоке: BLOCK "040904E4"
Поэтому функция _WinAPI_VerQueryValue() работает не правильно, она обращается в
Код:
DllCall('version.dll', 'bool', 'VerQueryValueW', 'ptr', $pData, 'wstr', '\VarFileInfo\Translation', 'ptr*', 0, 'uint*', 0)

и получает не тот код языка.
Наткнулся на ряд статей, которые обходят эту проблему,вот одна из них - там приведён код на ассемблере с комментариями, но я так и не смог его переложить на Autoit, а именно этот кусок:
Код:
;// Не рабочий метод получения Lang/CodePage
;// лучше гарантированно вытащить их семью строками ниже
;//         invoke  VerQueryValue,verData,<'\VarFileInfo\Translation',0>,lpBuff,lpLen

;// Здесь нужно взять из буфера "язык и кодировку" (unicode),
;// чтобы передать их в функцию VerQueryValue() как ANSI.
         mov     esi,verData    ;// начало данных
         add     esi,0x86       ;// +86h = адрес Lang/CP (источник)
         mov     edi,cp         ;// приёмник для конкатенации строк
         mov     ecx,8          ;// длина Unicode-строки
@@:      lodsw                  ;// взять 2-байта
         stosb                  ;// сохранить 1 младший
         loop    @b             ;// повторить ECX-раз..


Есть пример из справки:

Код:
#include <Array.au3>
#include <WinAPIMem.au3>
#include <WinAPIRes.au3>
$sFile='D:\test\xxxxx'
Local $pData = 0, $iSize = _WinAPI_GetFileVersionInfo($sFile, $pData,1)
If Not $iSize Then Exit
Local $aData = _WinAPI_VerQueryValue($pData)
_ArrayDisplay($aData, '_WinAPI_VerQueryValue')
_WinAPI_FreeMemory($pData)


После функции _WinAPI_GetFileVersionInfo() у нас есть: $iSize - размер ресурса "VERSION" и $pData - указатель на буфер.
Как получить реальный код языка?
Возможно кто-то имеет больше опыта чтения информации из указателя на буфер.
Сообщение автоматически объединено:

Жаль, что ответа пока нет.
Но вопрос остаётся актуальным, т.к. каждый из Вас может столкнуться с тем, что FileGetVersion() не возвращает точные данные, таких файлов не один и ни два - их тысячи (сейчас занимаюсь определением файлов по содержимому и это очевидно)
Производя поиск по сайтам, увидел (вспомнил), что уже поднимал подобную тему 8 лет назад , тогда и в голову не могло прийти, что это из-за проблем определения языка..., но за 8 лет вопрос не решился.

Раз тут никто не может помочь, у меня просьба к тем, кто знает английский язык и помочь создать эту тему на официальном форуме.
Заранее спасибо!
 
Последнее редактирование:

InnI

AutoIT Гуру
Сообщения
4,949
Репутация
1,443
Возможно, файл придётся переименовать в dll или exe


 
Автор
A

Andrey_A

Продвинутый
Сообщения
325
Репутация
68
Возможно, файл придётся переименовать в dll или exe
Спасибо за ответ, но это не решает проблему, мне в частности версия файла неважна, необходимо получать информацию полей "OriginalFilename", "InternalName", "FileDescription", именно по ним можно определить имя и тип файла...
 

Alecsis

Осваивающий
Сообщения
119
Репутация
43
Andrey_A, вот такой встречный вопрос: вам принципиально вытягивать данные из VERSIONINFO посредством именно AutoIt'а?
Если нет, то почему бы, к примеру, не устроить «случку» AutoIt-скрипта с Powershell'ом?
Что-то такое, склёпанное «на табуретке» с вашим файлом:
Код:
Opt('MustDeclareVars', 1)
#include <AutoItConstants.au3>
Local $sFile   = 'D:\Alecsis\Prog\AutoIt\_Debug\xxxxx'
Local $sPShell = 'powershell.exe $oFV = (Get-Item D:\Alecsis\Prog\AutoIt\_Debug\xxxxx).VersionInfo; ' & _
                 'Set-Clipboard $oFV.ProductName; ' & _
                 'Set-Clipboard $oFV.FileDescription -Append; ' & _
                 'Set-Clipboard $oFV.InternalName    -Append; '
RunWait($sPShell, @ScriptDir, @SW_HIDE)
Local $sResult = ClipGet()
ConsoleWrite($sResult)
ConsoleWrite(@CRLF)
Exit 0

Понятно, что сей акт = извращение, но если результат нужен «здесь и позавчера»… в общем, по-армейски :rolleyes:
И переименований в .exe/.dll не надо.
>"C:\DevTools\AutoIt3\SciTE\..\AutoIt3.exe" /ErrorStdOut "D:\Alecsis\Prog\AutoIt\_Debug\ttgetv.au3"
Microsoft Clip Organizer
Office10
mstore10
>Exit code: 0 Time: 1.015
PS: Разумеетсявсё это всего лишь иллюстрация подхода.
 
Автор
A

Andrey_A

Продвинутый
Сообщения
325
Репутация
68
Спасибо! До этого я давно дошёл и лучше не через буфер:
Код:
#Include <Encoding.au3>
#include <Array.au3>
Local $sLine,$sFile='C:\xxxxx'
Local $sPShell="PowerShell Get-ChildItem '"&$sFile&"' | Format-List -Property VersionInfo"
Local $iPID=Run($sPShell,'',@SW_HIDE,2)
Do
  Sleep(9)
  $sLine&=StdoutRead($iPID)
Until @error
$sLine=_Encoding_OEM2ANSI($sLine)
MsgBox(4096,'Переменная $sLine',$sLine)


Да и PowerShell тоже не выдаёт полную информацию...
Но все равно хотелось бы на Autoit
Будет время попробую на офф форуме на "ломаном" языке написать про неправильное определение языка )))
 

InnI

AutoIT Гуру
Сообщения
4,949
Репутация
1,443
неправильное определение языка
Язык определяется правильно. Просто для этого языка нет секции (блока).
Код:
$Lang = FileGetVersion("xxxxx.dll", "DefaultLangCodepage")
ConsoleWrite($Lang & @CRLF) ; 041904E4


При указании нужного языка значения тоже выводятся правильно. Вопрос в том, как определить тот язык, для которого указаны значения.
Код:
$FN = FileGetVersion("xxxxx.dll", "040904E4\OriginalFilename")
ConsoleWrite($FN & @CRLF) ; Office10.MMW
$IN = FileGetVersion("xxxxx.dll", "040904E4\InternalName")
ConsoleWrite($IN & @CRLF) ; mstore10
$FD = FileGetVersion("xxxxx.dll", "040904E4\FileDescription")
ConsoleWrite($FD & @CRLF) ; Office10
 
Автор
A

Andrey_A

Продвинутый
Сообщения
325
Репутация
68
как определить тот язык, для которого указаны значения
Я давал ссылку https://codeby.net/threads/re-fajly-znakomstvo-s-resursom-rt_version.76550/ на тему где это реализовано - возможно кто-то сможет это сделать на Autoit
---
Более того вопрос был о получении не только о языке, а о всей секции ресурсов VersionInfo
Раз ResHacker может, значит и Autoit тоже - только надо знать метод
Кроме известных полей "OriginalFilename"... в файл может быть записаны и другие поля "Build Number" ...
Т.е. вопрос остаётся открытым "Как получить всю информацию VersionInfo"
Сообщение автоматически объединено:

Если открыть исполняемый файл в блокноте или в другом редакторе, то можно увидеть информацию (возможно это не всегда так)
Можно получить топорным методом, тем более знаем длину VersionInfo в $iSize:

Код:
#include <WinAPIMem.au3>
#include <WinAPIRes.au3>
$sFile='C:\xxxxx'
Local $pData = 0, $iSize = _WinAPI_GetFileVersionInfo($sFile, $pData,1)
If Not $iSize Then Exit

$hFile=FileOpen($sFile,16)
$sBin=FileRead($hFile)
$n=StringInStr($sBin,'560053005F00560045005200530049004F004E005F0049004E0046004F') ; 56535F56455253494F4E5F494E464F=VS_VERSION_INFO

$sBinInfo=StringMid($sBin,$n,$iSize*2)
$ssBinInfo=BinaryToString('0x'&$sBinInfo,4)
$ssBinInfo=StringReplace($ssBinInfo,Chr(0),'')
MsgBox(4096,'Переменная $ssBinInfo',$ssBinInfo)
; Local $aData = _WinAPI_VerQueryValue($pData)
; _ArrayDisplay($aData, '_WinAPI_VerQueryValue')
_WinAPI_FreeMemory($pData)


Но я это не рассматриваю как результат и не делал парсинг этой строки, т.к. этот путь тупиковый при размере исполняемых файлов в 10, 20, 50, 100 мб.

Думаю тут все просто, но нужен знаток в структуре или извлечении данных из буфера: у нас есть: $iSize - размер ресурса "VersionInfo" и $pData - указатель на буфер - но вот как получить всю информацию?
 
Последнее редактирование:

InnI

AutoIT Гуру
Сообщения
4,949
Репутация
1,443
Реализация алгоритма из статьи для одного файла
Код:
#include <WinAPIMem.au3>
#include <WinAPIRes.au3>

$sFile = "xxxxx.dll"
$pData = 0
$iBytes = _WinAPI_GetFileVersionInfo($sFile, $pData)
$tData = DllStructCreate("byte [" & $iBytes & "]", $pData)
$bData = DllStructGetData($tData, 1)
$sLang = BinaryToString(BinaryMid($bData, 0x86, 16), 3)
_WinAPI_FreeMemory($pData)
$sVal = "Comments|CompanyName|FileDescription|FileVersion|InternalName|LegalCopyright|LegalTrademarks|OriginalFilename|ProductName|ProductVersion|PrivateBuild|SpecialBuild"
;~ $sVal &= "|LegalTrademarks1|LegalTrademarks2|Built by"
$aVal = StringSplit($sVal, "|")
For $i = 1 To $aVal[0]
  $Val = FileGetVersion($sFile, $sLang & "\" & $aVal[$i])
  ConsoleWrite($aVal[$i] & " : " & $Val & @CRLF)
Next
 
Верх