Link to home
Start Free TrialLog in
Avatar of RJV
RJV

asked on

Application's version, copyright, etc

Is it possible to use the version resources in VC++ to insert the likes of the application's version, copyright, name, etc into a string? Or, must one declare that elsewhere? The goal is to display that and keep only one version control.

If it is possible, how?

Thanks for your input in advance!

RJV
ASKER CERTIFIED SOLUTION
Avatar of SamratAshok
SamratAshok

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of RJV
RJV

ASKER

Thanks, Samra.

I gather from your explanation that you update the version info according to the build, or else the build. What I'd like to do is ideally use what is in the resource file in the likes of the splash screen, the about screen. Thus I needn't think of updating more than one location.

How to do that -- unless, that is, I didn't understand your explanation.

RJV

Thats precisely what I described. I want the version number to be updated in the RC file.
but I dont want to do it manually. This is very the console application comes in. It parses
.RC file as a regular text file, and increments the necessary digits in the .RC file.
Compiler than recompiles it, causing updated versioninfo to be stamped into your project.

I used to update it everytime, but you could easily make it dependant on some key file
of your project.

Unfortunately I cannot give you the source of console app, because I lost the code,
(I still have executable which adds my companys name, my name and several such trivia)

I can however send you the CVersion class that wraps around the VERSIONINFO
structure. You can use this class in aboutbox, splash screen or even in your other part of
application that may be version dependant.

send me your email address if you need it.
Avatar of RJV

ASKER

Hi,

Yes, I'd be interested in your CVersion class as this is my goal. Namely, take info from one place, where it already exists, rather than having to keep extra headers just for this purpose. The extra header concept doesn't seem very efficient, particularly as the information already exists.

You can email me at rjv@dispro.com.br.

RJV


I was about to offer my code for access to resources as well.

What the heck .. this is the sort of thing I do...

class CMyWinApp : public CWinApp {
  // ...
private:
  CString m_ProgramPathAndName;
  int m_fv[4];      // file version
  int m_pv[4];      // product version
  CString m_Comments;
  CString m_CompanyName;
  CString m_CompanyName;
  CString m_FileDescription;
  CString m_InternalName;
  CString m_LegalCopyright;
  CString m_LegalTrademarks;
  CString m_OriginalFilename;
  CString m_PrivateBuild;
  CString m_ProductName;
  CString m_SpecialBuild;
};

const CString& CMyWinApp::ProgramPathAndName() {
  if (m_ProgramPathAndName.IsEmpty()) {
    char *buffer = m_ProgramPathAndName.GetBuffer(_MAX_PATH);
    bool ok = ::GetModuleFileName(0,buffer,_MAX_PATH) != 0;
    m_ProgramPathAndName.ReleaseBuffer();
  }
  return m_ProgramPathAndName;
}

CString CMyWinApp::GetVersionInfoField (LPCTSTR name, void* bufVersionInfo, WORD* pLanguage) {
  CString key;
  key.Format("\\StringFileInfo\\%04X%04x\\%s",*((WORD*)pLanguage),*(((WORD*)pLanguage)+1),name);
  UINT ulen;
  char* bufString;
  if (! ::VerQueryValue(bufVersionInfo, const_cast<char*>(LPCTSTR(key)), reinterpret_cast<LPVOID*>(&bufString), &ulen)) return CString();
  return CString(bufString, ulen+1);
}

