/*++ Copyright (c) 1996 Microsoft Corporation Module Name: version.c Abstract: Implements a set of enumeration routines to access version information from a Win32 binary. Author: Jim Schmidt (jimschm) 03-Dec-1997 Revision History: calinn 03-Sep-1999 Moved over from Win9xUpg project. --*/ // // Includes // #include "pch.h" // // Debug constants // #define DBG_VERSION "VerAPI" // // Strings // // None // // Constants // // None // // Macros // // None // // Types // // None // // Globals // PCSTR g_DefaultTranslationsA[] = { "04090000", "040904E4", "040904B0", NULL }; PCWSTR g_DefaultTranslationsW[] = { L"04090000", L"040904E4", L"040904B0", NULL }; // // Macro expansion list // // None // // Private function prototypes // PCSTR pVrEnumValueA ( IN OUT PVRVALUE_ENUMA VrValueEnum ); PCWSTR pVrEnumValueW ( IN OUT PVRVALUE_ENUMW VrValueEnum ); PCSTR pVrEnumNextTranslationA ( IN OUT PVRVALUE_ENUMA VrValueEnum ); PCWSTR pVrEnumNextTranslationW ( IN OUT PVRVALUE_ENUMW VrValueEnum ); // // Macro expansion definition // // None // // Code // /*++ Routine Description: VrCreateEnumStructA and VrCreateEnumStructW are 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 VrDestroyEnumStruct after the VrValueEnum is no longer needed. Arguments: VrValueEnum - Receives the version stamp info to be used by other functions in this module FileSpec - Specifies the file to obtain version information from Return Value: TRUE if the routine was able to get version info, or FALSE if an error occurred. --*/ BOOL VrCreateEnumStructA ( OUT PVRVALUE_ENUMA VrValueEnum, IN PCSTR FileSpec ) { // // Initialize the structure // ZeroMemory (VrValueEnum, sizeof (VRVALUE_ENUMA)); VrValueEnum->FileSpec = FileSpec; // // Allocate enough memory for the version stamp // VrValueEnum->Size = GetFileVersionInfoSizeA ( (PSTR) FileSpec, &VrValueEnum->Handle ); if (!VrValueEnum->Size) { DEBUGMSG ((DBG_VERSION, "File %s does not have version information", 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 // VrValueEnum->VersionBuffer = GbGrow (&VrValueEnum->GrowBuf, VrValueEnum->Size * 2); if (!VrValueEnum->VersionBuffer) { return FALSE; } VrValueEnum->StringBuffer = VrValueEnum->GrowBuf.Buf + VrValueEnum->Size; // // Now get the version info from the file // if (!GetFileVersionInfoA ( (PSTR) FileSpec, VrValueEnum->Handle, VrValueEnum->Size, VrValueEnum->VersionBuffer )) { VrDestroyEnumStructA (VrValueEnum); return FALSE; } // // Extract the fixed info // VerQueryValueA ( VrValueEnum->VersionBuffer, "\\", &VrValueEnum->FixedInfo, &VrValueEnum->FixedInfoSize ); return TRUE; } BOOL VrCreateEnumStructW ( OUT PVRVALUE_ENUMW VrValueEnum, IN PCWSTR FileSpec ) { ZeroMemory (VrValueEnum, sizeof (VRVALUE_ENUMW)); VrValueEnum->FileSpec = FileSpec; // // Allocate enough memory for the version stamp // VrValueEnum->Size = GetFileVersionInfoSizeW ( (PWSTR) FileSpec, &VrValueEnum->Handle ); if (!VrValueEnum->Size) { DEBUGMSG ((DBG_VERSION, "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 // VrValueEnum->VersionBuffer = GbGrow (&VrValueEnum->GrowBuf, VrValueEnum->Size * 2); if (!VrValueEnum->VersionBuffer) { return FALSE; } VrValueEnum->StringBuffer = VrValueEnum->GrowBuf.Buf + VrValueEnum->Size; // // Now get the version info from the file // if (!GetFileVersionInfoW ( (PWSTR) FileSpec, VrValueEnum->Handle, VrValueEnum->Size, VrValueEnum->VersionBuffer )) { VrDestroyEnumStructW (VrValueEnum); return FALSE; } // // Extract the fixed info // VerQueryValueW ( VrValueEnum->VersionBuffer, L"\\", &VrValueEnum->FixedInfo, &VrValueEnum->FixedInfoSize ); return TRUE; } /*++ Routine Description: VrDestroyEnumStructA and VrDestroyEnumStructW cleans up all memory allocated by the routines in this module. Arguments: VrValueEnum - Specifies the structure to clean up Return Value: none --*/ VOID VrDestroyEnumStructA ( IN PVRVALUE_ENUMA VrValueEnum ) { // // Clean up all allocations made by any routine using // the VrValueEnum // if (VrValueEnum->GrowBuf.Buf) { GbFree (&VrValueEnum->GrowBuf); } ZeroMemory (VrValueEnum, sizeof (VRVALUE_ENUMA)); } VOID VrDestroyEnumStructW ( IN PVRVALUE_ENUMW VrValueEnum ) { // // Clean up all allocations made by any routine using // the VrValueEnum // if (VrValueEnum->GrowBuf.Buf) { GbFree (&VrValueEnum->GrowBuf); } ZeroMemory (VrValueEnum, sizeof (VRVALUE_ENUMW)); } /*++ Routine Description: pVrEnumFirstTranslationA and pVrEnumFirstTranslationW return the translation string needed to access the string table of a version stamp. Arguments: VrValueEnum - Specifies the structure that has been initialized by VrCreateEnumStruct. Return Value: A pointer to a string specifying the first translation, or NULL if no translations exist. --*/ PCSTR pVrEnumFirstTranslationA ( IN OUT PVRVALUE_ENUMA VrValueEnum ) { UINT arraySize; // // Query version block for array of code pages/languages // if (!VerQueryValueA ( VrValueEnum->VersionBuffer, "\\VarFileInfo\\Translation", &VrValueEnum->Translations, &arraySize )) { // // No translations are available // arraySize = 0; } // // Return a pointer to the first translation // VrValueEnum->CurrentDefaultTranslation = 0; VrValueEnum->MaxTranslations = arraySize / sizeof (TRANSLATION); VrValueEnum->CurrentTranslation = 0; DEBUGMSG_IF (( VrValueEnum->MaxTranslations == 0, DBG_VERSION, "File %s has no translations", VrValueEnum->FileSpec )); return pVrEnumNextTranslationA (VrValueEnum); } PCWSTR pVrEnumFirstTranslationW ( IN OUT PVRVALUE_ENUMW VrValueEnum ) { UINT arraySize; // // Query version block for array of code pages/languages // if (!VerQueryValueW ( VrValueEnum->VersionBuffer, L"\\VarFileInfo\\Translation", &VrValueEnum->Translations, &arraySize )) { // // No translations are available // arraySize = 0; } // // Return a pointer to the first translation // VrValueEnum->CurrentDefaultTranslation = 0; VrValueEnum->MaxTranslations = arraySize / sizeof (TRANSLATION); VrValueEnum->CurrentTranslation = 0; DEBUGMSG_IF (( VrValueEnum->MaxTranslations == 0, DBG_VERSION, "File %S has no translations", VrValueEnum->FileSpec )); return pVrEnumNextTranslationW (VrValueEnum); } /*++ Routine Description: pIsDefaultTranslationA and pIsDefaultTranslationW return TRUE if the specified translation string is enumerated by default. These routines 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. --*/ BOOL pIsDefaultTranslationA ( IN PCSTR TranslationStr ) { INT i; for (i = 0 ; g_DefaultTranslationsA[i] ; i++) { if (StringIMatchA (TranslationStr, g_DefaultTranslationsA[i])) { return TRUE; } } return FALSE; } BOOL pIsDefaultTranslationW ( IN PCWSTR TranslationStr ) { INT i; for (i = 0 ; g_DefaultTranslationsW[i] ; i++) { if (StringIMatchW (TranslationStr, g_DefaultTranslationsW[i])) { return TRUE; } } return FALSE; } /*++ Routine Description: pVrEnumNextTranslationA and pVrEnumNextTranslationW continue the enumeration of translation strings, needed to access the string table in a version stamp. Arguments: VrValueEnum - Specifies the same structure passed to pVrEnumFirstTranslation. Return Value: A pointer to a string specifying the next translation, or NULL if no additional translations exist. --*/ PCSTR pVrEnumNextTranslationA ( IN OUT PVRVALUE_ENUMA VrValueEnum ) { PTRANSLATION translation; if (g_DefaultTranslationsA[VrValueEnum->CurrentDefaultTranslation]) { // // Return default translations first // StringCopyA ( VrValueEnum->TranslationStr, g_DefaultTranslationsA[VrValueEnum->CurrentDefaultTranslation] ); VrValueEnum->CurrentDefaultTranslation++; } else { do { // // Return NULL if all translations have been enumerated // if (VrValueEnum->CurrentTranslation == VrValueEnum->MaxTranslations) { return NULL; } // // Otherwise build translation string and return pointer to it // translation = &VrValueEnum->Translations[VrValueEnum->CurrentTranslation]; wsprintfA ( VrValueEnum->TranslationStr, "%04x%04x", translation->CodePage, translation->Language ); VrValueEnum->CurrentTranslation++; } while (pIsDefaultTranslationA (VrValueEnum->TranslationStr)); } return VrValueEnum->TranslationStr; } PCWSTR pVrEnumNextTranslationW ( IN OUT PVRVALUE_ENUMW VrValueEnum ) { PTRANSLATION translation; if (g_DefaultTranslationsW[VrValueEnum->CurrentDefaultTranslation]) { StringCopyW ( VrValueEnum->TranslationStr, g_DefaultTranslationsW[VrValueEnum->CurrentDefaultTranslation] ); VrValueEnum->CurrentDefaultTranslation++; } else { do { // // Return NULL if all translations have been enumerated // if (VrValueEnum->CurrentTranslation == VrValueEnum->MaxTranslations) { return NULL; } // // Otherwise build translation string and return pointer to it // translation = &VrValueEnum->Translations[VrValueEnum->CurrentTranslation]; wsprintfW ( VrValueEnum->TranslationStr, L"%04x%04x", translation->CodePage, translation->Language ); VrValueEnum->CurrentTranslation++; } while (pIsDefaultTranslationW (VrValueEnum->TranslationStr)); } return VrValueEnum->TranslationStr; } /*++ Routine Description: VrEnumFirstValueA and VrEnumFirstValueW return the first value stored in a version stamp for a specific field. If the field does not exist, the functions returns NULL. An enumeration of VrEnumFirstValue/VrEnumNextValue is used to list all localized strings for a field. Arguments: VrValueEnum - Specifies the structure that was initialized by VrCreateEnumStruct. 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 VrEnumFirstValueA ( IN OUT PVRVALUE_ENUMA VrValueEnum, IN PCSTR VersionField ) { PCSTR result = NULL; if (!pVrEnumFirstTranslationA (VrValueEnum)) { return NULL; } VrValueEnum->VersionField = VersionField; result = pVrEnumValueA (VrValueEnum); if (!result) { result = VrEnumNextValueA (VrValueEnum); } return result; } PCWSTR VrEnumFirstValueW ( IN OUT PVRVALUE_ENUMW VrValueEnum, IN PCWSTR VersionField ) { PCWSTR result = NULL; if (!pVrEnumFirstTranslationW (VrValueEnum)) { return NULL; } VrValueEnum->VersionField = VersionField; result = pVrEnumValueW (VrValueEnum); if (!result) { result = VrEnumNextValueW (VrValueEnum); } return result; } /*++ Routine Description: VrEnumNextValueA and VrEnumNextValueW return the next value stored in a version stamp for a specific field. Arguments: VrValueEnum - Specifies the same structure passed to VrEnumFirstValue Return Value: A pointer to the next value of the field, or NULL if another field does not exist. --*/ PCSTR VrEnumNextValueA ( IN OUT PVRVALUE_ENUMA VrValueEnum ) { PCSTR result = NULL; do { if (!pVrEnumNextTranslationA (VrValueEnum)) { break; } result = pVrEnumValueA (VrValueEnum); } while (!result); return result; } PCWSTR VrEnumNextValueW ( IN OUT PVRVALUE_ENUMW VrValueEnum ) { PCWSTR result = NULL; do { if (!pVrEnumNextTranslationW (VrValueEnum)) { break; } result = pVrEnumValueW (VrValueEnum); } while (!result); return result; } /*++ Routine Description: pVrEnumValueA and pVrEnumValueW are routines that obtain the value of a version field. They are used for both VrEnumFirstValue and VrEnumNextValue. Arguments: VrValueEnum - 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. --*/ PCSTR pVrEnumValueA ( IN OUT PVRVALUE_ENUMA VrValueEnum ) { PSTR text; UINT stringLen; PBYTE string; PCSTR result = NULL; // // Prepare sub block for VerQueryValue API // text = RealAllocTextExA (NULL, 0); text = AllocTextA ( SizeOfStringA (VrValueEnum->TranslationStr) + SizeOfStringA (VrValueEnum->VersionField) + 16 ); if (!text) { return NULL; } wsprintfA ( text, "\\StringFileInfo\\%s\\%s", VrValueEnum->TranslationStr, VrValueEnum->VersionField ); __try { // // Get the value from the version stamp // if (!VerQueryValueA ( VrValueEnum->VersionBuffer, text, &string, &stringLen )) { // // No value is available // __leave; } // // Copy value into buffer // StringCopyByteCountA (VrValueEnum->StringBuffer, (PCSTR) string, stringLen); result = (PCSTR)VrValueEnum->StringBuffer; } __finally { FreeTextA (text); } return result; } PCWSTR pVrEnumValueW ( IN OUT PVRVALUE_ENUMW VrValueEnum ) { PWSTR text; UINT stringLen; PBYTE string; PCWSTR result = NULL; // // Prepare sub block for VerQueryValue API // text = AllocTextW ( 18 + CharCountW (VrValueEnum->TranslationStr) + CharCountW (VrValueEnum->VersionField) ); if (!text) { return NULL; } wsprintfW ( text, L"\\StringFileInfo\\%s\\%s", VrValueEnum->TranslationStr, VrValueEnum->VersionField ); __try { // // Get the value from the version stamp // if (!VerQueryValueW ( VrValueEnum->VersionBuffer, text, &string, &stringLen )) { // // No value is available // __leave; } // // Copy value into buffer // CopyMemory (VrValueEnum->StringBuffer, string, stringLen * sizeof (WCHAR)); VrValueEnum->StringBuffer [stringLen * sizeof (WCHAR)] = 0; result = (PWSTR) VrValueEnum->StringBuffer; } __finally { FreeTextW (text); } return result; } ULONGLONG VrGetBinaryFileVersionA ( IN PVRVALUE_ENUMA VrValueEnum ) /*++ Routine Description: VrGetBinaryFileVersion returns the FileVersion field from the fixed info structure of version information. Arguments: VrValueEnum - Specifies the structure being processed Return Value: A ULONGLONG FileVersion field --*/ { ULONGLONG result = 0; if (VrValueEnum->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { *((PDWORD) (&result)) = VrValueEnum->FixedInfo->dwFileVersionLS; *(((PDWORD) (&result)) + 1) = VrValueEnum->FixedInfo->dwFileVersionMS; } return result; } ULONGLONG VrGetBinaryProductVersionA ( IN PVRVALUE_ENUMA VrValueEnum ) /*++ Routine Description: VrGetBinaryProductVersion returns the ProductVersion field from the fixed info structure of version information. Arguments: VrValueEnum - Specifies the structure being processed Return Value: A ULONGLONG ProductVersion field --*/ { ULONGLONG result = 0; if (VrValueEnum->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { *((PDWORD) (&result)) = VrValueEnum->FixedInfo->dwProductVersionLS; *(((PDWORD) (&result)) + 1) = VrValueEnum->FixedInfo->dwProductVersionMS; } return result; } DWORD VrGetBinaryFileDateLoA ( IN PVRVALUE_ENUMA VrValueEnum ) /*++ Routine Description: VrGetBinaryFileDateLo returns the LS dword from FileDate field from the fixed info structure of version information. Arguments: VrValueEnum - Specifies the structure being processed Return Value: A DWORD, LS dword of the FileDate field --*/ { if (VrValueEnum->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VrValueEnum->FixedInfo->dwFileDateLS; } return 0; } DWORD VrGetBinaryFileDateHiA ( IN PVRVALUE_ENUMA VrValueEnum ) /*++ Routine Description: VrGetBinaryFileDateHi returns the MS dword from FileDate field from the fixed info structure of version information. Arguments: VrValueEnum - Specifies the structure being processed Return Value: A DWORD, MS dword of the FileDate field --*/ { if (VrValueEnum->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VrValueEnum->FixedInfo->dwFileDateMS; } return 0; } DWORD VrGetBinaryOsVersionA ( IN PVRVALUE_ENUMA VrValueEnum ) /*++ Routine Description: VrGetBinaryOsVersion returns the FileOS field from the fixed info structure of version information. Arguments: VrValueEnum - Specifies the structure being processed Return Value: A DWORD FileOS field --*/ { if (VrValueEnum->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VrValueEnum->FixedInfo->dwFileOS; } return 0; } DWORD VrGetBinaryFileType ( IN PVRVALUE_ENUMA VrValueEnum ) /*++ Routine Description: VrGetBinaryFileType returns the FileType field from the fixed info structure of version information. Arguments: VrValueEnum - Specifies the structure being processed Return Value: A DWORD FileType field --*/ { if (VrValueEnum->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) { return VrValueEnum->FixedInfo->dwFileType; } return 0; }