Comparing file names in native code

There are new recommendations for comparing strings with the 2.0 .NET Framework (see https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/StringsinNET20.asp). You can now use OrdinalIgnoreCase to accurately compare file names, user names, etc.

The new APIs are great. For the first time there is an easy way to compare strings in the same way that much of the operating system compares strings. There is only one problem – what do you do for native code? Native has many string comparisons APIs; wcsicmp/CompareString/LCMapString to name a few. However, none of them implement an OrdinalIgnoreCase string comparison. So the result is that there is no way to accurately compare file names, user names, etc from native code.

So I decided to try and solve this problem. It turns out that the OS does have an API to convert a character to uppercase, and so we can use this to build a OrdinalIgnoreCase string comparison.

wchar_t ToUpperInvariant(wchar_t input)

{

    wchar_t result;

    LONG lres = LCMapStringW(

        LOCALE_INVARIANT,

        LCMAP_UPPERCASE,

        &input,

        1,

        &result,

        1

        );

    if (lres == 0)

    {

        ASSERT(!"LCMapStringW failed to convert a character to upper case");

        result = input;

    }

    return result;

}

int OrdinalIgnoreCaseCompareStrings(LPCWSTR sz1, LPCWSTR sz2)

{

    if (sz1 == sz2)

    {

        return 0;

    }

    if (sz1 == NULL) sz1 = L"";

    if (sz2 == NULL) sz2 = L"";

    for (;; sz1++, sz2++)

    {

        const wchar_t c1 = *sz1;

        const wchar_t c2 = *sz2;

        // check for binary equality first

        if (c1 == c2)

        {

            if (c1 == 0)

            {

                return 0; // We have reached the end of both strings. No difference found.

            }

        }

        else

        {

            if (c1 == 0 || c2 == 0)

            {

                return (c1-c2); // We have reached the end of one string

            }

            // IMPORTANT: this needs to be upper case to match the behavior of the operating system.

            // See https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/StringsinNET20.asp

            const wchar_t u1 = ToUpperInvariant(c1);

            const wchar_t u2 = ToUpperInvariant(c2);

            if (u1 != u2)

            {

                return (u1-u2); // strings are different

            }

        }

    }

}