[KERNEL32] Sync other functions with Wine 1.9.17
authorDmitry Chapyshev <dmitry@reactos.org>
Mon, 19 Sep 2016 11:28:20 +0000 (11:28 +0000)
committerDmitry Chapyshev <dmitry@reactos.org>
Mon, 19 Sep 2016 11:28:20 +0000 (11:28 +0000)
* Fixes 1 test for kernel32:locale

svn path=/trunk/; revision=72740

reactos/dll/win32/kernel32/winnls/string/lang.c

index 4ac3cec..f6e6364 100644 (file)
@@ -142,6 +142,100 @@ static LCID lcid_LC_PAPER;
 static LCID lcid_LC_MEASUREMENT;
 static LCID lcid_LC_TELEPHONE;
 
+static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
+static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
+static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
+static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
+static const WCHAR iDateW[] = {'i','D','a','t','e',0};
+static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
+static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
+static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
+static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
+static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
+static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
+static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
+static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
+static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
+static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
+static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
+static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
+static const WCHAR s1159W[] = {'s','1','1','5','9',0};
+static const WCHAR s2359W[] = {'s','2','3','5','9',0};
+static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
+static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
+static const WCHAR sDateW[] = {'s','D','a','t','e',0};
+static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
+static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
+static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
+static const WCHAR sListW[] = {'s','L','i','s','t',0};
+static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
+static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
+static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
+static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
+static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
+static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
+static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
+static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
+static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
+static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
+static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
+static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
+static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
+
+static struct registry_value
+{
+    DWORD           lctype;
+    const WCHAR    *name;
+    WCHAR          *cached_value;
+} registry_values[] =
+{
+    { LOCALE_ICALENDARTYPE, iCalendarTypeW },
+    { LOCALE_ICURRDIGITS, iCurrDigitsW },
+    { LOCALE_ICURRENCY, iCurrencyW },
+    { LOCALE_IDIGITS, iDigitsW },
+    { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
+    { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
+    { LOCALE_ILZERO, iLZeroW },
+    { LOCALE_IMEASURE, iMeasureW },
+    { LOCALE_INEGCURR, iNegCurrW },
+    { LOCALE_INEGNUMBER, iNegNumberW },
+    { LOCALE_IPAPERSIZE, iPaperSizeW },
+    { LOCALE_ITIME, iTimeW },
+    { LOCALE_S1159, s1159W },
+    { LOCALE_S2359, s2359W },
+    { LOCALE_SCURRENCY, sCurrencyW },
+    { LOCALE_SDATE, sDateW },
+    { LOCALE_SDECIMAL, sDecimalW },
+    { LOCALE_SGROUPING, sGroupingW },
+    { LOCALE_SLIST, sListW },
+    { LOCALE_SLONGDATE, sLongDateW },
+    { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
+    { LOCALE_SMONGROUPING, sMonGroupingW },
+    { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
+    { LOCALE_SNEGATIVESIGN, sNegativeSignW },
+    { LOCALE_SPOSITIVESIGN, sPositiveSignW },
+    { LOCALE_SSHORTDATE, sShortDateW },
+    { LOCALE_STHOUSAND, sThousandW },
+    { LOCALE_STIME, sTimeW },
+    { LOCALE_STIMEFORMAT, sTimeFormatW },
+    { LOCALE_SYEARMONTH, sYearMonthW },
+    /* The following are not listed under MSDN as supported,
+     * but seem to be used and also stored in the registry.
+     */
+    { LOCALE_ICOUNTRY, iCountryW },
+    { LOCALE_IDATE, iDateW },
+    { LOCALE_ILDATE, iLDateW },
+    { LOCALE_ITLZERO, iTLZeroW },
+    { LOCALE_SCOUNTRY, sCountryW },
+    { LOCALE_SABBREVLANGNAME, sLanguageW },
+    /* The following are used in XP and later */
+    { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
+    { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
+    { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
+};
+
+static RTL_CRITICAL_SECTION cache_section = { NULL, -1, 0, 0, 0, 0 };
+
 #ifndef __REACTOS__
 /* Copy Ascii string to Unicode without using codepages */
 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
@@ -491,107 +585,6 @@ LANGID WINAPI GetSystemDefaultUILanguage(void)
     return lang;
 }
 
-/******************************************************************************
- *             get_locale_value_name
- *
- * Gets the registry value name for a given lctype.
- */
-static const WCHAR *get_locale_value_name( DWORD lctype )
-{
-    static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
-    static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
-    static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
-    static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
-    static const WCHAR iDateW[] = {'i','D','a','t','e',0};
-    static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
-    static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
-    static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
-    static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
-    static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
-    static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
-    static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
-    static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
-    static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
-    static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
-    static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
-    static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
-    static const WCHAR s1159W[] = {'s','1','1','5','9',0};
-    static const WCHAR s2359W[] = {'s','2','3','5','9',0};
-    static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
-    static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
-    static const WCHAR sDateW[] = {'s','D','a','t','e',0};
-    static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
-    static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
-    static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
-    static const WCHAR sListW[] = {'s','L','i','s','t',0};
-    static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
-    static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
-    static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
-    static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
-    static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
-    static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
-    static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
-    static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
-    static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
-    static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
-    static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
-    static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
-    static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
-
-    switch (lctype)
-    {
-    /* These values are used by SetLocaleInfo and GetLocaleInfo, and
-     * the values are stored in the registry, confirmed under Windows.
-     */
-    case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
-    case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
-    case LOCALE_ICURRENCY:        return iCurrencyW;
-    case LOCALE_IDIGITS:          return iDigitsW;
-    case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
-    case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
-    case LOCALE_ILZERO:           return iLZeroW;
-    case LOCALE_IMEASURE:         return iMeasureW;
-    case LOCALE_INEGCURR:         return iNegCurrW;
-    case LOCALE_INEGNUMBER:       return iNegNumberW;
-    case LOCALE_IPAPERSIZE:       return iPaperSizeW;
-    case LOCALE_ITIME:            return iTimeW;
-    case LOCALE_S1159:            return s1159W;
-    case LOCALE_S2359:            return s2359W;
-    case LOCALE_SCURRENCY:        return sCurrencyW;
-    case LOCALE_SDATE:            return sDateW;
-    case LOCALE_SDECIMAL:         return sDecimalW;
-    case LOCALE_SGROUPING:        return sGroupingW;
-    case LOCALE_SLIST:            return sListW;
-    case LOCALE_SLONGDATE:        return sLongDateW;
-    case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
-    case LOCALE_SMONGROUPING:     return sMonGroupingW;
-    case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
-    case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
-    case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
-    case LOCALE_SSHORTDATE:       return sShortDateW;
-    case LOCALE_STHOUSAND:        return sThousandW;
-    case LOCALE_STIME:            return sTimeW;
-    case LOCALE_STIMEFORMAT:      return sTimeFormatW;
-    case LOCALE_SYEARMONTH:       return sYearMonthW;
-
-    /* The following are not listed under MSDN as supported,
-     * but seem to be used and also stored in the registry.
-     */
-    case LOCALE_ICOUNTRY:         return iCountryW;
-    case LOCALE_IDATE:            return iDateW;
-    case LOCALE_ILDATE:           return iLDateW;
-    case LOCALE_ITLZERO:          return iTLZeroW;
-    case LOCALE_SCOUNTRY:         return sCountryW;
-    case LOCALE_SABBREVLANGNAME:  return sLanguageW;
-
-    /* The following are used in XP and later */
-    case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
-    case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
-    case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
-    }
-    return NULL;
-}
-
 
 /******************************************************************************
  *             get_registry_locale_info
@@ -599,7 +592,7 @@ static const WCHAR *get_locale_value_name( DWORD lctype )
  * Retrieve user-modified locale info from the registry.
  * Return length, 0 on error, -1 if not found.
  */
-static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
+static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
 {
     DWORD size;
     INT ret;
@@ -609,54 +602,103 @@ static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
     KEY_VALUE_PARTIAL_INFORMATION *info;
     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
 
-    if (!(hkey = create_registry_key())) return -1;
-
-    RtlInitUnicodeString( &nameW, value );
-    size = info_size + len * sizeof(WCHAR);
+    RtlEnterCriticalSection( &cache_section );
 
-    if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
+    if (!registry_value->cached_value)
     {
+        if (!(hkey = create_registry_key()))
+        {
+            RtlLeaveCriticalSection( &cache_section );
+            return -1;
+        }
+
+        RtlInitUnicodeString( &nameW, registry_value->name );
+        size = info_size + len * sizeof(WCHAR);
+
+        if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
+        {
+            NtClose( hkey );
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            RtlLeaveCriticalSection( &cache_section );
+            return 0;
+        }
+
+        status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
+
+        /* try again with a bigger buffer when we have to return the correct size */
+        if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
+        {
+            KEY_VALUE_PARTIAL_INFORMATION *new_info;
+            if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
+            {
+                info = new_info;
+                status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
+            }
+        }
+
         NtClose( hkey );
-        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
-        return 0;
-    }
 
-    status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
+        if (!status)
+        {
+            INT length = (size - info_size) / sizeof(WCHAR);
+            LPWSTR cached_value;
+
+            if (!length || ((WCHAR *)&info->Data)[length-1])
+                length++;
 
-    if (!status)
-    {
-        ret = (size - info_size) / sizeof(WCHAR);
-        /* append terminating null if needed */
-        if (!ret || ((WCHAR *)info->Data)[ret-1])
+            cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
+
+            if (!cached_value)
+            {
+                HeapFree( GetProcessHeap(), 0, info );
+                SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+                RtlLeaveCriticalSection( &cache_section );
+                return 0;
+            }
+
+            memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
+            cached_value[length-1] = 0;
+            HeapFree( GetProcessHeap(), 0, info );
+            registry_value->cached_value = cached_value;
+        }
+        else
         {
-            if (ret < len || !buffer) ret++;
+            if (status == STATUS_BUFFER_OVERFLOW && !buffer)
+            {
+                ret = (size - info_size) / sizeof(WCHAR);
+            }
+            else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
+            {
+                ret = -1;
+            }
             else
             {
-                SetLastError( ERROR_INSUFFICIENT_BUFFER );
+                SetLastError( RtlNtStatusToDosError(status) );
                 ret = 0;
             }
-        }
-        if (ret && buffer)
-        {
-            memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
-            buffer[ret-1] = 0;
+            HeapFree( GetProcessHeap(), 0, info );
+            RtlLeaveCriticalSection( &cache_section );
+            return ret;
         }
     }
-    else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
-    {
-        ret = (size - info_size) / sizeof(WCHAR) + 1;
-    }
-    else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
-    {
-        ret = -1;
-    }
-    else
+
+    ret = lstrlenW( registry_value->cached_value ) + 1;
+
+    if (buffer)
     {
-        SetLastError( RtlNtStatusToDosError(status) );
-        ret = 0;
+        if (ret > len)
+        {
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            ret = 0;
+        }
+        else
+        {
+            lstrcpyW( buffer, registry_value->cached_value );
+        }
     }
-    NtClose( hkey );
-    HeapFree( GetProcessHeap(), 0, info );
+
+    RtlLeaveCriticalSection( &cache_section );
+
     return ret;
 }
 
@@ -694,7 +736,8 @@ INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
         SetLastError( ERROR_INVALID_PARAMETER );
         return 0;
     }
-    if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
+    if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
+         (lctype & LOCALE_RETURN_GENITIVE_NAMES))
     {
         SetLastError( ERROR_INVALID_FLAGS );
         return 0;
@@ -742,6 +785,20 @@ static int get_value_base_by_lctype( LCTYPE lctype )
     return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
 }
 
+/******************************************************************************
+ *             get_locale_registry_value
+ *
+ * Gets the registry value name and cache for a given lctype.
+ */
+static struct registry_value *get_locale_registry_value( DWORD lctype )
+{
+    int i;
+    for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
+        if (registry_values[i].lctype == lctype)
+            return &registry_values[i];
+    return NULL;
+}
+
 /******************************************************************************
  *             GetLocaleInfoW (KERNEL32.@)
  *
@@ -783,7 +840,7 @@ INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
     if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
         lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
     {
-        const WCHAR *value = get_locale_value_name(lctype);
+        struct registry_value *value = get_locale_registry_value(lctype);
 
         if (value)
         {
@@ -950,14 +1007,14 @@ BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
  */
 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
 {
-    const WCHAR *value;
+    struct registry_value *value;
     static const WCHAR intlW[] = {'i','n','t','l',0 };
     UNICODE_STRING valueW;
     NTSTATUS status;
     HANDLE hkey;
 
     lctype &= 0xffff;
-    value = get_locale_value_name( lctype );
+    value = get_locale_registry_value( lctype );
 
     if (!data || !value)
     {
@@ -971,17 +1028,22 @@ BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
         return FALSE;
     }
 
-    TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
+    TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
 
     /* FIXME: should check that data to set is sane */
 
     /* FIXME: profile functions should map to registry */
-    WriteProfileStringW( intlW, value, data );
+    WriteProfileStringW( intlW, value->name, data );
 
     if (!(hkey = create_registry_key())) return FALSE;
-    RtlInitUnicodeString( &valueW, value );
+    RtlInitUnicodeString( &valueW, value->name );
     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, (PVOID)data, (strlenW(data)+1)*sizeof(WCHAR) );
 
+    RtlEnterCriticalSection( &cache_section );
+    HeapFree( GetProcessHeap(), 0, value->cached_value );
+    value->cached_value = NULL;
+    RtlLeaveCriticalSection( &cache_section );
+
     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
     {
       /* Set I-value from S value */
@@ -1011,12 +1073,17 @@ BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
       else
         lctype = LOCALE_ILDATE;
 
-      value = get_locale_value_name( lctype );
+      value = get_locale_registry_value( lctype );
 
-      WriteProfileStringW( intlW, value, szBuff );
+      WriteProfileStringW( intlW, value->name, szBuff );
 
-      RtlInitUnicodeString( &valueW, value );
+      RtlInitUnicodeString( &valueW, value->name );
       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
+
+      RtlEnterCriticalSection( &cache_section );
+      HeapFree( GetProcessHeap(), 0, value->cached_value );
+      value->cached_value = NULL;
+      RtlLeaveCriticalSection( &cache_section );
     }
 
     NtClose( hkey );
@@ -1095,6 +1162,9 @@ LCID WINAPI ConvertDefaultLocale( LCID lcid )
 
     switch (lcid)
     {
+    case LOCALE_INVARIANT:
+        /* keep as-is */
+        break;
     case LOCALE_SYSTEM_DEFAULT:
         lcid = GetSystemDefaultLCID();
         break;
@@ -1426,7 +1496,11 @@ INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPW
 
     if (version) FIXME("unsupported version structure %p\n", version);
     if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
-    if (lparam) FIXME("unsupported lparam %lx\n", lparam);
+    if (lparam)
+    {
+        static int once;
+        if (!once++) FIXME("unsupported lparam %lx\n", lparam);
+    }
 
     if (!src || !srclen || dstlen < 0)
     {
@@ -1756,20 +1830,21 @@ INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
 }
 
 /******************************************************************************
- *           CompareStringW    (KERNEL32.@)
- *
- * See CompareStringA.
+ *           CompareStringEx    (KERNEL32.@)
  */
-INT WINAPI CompareStringW(LCID lcid, DWORD flags,
-                          LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
+INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
+                           LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
 {
-    static const DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
-                                        |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP
-                                        |NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
-    static DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
+    DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
+                           |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
+    DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
     /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
     INT ret;
+    static int once;
 
+    if (version) FIXME("unexpected version parameter\n");
+    if (reserved) FIXME("unexpected reserved value\n");
+    if (lParam) FIXME("unexpected lParam\n");
 
     if (!str1 || !str2)
     {
@@ -1777,7 +1852,7 @@ INT WINAPI CompareStringW(LCID lcid, DWORD flags,
         return 0;
     }
 
-    if (flags & ~supported_flags)
+    if (flags & ~(supported_flags|semistub_flags))
     {
         SetLastError(ERROR_INVALID_FLAGS);
         return 0;
@@ -1785,8 +1860,8 @@ INT WINAPI CompareStringW(LCID lcid, DWORD flags,
 
     if (flags & semistub_flags)
     {
-        FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
-        semistub_flags &= ~flags;
+        if (!once++)
+            FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
     }
 
     if (len1 < 0) len1 = strlenW(str1);
@@ -1799,6 +1874,17 @@ INT WINAPI CompareStringW(LCID lcid, DWORD flags,
     return CSTR_EQUAL;
 }
 
+/******************************************************************************
+ *           CompareStringW    (KERNEL32.@)
+ *
+ * See CompareStringA.
+ */
+INT WINAPI CompareStringW(LCID lcid, DWORD flags,
+                          LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
+{
+    return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
+}
+
 /******************************************************************************
  *           CompareStringA    (KERNEL32.@)
  *
@@ -1883,7 +1969,7 @@ INT WINAPI CompareStringA(LCID lcid, DWORD flags,
         str2W = buf2W;
     }
 
-    ret = CompareStringW(lcid, flags, str1W, len1W, str2W, len2W);
+    ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
 
     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
@@ -1897,7 +1983,7 @@ static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
     HANDLE hkey;
 
     RtlInitUnicodeString( &keyName, szKeyName );
-    InitializeObjectAttributes(&attr, &keyName, OBJ_CASE_INSENSITIVE, hRootKey, NULL);
+    InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
 
     if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
         hkey = 0;
@@ -2041,7 +2127,7 @@ static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
                               szValue, sizeof(szValue) ))
         {
-            BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
+            BOOL bInstalled = szValue[0] == '1';
             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
 
             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),