1089 lines
21 KiB
C
1089 lines
21 KiB
C
|
/*++
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|