Как использовать функцию CompareString, чтобы она работала как в Windows XP, так и в Windows 7
У меня есть приложение, построенное на Windows 7 SP1 Visual Studio 2010 SP1.
Похоже на то CompareString
не работает одинаково на Windows 7 и Windows XP. Я создаю EndsWith
/StartsWith
-подобный (см. C# String.EndsWith
методы, но где он работает на Windows 7, как и ожидалось, но не на Windows XP.
Вот мои StartsWith
а также 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;
}
Если бы кто-нибудь мог помочь мне, я был бы очень благодарен.
1 ответ
Таким образом, было сочетание вещей.
Во-первых, CompareString возвращает 0 при ошибке, и, как вы можете видеть, я не проверяю это. В Windows XP произошел сбой, и GetLastError() установили в 1004 = "Недопустимые флаги". Ошибка является ключом к следующей проблеме.
Во-вторых, я всегда использую тот или иной флаг, а в Windows XP NORM_LINGUISTIC_CASING вызывает ошибку 1004. А поскольку поведение по умолчанию для этой функции чувствительно к регистру, большую часть времени этот флаг на самом деле не нужен, и для флагов можно установить значение 0.
Я создал демонстрационную программу, которая реализует 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);
}