void CMyWinApp::GetVersionInfo () {
  char* path = const_cast<char*>(LPCTSTR(ProgramPathAndName()));
  DWORD hVerDummy;
  DWORD dwVersionInfoSize = ::GetFileVersionInfoSize(path,&hVerDummy);
  if (dwVersionInfoSize == 0L) return;
  void* bufVersionInfo = malloc(dwVersionInfoSize);
  if (! bufVersionInfo) return;
  if (::GetFileVersionInfo(path,hVerDummy,dwVersionInfoSize,bufVersionInfo)) {
    UINT ulen;
    // Get Version Info
    VS_FIXEDFILEINFO *bufFixedFileInfo;
    if (::VerQueryValue(bufVersionInfo,"\\",reinterpret_cast<LPVOID*>(&bufFixedFileInfo), &ulen)){
      m_fv[0] = HIWORD(bufFixedFileInfo->dwFileVersionMS);
      m_fv[1] = LOWORD(bufFixedFileInfo->dwFileVersionMS);
      m_fv[2] = HIWORD(bufFixedFileInfo->dwFileVersionLS);
      m_fv[3] = LOWORD(bufFixedFileInfo->dwFileVersionLS);
      m_pv[0] = HIWORD(bufFixedFileInfo->dwProductVersionMS);
      m_pv[1] = LOWORD(bufFixedFileInfo->dwProductVersionMS);
      m_pv[2] = HIWORD(bufFixedFileInfo->dwProductVersionLS);
      m_pv[3] = LOWORD(bufFixedFileInfo->dwProductVersionLS);
    }
    WORD* pLanguage;
    if(::VerQueryValue(bufVersionInfo, "\\VarFileInfo\\Translation", reinterpret_cast<LPVOID*>(&pLanguage), &ulen) && ulen != 0) {
      m_Comments = GetVersionInfoField ("Comments",bufVersionInfo,pLanguage);
      m_CompanyName = GetVersionInfoField ("CompanyName",bufVersionInfo,pLanguage);
      m_FileDescription = GetVersionInfoField ("FileDescription",bufVersionInfo,pLanguage);
      m_InternalName = GetVersionInfoField ("InternalName",bufVersionInfo,pLanguage);
      m_LegalCopyright = GetVersionInfoField ("LegalCopyright",bufVersionInfo,pLanguage);
      m_LegalTrademarks = GetVersionInfoField ("LegalTrademarks",bufVersionInfo,pLanguage);
      m_OriginalFilename = GetVersionInfoField ("OriginalFilename",bufVersionInfo,pLanguage);
      m_PrivateBuild = GetVersionInfoField ("PrivateBuild",bufVersionInfo,pLanguage);
      m_ProductName = GetVersionInfoField ("ProductName",bufVersionInfo,pLanguage);
      m_SpecialBuild = GetVersionInfoField ("SpecialBuild",bufVersionInfo,pLanguage);
    }      
  }
  free(bufVersionInfo);
}

const int* CMyWinApp::FileVersion () {
  if (m_fv[0] < 0) GetVersionInfo();
  return m_fv;
}

const int* CMyWinApp::ProductVersion () {
  if (m_pv[0] < 0) GetVersionInfo();
  return m_pv;
}

const CString& CMyWinApp::Comments () {
  if (m_Comments.IsEmpty()) GetVersionInfo();
  return m_Comments;
}

const CString& CMyWinApp::CompanyName () {
  if (m_CompanyName.IsEmpty()) GetVersionInfo();
  return m_CompanyName;
}

const CString& CMyWinApp::FileDescription () {
  if (m_FileDescription.IsEmpty()) GetVersionInfo();
  return m_FileDescription;
}

const CString& CMyWinApp::InternalName () {
  if (m_InternalName.IsEmpty()) GetVersionInfo();
  return m_InternalName;
}

const CString& CMyWinApp::LegalCopyright () {
  if (m_LegalCopyright.IsEmpty()) GetVersionInfo();
  return m_LegalCopyright;
}

const CString& CMyWinApp::LegalTrademarks () {
  if (m_LegalTrademarks.IsEmpty()) GetVersionInfo();
  return m_LegalTrademarks;
}

const CString& CMyWinApp::OriginalFilename () {
  if (m_OriginalFilename.IsEmpty()) GetVersionInfo();
  return m_OriginalFilename;
}

const CString& CMyWinApp::PrivateBuild () {
  if (m_PrivateBuild.IsEmpty()) GetVersionInfo();
  return m_PrivateBuild;
}

const CString& CMyWinApp::ProductName () {
  if (m_ProductName.IsEmpty()) GetVersionInfo();
  return m_ProductName;
}

const CString& CMyWinApp::SpecialBuild () {
  if (m_SpecialBuild.IsEmpty()) GetVersionInfo();
  return m_SpecialBuild;
}

Avatar of RJV

ASKER

Hi ROnslow,

I had a feeling there wasn't any straight forward solution, though that doesn't make much sense. After all, to keep things organized means more code size than simply keeping the same information in two different places. That's why I refused to believe there wouldn't be a simpler solution and came here for help.

I'll check it out and get back here. It looks like both of you deserve the credits (though I haven't received the email).

RJV

Avatar of RJV

ASKER

Hi ROnslow,

