/*++ Copyright (c) 1997 Microsoft Corporation Module Name: version.c Abstract: This file implements a set of enumeration routines to access version info in a Win32 binary. Author: Jim Schmidt (jimschm) 03-Dec-1997 Revision History: --*/ #include "pch.h" #define DBG_ACTION "Action" // // Globals // PCSTR g_DefaultTranslationsA[] = { "04090000", "040904E4", "040904B0", NULL }; PCWSTR g_DefaultTranslationsW[] = { L"04090000", L"040904E4", L"040904B0", NULL }; // // Prototypes // PCSTR pEnumVersionValueCommonA ( IN OUT PVERSION_STRUCTA VersionStruct ); PCWSTR pEnumVersionValueCommonW ( IN OUT PVERSION_STRUCTW VersionStruct ); // // Implementation // BOOL CreateVersionStructA ( OUT PVERSION_STRUCTA VersionStruct, IN PCSTR FileSpec ) /*++ Routine Description: CreateVersionStruct is called to load a version structure from a file and to obtain the fixed version stamp info that is language-independent. The caller must call DestroyVersionStruct after the VersionStruct is no longer needed. Arguments: VersionStruct - Receives the version stamp info to be used by other functions in this module FileSpec - Specifies the file to obtain version info from Return Value: TRUE if the routine was able to get version info, or FALSE if an error occurred. --*/ { // // Init the struct // ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTA)); VersionStruct->FileSpec = FileSpec; // // Allocate enough memory for the version stamp // VersionStruct->Size = GetFileVersionInfoSizeA ( (PSTR) FileSpec, &VersionStruct->Handle ); if (!VersionStruct->Size) { DEBUGMSG ((DBG_WARNING, "File %s does not have version info", FileSpec)); return FALSE; } // // fix for version info bug: // allocate both buffers at once; this way the first buffer will not point to invalid // memory when a reallocation occurs because of the second grow // VersionStruct->VersionBuffer = GrowBuffer (&VersionStruct->GrowBuf, VersionStruct->Size * 2); if (!VersionStruct->VersionBuffer) { return FALSE; } VersionStruct->StringBuffer = VersionStruct->GrowBuf.Buf + VersionStruct->Size; // // Now get the version info from the file // if (!GetFileVersionInfoA ( (PSTR) FileSpec, VersionStruct->Handle, VersionStruct->Size, VersionStruct->VersionBuffer )) { DestroyVersionStructA (VersionStruct); return FALSE; } // // Extract the fixed info // VerQueryValueA ( VersionStruct->VersionBuffer, "\\", &VersionStruct->FixedInfo, &VersionStruct->FixedInfoSize ); return TRUE; } ULONGLONG VerGetFileVer ( IN PVERSION_STRUCTA VersionStruct ) { ULONGLONG result = 0; if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { *((PDWORD) (&result)) = VersionStruct->FixedInfo->dwFileVersionLS; *(((PDWORD) (&result)) + 1) = VersionStruct->FixedInfo->dwFileVersionMS; } return result; } ULONGLONG VerGetProductVer ( IN PVERSION_STRUCTA VersionStruct ) { ULONGLONG result = 0; if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { *((PDWORD) (&result)) = VersionStruct->FixedInfo->dwProductVersionLS; *(((PDWORD) (&result)) + 1) = VersionStruct->FixedInfo->dwProductVersionMS; } return result; } DWORD VerGetFileDateLo ( IN PVERSION_STRUCTA VersionStruct ) { if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VersionStruct->FixedInfo->dwFileDateLS; } return 0; } DWORD VerGetFileDateHi ( IN PVERSION_STRUCTA VersionStruct ) { if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VersionStruct->FixedInfo->dwFileDateMS; } return 0; } DWORD VerGetFileVerOs ( IN PVERSION_STRUCTA VersionStruct ) { if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VersionStruct->FixedInfo->dwFileOS; } return 0; } DWORD VerGetFileVerType ( IN PVERSION_STRUCTA VersionStruct ) { if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VersionStruct->FixedInfo->dwFileType; } return 0; } VOID DestroyVersionStructA ( IN PVERSION_STRUCTA VersionStruct ) /*++ Routine Description: DestroyVersionStruct cleans up all memory allocated by the routines in this module. Arguments: VersionStruct - Specifies the structure to clean up Return Value: none --*/ { // // Clean up all allocations made by any routine using // the VersionStruct // if (VersionStruct->GrowBuf.Buf) { FreeGrowBuffer (&VersionStruct->GrowBuf); } ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTA)); } PCSTR EnumFirstVersionTranslationA ( IN OUT PVERSION_STRUCTA VersionStruct ) /*++ Routine Description: EnumFirstVersionTranslation returins the translation string needed to access the string table of a version stamp. Arguments: VersionStruct - Specifies the structure that has been initialized by InitializeVersionStruct. Return Value: A pointer to a string specifying the first translation, or NULL if no translations exist. --*/ { UINT ArraySize; // // Query version block for array of code pages/languages // if (!VerQueryValueA ( VersionStruct->VersionBuffer, "\\VarFileInfo\\Translation", &VersionStruct->Translations, &ArraySize )) { // // No translations are available // ArraySize = 0; } // // Return a pointer to the first translation // VersionStruct->CurrentDefaultTranslation = 0; VersionStruct->MaxTranslations = ArraySize / sizeof (TRANSLATION); VersionStruct->CurrentTranslation = 0; DEBUGMSG_IF (( VersionStruct->MaxTranslations == 0, DBG_WARNING, "File %s has no translations", VersionStruct->FileSpec )); return EnumNextVersionTranslationA (VersionStruct); } BOOL pIsDefaultTranslationA ( IN PCSTR TranslationStr ) /*++ Routine Description: pIsDefaultTranslationA returns TRUE if the specified translation string is enumerated by default. This routine stops multiple enumeration of the same translation string. Arguments: TranslationStr - Specifies the translation string to test Return Value: TRUE if the translation string is the same as a default translation string, or FALSE if it is not. --*/ { INT i; for (i = 0 ; g_DefaultTranslationsA[i] ; i++) { if (StringIMatchA (TranslationStr, g_DefaultTranslationsA[i])) { return TRUE; } } return FALSE; } PCSTR EnumNextVersionTranslationA ( IN OUT PVERSION_STRUCTA VersionStruct ) /*++ Routine Description: EnumNextVersionTranslation continues the enumeration of translation strings, needed to access the string table in a version stamp. Arguments: VersionStruct - Specifies the same structure passed to EnumFirstVersionTranslation. Return Value: A pointer to a string specifying the next translation, or NULL if no additional translations exist. --*/ { PTRANSLATION Translation; if (g_DefaultTranslationsA[VersionStruct->CurrentDefaultTranslation]) { // // Return default translations first // StringCopyA ( VersionStruct->TranslationStr, g_DefaultTranslationsA[VersionStruct->CurrentDefaultTranslation] ); VersionStruct->CurrentDefaultTranslation++; } else { do { // // Return NULL if all translations have been enumerated // if (VersionStruct->CurrentTranslation == VersionStruct->MaxTranslations) { return NULL; } // // Otherwise build translation string and return pointer to it // Translation = &VersionStruct->Translations[VersionStruct->CurrentTranslation]; wsprintfA ( VersionStruct->TranslationStr, "%04x%04x", Translation->CodePage, Translation->Language ); VersionStruct->CurrentTranslation++; } while (pIsDefaultTranslationA (VersionStruct->TranslationStr)); } return VersionStruct->TranslationStr; } PCSTR EnumFirstVersionValueA ( IN OUT PVERSION_STRUCTA VersionStruct, IN PCSTR VersionField ) /*++ Routine Description: EnumFirstVersionValue returns the first value stored in a version stamp for a specific field. If the field does not exist, the function returns NULL. An enumeration of EnumFirstVersionValue/EnumNextVersionValue is used to list all localized strings for a field. Arguments: VersionStruct - Specifies the structure that was initialized by InitializeVersionStruct. VersionField - Specifies the name of the version field to enumerate Return Value: A pointer to the first value of the field, or NULL if the field does not exist. --*/ { PCSTR rc; if (!EnumFirstVersionTranslationA (VersionStruct)) { return NULL; } VersionStruct->VersionField = VersionField; rc = pEnumVersionValueCommonA (VersionStruct); if (!rc) { rc = EnumNextVersionValueA (VersionStruct); } return rc; } PCSTR EnumNextVersionValueA ( IN OUT PVERSION_STRUCTA VersionStruct ) /*++ Routine Description: EnumNextVersionValue returns the next value stored in a version stamp for a specific field. Arguments: VersionStruct - Specifies the same structure passed to EnumFirstVersionField Return Value: A pointer to the next value of the field, or NULL if another field does not exist. --*/ { PCSTR rc = NULL; do { if (!EnumNextVersionTranslationA (VersionStruct)) { break; } rc = pEnumVersionValueCommonA (VersionStruct); } while (!rc); return rc; } PCSTR pEnumVersionValueCommonA ( IN OUT PVERSION_STRUCTA VersionStruct ) /*++ Routine Description: pEnumVersionValueCommon is a routine that obtains the value of a version field. It is used for both EnumFirstVersionValue and EnumNextVersionValue. Arguments: VersionStruct - Specifies the structure being processed Return Value: A pointer to the version value for the current translation, or NULL if the value does not exist for the current translation. --*/ { PSTR Text; UINT StringLen; PBYTE String; PCSTR Result = NULL; // // Prepare sub block for VerQueryValue API // Text = AllocTextA ( 16 + SizeOfStringA (VersionStruct->TranslationStr) + SizeOfStringA (VersionStruct->VersionField) ); if (!Text) { return NULL; } wsprintfA ( Text, "\\StringFileInfo\\%s\\%s", VersionStruct->TranslationStr, VersionStruct->VersionField ); __try { // // Get the value from the version stamp // if (!VerQueryValueA ( VersionStruct->VersionBuffer, Text, &String, &StringLen )) { // // No value is available // __leave; } // // Copy value into buffer // _mbsnzcpy (VersionStruct->StringBuffer, (PCSTR) String, StringLen); Result = VersionStruct->StringBuffer; } __finally { FreeTextA (Text); } return Result; } BOOL CreateVersionStructW ( OUT PVERSION_STRUCTW VersionStruct, IN PCWSTR FileSpec ) /*++ Routine Description: CreateVersionStruct is called to load a version structure from a file and to obtain the fixed version stamp info that is language-independent. The caller must call DestroyVersionStruct after the VersionStruct is no longer needed. Arguments: VersionStruct - Receives the version stamp info to be used by other functions in this module FileSpec - Specifies the file to obtain version info from Return Value: TRUE if the routine was able to get version info, or FALSE if an error occurred. --*/ { ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTW)); VersionStruct->FileSpec = FileSpec; // // Allocate enough memory for the version stamp // VersionStruct->Size = GetFileVersionInfoSizeW ( (PWSTR) FileSpec, &VersionStruct->Handle ); if (!VersionStruct->Size) { DEBUGMSG ((DBG_WARNING, "File %S does not have version info", FileSpec)); return FALSE; } // // fix for version info bug: // allocate both buffers at once; this way the first buffer will not point to invalid // memory when a reallocation occurs because of the second grow // VersionStruct->VersionBuffer = GrowBuffer (&VersionStruct->GrowBuf, VersionStruct->Size * 2); if (!VersionStruct->VersionBuffer) { return FALSE; } VersionStruct->StringBuffer = VersionStruct->GrowBuf.Buf + VersionStruct->Size; // // Now get the version info from the file // if (!GetFileVersionInfoW ( (PWSTR) FileSpec, VersionStruct->Handle, VersionStruct->Size, VersionStruct->VersionBuffer )) { DestroyVersionStructW (VersionStruct); return FALSE; } // // Extract the fixed info // VerQueryValueW ( VersionStruct->VersionBuffer, L"\\", &VersionStruct->FixedInfo, &VersionStruct->FixedInfoSize ); return TRUE; } VOID DestroyVersionStructW ( IN PVERSION_STRUCTW VersionStruct ) /*++ Routine Description: DestroyVersionStruct cleans up all memory allocated by the routines in this module. Arguments: VersionStruct - Specifies the structure to clean up Return Value: none --*/ { if (VersionStruct->GrowBuf.Buf) { FreeGrowBuffer (&VersionStruct->GrowBuf); } ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTW)); } PCWSTR EnumFirstVersionTranslationW ( IN OUT PVERSION_STRUCTW VersionStruct ) /*++ Routine Description: EnumFirstVersionTranslation returins the translation string needed to access the string table of a version stamp. Arguments: VersionStruct - Specifies the structure that has been initialized by InitializeVersionStruct. Return Value: A pointer to a string specifying the first translation, or NULL if no translations exist. --*/ { UINT ArraySize; if (!VerQueryValueW ( VersionStruct->VersionBuffer, L"\\VarFileInfo\\Translation", &VersionStruct->Translations, &ArraySize )) { // // No translations are available // ArraySize = 0; } // // Return a pointer to the first translation // VersionStruct->CurrentDefaultTranslation = 0; VersionStruct->MaxTranslations = ArraySize / sizeof (TRANSLATION); VersionStruct->CurrentTranslation = 0; DEBUGMSG_IF (( VersionStruct->MaxTranslations == 0, DBG_WARNING, "File %S has no translations", VersionStruct->FileSpec )); return EnumNextVersionTranslationW (VersionStruct); } BOOL pIsDefaultTranslationW ( IN PCWSTR TranslationStr ) { INT i; for (i = 0 ; g_DefaultTranslationsW[i] ; i++) { if (StringIMatchW (TranslationStr, g_DefaultTranslationsW[i])) { return TRUE; } } return FALSE; } PCWSTR EnumNextVersionTranslationW ( IN OUT PVERSION_STRUCTW VersionStruct ) /*++ Routine Description: EnumNextVersionTranslation continues the enumeration of translation strings, needed to access the string table in a version stamp. Arguments: VersionStruct - Specifies the same structure passed to EnumFirstVersionTranslation. Return Value: A pointer to a string specifying the next translation, or NULL if no additional translations exist. --*/ { PTRANSLATION Translation; if (g_DefaultTranslationsW[VersionStruct->CurrentDefaultTranslation]) { StringCopyW ( VersionStruct->TranslationStr, g_DefaultTranslationsW[VersionStruct->CurrentDefaultTranslation] ); VersionStruct->CurrentDefaultTranslation++; } else { do { if (VersionStruct->CurrentTranslation == VersionStruct->MaxTranslations) { return NULL; } Translation = &VersionStruct->Translations[VersionStruct->CurrentTranslation]; wsprintfW ( VersionStruct->TranslationStr, L"%04x%04x", Translation->CodePage, Translation->Language ); VersionStruct->CurrentTranslation++; } while (pIsDefaultTranslationW (VersionStruct->TranslationStr)); } return VersionStruct->TranslationStr; } PCWSTR EnumFirstVersionValueW ( IN OUT PVERSION_STRUCTW VersionStruct, IN PCWSTR VersionField ) /*++ Routine Description: EnumFirstVersionValue returns the first value stored in a version stamp for a specific field. If the field does not exist, the function returns NULL. An enumeration of EnumFirstVersionValue/EnumNextVersionValue is used to list all localized strings for a field. Arguments: VersionStruct - Specifies the structure that was initialized by InitializeVersionStruct. VersionField - Specifies the name of the version field to enumerate Return Value: A pointer to the first value of the field, or NULL if the field does not exist. --*/ { PCWSTR rc; if (!EnumFirstVersionTranslationW (VersionStruct)) { return NULL; } VersionStruct->VersionField = VersionField; rc = pEnumVersionValueCommonW (VersionStruct); if (!rc) { rc = EnumNextVersionValueW (VersionStruct); } return rc; } PCWSTR EnumNextVersionValueW ( IN OUT PVERSION_STRUCTW VersionStruct ) /*++ Routine Description: EnumNextVersionValue returns the next value stored in a version stamp for a specific field. Arguments: VersionStruct - Specifies the same structure passed to EnumFirstVersionField Return Value: A pointer to the next value of the field, or NULL if another field does not exist. --*/ { PCWSTR rc = NULL; do { if (!EnumNextVersionTranslationW (VersionStruct)) { break; } rc = pEnumVersionValueCommonW (VersionStruct); } while (!rc); return rc; } PCWSTR pEnumVersionValueCommonW ( IN OUT PVERSION_STRUCTW VersionStruct ) /*++ Routine Description: pEnumVersionValueCommon is a routine that obtains the value of a version field. It is used for both EnumFirstVersionValue and EnumNextVersionValue. Arguments: VersionStruct - Specifies the structure being processed Return Value: A pointer to the version value for the current translation, or NULL if the value does not exist for the current translation. --*/ { PWSTR Text; UINT StringLen; PBYTE String; PCWSTR Result = NULL; // // Prepare sub block for VerQueryValue API // Text = AllocTextW ( 18 + CharCountW (VersionStruct->TranslationStr) + CharCountW (VersionStruct->VersionField) ); if (!Text) { return NULL; } wsprintfW ( Text, L"\\StringFileInfo\\%s\\%s", VersionStruct->TranslationStr, VersionStruct->VersionField ); __try { // // Get the value from the version stamp // if (!VerQueryValueW ( VersionStruct->VersionBuffer, Text, &String, &StringLen )) { // // No value is available // return NULL; } CopyMemory (VersionStruct->StringBuffer, String, StringLen * sizeof (WCHAR)); VersionStruct->StringBuffer [StringLen * sizeof (WCHAR)] = 0; Result = (PWSTR) VersionStruct->StringBuffer; } __finally { FreeTextW (Text); } return Result; } PSTR UnicodeToCcs ( PCWSTR Source ) /*++ Routine Description: UnicodeToCcs will walk the unicode string and convert it to ANSII by encoding all DBCS characters to hex values. Arguments: Source - the Unicode string Return Value: An encoded ANSII string. --*/ { CHAR result [MEMDB_MAX]; UINT srcIndex = 0; UINT destIndex = 0; while (Source [srcIndex]) { if ((Source [srcIndex] >=32) && (Source [srcIndex] <=126) ) { result [destIndex] = (BYTE) Source [srcIndex]; destIndex ++; } else { if ((destIndex == 0) || (result [destIndex-1] != '*') ) { result [destIndex] = '*'; destIndex ++; } } srcIndex ++; } result [destIndex] = 0; return DuplicatePathString (result, 0); }