File version information

Most win32 binaries, like executables (exe), dynamic link libraries (dll) and automation controls (ocx) contain a version information structure. The data in this version information structure is used by setup programs to decide if it's ok to overwrite the file.
The version information can also be useful for support engineers for determining why a feature doesn't work as expected. Especially when used in a list of all modules loaded by the current Progress process, see ListModules.
Typically a file contains both a productversion and fileversion. The version info structure may also contain strings describing the file or its publisher, but this textual information is kind of difficult to read because they are grouped together in codepage blocks. Productversion and fileversion are not language specific so these are easier to read.

DEFINE VARIABLE vProductVersion AS CHARACTER NO-UNDO.
DEFINE VARIABLE vFileVersion    AS CHARACTER NO-UNDO.
RUN GetProductVersion ( 'c:\windows\system\mfc42.dll', 
                        OUTPUT vProductVersion,
                        OUTPUT vFileVersion).
MESSAGE vProductVersion SKIP 
        vFileVersion
        VIEW-AS ALERT-BOX.
 
/* shows 6.0.1.0 
         6.0.8267.0 
   on my system. This indicates someone or something installed the 
   runtime modules for MS Visual Studio 6 */
PROCEDURE GetProductVersion :
 
    DEFINE INPUT  PARAMETER pFilename       AS CHARACTER NO-UNDO.
    DEFINE OUTPUT PARAMETER pProductVersion AS CHARACTER NO-UNDO.
    DEFINE OUTPUT PARAMETER pFileVersion    AS CHARACTER NO-UNDO.
 
    DEFINE VARIABLE dummy           AS INTEGER NO-UNDO.
    DEFINE VARIABLE ReturnValue     AS INTEGER NO-UNDO.
    DEFINE VARIABLE lpVersionInfo   AS MEMPTR  NO-UNDO.  /* VS_VERSION_INFO structure  */
    DEFINE VARIABLE lpFixedFileInfo AS MEMPTR  NO-UNDO.  /* VS_FIXEDFILEINFO structure */
    DEFINE VARIABLE versize         AS INTEGER NO-UNDO.  /* size of lpVersionInfo      */
    DEFINE VARIABLE ptrInfo         AS INTEGER NO-UNDO.  /* address of lpFixedFileInfo */
    DEFINE VARIABLE cInfo           AS INTEGER NO-UNDO.  /* size of lpFixedFileInfo    */
 
    RUN GetFileVersionInfoSizeA (pFileName,
                                 OUTPUT dummy,
                                 OUTPUT versize).
 
    IF versize = 0 THEN RETURN.
 
    SET-SIZE(lpVersionInfo) = 0.
    SET-SIZE(lpVersionInfo) = versize.
 
    RUN GetFileVersionInfoA ( pFileName,
                              0,
                              INPUT versize,
                              INPUT GET-POINTER-VALUE(lpVersionInfo),
                              OUTPUT returnvalue).
 
    IF returnvalue = 0 THEN DO:
      SET-SIZE(lpVersionInfo) = 0.
      RETURN.
    END.
 
    RUN VerQueryValueA (GET-POINTER-VALUE(lpVersionInfo),
                        "\":U,
                        OUTPUT ptrInfo,
                        OUTPUT cInfo,
                        OUTPUT returnvalue).
 
    IF NOT (returnvalue=0 OR cInfo=0) THEN DO:
       SET-SIZE(lpFixedFileInfo)          = cInfo.
       SET-POINTER-VALUE(lpFixedFileInfo) = ptrInfo.
 
       pProductVersion =  STRING(GET-SHORT (lpFixedFileInfo,19)) + '.' +
                          STRING(GET-SHORT (lpFixedFileInfo,17)) + '.' +
                          STRING(GET-SHORT (lpFixedFileInfo,23)) + '.' +
                          STRING(GET-SHORT (lpFixedFileInfo,21)).
 
       pFileVersion    =  STRING(GET-SHORT (lpFixedFileInfo,11)) + '.' +
                          STRING(GET-SHORT (lpFixedFileInfo, 9)) + '.' +
                          STRING(GET-SHORT (lpFixedFileInfo,15)) + '.' +
                          STRING(GET-SHORT (lpFixedFileInfo,13)).
 
    END.
 
    SET-SIZE (lpVersionInfo)   = 0.
 
/*  ------ DON'T DO THIS: --------
    SET-SIZE (lpFixedFileInfo) = 0. */
 
END PROCEDURE.

Definitions used in this procedure:

PROCEDURE GetFileVersionInfoSizeA EXTERNAL "version.dll" :
  DEFINE INPUT  PARAMETER lptstrFilename  AS CHARACTER.
  DEFINE OUTPUT PARAMETER lpdwHandle      AS LONG.
  DEFINE RETURN PARAMETER VersionInfoSize AS LONG.
END PROCEDURE.
 
 
PROCEDURE GetFileVersionInfoA EXTERNAL "version.dll" :
  DEFINE INPUT  PARAMETER lptstrFilename  AS CHARACTER.
  DEFINE INPUT  PARAMETER dwHandle        AS LONG.
  DEFINE INPUT  PARAMETER dwLen           AS LONG.
  DEFINE INPUT  PARAMETER lpData          AS LONG.
  DEFINE RETURN PARAMETER ReturnValue     AS LONG.
END PROCEDURE.
 
 
PROCEDURE VerQueryValueA EXTERNAL "version.dll" :
  DEFINE INPUT  PARAMETER lpBlock     AS LONG.
  DEFINE INPUT  PARAMETER lpSubBlock  AS CHARACTER.
  DEFINE OUTPUT PARAMETER lplpBuffer  AS LONG.
  DEFINE OUTPUT PARAMETER puLen       AS LONG.
  DEFINE RETURN PARAMETER ReturnValue AS LONG.
END PROCEDURE.