[PATCH 0/4] MR10857: Draft: oleaut32: Bound GetFuncDesc impltypes walk by stored cbSizeVft.
ITypeInfoImpl_GetInternalDispatchFuncDesc walked impltypes whenever any impl type was registered, surfacing inherited IDispatch slots ahead of own funcs in the index space. That matches MIDL-built TKIND_DISPATCH typelibs (where cbSizeVft includes inherited slots and cFuncs is inflated to match) but mismatches ICreateTypeLib2-built ones where cbSizeVft only spans own funcs and GetFuncDesc should expose only those. Skip the walk when the stored cbSizeVft has no room beyond own_count * ptr_size, so GetFuncDesc on ICreateTypeLib2 typelibs hits own funcs at indices 0..cFuncs-1 and returns TYPE_E_ELEMENTNOTFOUND past the end, matching native. This is needs !10856 merged first -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10857
From: Francis De Brabandere <francisdb@gmail.com> A typeinfo built with ICreateTypeLib2 + AddImplType(IDispatch) reports cFuncs reflecting only the funcs added directly: the inherited IDispatch slots are not included, even though cImplTypes == 1. The same numbers hold after SaveAllChanges + LoadTypeLibEx. Mark these as todo_wine until LayOut is fixed to stop adding the IDispatch vtbl size to the stored cbSizeVft. --- dlls/oleaut32/tests/typelib.c | 100 ++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/dlls/oleaut32/tests/typelib.c b/dlls/oleaut32/tests/typelib.c index 153964e17d5..091defda68d 100644 --- a/dlls/oleaut32/tests/typelib.c +++ b/dlls/oleaut32/tests/typelib.c @@ -8763,6 +8763,105 @@ static void test_DeleteFuncDesc(void) DeleteFileW(filenameW); } +/* TKIND_DISPATCH built via ICreateTypeLib2 with an explicit + * AddImplType(IDispatch) - both in-memory and after Save+Reload. cFuncs + * should reflect only own funcs (the inherited IDispatch slots aren't + * counted), even though cImplTypes==1. */ +static void test_dispatch_with_implparent(void) +{ + OLECHAR ifaceW[] = L"iface"; + OLECHAR funcW[] = L"func"; + OLECHAR *funcnames[] = { funcW }; + WCHAR filenameW[MAX_PATH], temp_path[MAX_PATH]; + ICreateTypeInfo *createti; + ICreateTypeLib2 *createtl; + ITypeInfo *idispatch_ti, *parent_ti, *ti; + ITypeLib *stdole, *tl; + FUNCDESC funcdesc; + TYPEATTR *typeattr; + HREFTYPE href; + HRESULT hr; + + GetTempPathW(ARRAY_SIZE(temp_path), temp_path); + GetTempFileNameW(temp_path, L"tlb", 0, filenameW); + + hr = LoadTypeLib(wszStdOle2, &stdole); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeLib_GetTypeInfoOfGuid(stdole, &IID_IDispatch, &idispatch_ti); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CreateTypeLib2(SYS_WIN64, filenameW, &createtl); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ICreateTypeLib2_CreateTypeInfo(createtl, ifaceW, TKIND_DISPATCH, &createti); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeInfo_SetTypeFlags(createti, TYPEFLAG_FDISPATCHABLE); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ICreateTypeInfo_AddRefTypeInfo(createti, idispatch_ti, &href); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeInfo_AddImplType(createti, 0, href); + ok(hr == S_OK, "got %#lx\n", hr); + + memset(&funcdesc, 0, sizeof(funcdesc)); + funcdesc.memid = 0x100; + funcdesc.funckind = FUNC_DISPATCH; + funcdesc.invkind = INVOKE_FUNC; + funcdesc.callconv = CC_STDCALL; + funcdesc.elemdescFunc.tdesc.vt = VT_VOID; + hr = ICreateTypeInfo_AddFuncDesc(createti, 0, &funcdesc); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeInfo_SetFuncAndParamNames(createti, 0, funcnames, 1); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ICreateTypeInfo_LayOut(createti); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ICreateTypeInfo_QueryInterface(createti, &IID_ITypeInfo, (void **)&ti); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeInfo_GetTypeAttr(ti, &typeattr); + ok(hr == S_OK, "got %#lx\n", hr); + ok(typeattr->typekind == TKIND_DISPATCH, "typekind %d\n", typeattr->typekind); + todo_wine + ok(typeattr->cFuncs == 1, "in-memory cFuncs %u\n", typeattr->cFuncs); + ok(typeattr->cImplTypes == 1, "in-memory cImplTypes %u\n", typeattr->cImplTypes); + ok(typeattr->cbSizeVft == sizeof(IDispatchVtbl), "in-memory cbSizeVft %u\n", typeattr->cbSizeVft); + ITypeInfo_ReleaseTypeAttr(ti, typeattr); + ITypeInfo_Release(ti); + + hr = ICreateTypeLib2_SaveAllChanges(createtl); + ok(hr == S_OK, "got %#lx\n", hr); + ICreateTypeInfo_Release(createti); + ICreateTypeLib2_Release(createtl); + ITypeInfo_Release(idispatch_ti); + ITypeLib_Release(stdole); + + hr = LoadTypeLibEx(filenameW, REGKIND_NONE, &tl); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeLib_GetTypeInfo(tl, 0, &ti); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeInfo_GetTypeAttr(ti, &typeattr); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine + ok(typeattr->cFuncs == 1, "reloaded cFuncs %u\n", typeattr->cFuncs); + ok(typeattr->cImplTypes == 1, "reloaded cImplTypes %u\n", typeattr->cImplTypes); + ITypeInfo_ReleaseTypeAttr(ti, typeattr); + + hr = ITypeInfo_GetRefTypeOfImplType(ti, 0, &href); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeInfo_GetRefTypeInfo(ti, href, &parent_ti); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeInfo_GetTypeAttr(parent_ti, &typeattr); + ok(hr == S_OK, "got %#lx\n", hr); + ok(IsEqualGUID(&typeattr->guid, &IID_IDispatch), "parent guid %s\n", wine_dbgstr_guid(&typeattr->guid)); + ITypeInfo_ReleaseTypeAttr(parent_ti, typeattr); + ITypeInfo_Release(parent_ti); + + ITypeInfo_Release(ti); + ITypeLib_Release(tl); + DeleteFileW(filenameW); +} + START_TEST(typelib) { const WCHAR *filename; @@ -8806,4 +8905,5 @@ START_TEST(typelib) test_stub(); test_DeleteImplType(); test_DeleteFuncDesc(); + test_dispatch_with_implparent(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10857
From: Francis De Brabandere <francisdb@gmail.com> ICreateTypeInfo2_fnLayOut starts cbSizeVft at 7*ptr_size for TKIND_DISPATCH to position own funcs after the IDispatch vtbl, then increments per added func. The own funcs receive correct oVft offsets that way, but the resulting stored cbSizeVft is too large by exactly the IDispatch vtbl size: GetTypeAttr later derives cFuncs as cbSizeVft/ptr_size and so reports own_count + 7 instead of own_count. Subtract the prefix at the end of LayOut so cFuncs matches native, both in-memory and after SaveAllChanges + LoadTypeLibEx. Public cbSizeVft is unchanged: GetTypeAttr already overrides it to sizeof(IDispatchVtbl). --- dlls/oleaut32/tests/typelib.c | 2 -- dlls/oleaut32/typelib.c | 9 +++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dlls/oleaut32/tests/typelib.c b/dlls/oleaut32/tests/typelib.c index 091defda68d..7010b879582 100644 --- a/dlls/oleaut32/tests/typelib.c +++ b/dlls/oleaut32/tests/typelib.c @@ -8822,7 +8822,6 @@ static void test_dispatch_with_implparent(void) hr = ITypeInfo_GetTypeAttr(ti, &typeattr); ok(hr == S_OK, "got %#lx\n", hr); ok(typeattr->typekind == TKIND_DISPATCH, "typekind %d\n", typeattr->typekind); - todo_wine ok(typeattr->cFuncs == 1, "in-memory cFuncs %u\n", typeattr->cFuncs); ok(typeattr->cImplTypes == 1, "in-memory cImplTypes %u\n", typeattr->cImplTypes); ok(typeattr->cbSizeVft == sizeof(IDispatchVtbl), "in-memory cbSizeVft %u\n", typeattr->cbSizeVft); @@ -8842,7 +8841,6 @@ static void test_dispatch_with_implparent(void) ok(hr == S_OK, "got %#lx\n", hr); hr = ITypeInfo_GetTypeAttr(ti, &typeattr); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(typeattr->cFuncs == 1, "reloaded cFuncs %u\n", typeattr->cFuncs); ok(typeattr->cImplTypes == 1, "reloaded cImplTypes %u\n", typeattr->cImplTypes); ITypeInfo_ReleaseTypeAttr(ti, typeattr); diff --git a/dlls/oleaut32/typelib.c b/dlls/oleaut32/typelib.c index e9997a86d24..d604ccc0fd8 100644 --- a/dlls/oleaut32/typelib.c +++ b/dlls/oleaut32/typelib.c @@ -11193,6 +11193,15 @@ static HRESULT WINAPI ICreateTypeInfo2_fnLayOut(ICreateTypeInfo2 *iface) if (user_vft > This->typeattr.cbSizeVft) This->typeattr.cbSizeVft = user_vft + This->pTypeLib->ptr_size; + /* For TKIND_DISPATCH, the IDispatch vtbl slots are not part of the + * stored cbSizeVft on native ICreateTypeLib2 typelibs - GetTypeAttr + * later overrides cbSizeVft to sizeof(IDispatchVtbl) for the public + * API regardless. Subtract the prefix here so that GetTypeAttr's + * `cFuncs = cbSizeVft / ptr_size` derivation yields the own-func + * count (matching native), without changing oVft assignments. */ + if (This->typeattr.typekind == TKIND_DISPATCH) + This->typeattr.cbSizeVft -= 7 * This->pTypeLib->ptr_size; + for(i = 0; i < This->typeattr.cVars; ++i){ TLBVarDesc *var_desc = &This->vardescs[i]; if(var_desc->vardesc.memid == MEMBERID_NIL){ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10857
From: Francis De Brabandere <francisdb@gmail.com> A TKIND_DISPATCH built via ICreateTypeLib2 + AddImplType(IDispatch) exposes only own funcs through GetFuncDesc: GetFuncDesc(own_count) returns TYPE_E_ELEMENTNOTFOUND on native, even though cImplTypes == 1. Wine instead walks the impltypes chain and surfaces the inherited IDispatch slots at the front of the index space, returning them at indices 0..6 with own funcs only reachable from index 7. Mark these mismatches as todo_wine until GetInternalDispatchFuncDesc is taught to respect the cbSizeVft signature stored by ICreateTypeLib2. --- dlls/oleaut32/tests/typelib.c | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/dlls/oleaut32/tests/typelib.c b/dlls/oleaut32/tests/typelib.c index 7010b879582..d1fc449dd52 100644 --- a/dlls/oleaut32/tests/typelib.c +++ b/dlls/oleaut32/tests/typelib.c @@ -8860,6 +8860,92 @@ static void test_dispatch_with_implparent(void) DeleteFileW(filenameW); } +/* GetFuncDesc on a TKIND_DISPATCH built via ICreateTypeLib2 with explicit + * AddImplType(IDispatch) exposes only own funcs - GetFuncDesc(own_count) + * returns TYPE_E_ELEMENTNOTFOUND, not the inherited IDispatch slots. */ +static void test_dispatch_funcdesc_bound(void) +{ + OLECHAR ifaceW[] = L"iface"; + OLECHAR f0W[] = L"f0", f1W[] = L"f1"; + OLECHAR *f0names[] = { f0W }; + OLECHAR *f1names[] = { f1W }; + WCHAR filenameW[MAX_PATH], temp_path[MAX_PATH]; + ICreateTypeInfo *createti; + ICreateTypeLib2 *createtl; + ITypeInfo *idispatch_ti, *ti; + ITypeLib *stdole; + FUNCDESC fd, *got; + HREFTYPE href; + HRESULT hr; + + GetTempPathW(ARRAY_SIZE(temp_path), temp_path); + GetTempFileNameW(temp_path, L"tlb", 0, filenameW); + + hr = LoadTypeLib(wszStdOle2, &stdole); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ITypeLib_GetTypeInfoOfGuid(stdole, &IID_IDispatch, &idispatch_ti); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CreateTypeLib2(SYS_WIN64, filenameW, &createtl); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeLib2_CreateTypeInfo(createtl, ifaceW, TKIND_DISPATCH, &createti); + ok(hr == S_OK, "got %#lx\n", hr); + ICreateTypeInfo_SetTypeFlags(createti, TYPEFLAG_FDISPATCHABLE); + + hr = ICreateTypeInfo_AddRefTypeInfo(createti, idispatch_ti, &href); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeInfo_AddImplType(createti, 0, href); + ok(hr == S_OK, "got %#lx\n", hr); + + memset(&fd, 0, sizeof(fd)); + fd.memid = 0x100; + fd.funckind = FUNC_DISPATCH; + fd.invkind = INVOKE_FUNC; + fd.callconv = CC_STDCALL; + fd.elemdescFunc.tdesc.vt = VT_VOID; + hr = ICreateTypeInfo_AddFuncDesc(createti, 0, &fd); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeInfo_SetFuncAndParamNames(createti, 0, f0names, 1); + ok(hr == S_OK, "got %#lx\n", hr); + + fd.memid = 0x101; + hr = ICreateTypeInfo_AddFuncDesc(createti, 1, &fd); + ok(hr == S_OK, "got %#lx\n", hr); + hr = ICreateTypeInfo_SetFuncAndParamNames(createti, 1, f1names, 1); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ICreateTypeInfo_LayOut(createti); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ICreateTypeInfo_QueryInterface(createti, &IID_ITypeInfo, (void **)&ti); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = ITypeInfo_GetFuncDesc(ti, 0, &got); + ok(hr == S_OK, "GetFuncDesc(0) got %#lx\n", hr); + if (SUCCEEDED(hr)) { + todo_wine + ok(got->memid == 0x100, "GetFuncDesc(0) memid %#lx\n", got->memid); + ITypeInfo_ReleaseFuncDesc(ti, got); + } + hr = ITypeInfo_GetFuncDesc(ti, 1, &got); + ok(hr == S_OK, "GetFuncDesc(1) got %#lx\n", hr); + if (SUCCEEDED(hr)) { + todo_wine + ok(got->memid == 0x101, "GetFuncDesc(1) memid %#lx\n", got->memid); + ITypeInfo_ReleaseFuncDesc(ti, got); + } + todo_wine + ok(ITypeInfo_GetFuncDesc(ti, 2, &got) == TYPE_E_ELEMENTNOTFOUND, + "GetFuncDesc(2) should return TYPE_E_ELEMENTNOTFOUND\n"); + + ITypeInfo_Release(ti); + ICreateTypeInfo_Release(createti); + ICreateTypeLib2_Release(createtl); + ITypeInfo_Release(idispatch_ti); + ITypeLib_Release(stdole); + DeleteFileW(filenameW); +} + START_TEST(typelib) { const WCHAR *filename; @@ -8904,4 +8990,5 @@ START_TEST(typelib) test_DeleteImplType(); test_DeleteFuncDesc(); test_dispatch_with_implparent(); + test_dispatch_funcdesc_bound(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10857
From: Francis De Brabandere <francisdb@gmail.com> ITypeInfoImpl_GetInternalDispatchFuncDesc walked impltypes whenever any impl type was registered, surfacing inherited IDispatch slots ahead of own funcs in the index space. That matches MIDL-built TKIND_DISPATCH typelibs (where cbSizeVft includes inherited slots and cFuncs is inflated to match) but mismatches ICreateTypeLib2-built ones where cbSizeVft only spans own funcs and GetFuncDesc should expose only those. Skip the walk when the stored cbSizeVft has no room beyond own_count * ptr_size, so GetFuncDesc on ICreateTypeLib2 typelibs hits own funcs at indices 0..cFuncs-1 and returns TYPE_E_ELEMENTNOTFOUND past the end, matching native. --- dlls/oleaut32/tests/typelib.c | 3 --- dlls/oleaut32/typelib.c | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dlls/oleaut32/tests/typelib.c b/dlls/oleaut32/tests/typelib.c index d1fc449dd52..21be6d97fb1 100644 --- a/dlls/oleaut32/tests/typelib.c +++ b/dlls/oleaut32/tests/typelib.c @@ -8923,18 +8923,15 @@ static void test_dispatch_funcdesc_bound(void) hr = ITypeInfo_GetFuncDesc(ti, 0, &got); ok(hr == S_OK, "GetFuncDesc(0) got %#lx\n", hr); if (SUCCEEDED(hr)) { - todo_wine ok(got->memid == 0x100, "GetFuncDesc(0) memid %#lx\n", got->memid); ITypeInfo_ReleaseFuncDesc(ti, got); } hr = ITypeInfo_GetFuncDesc(ti, 1, &got); ok(hr == S_OK, "GetFuncDesc(1) got %#lx\n", hr); if (SUCCEEDED(hr)) { - todo_wine ok(got->memid == 0x101, "GetFuncDesc(1) memid %#lx\n", got->memid); ITypeInfo_ReleaseFuncDesc(ti, got); } - todo_wine ok(ITypeInfo_GetFuncDesc(ti, 2, &got) == TYPE_E_ELEMENTNOTFOUND, "GetFuncDesc(2) should return TYPE_E_ELEMENTNOTFOUND\n"); diff --git a/dlls/oleaut32/typelib.c b/dlls/oleaut32/typelib.c index d604ccc0fd8..07aeac53350 100644 --- a/dlls/oleaut32/typelib.c +++ b/dlls/oleaut32/typelib.c @@ -5954,7 +5954,13 @@ static HRESULT ITypeInfoImpl_GetInternalDispatchFuncDesc( ITypeInfo *iface, else *hrefoffset = DISPATCH_HREF_OFFSET; - if(This->impltypes) + /* Only walk impltypes when the stored cbSizeVft has room for inherited + * slots beyond our own funcs. Typelibs built via ICreateTypeLib2 store + * cbSizeVft = own_count * ptr_size and expose only own funcs through + * GetFuncDesc; MIDL-built typelibs store cbSizeVft = (own+inh)*ptr_size + * and rely on impltypes traversal to surface inherited slots. */ + if(This->impltypes + && This->typeattr.cbSizeVft > This->typeattr.cFuncs * This->pTypeLib->ptr_size) { ITypeInfo *pSubTypeInfo; UINT sub_funcs; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10857
Closed as requested by reviewer, no native-parity fixes without app evidence. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10857#note_139678
This merge request was closed by Francis De Brabandere. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10857
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)