Comprobación de versiones en archivos redistribuibles

Actualización: noviembre 2007

Es importante instalar sólo la versión más reciente de un archivo. En casi todas las ocasiones, se desea instalar una versión más moderna de un archivo DLL o de un componente sobre una versión más antigua, y no al contrario. Los archivos DLL del sistema, como Kernel32.dll, User32.dll, Ole32.dll y ShDocVW.dll, no se deben redistribuir. Si se necesita redistribuir algún archivo, no olvide leer los contratos de licencia correspondientes y asegurarse de que los archivos pueden redistribuirse según las diferentes versiones del sistema operativo y la ubicación de instalación. Normalmente, la comprobación de versiones corresponde al programa de instalación.

Si crea un programa de instalación propio, debe comprobar manualmente la información de la versión al instalar los archivos redistribuibles. Este tema incluye un programa de ejemplo que comprueba los números de versión, o si falta el número de versión de un archivo, las marcas de fecha y hora.

Ejemplo

Descripción

En el ejemplo siguiente se muestra un método para comprobar entre dos bibliotecas dinámicas o aplicaciones ejecutables.

Código

// checkversion.cpp
// compile with: /link version.lib

#include <windows.h>
#include <stdio.h>

void EmitErrorMsg (HRESULT hr);
HRESULT GetFileVersion (char *filename, VS_FIXEDFILEINFO *vsf);
HRESULT GetFileDate (char *filename, FILETIME *pft);
HRESULT LastError();
int WhichIsNewer (char *fname1, char *fname2);

void printtime (FILETIME *t)
{
    FILETIME lft;
    FILETIME *ft = &lft;
    FileTimeToLocalFileTime(t,ft);
    printf_s("%08x %08x",ft->dwHighDateTime,ft->dwLowDateTime); 
    {
        SYSTEMTIME stCreate;
        BOOL bret = FileTimeToSystemTime(ft,&stCreate);
        printf_s("    %02d/%02d/%d  %02d:%02d:%02d\n",
                        stCreate.wMonth, stCreate.wDay, stCreate.wYear,
                        stCreate.wHour, stCreate.wMinute,
                        stCreate.wSecond);
    }
}

int main(int argc, char* argv[]) 
{
    printf_s("usage: checkversion file1 file2\n"
             "\tReports which file is newer, first by checking "
             "the file version in "
             "\tthe version resource, then by checking the date\n\n" );

    if (argc != 3) 
        return 1;

    int newer = WhichIsNewer(argv[1],argv[2]);
    switch(newer) 
    {
        case 1:
        case 2: 
            printf_s("%s is newer\n",argv[newer]); 
        break;
        case 3: 
            printf_s("they are the same version\n"); 
        break;
        case 0:
        default: 
            printf_s("there was an error\n"); 
        break;
    }

    return !newer;
}

int WhichIsNewer (char *fname1, char *fname2) 
{
    // 1 if argv[1] is newer
    // 2 if argv[2] is newer
    // 3 if they are the same version
    // 0 if there is an error

    int ndxNewerFile;
    HRESULT ret;
    VS_FIXEDFILEINFO vsf1,vsf2;

    if ( SUCCEEDED((ret=GetFileVersion(fname1,&vsf1))) &&
         SUCCEEDED((ret=GetFileVersion(fname2,&vsf2)))) 
    {
        // both files have a file version resource
        // compare by file version resource
        if (vsf1.dwFileVersionMS > vsf2.dwFileVersionMS) 
        {
            ndxNewerFile = 1;
        }
        else 
            if (vsf1.dwFileVersionMS < vsf2.dwFileVersionMS) 
            {
                ndxNewerFile = 2;
            }
            else {   
              // if (vsf1.dwFileVersionMS == vsf2.dwFileVersionMS)
              if (vsf1.dwFileVersionLS > vsf2.dwFileVersionLS) {
                   ndxNewerFile = 1;
              }
              else 
                  if (vsf1.dwFileVersionLS < vsf2.dwFileVersionLS) {
                      ndxNewerFile = 2;
              }
            else 
            {   
                // if (vsf1.dwFileVersionLS == vsf2.dwFileVersionLS)
                ndxNewerFile = 3;
            }
        }
    }
    else 
    {
        // compare by date
        FILETIME ft1,ft2;
        if (SUCCEEDED((ret=GetFileDate(fname1,&ft1))) &&
            SUCCEEDED((ret=GetFileDate(fname2,&ft2))))
        {
            LONG x = CompareFileTime(&ft1,&ft2);
            if (x == -1) 
                ndxNewerFile = 2;
            else 
                if (x == 0) 
                   ndxNewerFile = 3;
                else 
                   if (x == 1) 
                       ndxNewerFile = 1;
                   else 
                   {
                       EmitErrorMsg(E_FAIL);
                       return 0;
                   }
        }
        else 
        {
            EmitErrorMsg(ret);
            return 0;
        }
    }
    return ndxNewerFile;
}

HRESULT GetFileDate (char *filename, FILETIME *pft) 
{
    // we are interested only in the create time
    // this is the equiv of "modified time" in the 
    // Windows Explorer properties dialog
    FILETIME ct,lat;
    HANDLE hFile = CreateFile(filename, GENERIC_READ,FILE_SHARE_READ |
                         FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
    if (hFile == INVALID_HANDLE_VALUE) 
        return LastError();
    BOOL bret = GetFileTime(hFile,&ct,&lat,pft);
    if (bret == 0) 
        return LastError();
    return S_OK;
}

// This function gets the file version info structure
HRESULT GetFileVersion (char *filename, VS_FIXEDFILEINFO *pvsf) 
{
    DWORD dwHandle;
    DWORD cchver = GetFileVersionInfoSize(filename,&dwHandle);
    if (cchver == 0) 
        return LastError();
    char* pver = new char[cchver];
    BOOL bret = GetFileVersionInfo(filename,dwHandle,cchver,pver);
    if (!bret) 
        return LastError();
    UINT uLen;
    void *pbuf;
    bret = VerQueryValue(pver,"\\",&pbuf,&uLen);
    if (!bret) 
        return LastError();
    memcpy(pvsf,pbuf,sizeof(VS_FIXEDFILEINFO));
    delete[] pver;
    return S_OK;
}

HRESULT LastError () 
{
    HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
    if (SUCCEEDED(hr)) 
        return E_FAIL;
    return hr;
}

// This little function emits an error message 
// based on WIN32 error messages
void EmitErrorMsg (HRESULT hr) 
{
    char szMsg[1024];
    FormatMessage( 
        FORMAT_MESSAGE_FROM_SYSTEM, 
        NULL,
        hr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        szMsg,
        1024,
        NULL 
    );

    printf_s("%s\n",szMsg);
}

Vea también

Otros recursos

Implementación (C++)