Author: akhaldi Date: Fri Oct 3 17:10:15 2014 New Revision: 64505
URL: http://svn.reactos.org/svn/reactos?rev=64505&view=rev Log: [OLEAUT32] * Sync with Wine 1.7.27. CORE-8540
Modified: trunk/reactos/dll/win32/oleaut32/recinfo.c trunk/reactos/dll/win32/oleaut32/tmarshal.c trunk/reactos/dll/win32/oleaut32/typelib.c trunk/reactos/dll/win32/oleaut32/varformat.c trunk/reactos/dll/win32/oleaut32/variant.c trunk/reactos/dll/win32/oleaut32/variant.h trunk/reactos/dll/win32/oleaut32/vartype.c trunk/reactos/media/doc/README.WINE
Modified: trunk/reactos/dll/win32/oleaut32/recinfo.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/recinfo.... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/recinfo.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/recinfo.c [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -166,7 +166,7 @@ int i; for(i=0; i<This->n_vars; i++) SysFreeString(This->fields[i].name); - HeapFree(GetProcessHeap(), 0, This->name); + SysFreeString(This->name); HeapFree(GetProcessHeap(), 0, This->fields); ITypeInfo_Release(This->pTypeInfo); HeapFree(GetProcessHeap(), 0, This);
Modified: trunk/reactos/dll/win32/oleaut32/tmarshal.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/tmarshal... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/tmarshal.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/tmarshal.c [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -248,11 +248,13 @@ _get_typeinfo_for_iid(REFIID riid, ITypeInfo**ti) { HRESULT hres; HKEY ikey; + REGSAM opposite = (sizeof(void*) == 8) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY; + BOOL is_wow64; char tlguid[200],typelibkey[300],interfacekey[300],ver[100]; char tlfn[260]; OLECHAR tlfnW[260]; DWORD tlguidlen, verlen, type; - LONG tlfnlen; + LONG tlfnlen, err; ITypeLib *tl;
sprintf( interfacekey, "Interface\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\Typelib", @@ -261,9 +263,14 @@ riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7] );
- if (RegOpenKeyA(HKEY_CLASSES_ROOT,interfacekey,&ikey)) { - ERR("No %s key found.\n",interfacekey); - return E_FAIL; + err = RegOpenKeyExA(HKEY_CLASSES_ROOT,interfacekey,0,KEY_READ,&ikey); + if (err && (opposite == KEY_WOW64_32KEY || (IsWow64Process(GetCurrentProcess(), &is_wow64) + && is_wow64))) { + err = RegOpenKeyExA(HKEY_CLASSES_ROOT,interfacekey,0,KEY_READ|opposite,&ikey); + } + if (err) { + ERR("No %s key found.\n",interfacekey); + return E_FAIL; } tlguidlen = sizeof(tlguid); if (RegQueryValueExA(ikey,NULL,NULL,&type,(LPBYTE)tlguid,&tlguidlen)) { @@ -694,7 +701,7 @@ hres = xbuf_add(buf,(LPBYTE)arg,sizeof(DWORD)); return hres; case VT_VARIANT: { - if (debugout) TRACE_(olerelay)("Vt(%s%s)(",debugstr_vt(V_VT((VARIANT *)arg)),debugstr_vf(V_VT((VARIANT *)arg))); + if (debugout) TRACE_(olerelay)("%s", debugstr_variant((VARIANT *)arg)); if (writeit) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION);
Modified: trunk/reactos/dll/win32/oleaut32/typelib.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/typelib.... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/typelib.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/typelib.c [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -581,12 +581,17 @@ TLIBATTR *attr;
res = ITypeLib_GetLibAttr(*ppTLib, &attr); - if (res == S_OK && (attr->wMajorVerNum != wVerMajor || attr->wMinorVerNum < wVerMinor)) + if (res == S_OK) { + BOOL mismatch = attr->wMajorVerNum != wVerMajor || attr->wMinorVerNum < wVerMinor; ITypeLib_ReleaseTLibAttr(*ppTLib, attr); - ITypeLib_Release(*ppTLib); - *ppTLib = NULL; - res = TYPE_E_LIBNOTREGISTERED; + + if (mismatch) + { + ITypeLib_Release(*ppTLib); + *ppTLib = NULL; + res = TYPE_E_LIBNOTREGISTERED; + } } } } @@ -872,6 +877,24 @@ return res; }
+static void TLB_unregister_interface(GUID *guid, REGSAM flag) +{ + WCHAR subKeyName[50]; + HKEY subKey; + + /* the path to the type */ + get_interface_key( guid, subKeyName ); + + /* Delete its bits */ + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, subKeyName, 0, KEY_WRITE | flag, &subKey) != ERROR_SUCCESS) + return; + + RegDeleteKeyW(subKey, ProxyStubClsidW); + RegDeleteKeyW(subKey, ProxyStubClsid32W); + RegDeleteKeyW(subKey, TypeLibW); + RegCloseKey(subKey); + RegDeleteKeyExW(HKEY_CLASSES_ROOT, subKeyName, flag, 0); +}
/****************************************************************************** * UnRegisterTypeLib [OLEAUT32.186] @@ -897,7 +920,6 @@ DWORD i = 0; BOOL deleteOtherStuff; HKEY key = NULL; - HKEY subKey = NULL; TYPEATTR* typeAttr = NULL; TYPEKIND kind; ITypeInfo* typeInfo = NULL; @@ -956,19 +978,16 @@ if ((kind == TKIND_INTERFACE && (typeAttr->wTypeFlags & TYPEFLAG_FOLEAUTOMATION)) || kind == TKIND_DISPATCH) { - /* the path to the type */ - get_interface_key( &typeAttr->guid, subKeyName ); - - /* Delete its bits */ - if (RegOpenKeyExW(HKEY_CLASSES_ROOT, subKeyName, 0, KEY_WRITE, &subKey) != ERROR_SUCCESS) - goto enddeleteloop; - - RegDeleteKeyW(subKey, ProxyStubClsidW); - RegDeleteKeyW(subKey, ProxyStubClsid32W); - RegDeleteKeyW(subKey, TypeLibW); - RegCloseKey(subKey); - subKey = NULL; - RegDeleteKeyW(HKEY_CLASSES_ROOT, subKeyName); + BOOL is_wow64; + REGSAM opposite = (sizeof(void*) == 8 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + + TLB_unregister_interface(&typeAttr->guid, 0); + + /* unregister TLBs into the opposite registry view, too */ + if(opposite == KEY_WOW64_32KEY || + (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)) { + TLB_unregister_interface(&typeAttr->guid, opposite); + } }
enddeleteloop: @@ -1014,7 +1033,6 @@ end: SysFreeString(tlibPath); if (typeLib) ITypeLib_Release(typeLib); - if (subKey) RegCloseKey(subKey); if (key) RegCloseKey(key); return result; } @@ -1571,61 +1589,6 @@ } }
-static void dump_Variant(const VARIANT * pvar) -{ - SYSTEMTIME st; - - TRACE("%p->{%s%s", pvar, debugstr_VT(pvar), debugstr_VF(pvar)); - - if (pvar) - { - if (V_ISBYREF(pvar) || V_TYPE(pvar) == VT_UNKNOWN || - V_TYPE(pvar) == VT_DISPATCH || V_TYPE(pvar) == VT_RECORD) - { - TRACE(",%p", V_BYREF(pvar)); - } - else if (V_ISARRAY(pvar) || V_ISVECTOR(pvar)) - { - TRACE(",%p", V_ARRAY(pvar)); - } - else switch (V_TYPE(pvar)) - { - case VT_I1: TRACE(",%d", V_I1(pvar)); break; - case VT_UI1: TRACE(",%d", V_UI1(pvar)); break; - case VT_I2: TRACE(",%d", V_I2(pvar)); break; - case VT_UI2: TRACE(",%d", V_UI2(pvar)); break; - case VT_INT: - case VT_I4: TRACE(",%d", V_I4(pvar)); break; - case VT_UINT: - case VT_UI4: TRACE(",%d", V_UI4(pvar)); break; - case VT_I8: TRACE(",0x%08x,0x%08x", (ULONG)(V_I8(pvar) >> 32), - (ULONG)(V_I8(pvar) & 0xffffffff)); break; - case VT_UI8: TRACE(",0x%08x,0x%08x", (ULONG)(V_UI8(pvar) >> 32), - (ULONG)(V_UI8(pvar) & 0xffffffff)); break; - case VT_R4: TRACE(",%3.3e", V_R4(pvar)); break; - case VT_R8: TRACE(",%3.3e", V_R8(pvar)); break; - case VT_BOOL: TRACE(",%s", V_BOOL(pvar) ? "TRUE" : "FALSE"); break; - case VT_BSTR: TRACE(",%s", debugstr_w(V_BSTR(pvar))); break; - case VT_CY: TRACE(",0x%08x,0x%08x", V_CY(pvar).s.Hi, - V_CY(pvar).s.Lo); break; - case VT_DATE: - if(!VariantTimeToSystemTime(V_DATE(pvar), &st)) - TRACE(",<invalid>"); - else - TRACE(",%04d/%02d/%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, - st.wHour, st.wMinute, st.wSecond); - break; - case VT_ERROR: - case VT_VOID: - case VT_USERDEFINED: - case VT_EMPTY: - case VT_NULL: break; - default: TRACE(",?"); break; - } - } - TRACE("}\n"); -} - static void dump_DispParms(const DISPPARAMS * pdp) { unsigned int index; @@ -1643,7 +1606,7 @@ { TRACE("args:\n"); for (index = 0; index < pdp->cArgs; index++) - dump_Variant( &pdp->rgvarg[index] ); + TRACE(" [%d] %s\n", index, debugstr_variant(pdp->rgvarg+index)); } }
@@ -6832,8 +6795,7 @@ args[argspos++] = V_UI4(arg); break; } - TRACE("arg %u: type %d\n",i,prgvt[i]); - dump_Variant(arg); + TRACE("arg %u: type %s %s\n", i, debugstr_vt(prgvt[i]), debugstr_variant(arg)); }
switch (vtReturn) @@ -6873,7 +6835,7 @@ return DISP_E_BADCALLEE; } if (vtReturn != VT_VARIANT) V_VT(pvargResult) = vtReturn; - TRACE("retval: "); dump_Variant(pvargResult); + TRACE("retval: %s\n", debugstr_variant(pvargResult)); return S_OK;
#elif defined(__x86_64__) @@ -6922,8 +6884,7 @@ args[argspos++] = V_UI8(arg); break; } - TRACE("arg %u: type %d\n",i,prgvt[i]); - dump_Variant(arg); + TRACE("arg %u: type %s %s\n", i, debugstr_vt(prgvt[i]), debugstr_variant(arg)); }
switch (vtReturn) @@ -6950,7 +6911,7 @@ } heap_free( args ); if (vtReturn != VT_VARIANT) V_VT(pvargResult) = vtReturn; - TRACE("retval: "); dump_Variant(pvargResult); + TRACE("retval: %s\n", debugstr_variant(pvargResult)); return S_OK;
#else @@ -7145,7 +7106,7 @@ } else if (src_arg) { - dump_Variant(src_arg); + TRACE("%s\n", debugstr_variant(src_arg));
if(rgvt[i]!=V_VT(src_arg)) { @@ -7222,9 +7183,8 @@
if (FAILED(hres)) { - ERR("failed to convert param %d to %s%s from %s%s\n", i, - debugstr_vt(rgvt[i]), debugstr_vf(rgvt[i]), - debugstr_VT(src_arg), debugstr_VF(src_arg)); + ERR("failed to convert param %d to %s from %s\n", i, + debugstr_vt(rgvt[i]), debugstr_variant(src_arg)); break; } prgpvarg[i] = &rgvarg[i]; @@ -7316,11 +7276,7 @@ continue; else if (wParamFlags & PARAMFLAG_FRETVAL) { - if (TRACE_ON(ole)) - { - TRACE("[retval] value: "); - dump_Variant(prgpvarg[i]); - } + TRACE("[retval] value: %s\n", debugstr_variant(prgpvarg[i]));
if (pVarResult) { @@ -7409,8 +7365,7 @@ } if (V_VT(&varresult) != VT_ERROR) { - TRACE("varresult value: "); - dump_Variant(&varresult); + TRACE("varresult value: %s\n", debugstr_variant(&varresult));
if (pVarResult) {
Modified: trunk/reactos/dll/win32/oleaut32/varformat.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/varforma... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/varformat.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/varformat.c [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -1187,9 +1187,8 @@ const BYTE* pToken = NULL; HRESULT hRes = S_OK;
- TRACE("(%p->(%s%s),%s,%p,0x%08x,%p,0x%08x)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut, - lcid); + TRACE("(%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat), + rgbTok, dwFlags, pbstrOut, lcid);
V_VT(&vString) = VT_EMPTY; V_VT(&vBool) = VT_BOOL; @@ -1598,9 +1597,8 @@ const BYTE* pToken = NULL; HRESULT hRes;
- TRACE("(%p->(%s%s),%s,%p,0x%08x,%p,0x%08x)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut, - lcid); + TRACE("(%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), + debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut, lcid);
V_VT(&vDate) = VT_EMPTY;
@@ -1940,9 +1938,8 @@ BOOL bUpper = FALSE; HRESULT hRes = S_OK;
- TRACE("(%p->(%s%s),%s,%p,0x%08x,%p,0x%08x)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut, - lcid); + TRACE("%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat), + rgbTok, dwFlags, pbstrOut, lcid);
V_VT(&vStr) = VT_EMPTY;
@@ -2154,9 +2151,8 @@ BYTE buff[256]; HRESULT hres;
- TRACE("(%p->(%s%s),%s,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), debugstr_w(lpszFormat), nFirstDay, nFirstWeek, - dwFlags, pbstrOut); + TRACE("(%s,%s,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat), + nFirstDay, nFirstWeek, dwFlags, pbstrOut);
if (!pbstrOut) return E_INVALIDARG; @@ -2205,8 +2201,7 @@ static WCHAR szEmpty[] = { '\0' }; const BYTE* lpFmt = NULL;
- TRACE("(%p->(%s%s),%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), nFormat, dwFlags, pbstrOut); + TRACE("%s,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nFormat, dwFlags, pbstrOut);
if (!pVarIn || !pbstrOut || nFormat < 0 || nFormat > 4) return E_INVALIDARG; @@ -2258,8 +2253,8 @@ HRESULT hRet; VARIANT vStr;
- TRACE("(%p->(%s%s),%d,%d,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), nDigits, nLeading, nParens, nGrouping, dwFlags, pbstrOut); + TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading, + nParens, nGrouping, dwFlags, pbstrOut);
if (!pVarIn || !pbstrOut || nDigits > 9) return E_INVALIDARG; @@ -2369,9 +2364,8 @@ HRESULT hRet; VARIANT vDbl;
- TRACE("(%p->(%s%s),%d,%d,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), nDigits, nLeading, nParens, nGrouping, - dwFlags, pbstrOut); + TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading, + nParens, nGrouping, dwFlags, pbstrOut);
if (!pVarIn || !pbstrOut || nDigits > 9) return E_INVALIDARG; @@ -2443,8 +2437,8 @@ HRESULT hRet; VARIANT vStr;
- TRACE("(%p->(%s%s),%d,%d,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), nDigits, nLeading, nParens, nGrouping, dwFlags, pbstrOut); + TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading, + nParens, nGrouping, dwFlags, pbstrOut);
if (!pVarIn || !pbstrOut || nDigits > 9) return E_INVALIDARG;
Modified: trunk/reactos/dll/win32/oleaut32/variant.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/variant.... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/variant.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/variant.c [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -29,7 +29,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(variant);
-const char * const wine_vtypes[VT_CLSID+1] = +static const char * const variant_types[] = { "VT_EMPTY","VT_NULL","VT_I2","VT_I4","VT_R4","VT_R8","VT_CY","VT_DATE", "VT_BSTR","VT_DISPATCH","VT_ERROR","VT_BOOL","VT_VARIANT","VT_UNKNOWN", @@ -39,10 +39,11 @@ "VT_RECORD","VT_INT_PTR","VT_UINT_PTR","39","40","41","42","43","44","45", "46","47","48","49","50","51","52","53","54","55","56","57","58","59","60", "61","62","63","VT_FILETIME","VT_BLOB","VT_STREAM","VT_STORAGE", - "VT_STREAMED_OBJECT","VT_STORED_OBJECT","VT_BLOB_OBJECT","VT_CF","VT_CLSID" + "VT_STREAMED_OBJECT","VT_STORED_OBJECT","VT_BLOB_OBJECT","VT_CF","VT_CLSID", + "VT_VERSIONED_STREAM" };
-const char * const wine_vflags[16] = +static const char * const variant_flags[16] = { "", "|VT_VECTOR", @@ -52,14 +53,14 @@ "|VT_VECTOR|VT_ARRAY", "|VT_ARRAY|VT_BYREF", "|VT_VECTOR|VT_ARRAY|VT_BYREF", - "|VT_HARDTYPE", - "|VT_VECTOR|VT_HARDTYPE", - "|VT_ARRAY|VT_HARDTYPE", - "|VT_VECTOR|VT_ARRAY|VT_HARDTYPE", - "|VT_BYREF|VT_HARDTYPE", - "|VT_VECTOR|VT_ARRAY|VT_HARDTYPE", - "|VT_ARRAY|VT_BYREF|VT_HARDTYPE", - "|VT_VECTOR|VT_ARRAY|VT_BYREF|VT_HARDTYPE", + "|VT_RESERVED", + "|VT_VECTOR|VT_RESERVED", + "|VT_ARRAY|VT_RESERVED", + "|VT_VECTOR|VT_ARRAY|VT_RESERVED", + "|VT_BYREF|VT_RESERVED", + "|VT_VECTOR|VT_ARRAY|VT_RESERVED", + "|VT_ARRAY|VT_BYREF|VT_RESERVED", + "|VT_VECTOR|VT_ARRAY|VT_BYREF|VT_RESERVED", };
/* Convert a variant from one type to another */ @@ -70,9 +71,8 @@ VARTYPE vtFrom = V_TYPE(ps); DWORD dwFlags = 0;
- TRACE("(%p->(%s%s),0x%08x,0x%04x,%p->(%s%s),%s%s)\n", pd, debugstr_VT(pd), - debugstr_VF(pd), lcid, wFlags, ps, debugstr_VT(ps), debugstr_VF(ps), - debugstr_vt(vt), debugstr_vf(vt)); + TRACE("(%s,0x%08x,0x%04x,%s,%s)\n", debugstr_variant(pd), lcid, wFlags, + debugstr_variant(ps), debugstr_vt(vt));
if (vt == VT_BSTR || vtFrom == VT_BSTR) { @@ -567,7 +567,7 @@ { HRESULT hres;
- TRACE("(%p->(%s%s))\n", pVarg, debugstr_VT(pVarg), debugstr_VF(pVarg)); + TRACE("(%s)\n", debugstr_variant(pVarg));
hres = VARIANT_ValidateType(V_VT(pVarg)); if (FAILED(hres)) @@ -639,7 +639,7 @@ { HRESULT hres;
- TRACE("(%p->(%s%s))\n", pVarg, debugstr_VT(pVarg), debugstr_VF(pVarg)); + TRACE("(%s)\n", debugstr_variant(pVarg));
hres = VARIANT_ValidateType(V_VT(pVarg));
@@ -739,9 +739,7 @@ { HRESULT hres = S_OK;
- TRACE("(%p->(%s%s),%p->(%s%s))\n", pvargDest, debugstr_VT(pvargDest), - debugstr_VF(pvargDest), pvargSrc, debugstr_VT(pvargSrc), - debugstr_VF(pvargSrc)); + TRACE("(%s,%s)\n", debugstr_variant(pvargDest), debugstr_variant(pvargSrc));
if (V_TYPE(pvargSrc) == VT_CLSID || /* VT_CLSID is a special case */ FAILED(VARIANT_ValidateType(V_VT(pvargSrc)))) @@ -804,7 +802,7 @@ case VT_CY: return sizeof(CY); case VT_ERROR: return sizeof(SCODE); } - TRACE("Shouldn't be called for vt %s%s!\n", debugstr_VT(pv), debugstr_VF(pv)); + TRACE("Shouldn't be called for variant %s!\n", debugstr_variant(pv)); return 0; }
@@ -842,9 +840,7 @@ VARTYPE vt; HRESULT hres = S_OK;
- TRACE("(%p->(%s%s),%p->(%s%s))\n", pvargDest, debugstr_VT(pvargDest), - debugstr_VF(pvargDest), pvargSrc, debugstr_VT(pvargSrc), - debugstr_VF(pvargSrc)); + TRACE("(%s,%s)\n", debugstr_variant(pvargDest), debugstr_variant(pvargSrc));
if (!V_ISBYREF(pvargSrc)) return VariantCopy(pvargDest, pvargSrc); @@ -930,8 +926,7 @@ if (pSrc != pvargSrc) VariantClear(pSrc);
- TRACE("returning 0x%08x, %p->(%s%s)\n", hres, pvargDest, - debugstr_VT(pvargDest), debugstr_VF(pvargDest)); + TRACE("returning 0x%08x, %s\n", hres, debugstr_variant(pvargDest)); return hres; }
@@ -985,10 +980,8 @@ { HRESULT res = S_OK;
- TRACE("(%p->(%s%s),%p->(%s%s),0x%08x,0x%04x,%s%s)\n", pvargDest, - debugstr_VT(pvargDest), debugstr_VF(pvargDest), pvargSrc, - debugstr_VT(pvargSrc), debugstr_VF(pvargSrc), lcid, wFlags, - debugstr_vt(vt), debugstr_vf(vt)); + TRACE("(%s,%s,0x%08x,0x%04x,%s)\n", debugstr_variant(pvargDest), + debugstr_variant(pvargSrc), lcid, wFlags, debugstr_vt(vt));
if (vt == VT_CLSID) res = DISP_E_BADVARTYPE; @@ -1026,7 +1019,7 @@
if (SUCCEEDED(res)) { V_VT(&vTmp) = vt; - VariantCopy(pvargDest, &vTmp); + res = VariantCopy(pvargDest, &vTmp); } VariantClear(&vTmp); VariantClear(&vSrcDeref); @@ -1036,8 +1029,7 @@ } }
- TRACE("returning 0x%08x, %p->(%s%s)\n", res, pvargDest, - debugstr_VT(pvargDest), debugstr_VF(pvargDest)); + TRACE("returning 0x%08x, %s\n", res, debugstr_variant(pvargDest)); return res; }
@@ -1343,7 +1335,7 @@ HRESULT WINAPI VarDateFromUdateEx(UDATE *pUdateIn, LCID lcid, ULONG dwFlags, DATE *pDateOut) { UDATE ud; - double dateVal, dateSign; + double dateVal = 0;
TRACE("(%p->%d/%d/%d %d:%d:%d:%d %d %d,0x%08x,0x%08x,%p)\n", pUdateIn, pUdateIn->st.wMonth, pUdateIn->st.wDay, pUdateIn->st.wYear, @@ -1353,6 +1345,8 @@
if (lcid != MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)) FIXME("lcid possibly not handled, treating as en-us\n"); + if (dwFlags & ~(VAR_TIMEVALUEONLY|VAR_DATEVALUEONLY)) + FIXME("unsupported flags: %x\n", dwFlags);
ud = *pUdateIn;
@@ -1363,15 +1357,18 @@ return E_INVALIDARG;
/* Date */ - dateVal = VARIANT_DateFromJulian(VARIANT_JulianFromDMY(ud.st.wYear, ud.st.wMonth, ud.st.wDay)); - - /* Sign */ - dateSign = (dateVal < 0.0) ? -1.0 : 1.0; - - /* Time */ - dateVal += ud.st.wHour / 24.0 * dateSign; - dateVal += ud.st.wMinute / 1440.0 * dateSign; - dateVal += ud.st.wSecond / 86400.0 * dateSign; + if (!(dwFlags & VAR_TIMEVALUEONLY)) + dateVal = VARIANT_DateFromJulian(VARIANT_JulianFromDMY(ud.st.wYear, ud.st.wMonth, ud.st.wDay)); + + if ((dwFlags & VAR_TIMEVALUEONLY) || !(dwFlags & VAR_DATEVALUEONLY)) + { + double dateSign = (dateVal < 0.0) ? -1.0 : 1.0; + + /* Time */ + dateVal += ud.st.wHour / 24.0 * dateSign; + dateVal += ud.st.wMinute / 1440.0 * dateSign; + dateVal += ud.st.wSecond / 86400.0 * dateSign; + }
TRACE("Returning %g\n", dateVal); *pDateOut = dateVal; @@ -2517,8 +2514,7 @@ leftvt = V_VT(left); rightvt = V_VT(right);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), out); + TRACE("%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), out);
if (!str_true[0]) { VARIANT_GetLocalisedText(LOCALE_USER_DEFAULT, IDS_FALSE, str_false); @@ -2761,8 +2757,7 @@ DWORD xmask; HRESULT rc;
- TRACE("(%p->(%s%s),%p->(%s%s),0x%08x,0x%08x)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), lcid, flags); + TRACE("(%s,%s,0x%08x,0x%08x)\n", debugstr_variant(left), debugstr_variant(right), lcid, flags);
lvt = V_VT(left) & VT_TYPEMASK; rvt = V_VT(right) & VT_TYPEMASK; @@ -2966,8 +2961,7 @@ VariantInit(&tempLeft); VariantInit(&tempRight);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(left) & VT_TYPEMASK) == VT_DISPATCH) @@ -3012,7 +3006,6 @@ else if (leftvt == VT_I4 || rightvt == VT_I4 || leftvt == VT_UINT || rightvt == VT_UINT || leftvt == VT_INT || rightvt == VT_INT || - leftvt == VT_UINT || rightvt == VT_UINT || leftvt == VT_R4 || rightvt == VT_R4 || leftvt == VT_R8 || rightvt == VT_R8 || leftvt == VT_CY || rightvt == VT_CY || @@ -3221,9 +3214,7 @@ vt_ERROR, vt_ERROR, vt_UI1, vt_ERROR, vt_ERROR, vt_I8 };
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), - result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
VariantInit(&lv); VariantInit(&rv); @@ -3377,7 +3368,7 @@ VariantClear(&tv); VariantClear(&tempLeft); VariantClear(&tempRight); - TRACE("returning 0x%8x (variant type %s)\n", hres, debugstr_VT(result)); + TRACE("returning 0x%8x %s\n", hres, debugstr_variant(result)); return hres; }
@@ -3434,9 +3425,7 @@ vt_ERROR, vt_ERROR, vt_UI1, vt_ERROR, vt_ERROR, vt_I8 };
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), - result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
VariantInit(&lv); VariantInit(&rv); @@ -3568,7 +3557,7 @@ VariantClear(&tv); VariantClear(&tempLeft); VariantClear(&tempRight); - TRACE("returning 0x%8x (variant type %s)\n", hres, debugstr_VT(result)); + TRACE("returning 0x%8x %s\n", hres, debugstr_variant(result)); return hres; }
@@ -3600,8 +3589,7 @@ VariantInit(&lv); VariantInit(&rv);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(left) & VT_TYPEMASK) == VT_DISPATCH) @@ -3731,7 +3719,7 @@ VariantClear(&rv); VariantClear(&tempLeft); VariantClear(&tempRight); - TRACE("returning 0x%8x (variant type %s)\n", hres, debugstr_VT(result)); + TRACE("returning 0x%8x %s\n", hres, debugstr_variant(result)); return hres; }
@@ -3763,8 +3751,7 @@ VariantInit(&tempLeft); VariantInit(&tempRight);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
if ((V_VT(left) & VT_TYPEMASK) == VT_DISPATCH && (V_VT(left)&(~VT_TYPEMASK)) == 0 && @@ -3998,7 +3985,7 @@ VariantClear(&rv); VariantClear(&tempLeft); VariantClear(&tempRight); - TRACE("returning 0x%8x (variant type %s)\n", hres, debugstr_VT(result)); + TRACE("returning 0x%8x %s\n", hres, debugstr_variant(result)); return hres; }
@@ -4034,9 +4021,7 @@ VariantInit(&varRight); VariantInit(&varStr);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", pVarLeft, debugstr_VT(pVarLeft), - debugstr_VF(pVarLeft), pVarRight, debugstr_VT(pVarRight), - debugstr_VF(pVarRight), pVarOut); + TRACE("(%s,%s,%p)\n", debugstr_variant(pVarLeft), debugstr_variant(pVarRight), pVarOut);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarLeft) & VT_TYPEMASK) == VT_DISPATCH) @@ -4343,8 +4328,7 @@
VariantInit(&temp);
- TRACE("(%p->(%s%s),%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), pVarOut); + TRACE("(%s,%p)\n", debugstr_variant(pVarIn), pVarOut);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarIn) & VT_TYPEMASK) == VT_DISPATCH && ((V_VT(pVarIn) & ~VT_TYPEMASK) == 0)) @@ -4447,8 +4431,7 @@
VariantInit(&temp);
- TRACE("(%p->(%s%s),%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), pVarOut); + TRACE("(%s,%p)\n", debugstr_variant(pVarIn), pVarOut);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarIn) & VT_TYPEMASK) == VT_DISPATCH && ((V_VT(pVarIn) & ~VT_TYPEMASK) == 0)) @@ -4554,8 +4537,7 @@
VariantInit(&temp);
- TRACE("(%p->(%s%s),%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), pVarOut); + TRACE("(%s,%p)\n", debugstr_variant(pVarIn), pVarOut);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarIn) & VT_TYPEMASK) == VT_DISPATCH && ((V_VT(pVarIn) & ~VT_TYPEMASK) == 0)) @@ -4629,9 +4611,7 @@ double d; HRESULT hRet;
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", pVarLeft, debugstr_VT(pVarLeft), - debugstr_VF(pVarLeft), pVarRight, debugstr_VT(pVarRight), - debugstr_VF(pVarRight), pVarOut); + TRACE("(%s,%s,%p)\n", debugstr_variant(pVarLeft), debugstr_variant(pVarRight), pVarOut);
if (V_EXTRA_TYPE(pVarLeft) || V_EXTRA_TYPE(pVarRight) || V_VT(pVarLeft) > VT_UINT || V_VT(pVarRight) > VT_UINT || @@ -4808,9 +4788,7 @@ { HRESULT hRet;
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", pVarLeft, debugstr_VT(pVarLeft), - debugstr_VF(pVarLeft), pVarRight, debugstr_VT(pVarRight), - debugstr_VF(pVarRight), pVarOut); + TRACE("(%s,%s,%p)\n", debugstr_variant(pVarLeft), debugstr_variant(pVarRight), pVarOut);
hRet = VarXor(pVarLeft, pVarRight, pVarOut); if (SUCCEEDED(hRet)) @@ -4867,8 +4845,7 @@
VariantInit(&temp);
- TRACE("(%p->(%s%s),%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), pVarOut); + TRACE("(%s,%p)\n", debugstr_variant(pVarIn), pVarOut);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarIn) & VT_TYPEMASK) == VT_DISPATCH && ((V_VT(pVarIn) & ~VT_TYPEMASK) == 0)) @@ -4997,8 +4974,7 @@
VariantInit(&temp);
- TRACE("(%p->(%s%s),%p)\n", pVarIn, debugstr_VT(pVarIn), - debugstr_VF(pVarIn), pVarOut); + TRACE("(%s,%p)\n", debugstr_variant(pVarIn), pVarOut);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarIn) & VT_TYPEMASK) == VT_DISPATCH && ((V_VT(pVarIn) & ~VT_TYPEMASK) == 0)) @@ -5122,7 +5098,7 @@
VariantInit(&temp);
- TRACE("(%p->(%s%s),%d)\n", pVarIn, debugstr_VT(pVarIn), debugstr_VF(pVarIn), deci); + TRACE("(%s,%d)\n", debugstr_variant(pVarIn), deci);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(pVarIn) & VT_TYPEMASK) == VT_DISPATCH && ((V_VT(pVarIn) & ~VT_TYPEMASK) == 0)) @@ -5227,10 +5203,7 @@ V_VT(pVarOut) = VT_EMPTY; VariantClear(&temp);
- TRACE("returning 0x%08x (%s%s),%f\n", hRet, debugstr_VT(pVarOut), - debugstr_VF(pVarOut), (V_VT(pVarOut) == VT_R4) ? V_R4(pVarOut) : - (V_VT(pVarOut) == VT_R8) ? V_R8(pVarOut) : 0); - + TRACE("returning 0x%08x %s\n", hRet, debugstr_variant(pVarOut)); return hRet; }
@@ -5260,8 +5233,7 @@ VARIANT lv,rv; VARIANT tempLeft, tempRight;
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
VariantInit(&lv); VariantInit(&rv); @@ -5419,8 +5391,7 @@ VariantInit(&lv); VariantInit(&rv);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(left) & VT_TYPEMASK) == VT_DISPATCH) @@ -5658,8 +5629,7 @@ VARTYPE rightExtraFlags,leftExtraFlags,ExtraFlags; VARIANT tempLeft, tempRight;
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), debugstr_VF(left), - right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
VariantInit(&dl); VariantInit(&dr); @@ -5779,8 +5749,7 @@ VariantInit(&tempLeft); VariantInit(&tempRight);
- TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + TRACE("(%s,%s,%p)\n", debugstr_variant(left), debugstr_variant(right), result);
/* Handle VT_DISPATCH by storing and taking address of returned value */ if ((V_VT(left) & VT_TYPEMASK) == VT_DISPATCH)
Modified: trunk/reactos/dll/win32/oleaut32/variant.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/variant.... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/variant.h [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/variant.h [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -44,15 +44,6 @@ #define VTBIT_UNKNOWN (1 << VT_UNKNOWN) #define VTBIT_VARIANT (1 << VT_VARIANT) #define VTBIT_15 (1 << 15) /* no variant type with this number */ - -extern const char * const wine_vtypes[] DECLSPEC_HIDDEN; -#define debugstr_vt(v) (((v)&VT_TYPEMASK) <= VT_CLSID ? wine_vtypes[((v)&VT_TYPEMASK)] : \ - ((v)&VT_TYPEMASK) == VT_BSTR_BLOB ? "VT_BSTR_BLOB": "Invalid") -#define debugstr_VT(v) (!(v) ? "(null)" : debugstr_vt(V_TYPE((v)))) - -extern const char * const wine_vflags[] DECLSPEC_HIDDEN; -#define debugstr_vf(v) (wine_vflags[((v)&VT_EXTRA_TYPE)>>12]) -#define debugstr_VF(v) (!(v) ? "(null)" : debugstr_vf(V_EXTRA_TYPE(v)))
/* Size constraints */ #define I1_MAX 0x7f
Modified: trunk/reactos/dll/win32/oleaut32/vartype.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/oleaut32/vartype.... ============================================================================== --- trunk/reactos/dll/win32/oleaut32/vartype.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/oleaut32/vartype.c [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -4154,6 +4154,8 @@ static HRESULT VARIANT_DI_FromR8(double source, VARIANT_DI * dest); static void VARIANT_DIFromDec(const DECIMAL * from, VARIANT_DI * to); static void VARIANT_DecFromDI(const VARIANT_DI * from, DECIMAL * to); +static unsigned char VARIANT_int_divbychar(DWORD * p, unsigned int n, unsigned char divisor); +static BOOL VARIANT_int_iszero(const DWORD * p, unsigned int n);
/************************************************************************ * VarDecFromR4 (OLEAUT32.193) @@ -4429,12 +4431,13 @@ /* Make two DECIMALS the same scale; used by math functions below */ static HRESULT VARIANT_DecScale(const DECIMAL** ppDecLeft, const DECIMAL** ppDecRight, - DECIMAL* pDecOut) + DECIMAL pDecOut[2]) { static DECIMAL scaleFactor; + unsigned char remainder; DECIMAL decTemp; + VARIANT_DI di; int scaleAmount, i; - HRESULT hRet = S_OK;
if (DEC_SIGN(*ppDecLeft) & ~DECIMAL_NEG || DEC_SIGN(*ppDecRight) & ~DECIMAL_NEG) return E_INVALIDARG; @@ -4449,27 +4452,62 @@ if (scaleAmount > 0) { decTemp = *(*ppDecRight); /* Left is bigger - scale the right hand side */ - *ppDecRight = pDecOut; + *ppDecRight = &pDecOut[0]; } else { decTemp = *(*ppDecLeft); /* Right is bigger - scale the left hand side */ - *ppDecLeft = pDecOut; - i = scaleAmount = -scaleAmount; + *ppDecLeft = &pDecOut[0]; + i = -scaleAmount; }
- if (DEC_SCALE(&decTemp) + scaleAmount > DEC_MAX_SCALE) - return DISP_E_OVERFLOW; /* Can't scale up */ - - /* Multiply up the value to be scaled by the correct amount */ - while (SUCCEEDED(hRet) && i--) + /* Multiply up the value to be scaled by the correct amount (if possible) */ + while (i > 0 && SUCCEEDED(VarDecMul(&decTemp, &scaleFactor, &pDecOut[0]))) { - /* Note we are multiplying by a value with a scale of 0, so we don't recurse */ - hRet = VarDecMul(&decTemp, &scaleFactor, pDecOut); - decTemp = *pDecOut; + decTemp = pDecOut[0]; + i--; } - DEC_SCALE(pDecOut) += scaleAmount; /* Set the new scale */ - return hRet; + + if (!i) + { + DEC_SCALE(&pDecOut[0]) += (scaleAmount > 0) ? scaleAmount : (-scaleAmount); + return S_OK; /* Same scale */ + } + + /* Scaling further not possible, reduce accuracy of other argument */ + pDecOut[0] = decTemp; + if (scaleAmount > 0) + { + DEC_SCALE(&pDecOut[0]) += scaleAmount - i; + VARIANT_DIFromDec(*ppDecLeft, &di); + *ppDecLeft = &pDecOut[1]; + } + else + { + DEC_SCALE(&pDecOut[0]) += (-scaleAmount) - i; + VARIANT_DIFromDec(*ppDecRight, &di); + *ppDecRight = &pDecOut[1]; + } + + di.scale -= i; + remainder = 0; + while (i-- > 0 && !VARIANT_int_iszero(di.bitsnum, sizeof(di.bitsnum)/sizeof(DWORD))) + { + remainder = VARIANT_int_divbychar(di.bitsnum, sizeof(di.bitsnum)/sizeof(DWORD), 10); + if (remainder > 0) WARN("losing significant digits (remainder %u)...\n", remainder); + } + + /* round up the result - native oleaut32 does this */ + if (remainder >= 5) { + for (remainder = 1, i = 0; i < sizeof(di.bitsnum)/sizeof(DWORD) && remainder; i++) { + ULONGLONG digit = di.bitsnum[i] + 1; + remainder = (digit > 0xFFFFFFFF) ? 1 : 0; + di.bitsnum[i] = digit & 0xFFFFFFFF; + } + } + + VARIANT_DecFromDI(&di, &pDecOut[1]); + return S_OK; }
/* Add two unsigned 32 bit values with overflow */ @@ -4544,9 +4582,9 @@ HRESULT WINAPI VarDecAdd(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut) { HRESULT hRet; - DECIMAL scaled; - - hRet = VARIANT_DecScale(&pDecLeft, &pDecRight, &scaled); + DECIMAL scaled[2]; + + hRet = VARIANT_DecScale(&pDecLeft, &pDecRight, scaled);
if (SUCCEEDED(hRet)) {
Modified: trunk/reactos/media/doc/README.WINE URL: http://svn.reactos.org/svn/reactos/trunk/reactos/media/doc/README.WINE?rev=6... ============================================================================== --- trunk/reactos/media/doc/README.WINE [iso-8859-1] (original) +++ trunk/reactos/media/doc/README.WINE [iso-8859-1] Fri Oct 3 17:10:15 2014 @@ -152,7 +152,7 @@ reactos/dll/win32/odbccp32 # Synced to Wine-1.7.17 reactos/dll/win32/ole32 # Synced to Wine-1.7.27 reactos/dll/win32/oleacc # Synced to Wine-1.7.27 -reactos/dll/win32/oleaut32 # Synced to Wine-1.7.17 +reactos/dll/win32/oleaut32 # Synced to Wine-1.7.27 reactos/dll/win32/olecli32 # Synced to Wine-1.7.17 reactos/dll/win32/oledlg # Synced to Wine-1.7.17 reactos/dll/win32/olepro32 # Synced to Wine-1.7.17