1

I have an application built on Windows 7 SP1 Visual Studio 2010 SP1.

It seems CompareString doesn't work the same way on Windows 7 and Windows XP. I am creating an EndsWith/StartsWith-like (See C# String.EndsWith) methods, but where it works on Windows 7 as expected, but not on Windows XP.

Here are my StartsWith and EndsWith:

bool String::StartsWith( const String& value, bool bCaseSensitive ) const
{
    if(this->strLen == 0 || value.strLen == 0)
        return false;

    DWORD flags;
    if(bCaseSensitive == false)
        flags = LINGUISTIC_IGNORECASE;
    else
        flags = NORM_LINGUISTIC_CASING;

    if( CSTR_EQUAL == CompareStringW(LOCALE_USER_DEFAULT, flags, this->_str, static_cast<int>(value.strLen), value._str, static_cast<int>(value.strLen)) )
        return true;
    else if(CSTR_EQUAL == CompareStringW(LOCALE_SYSTEM_DEFAULT, flags, this->_str, static_cast<int>(value.strLen), value._str, static_cast<int>(value.strLen)))
        return true;
    else if(CSTR_EQUAL == CompareStringW(GetThreadLocale(), flags, this->_str, static_cast<int>(value.strLen), value._str, static_cast<int>(value.strLen)))
        return true;
    else
        return false;
}

bool String::EndsWith( const String& value, bool bCaseSensitive ) const
{
    if(this->strLen == 0 || value.strLen == 0)
        return false;

    DWORD flags;
    if(bCaseSensitive == false)
        flags = LINGUISTIC_IGNORECASE;
    else
        flags = NORM_LINGUISTIC_CASING;

    size_t maxLen;
    if(this->strLen < value.strLen)
        maxLen = this->strLen;
    else
        maxLen = value.strLen;

    LPCWSTR szStartOffset;

    if(maxLen == this->strLen)
        szStartOffset = this->_str;
    else
        szStartOffset = (this->_str + (this->strLen - value.strLen));

    if( CSTR_EQUAL == CompareStringW(LOCALE_USER_DEFAULT, flags, szStartOffset, static_cast<int>(maxLen), value._str, static_cast<int>(maxLen)) )
        return true;
    else if(CSTR_EQUAL == CompareStringW(LOCALE_SYSTEM_DEFAULT, flags, szStartOffset, static_cast<int>(maxLen), value._str, static_cast<int>(maxLen)))
        return true;
    else if(CSTR_EQUAL == CompareStringW(GetThreadLocale(), flags, szStartOffset, static_cast<int>(maxLen), value._str, static_cast<int>(maxLen)))
        return true;
    else
        return false;
}

If somebody could help me I'd be very grateful.

Philipi Willemann
  • 658
  • 1
  • 9
  • 25
CS.
  • 1,845
  • 1
  • 19
  • 38
  • What exactly does not work ? What does it do on XP and what do you expect it to do ? Did you try to debug the code ? – Jabberwocky Jul 02 '13 at 12:34
  • I'm running the XP machine in VirtualBox, and I havent been able to figure out how to remotely debug it, see; http://stackoverflow.com/questions/17418955/how-to-remote-debug-a-windows-xp-process-from-visual-studio-2010-windows-not-s – CS. Jul 02 '13 at 12:49

1 Answers1

1

So there was a combination of things.

First, CompareString returns 0 on error, and as you can see, I do not check for that. On Windows XP, it did fail, and GetLastError() set to 1004 = "Invalid flags.". The error is the clue to the next issue.

Second, I am always using one flag or another other, and on Windows XP, NORM_LINGUISTIC_CASING causes the 1004 error. And since the default behavior of this function is case sensitive, this flag is not really needed most of the time, and flags can be set to 0.

I have created a demonstration program that implements StartsWith: stringtest.cpp

// cl /MTd /DUNICODE /D_UNICODE_ stringtest.cpp
// MTd  - statically compile libcrt to binary, this way you don't have to copy msvcrtd100.dll etc to the XP machine.

#include <windows.h>
#include <cstdio>

int StartsWith(LCID locale, const WCHAR* szOrg, const WCHAR* szValue, bool sensitive, bool expected /* Can be used for asserts. */)
{
    int retval(0)
    DWORD flags = sensitive ? 0 : NORM_IGNORECASE; // You could replace 0 with NORM_LINGUISTIC_CASING to make it fail on Windows XP.

    int cchValue = lstrlenW(szValue);

    // if szOrg can't hold szValue, it doesn't start with szValue.
    if( cchValue > lstrlenW(szOrg) )
        return CSTR_LESS_THAN;

    // We trick CompareString to think szOrg is the same length as szValue.
    // This is how we check only the cchValue first characters of szOrg.
    retval = CompareStringW(locale, flags, szOrg, cchValue, szValue, cchValue);

    // You could assert on retval and parameter 'expected '.

    return(retval);
}

// Some compiler magic..
#define SHOWBOOL(expr)  { \
                            int ret = expr; \
                            DWORD gle=GetLastError(); /* Cache GLE in case printf changes it. */ \
                            printf( "%s = %s\r\n", ( ret == CSTR_EQUAL ? "true" : "false"), #expr ); \
                            printf("GetLastError() = %d - ret = %d\r\n\r\n", gle, ret); \
                        }

// Named this way since we get the expression to console, easier to read.
bool ExpectsTrue = true;
bool ExpectsFalse = false;

bool Sensitive = true;
bool Insensitive = false;

int main(int argc, WCHAR* argv[])
{
    SHOWBOOL( StartsWith(LOCALE_USER_DEFAULT, L"Hello World", L"Hello", Sensitive, ExpectsTrue) );
    SHOWBOOL( StartsWith(LOCALE_USER_DEFAULT, L"Hello World", L"hello", Sensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(LOCALE_USER_DEFAULT, L"Hello World", L"hello", Insensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(LOCALE_USER_DEFAULT, L"Hello World", L"1Hello", Sensitive, ExpectsFalse) );

    SHOWBOOL( StartsWith(LOCALE_SYSTEM_DEFAULT, L"Hello World", L"Hello", Sensitive, ExpectsTrue) );
    SHOWBOOL( StartsWith(LOCALE_SYSTEM_DEFAULT, L"Hello World", L"hello", Sensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(LOCALE_SYSTEM_DEFAULT, L"Hello World", L"hello", Insensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(LOCALE_SYSTEM_DEFAULT, L"Hello World", L"1Hello", Sensitive, ExpectsFalse) );

    SHOWBOOL( StartsWith(GetThreadLocale(), L"Hello World", L"Hello", Sensitive, ExpectsTrue) );
    SHOWBOOL( StartsWith(GetThreadLocale(), L"Hello World", L"hello", Sensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(GetThreadLocale(), L"Hello World", L"hello", Insensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(GetThreadLocale(), L"Hello World", L"1Hello", Sensitive, ExpectsFalse) );

    SHOWBOOL( StartsWith(GetSystemDefaultLCID(), L"Hello World", L"Hello", Sensitive, ExpectsTrue) );
    SHOWBOOL( StartsWith(GetSystemDefaultLCID(), L"Hello World", L"hello", Sensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(GetSystemDefaultLCID(), L"Hello World", L"hello", Insensitive, ExpectsFalse) );
    SHOWBOOL( StartsWith(GetSystemDefaultLCID(), L"Hello World", L"1Hello", Sensitive, ExpectsFalse) );

    return(NO_ERROR);
}
CS.
  • 1,845
  • 1
  • 19
  • 38