windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/common/migutil/version.c

1089 lines
21 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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:
<alias> <date> <comments>
--*/
#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);
}