Вы здесь

Создать свой сервис обновления: проверка версии файла

CalendarВ прошлой заметке я затронул тему сжатия данных. Тема эта богатая, можно много чего сочинить, но надо идти дальше. Как говорила героиня мультфильма: «Одну ягодку беру, на вторую смотрю, третью примечаю, а четвертая мерещится». И следующая важная задача – контроль «свежести» файлов. Обычно это решается с помощью сверки дат и номеров версий. Звучит «вкусно». Так что – начнём!
 
Нам потребуются «инструменты». А что у нас есть в КаВо (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
 
Здесь мы читаем сразу два ответа:
  1. .dll-файл заранее должен быть загружен в память.
  2. В файле должна быть функция «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 г.      Карандаш.
 
 
ВложениеРазмер
Файл ms FileVersion_0001.rar9.48 КБ

Комментарии

I am so grateful for your article.Thanks Again. Great.

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer