////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// CModuleVersion provides an easy way to get version info
// for a module.(DLL or EXE).
//
// This code appeard in April 1998 edition of Microsoft Systems
// Journal.
//
// 27-July-1998 -- Adapted by James A. McLaughiln (Schlumberger
// Technology Corp.) for Smart Cards.  Merged with the concepts from
// CFileVersion class contributed by Manuel Laflamme on a posting to
// www.codeguru.com.  If these mods don't work, then you can blame me.

#include "StdAfx.h"
#include "slbModVer.h"

CModuleVersion::CModuleVersion()
    : m_pVersionInfo(NULL)
{
}

//////////////////
// Destroy: delete version info
//
CModuleVersion::~CModuleVersion()
{
    delete [] m_pVersionInfo;
}

BOOL CModuleVersion::GetFileVersionInfo(LPCTSTR modulename)
{
    // get module handle
    HMODULE hModule = ::GetModuleHandle(modulename);
    if (hModule==NULL && modulename!=NULL)
        return FALSE;

    return GetFileVersionInfo(hModule);
}

//////////////////
// Get file version info for a given module
// Allocates storage for all info, fills "this" with
// VS_FIXEDFILEINFO, and sets codepage.
//
BOOL CModuleVersion::GetFileVersionInfo(HMODULE hModule)
{
    m_translation.charset = 1252;               // default = ANSI code page
    memset((VS_FIXEDFILEINFO*)this, 0, sizeof(VS_FIXEDFILEINFO));

    // get module file name
    TCHAR filename[_MAX_PATH];
    DWORD len = GetModuleFileName(hModule, filename,
        sizeof(filename)/sizeof(filename[0]));
    if (len <= 0)
        return FALSE;

    // read file version info
    DWORD dwDummyHandle; // will always be set to zero
    len = GetFileVersionInfoSize(filename, &dwDummyHandle);
    if (len <= 0)
        return FALSE;

    m_pVersionInfo = new BYTE[len]; // allocate version info
    if (!::GetFileVersionInfo(filename, 0, len, m_pVersionInfo))
        return FALSE;

    // copy fixed info to myself, which am derived from VS_FIXEDFILEINFO
    if (!GetFixedInfo(*(VS_FIXEDFILEINFO*)this))
        return FALSE;

    // Get translation info
    LPVOID lpvi;
    UINT iLen;
    if (VerQueryValue(m_pVersionInfo,
        TEXT("\\VarFileInfo\\Translation"), &lpvi, &iLen) && iLen >= 4) {
        m_translation = *(TRANSLATION*)lpvi;
        TRACE(TEXT("code page = %d\n"), m_translation.charset);
    }

    return dwSignature == VS_FFI_SIGNATURE;
}

//////////////////
// Get string file info.
// Key name is something like "CompanyName".
// returns the value as a CString.
//
CString CModuleVersion::GetValue(LPCTSTR lpKeyName)
{
    CString sVal;
    if (m_pVersionInfo) {

        // To get a string value must pass query in the form
        //
        //    "\StringFileInfo\<langID><codepage>\keyname"
        //
        // where <lang-codepage> is the languageID concatenated with the
        // code page, in hex. Wow.
        //
        CString query;
        query.Format(_T("\\StringFileInfo\\%04x%04x\\%s"),
            m_translation.langID,
            m_translation.charset,
            lpKeyName);

        LPCTSTR pVal;
        UINT iLenVal;
        if (VerQueryValue(m_pVersionInfo, (LPTSTR)(LPCTSTR)query,
                (LPVOID*)&pVal, &iLenVal)) {

            sVal = pVal;
        }
    }
    return sVal;
}

// typedef for DllGetVersion proc
typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *);

/////////////////
// Get DLL Version by calling DLL's DllGetVersion proc
//
BOOL CModuleVersion::DllGetVersion(LPCTSTR modulename, DLLVERSIONINFO& dvi)
{
    HINSTANCE hinst = LoadLibrary(modulename);
    if (!hinst)
        return FALSE;

    // Must use GetProcAddress because the DLL might not implement
    // DllGetVersion. Depending upon the DLL, the lack of implementation of the
    // function may be a version marker in itself.
    //
    DLLGETVERSIONPROC pDllGetVersion =
        (DLLGETVERSIONPROC)GetProcAddress(hinst, reinterpret_cast<const char *>(_T("DllGetVersion")));

    if (!pDllGetVersion)
        return FALSE;

        memset(&dvi, 0, sizeof(dvi));                    // clear
        dvi.cbSize = sizeof(dvi);                                // set size for Windows

    return SUCCEEDED((*pDllGetVersion)(&dvi));
}

BOOL CModuleVersion::GetFixedInfo(VS_FIXEDFILEINFO& vsffi)
{
    // Must furst call GetFileVersionInfo or constructor with arg
    ASSERT(m_pVersionInfo != NULL);
    if ( m_pVersionInfo == NULL )
        return FALSE;

    UINT nQuerySize;
    VS_FIXEDFILEINFO* pVsffi;
    if ( ::VerQueryValue((void **)m_pVersionInfo, _T("\\"),
                         (void**)&pVsffi, &nQuerySize) )
    {
        vsffi = *pVsffi;
        return TRUE;
    }

    return FALSE;
}

CString CModuleVersion::GetFixedFileVersion()
{
    CString strVersion;
    VS_FIXEDFILEINFO vsffi;

    if (GetFixedInfo(vsffi))
    {
        strVersion.Format (_T("%u,%u,%u,%u"),HIWORD(dwFileVersionMS),
            LOWORD(dwFileVersionMS),
            HIWORD(dwFileVersionLS),
            LOWORD(dwFileVersionLS));
    }
    return strVersion;
}

CString CModuleVersion::GetFixedProductVersion()
{
    CString strVersion;
    VS_FIXEDFILEINFO vsffi;

    if (GetFixedInfo(vsffi))
    {
        strVersion.Format (_T("%u,%u,%u,%u"), HIWORD(dwProductVersionMS),
            LOWORD(dwProductVersionMS),
            HIWORD(dwProductVersionLS),
            LOWORD(dwProductVersionLS));
    }
    return strVersion;
}