Arghhh! This one is indeed a first! I've never had a link error with a Win32 function. Unbelievably, this one struck 'gold', with these unresolved externals:

   _VerQueryValueA@16
   _GetFileVersionInfoA@16
   _GetFileVersionInfoSizeA@8

What did you do on the #include 'front' to overcome this?

Thanks again!

RJV

It's not a #include that is missing .. otherwise it would be a compile time error instead.

You need to add "version.lib" to your link Object Modules/Libraries line (don't forget to do it for both debug and release).



Avatar of RJV

ASKER

I'll check it out and let you know.

RJV

RJV, I believe, my CVersion class is lot better than implementing it in CWinApp class.

However, Since Iam accessing the web right now from a client site, I will get someone to
send me an attachment tommorow and I will send it over.

Send me your email address, (one that can accept attachments) if you can.



Hold on a minute,

I talked to a colleague and he mentioned that there is some code in site
called www.codeguru.com

sure enough there is (it was made by Robert Rocco)
His design is preety much same as mine, (function name and class name are different)
But hey it should work.

This is the  URL.

http://www.codeguru.com/misc/version_info_from_resource.shtml

If you get lost  in that site, go to homepage, jump to link called "misc" and scroll down
till you see phrase like "VersionInfo from ... Resource file"

I will send my version of CVersion (pun intended) as soon as I can.

Ciao





Sorry for Information overload.

There is one more version of the same class at that site by Manual Laflamme (spelling not
sure)



You'll have plenty of alternatives to choose from.

I agree that a separate class is probably better than adding a class to CWinApp.  However, that is just what MFC do for registry access (put GetProfileString etc to CWinApp).  So either solution will work.

Perhaps the compromise is to add access to CVersion (or whatever) into your CWinApp class.  That way the CWinApp class can wrap the calls to CVersion so that CVersion will know to look in the current application's resources. (That is assuming CVersion is a class that can read from any exe/dll).

RONSLOW

Actually, I see the problem you are hinting at.

This is what I have done.

I have two constructors in CVersion object.

In first constructor, you can specify the filename you want to look into with full path

I did this with intention that I could see versions of my other dependat files (Application that i developed is spread over
4 executables and 3 dlls) on the same about box.

Somehow my boss rejected the idea, so I never actually did it.

In second constructor, I look for the resource in current executable.
This I accomplish by use of global MFC function
AfxGetApp and then get the module handle and ultimately
the executable name.
(preety much the same functions that you posted in code earlier)

{ God I wish MFC had simple thing like App.Path like Visual basic :---(   }

Motive behind this is :
Since I don't really need the precise App class. I do not need to cast it to current application derived CWinApp.

I hope I have made myself clear.

Here is my source code. I have not delved too deep into different language issues,
but you can go ahead and add it.

CVersion.h
>>>>>>>
#include "winver.h"

/*
This class works as follows

Whenever you need version information, create a cversion object either on stack or
by new

After creation, call Init member function and check the return value.
If value is true, use the version object, else dont use it

To obtain fileversion and productversion, use respective functions

To obtain any other information, use getvariableinfo function,

supply the key value and place holder for return value.

All functions return true on success, false on failure

*/


class CVersion
{
public:
      CVersion();
      CVersion(LPCSTR strFileName);
      ~CVersion();

      BOOL Init();

      BOOL GetFileVersion(CString &strVer);
      BOOL GetProductVersion(CString &strVer);
      BOOL GetVariableInfo(LPCSTR strKey, CString &strValue);

private:

      VS_FIXEDFILEINFO m_FixedInfo;
      char *m_lpData; // contains the entire Structure
      BOOL  m_bValid;
      BOOL  m_bLangFound;
      DWORD  m_structSize;
      CString m_strLanguage;
      CString m_strFileName;

      void GetLanguageID(CString &strLangID) {strLangID = m_strLanguage;};
};
<<<<<<

CVersion.cpp
>>>>>>
#include "stdafx.h"
#include "cversion.h"

// Link Library add Version.lib

CVersion::CVersion()
{
      m_lpData = NULL;
      m_bValid = FALSE;
      m_bLangFound = FALSE;
}

CVersion::CVersion(LPCSTR strFileName)
      : m_strFileName(strFileName)
{
      m_lpData = NULL;
      m_bValid = FALSE;
      m_bLangFound = FALSE;
}

#define SUFFICIENTLY_LARGE_BUFFER 500
BOOL CVersion::Init()
{
      CString strName;
      char *szName;
      DWORD temp;


      if (m_strFileName.IsEmpty())
      {
            szName = strName.GetBuffer(SUFFICIENTLY_LARGE_BUFFER);
             /////////////////////////
            // Get the executable name
            if (GetModuleFileName(AfxGetApp()->m_hInstance, szName, SUFFICIENTLY_LARGE_BUFFER) == 0)
            {
                  strName.ReleaseBuffer();
                  return FALSE;
            }
            strName.ReleaseBuffer();
      } else
      {
            strName = m_strFileName;
      }

      // We still need a char *
      szName = strName.GetBuffer();

      /////////////////////////
      // Get the Version Information of the executable
      m_structSize = ::GetFileVersionInfoSize(szName, &temp);
      if (m_structSize == 0)
      {
            strName.ReleaseBuffer();
            return FALSE;
      }

      m_lpData = new char [m_structSize];
      if (!GetFileVersionInfo(szName, 0, m_structSize, m_lpData))
      {
            delete  [] m_lpData;
            m_lpData = NULL;
            strName.ReleaseBuffer();
            return FALSE;
      }
      /////////////////////////
      // Now retrieve the fixedinfo structure from the version info

      LPVOID lplpBuff;
      UINT   nLen;

      VerQueryValue(m_lpData, "\\", &lplpBuff, &nLen);
      memcpy(&m_FixedInfo, lplpBuff, sizeof(VS_FIXEDFILEINFO));

      /////////////////////////
      // Now Determine the language id
      char *szLangugeID;
      if (VerQueryValue(m_lpData,"\\VarFileInfo\\Translation",
            &lplpBuff, &nLen))
      {
            UINT   nLangMajor = 0;
            UINT   nLangMinor = 0;

            szLangugeID = (char *)lplpBuff;
            // pick up the first language
            // for the more ambitious move, create an array and capture
            // all the language values

            memcpy(&nLangMajor, szLangugeID, 2);
            memcpy(&nLangMinor, szLangugeID + 2, 2);

            m_strLanguage.Format("%04X%04X",nLangMajor, nLangMinor);
            m_bLangFound = TRUE;
      } else
      {
            // Default to English
            m_strLanguage = "04090000";
      }

      // Init done successful
      m_bValid = TRUE;
      strName.ReleaseBuffer();
      return TRUE;
}

CVersion::~CVersion()
{
      if (m_lpData != NULL)
      {
            delete [] m_lpData;
      }
}

BOOL CVersion::GetFileVersion(CString &strVer)
{
      if (!m_bValid)
            return FALSE;

      UINT hMS = HIWORD(m_FixedInfo.dwFileVersionMS);
      UINT lMS = LOWORD(m_FixedInfo.dwFileVersionMS);

      UINT hLS = HIWORD(m_FixedInfo.dwFileVersionLS);
      UINT lLS = LOWORD(m_FixedInfo.dwFileVersionLS);

      strVer.Format("%d.%d.%d.%d",hMS, lMS, hLS, lLS);
      return TRUE;
}

BOOL CVersion::GetProductVersion(CString &strVer)
{
      if (!m_bValid)
            return FALSE;

      UINT hMS = HIWORD(m_FixedInfo.dwProductVersionMS);
      UINT lMS = LOWORD(m_FixedInfo.dwProductVersionMS);

      UINT hLS = HIWORD(m_FixedInfo.dwProductVersionLS);
      UINT lLS = LOWORD(m_FixedInfo.dwProductVersionLS);

      strVer.Format("%d.%d.%d.%d",hMS, lMS, hLS, lLS);
      return TRUE;
}

BOOL CVersion::GetVariableInfo(LPCSTR strKey, CString &strValue)
{
      LPVOID lplpBuff;
      UINT   nLen;
      CString strActualKey;
      char *szKey;


      // If one of them is false, return false
      if (!(m_bValid && m_bLangFound))
            return FALSE;

      strActualKey.Format("\\StringFileInfo\\%s\\%s",m_strLanguage, strKey);
      szKey = strActualKey.GetBuffer(strActualKey.GetLength());

      if (VerQueryValue(m_lpData, szKey, &lplpBuff, &nLen))
      {
            strActualKey.ReleaseBuffer();
            strValue = (char *)lplpBuff;
      } else
      {
            strActualKey.ReleaseBuffer();
            return FALSE;
      }

      return TRUE;
}
<<<<<<<