Нам потребуются «инструменты». А что у нас есть в КаВо (CA-Visual Objects)? А почти ничего – класс WinDLLVersion. Можно поискать по сторонним продуктам. И найти аналогичный класс в библиотеке «bBrowser», а в «FabTools» – класс FabFileVersion, который получается при выполнении функции FabGetFileVersion(). Увы, их возможностей – недостаточно… Как сказал, мой знакомый, «обнять и плакать».
Стандартный класс WinDLLVersion даёт только самую примитивную информацию: старший и младший номер версии, номер сборки, признак работоспособности в NT-среде. FabFileVersion – значительно лучше, но имеет свои ограничения и недоработки: даётся не вся информация о версии, нет информации об атрибутах (хотя бы о времени создания и модификации) файла, читается только первая страничка описания версии (а ведь бывает и мультиязычное описание). Поэтому, я решил написать свой класс.
Работу начал со стандартного класса WinDLLVersion. Открыл SDK, просмотрел, написал код и проверил его работу. И тут выяснилось, что он хорошо работает с .dll-файлами самого Windows, но применить его к .dll-кам, создаваемым КаВо – не получалось. Давайте глянем, почему так:
ASSIGN DLL( cDLL ) CLASS WinDLLVersion
LOCAL hDLL AS PTR
LOCAL hFunc AS PTR
LOCAL pVersionInfo IS _WINDLLVERSIONINFO // RvdH 060413 changed from AS to IS
SELF:cDLLName := cDLL
IF ! ( hDLL := GetModuleHandle( PSZ( cDLL ) ) ) == NULL_PTR
IF ! ( hFunc := GetProcAddress( hDLL, PSZ( "DllGetVersion" ) ) ) == NULL_PTR
pVersionInfo.cbSize := _SIZEOF( _winDLLVERSIONINFO )
IF PCALL( hFunc,@pVersionInfo ) == 0
SELF:MajorVersion := pVersionInfo.dwMajorVersion
SELF:MinorVersion := pVersionInfo.dwMinorVersion
SELF:Build := pVersionInfo.dwBuildNumber
SELF:PlatformId := pVersionInfo.dwPlatformId
ENDIF
ENDIF
ENDIF
RETURN cDLL
Здесь мы читаем сразу два ответа:
- .dll-файл заранее должен быть загружен в память.
- В файле должна быть функция «DllGetVersion».
Первое можно решить, используя функцию LoadLibrary(). А где взять DllGetVersion()? В КаВо её нет. Поэтому, пишем сами. Я нашёл код на Си и перевёл:
STATIC FUNCTION DllGetVersion( pDVI AS _winDLLVERSIONINFO ) AS PTR CALLBACK EXPORT LOCAL // Pencil 03.02.2014
// Description:
// Parameters :
// Returns :
LOCAL pRet AS PTR
// LOCAL pszName := String2Psz( "Test.dll" ) AS PSZ
LOCAL pszName := String2Psz( VER_ORIGINALFILENAME ) AS PSZ
LOCAL pBuffer AS PTR
LOCAL dwHandle AS DWORD
LOCAL pFFI AS _winVS_FIXEDFILEINFO
LOCAL dwSize AS DWORD
pRet := INVALID_HANDLE_VALUE // 0xFFFFFFFF
dwSize := GetFileVersionInfoSize( pszName, @dwHandle )
IF dwSize = 0
RETURN pRet
ENDIF
pBuffer := MemAlloc( dwSize )
IF .NOT. pBuffer == NULL_PTR
// Чистим память
IF .NOT. MemSet( pBuffer, 0, dwSize ) == NULL_PTR
// Получаем блок информации о версии
IF GetFileVersionInfo( pszName, dwHandle, dwSize, pBuffer )
// Общая информация о версии
IF VerQueryValue( pBuffer, String2Psz( "\\" ), @pFFI, @dwSize )
pDVI.dwMajorVersion := HiWord( pFFI.dwFileVersionMS )
pDVI.dwMinorVersion := LoWord( pFFI.dwFileVersionMS )
IF _AND( pFFI.dwFileOS, VOS_NT ) = VOS_NT
pDVI.dwPlatformID := DLLVER_PLATFORM_NT
ELSE
pDVI.dwPlatformID := DLLVER_PLATFORM_WINDOWS
ENDIF
pDVI.dwBuildNumber := HiWord( pFFI.dwFileVersionLS )
pRet := S_OK // 0L
ENDIF
ENDIF
ENDIF
MemFree( pBuffer )
ENDIF
RETURN pRet
Данный код, при использовании стандартного класса WinDLLVersion, нужно поместить в модуль создаваемого .dll-файла. И всё же, этого недостаточно. Что не так? Верно, где же он будет брать информацию о версии… А читает он её из ресурса «RESOURCE VS_VERSION_INFO». Естественно, об этом – ни слова в самом КаВо.
Обратившись к Интернету, я не нашёл «человеческого» описания. Но мне хватило обрывков информации и примеров, на основе которых методом «проб и ошибок» удалось создать текст ресурса. Вы спросите меня как же так, ведь в КаВо нет такого ресурса? Да очень просто: КаВо работает со всеми типами ресурсов, которые используются в языке Си (хотя, в самом КаВо – об этом, опять же, ни слова). Догадаться об этом просто, достаточно проявить лёгкое любопытство – и мы увидим, что при компиляции, КаВо использует утилитку «rc.exe» (Microsoft Windows Resource Compiler) из каталога «BIN».
Документацию о том, как создать вручную ресурс «VS_VERSION_INFO», я пока не даю. Не знаю, надо ли… Хотя, идея его генератора – есть. Если писать заметку, надо снова покопаться, систематизировать наброски и состыковать их со ссылками на сайт Майкрософта… Долго и утомительно. Но, если читатель попросит – постараюсь. Я, всё-таки, не писатель, хотя что-то и чёркаю :)) А так – добро пожаловать в Интернет, там есть всё!
«Вернёмся к нашим баранам»: теперь информация из .dll стала считываться. Но, этого не достаточно. Требуется считывать информацию из любого типа файла, а не только .dll. При этом крайне нежелательна предварительная его загрузка в память (зачем такое извращение?). Мне нужна информация о датах и атрибутах файла, и вообще – надо иметь возможность считывать любую стандартную информацию из ресурса версии (как это сделано в Делфи и др. нормальных языках). Для этого подошли средства самого Windows, API-функции: FindFirstFileEx(), GetFileVersionInfo() и VerQueryValue().
С их помощью я написал свой класс msFileVersion. Его можно скачать внизу заметки. Класс может считывать информации о версии из любых типов файлов (и .exe и .dll). Теоретически, текст о версии может иметь произвольный буквенно-цифровой формат, но наиболее часто используемый – четырёхчленный цифровой формат, разделённый точками. Поэтому, я его ограничил, и мой класс msFileVersion работает только с файлами, в которых версия задаётся в виде: «xxx.xxx.xxx.xxx», где «xxx» – число типа DWORD (от 0 до 4294967295).
Пользуйтесь на здоровье!
Напоминание: для чтения версии Ваших .dll-ок с использованием стандартного класса WinDLLVersion – необходимо добавлять в модуль Ваших библиотек функцию DllGetVersion(). Если этого не надо – пропустите.
03.02.2014 г. Карандаш.
Комментарии
I am so grateful for your
I am so grateful for your article.Thanks Again. Great.