/*++

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 = JoinPathsInPoolExA ((
                NULL,
                "StringFileInfo",
                VrValueEnum->TranslationStr,
                VrValueEnum->VersionField,
                NULL
                ));

    if (!text) {
        return NULL;
    }

    __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 {
        FreePathStringA (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 = JoinPathsInPoolExW ((
                NULL,
                L"StringFileInfo",
                VrValueEnum->TranslationStr,
                VrValueEnum->VersionField,
                NULL
                ));

    if (!text) {
        return NULL;
    }

    __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 {
        FreePathStringW (text);
    }

    return result;
}

/*++

Routine Description:

  VrCheckVersionValueA and VrCheckVersionValueW return TRUE
  if the version value name specified has the specified version
  value.

Arguments:

  VrValueEnum  - Specifies the structure being processed

  VersionName  - Specifies the version value name.

  VersionValue - Specifies the version value.

Return value:

  TRUE  - the query was successful
  FALSE - the query failed

--*/

BOOL
VrCheckVersionValueA (
    IN      PVRVALUE_ENUMA VrValueEnum,
    IN      PCSTR VersionName,
    IN      PCSTR VersionValue
    )
{
    PCSTR CurrentStr;
    BOOL result = FALSE;

    if ((!VersionName) || (!VersionValue)) {
        return FALSE;
    }

    CurrentStr = VrEnumFirstValueA (VrValueEnum, VersionName);
    while (CurrentStr) {
        CurrentStr = SkipSpaceA (CurrentStr);
        TruncateTrailingSpaceA ((PSTR) CurrentStr);
        if (IsPatternMatchA (VersionValue, CurrentStr)) {
            result = TRUE;
            break;
        }
        CurrentStr = VrEnumNextValueA (VrValueEnum);
    }
    return result;
}

BOOL
VrCheckVersionValueW (
    IN      PVRVALUE_ENUMW VrValueEnum,
    IN      PCWSTR VersionName,
    IN      PCWSTR VersionValue
    )
{
    PCWSTR CurrentStr;
    BOOL result = FALSE;

    if ((!VersionName) || (!VersionValue)) {
        return FALSE;
    }

    CurrentStr = VrEnumFirstValueW (VrValueEnum, VersionName);
    while (CurrentStr) {
        CurrentStr = SkipSpaceW (CurrentStr);
        TruncateTrailingSpaceW ((PWSTR) CurrentStr);
        if (IsPatternMatchW (VersionValue, CurrentStr)) {
            result = TRUE;
            break;
        }
        CurrentStr = VrEnumNextValueW (VrValueEnum);
    }
    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
VrGetBinaryFileTypeA (
    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;
}


/*++

Routine Description:

  VrCheckFileVersionA and VrCheckFileVersionW look in the file's version
  structure trying to see if a specific name has a specific value.

Arguments:

  FileName     - File to query for version struct.

  NameToCheck  - Name to query in version structure.

  ValueToCheck - Value to query in version structure.

Return value:

  TRUE  - the query was successful
  FALSE - the query failed

--*/

BOOL
VrCheckFileVersionA (
    IN      PCSTR FileName,
    IN      PCSTR NameToCheck,
    IN      PCSTR ValueToCheck
    )
{
    VRVALUE_ENUMA Version;
    PCSTR CurrentStr;
    BOOL result = FALSE;

    MYASSERT (NameToCheck);
    MYASSERT (ValueToCheck);

    if (VrCreateEnumStructA (&Version, FileName)) {
        __try {
            CurrentStr = VrEnumFirstValueA (&Version, NameToCheck);
            while (CurrentStr) {
                CurrentStr = SkipSpaceA (CurrentStr);
                TruncateTrailingSpaceA ((PSTR) CurrentStr);
                if (IsPatternMatchA (ValueToCheck, CurrentStr)) {
                    result = TRUE;
                    __leave;
                }

                CurrentStr = VrEnumNextValueA (&Version);
            }
        }
        __finally {
            VrDestroyEnumStructA (&Version);
        }
    }
    return result;
}

BOOL
VrCheckFileVersionW (
    IN      PCWSTR FileName,
    IN      PCWSTR NameToCheck,
    IN      PCWSTR ValueToCheck
    )
{
    VRVALUE_ENUMW Version;
    PCWSTR CurrentStr;
    BOOL result = FALSE;

    MYASSERT (NameToCheck);
    MYASSERT (ValueToCheck);

    if (VrCreateEnumStructW (&Version, FileName)) {
        __try {
            CurrentStr = VrEnumFirstValueW (&Version, NameToCheck);
            while (CurrentStr) {
                CurrentStr = SkipSpaceW (CurrentStr);
                TruncateTrailingSpaceW ((PWSTR) CurrentStr);
                if (IsPatternMatchW (ValueToCheck, CurrentStr)) {
                    result = TRUE;
                    __leave;
                }

                CurrentStr = VrEnumNextValueW (&Version);
            }
        }
        __finally {
            VrDestroyEnumStructW (&Version);
        }
    }
    return result;
}