From: Francis De Brabandere <francisdb@gmail.com> CStr and other string coercions of numeric/date variants went through VariantChangeType(..., 0, VT_BSTR), so they always formatted with the user default locale regardless of SetLocale. Thread the script LCID through the to_string() helper (and the shared err_string_prop) so conversions honor the script-level locale. --- dlls/vbscript/global.c | 88 ++++++++++++++++++------------------ dlls/vbscript/tests/lang.vbs | 7 +-- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index 3167aca131b..6596ccc9f50 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -453,13 +453,13 @@ static HRESULT to_float(VARIANT *v, float *ret) return S_OK; } -static HRESULT to_string(VARIANT *v, BSTR *ret) +static HRESULT to_string(LCID lcid, VARIANT *v, BSTR *ret) { VARIANT dst; HRESULT hres; V_VT(&dst) = VT_EMPTY; - hres = VariantChangeType(&dst, v, VARIANT_LOCALBOOL, VT_BSTR); + hres = VariantChangeTypeEx(&dst, v, lcid, VARIANT_LOCALBOOL, VT_BSTR); if(FAILED(hres)) return hres; @@ -836,7 +836,7 @@ static HRESULT Global_CStr(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V if(V_VT(arg) == VT_NULL) return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); - hres = to_string(arg, &str); + hres = to_string(This->ctx->lcid, arg, &str); if(FAILED(hres)) return hres; @@ -1283,7 +1283,7 @@ static HRESULT Global_Len(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VA if(V_VT(arg) != VT_BSTR) { BSTR str; - hres = to_string(arg, &str); + hres = to_string(This->ctx->lcid, arg, &str); if(FAILED(hres)) return hres; @@ -1313,7 +1313,7 @@ static HRESULT Global_Left(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, if(V_VT(args) == VT_BSTR) { str = V_BSTR(args); }else { - hres = to_string(args, &conv_str); + hres = to_string(This->ctx->lcid, args, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1370,7 +1370,7 @@ static HRESULT Global_Right(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, if(V_VT(args) == VT_BSTR) { str = V_BSTR(args); }else { - hres = to_string(args, &conv_str); + hres = to_string(This->ctx->lcid, args, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1438,7 +1438,7 @@ static HRESULT Global_Mid(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, V if(V_VT(args) == VT_BSTR) { str = V_BSTR(args); }else { - hres = to_string(args, &conv_str); + hres = to_string(This->ctx->lcid, args, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1496,11 +1496,11 @@ static HRESULT Global_StrComp(BuiltinDisp *This, VARIANT *args, unsigned args_cn else mode = 0; - hres = to_string(args, &left); + hres = to_string(This->ctx->lcid, args, &left); if(FAILED(hres)) return hres; - hres = to_string(args+1, &right); + hres = to_string(This->ctx->lcid, args+1, &right); if(FAILED(hres)) { SysFreeString(left); @@ -1526,7 +1526,7 @@ static HRESULT Global_LCase(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, return return_null(res); } - hres = to_string(arg, &str); + hres = to_string(This->ctx->lcid, arg, &str); if(FAILED(hres)) return hres; @@ -1555,7 +1555,7 @@ static HRESULT Global_UCase(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, return return_null(res); } - hres = to_string(arg, &str); + hres = to_string(This->ctx->lcid, arg, &str); if(FAILED(hres)) return hres; @@ -1584,7 +1584,7 @@ static HRESULT Global_LTrim(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, if(V_VT(arg) == VT_BSTR) { str = V_BSTR(arg); }else { - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1611,7 +1611,7 @@ static HRESULT Global_RTrim(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, if(V_VT(arg) == VT_BSTR) { str = V_BSTR(arg); }else { - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1638,7 +1638,7 @@ static HRESULT Global_Trim(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V if(V_VT(arg) == VT_BSTR) { str = V_BSTR(arg); }else { - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1779,7 +1779,7 @@ static HRESULT Global_InStr(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, return return_null(res); if(V_VT(str1v) != VT_BSTR) { - hres = to_string(str1v, &str1); + hres = to_string(This->ctx->lcid, str1v, &str1); if(FAILED(hres)) return hres; } @@ -1787,7 +1787,7 @@ static HRESULT Global_InStr(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, str1 = V_BSTR(str1v); if(V_VT(str2v) != VT_BSTR) { - hres = to_string(str2v, &str2); + hres = to_string(This->ctx->lcid, str2v, &str2); if(FAILED(hres)){ if(V_VT(str1v) != VT_BSTR) SysFreeString(str1); @@ -1843,7 +1843,7 @@ static HRESULT Global_Asc(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VA str = V_BSTR(arg); break; default: - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -1941,7 +1941,7 @@ static HRESULT Global_AscW(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V str = V_BSTR(arg); break; default: - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -2368,7 +2368,7 @@ static HRESULT Global_MsgBox(BuiltinDisp *This, VARIANT *args, unsigned args_cnt assert(1 <= args_cnt && args_cnt <= 5); - hres = to_string(args, &prompt); + hres = to_string(This->ctx->lcid, args, &prompt); if(FAILED(hres)) return hres; @@ -2376,7 +2376,7 @@ static HRESULT Global_MsgBox(BuiltinDisp *This, VARIANT *args, unsigned args_cnt hres = to_int(args+1, &type); if(SUCCEEDED(hres) && args_cnt > 2) - hres = to_string(args+2, &title); + hres = to_string(This->ctx->lcid, args+2, &title); if(SUCCEEDED(hres) && args_cnt > 3) { FIXME("unsupported arg_cnt %d\n", args_cnt); @@ -2494,7 +2494,7 @@ static HRESULT Global_DateAdd(BuiltinDisp *This, VARIANT *args, unsigned args_cn if (V_VT(args + 2) == VT_NULL) return return_null(res); - hres = to_string(args, &interval); + hres = to_string(This->ctx->lcid, args, &interval); if (SUCCEEDED(hres)) hres = to_int(args + 1, &count); if (SUCCEEDED(hres)) @@ -2691,7 +2691,7 @@ static HRESULT Global_Filter(BuiltinDisp *This, VARIANT *args, unsigned args_cnt if(V_VT(args+1) == VT_BSTR) { search = V_BSTR(args+1); }else { - hres = to_string(args+1, &conv_search); + hres = to_string(This->ctx->lcid, args+1, &conv_search); if(FAILED(hres)) return hres; search = conv_search; @@ -2746,7 +2746,7 @@ static HRESULT Global_Filter(BuiltinDisp *This, VARIANT *args, unsigned args_cnt if(V_VT(&data[i]) == VT_BSTR) { str = V_BSTR(&data[i]); }else { - hres = to_string(&data[i], &conv_str); + hres = to_string(This->ctx->lcid, &data[i], &conv_str); if(FAILED(hres)) { SafeArrayUnaccessData(sa); goto done; @@ -2855,7 +2855,7 @@ static HRESULT Global_Join(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, if (V_VT(args + 1) == VT_NULL) return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); if (V_VT(args + 1) != VT_BSTR) { - hres = to_string(args + 1, &free_delimiter); + hres = to_string(This->ctx->lcid, args + 1, &free_delimiter); if (FAILED(hres)) return hres; delimiter = free_delimiter; @@ -2891,7 +2891,7 @@ static HRESULT Global_Join(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, } for (i = 0; i < count; i++) { if (V_VT(&data[i]) != VT_BSTR) { - hres = to_string(&data[i], &str); + hres = to_string(This->ctx->lcid, &data[i], &str); if (FAILED(hres)) goto cleanup_data; } else { @@ -2957,7 +2957,7 @@ static HRESULT Global_Split(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); if(V_VT(args) != VT_BSTR) { - hres = to_string(args, &string); + hres = to_string(This->ctx->lcid, args, &string); if(FAILED(hres)) return hres; }else { @@ -2966,7 +2966,7 @@ static HRESULT Global_Split(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, if(args_cnt > 1) { if(V_VT(args+1) != VT_BSTR) { - hres = to_string(args+1, &delimiter); + hres = to_string(This->ctx->lcid, args+1, &delimiter); if(FAILED(hres)) goto done; }else { @@ -3107,7 +3107,7 @@ static HRESULT Global_Replace(BuiltinDisp *This, VARIANT *args, unsigned args_cn if(V_VT(args) != VT_BSTR) { - hres = to_string(args, &string); + hres = to_string(This->ctx->lcid, args, &string); if(FAILED(hres)) return hres; }else { @@ -3115,7 +3115,7 @@ static HRESULT Global_Replace(BuiltinDisp *This, VARIANT *args, unsigned args_cn } if(V_VT(args+1) != VT_BSTR) { - hres = to_string(args+1, &find); + hres = to_string(This->ctx->lcid, args+1, &find); if(FAILED(hres)) goto error; }else { @@ -3123,7 +3123,7 @@ static HRESULT Global_Replace(BuiltinDisp *This, VARIANT *args, unsigned args_cn } if(V_VT(args+2) != VT_BSTR) { - hres = to_string(args+2, &replace); + hres = to_string(This->ctx->lcid, args+2, &replace); if(FAILED(hres)) goto error; }else { @@ -3188,7 +3188,7 @@ static HRESULT Global_StrReverse(BuiltinDisp *This, VARIANT *arg, unsigned args_ TRACE("%s\n", debugstr_variant(arg)); - hres = to_string(arg, &ret); + hres = to_string(This->ctx->lcid, arg, &ret); if(FAILED(hres)) return hres; @@ -3235,7 +3235,7 @@ static HRESULT Global_InStrRev(BuiltinDisp *This, VARIANT *args, unsigned args_c } if(V_VT(args) != VT_BSTR) { - hres = to_string(args, &str1); + hres = to_string(This->ctx->lcid, args, &str1); if(FAILED(hres)) return hres; } @@ -3243,7 +3243,7 @@ static HRESULT Global_InStrRev(BuiltinDisp *This, VARIANT *args, unsigned args_c str1 = V_BSTR(args); if(V_VT(args+1) != VT_BSTR) { - hres = to_string(args+1, &str2); + hres = to_string(This->ctx->lcid, args+1, &str2); if(FAILED(hres)) { if(V_VT(args) != VT_BSTR) SysFreeString(str1); @@ -3748,7 +3748,7 @@ static HRESULT Global_Escape(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, if(V_VT(arg) == VT_BSTR) { str = V_BSTR(arg); }else { - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -3813,7 +3813,7 @@ static HRESULT Global_Unescape(BuiltinDisp *This, VARIANT *arg, unsigned args_cn if(V_VT(arg) == VT_BSTR) { str = V_BSTR(arg); }else { - hres = to_string(arg, &conv_str); + hres = to_string(This->ctx->lcid, arg, &conv_str); if(FAILED(hres)) return hres; str = conv_str; @@ -3884,7 +3884,7 @@ static HRESULT dispatch_to_string(script_ctx_t *ctx, IDispatch *disp, BSTR *ret) *ret = V_BSTR(&v); return S_OK; } - hres = to_string(&v, ret); + hres = to_string(ctx->lcid, &v, ret); VariantClear(&v); return hres; } @@ -4277,7 +4277,7 @@ static const builtin_prop_t global_props[] = { {L"Year", Global_Year, 0, 1} }; -static HRESULT err_string_prop(BSTR *prop, VARIANT *args, unsigned args_cnt, VARIANT *res) +static HRESULT err_string_prop(BuiltinDisp *This, BSTR *prop, VARIANT *args, unsigned args_cnt, VARIANT *res) { BSTR str; HRESULT hres; @@ -4285,7 +4285,7 @@ static HRESULT err_string_prop(BSTR *prop, VARIANT *args, unsigned args_cnt, VAR if(!args_cnt) return return_string(res, *prop ? *prop : L""); - hres = to_string(args, &str); + hres = to_string(This->ctx->lcid, args, &str); if(FAILED(hres)) return hres; @@ -4297,7 +4297,7 @@ static HRESULT err_string_prop(BSTR *prop, VARIANT *args, unsigned args_cnt, VAR static HRESULT Err_Description(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { TRACE("\n"); - return err_string_prop(&This->ctx->ei.bstrDescription, args, args_cnt, res); + return err_string_prop(This, &This->ctx->ei.bstrDescription, args, args_cnt, res); } static HRESULT Err_HelpContext(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -4315,7 +4315,7 @@ static HRESULT Err_HelpContext(BuiltinDisp *This, VARIANT *args, unsigned args_c static HRESULT Err_HelpFile(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { TRACE("\n"); - return err_string_prop(&This->ctx->ei.bstrHelpFile, args, args_cnt, res); + return err_string_prop(This, &This->ctx->ei.bstrHelpFile, args, args_cnt, res); } static HRESULT Err_Number(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -4336,7 +4336,7 @@ static HRESULT Err_Number(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, V static HRESULT Err_Source(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { TRACE("\n"); - return err_string_prop(&This->ctx->ei.bstrSource, args, args_cnt, res); + return err_string_prop(This, &This->ctx->ei.bstrSource, args, args_cnt, res); } static HRESULT Err_Clear(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -4362,11 +4362,11 @@ static HRESULT Err_Raise(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VA return E_INVALIDARG; if(args_cnt >= 2) - hres = to_string(args + 1, &source); + hres = to_string(This->ctx->lcid, args + 1, &source); if(args_cnt >= 3 && SUCCEEDED(hres)) - hres = to_string(args + 2, &description); + hres = to_string(This->ctx->lcid, args + 2, &description); if(args_cnt >= 4 && SUCCEEDED(hres)) - hres = to_string(args + 3, &helpfile); + hres = to_string(This->ctx->lcid, args + 3, &helpfile); if(args_cnt >= 5 && SUCCEEDED(hres)) hres = to_int(args + 4, &helpcontext); diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 7a4f152bdfd..a362eaa5a15 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -3556,10 +3556,11 @@ Call ok(GetLocale() = 1041, "GetLocale after SetLocale(1041)") Call ok(FormatNumber(1234567.89) = "1,234,567.89", "FormatNumber ja-JP: " & FormatNumber(1234567.89)) Call ok(FormatPercent(0.1234) = "12.34%", "FormatPercent ja-JP: " & FormatPercent(0.1234)) -' CStr goes through VariantChangeType with LCID=0 (LOCALE_USER_DEFAULT). -' Plumbing ctx->lcid into that path is follow-up work. +' Variant coercion to string (CStr etc.) uses the script LCID. Call SetLocale(1031) -Call todo_wine_ok(CStr(1.5) = "1,5", "CStr(1.5) de-DE: " & CStr(1.5)) +Call ok(CStr(1.5) = "1,5", "CStr(1.5) de-DE: " & CStr(1.5)) +Call SetLocale(1033) +Call ok(CStr(1.5) = "1.5", "CStr(1.5) en-US: " & CStr(1.5)) ' Restore original locale. Call SetLocale(origLcid) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10504