[PATCH v3 0/1] MR10851: Draft: vbscript: Implement IDispatch::GetTypeInfo for class instances via ICreateTypeLib2.
Alternative to !10461 Build a TKIND_DISPATCH ITypeInfo for each class instance using oleaut32's ICreateTypeLib2 / ICreateTypeInfo API rather than hand-rolling the 22 ITypeInfo methods. The class name comes back from GetDocumentation(MEMBERID_NIL), public funcs and Dim'd properties are exposed via GetFuncDesc/GetVarDesc, and inherited typeinfo machinery (GetContainingTypeLib etc.) works for free. A few assertions are marked todo_wine because Wine's oleaut32 CreateTypeLib2 currently: - adds 7 phantom inherited IDispatch methods to TKIND_DISPATCH typeinfos, inflating cFuncs and zeroing cImplTypes; - returns E_INVALIDARG instead of TYPE_E_BADMODULEKIND from CreateInstance; - doesn't zero the out-parameter on AddressOfMember failure; - returns a reftype from GetRefTypeOfImplType that GetRefTypeInfo can't resolve. The new test passes with 0 failures on native Windows VBScript and on Wine (8 todo_wine on the assertions above). -- v3: vbscript: Implement IDispatch::GetTypeInfo for class instances via ICreateTypeLib2. https://gitlab.winehq.org/wine/wine/-/merge_requests/10851
From: Francis De Brabandere <francisdb@gmail.com> Build a TKIND_DISPATCH ITypeInfo for each class instance using oleaut32's ICreateTypeLib2 / ICreateTypeInfo API rather than hand-rolling the 22 ITypeInfo methods. The class name comes back from GetDocumentation(MEMBERID_NIL), public funcs and Dim'd properties are exposed via GetFuncDesc/GetVarDesc, and inherited typeinfo machinery (GetContainingTypeLib etc.) works for free. A few assertions are marked todo_wine because Wine's oleaut32 CreateTypeLib2 currently: - adds 7 phantom inherited IDispatch methods to TKIND_DISPATCH typeinfos, inflating cFuncs and zeroing cImplTypes; - returns E_INVALIDARG instead of TYPE_E_BADMODULEKIND from CreateInstance; - doesn't zero the out-parameter on AddressOfMember failure; - returns a reftype from GetRefTypeOfImplType that GetRefTypeInfo can't resolve. The new test passes with 0 failures on native Windows VBScript and on Wine (8 todo_wine on the assertions above). --- dlls/vbscript/tests/api.vbs | 11 ++ dlls/vbscript/tests/vbscript.c | 261 +++++++++++++++++++++++++++++++++ dlls/vbscript/vbdisp.c | 140 +++++++++++++++++- 3 files changed, 410 insertions(+), 2 deletions(-) diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs index d6d5473a39a..8cbd5824b39 100644 --- a/dlls/vbscript/tests/api.vbs +++ b/dlls/vbscript/tests/api.vbs @@ -1726,6 +1726,17 @@ Call ok(TypeName(collectionObj) = "Object", "TypeName(collectionObj) = " & TypeN Dim regex set regex = new RegExp Call ok(TypeName(regex) = "IRegExp2", "TypeName(regex) = " & TypeName(regex)) + +' TypeName for VBScript class instances +Dim emptyClsObj +Set emptyClsObj = New EmptyClass +Call ok(TypeName(emptyClsObj) = "EmptyClass", "TypeName(EmptyClass) = " & TypeName(emptyClsObj)) +Call ok(getVT(TypeName(emptyClsObj)) = "VT_BSTR", "getVT(TypeName(EmptyClass)) = " & getVT(TypeName(emptyClsObj))) +Dim valClsObj +Set valClsObj = New ValClass +Call ok(TypeName(valClsObj) = "ValClass", "TypeName(ValClass) = " & TypeName(valClsObj)) +Set emptyClsObj = Nothing +Call ok(TypeName(emptyClsObj) = "Nothing", "TypeName after Set Nothing = " & TypeName(emptyClsObj)) Dim ec set ec = new EmptyClass Call ok(TypeName(ec) = "EmptyClass", "TypeName(EmptyClass) = " & TypeName(ec)) diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index bb25ae8017d..3b74acc5dca 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -1539,6 +1539,266 @@ static void test_script_typeinfo(void) IActiveScript_Release(vbscript); } +static void test_vbdisp_typeinfo(void) +{ + IActiveScriptParse *parser; + IDispatchEx *obj_disp; + IActiveScript *vbscript; + ITypeInfo *typeinfo, *typeinfo2; + FUNCDESC *funcdesc; + VARDESC *vardesc; + INT implTypeFlags; + UINT count; + HREFTYPE reftype; + MEMBERID memid; + TYPEATTR *attr; + HRESULT hr; + WCHAR str[64], *names = str; + BSTR bstr, bstrs[5]; + VARIANT var_result; + void *obj; + + vbscript = create_vbscript(); + + hr = IActiveScript_QueryInterface(vbscript, &IID_IActiveScriptParse, (void**)&parser); + ok(hr == S_OK, "Could not get IActiveScriptParse iface: %08lx\n", hr); + + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08lx\n", hr); + CHECK_CALLED(GetLCID); + + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScriptParse_InitNew(parser); + ok(hr == S_OK, "InitNew failed: %08lx\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08lx\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + + parse_script(parser, + L"class C\n" + " dim x\n" + " public sub method\n end sub\n" + " public function add(a, b)\n" + " add = a + b\n" + " end function\n" + " public property get val\n" + " val = x\n" + " end property\n" + " public property let val(v)\n" + " x = v\n" + " end property\n" + " public default property get defprop\n" + " defprop = x\n" + " end property\n" + " private function strret\n" + " strret = \"ret\"\n" + " end function\n" + " private sub helper\n end sub\n" + " private y\n" + "end class\n"); + + /* SCRIPTTEXT_ISEXPRESSION to obtain the class instance dispatch directly. */ + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + V_VT(&var_result) = VT_EMPTY; + bstr = SysAllocString(L"new C"); + hr = IActiveScriptParse_ParseScriptText(parser, bstr, NULL, NULL, NULL, 0, 0, + SCRIPTTEXT_ISEXPRESSION, &var_result, NULL); + SysFreeString(bstr); + ok(hr == S_OK, "ParseScriptText(new C) failed: %08lx\n", hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + ok(V_VT(&var_result) == VT_DISPATCH, "Unexpected vt %d\n", V_VT(&var_result)); + + if (V_VT(&var_result) != VT_DISPATCH || !V_DISPATCH(&var_result)) + { + skip("Could not get class instance dispatch, skipping vbdisp typeinfo tests\n"); + VariantClear(&var_result); + goto done; + } + hr = IDispatch_QueryInterface(V_DISPATCH(&var_result), &IID_IDispatchEx, (void**)&obj_disp); + ok(hr == S_OK, "QI(IDispatchEx) failed: %08lx\n", hr); + VariantClear(&var_result); + + hr = IDispatchEx_GetTypeInfoCount(obj_disp, &count); + ok(hr == S_OK, "GetTypeInfoCount failed: %08lx\n", hr); + ok(count == 1, "Unexpected count %u\n", count); + + hr = IDispatchEx_GetTypeInfo(obj_disp, 1, LOCALE_USER_DEFAULT, &typeinfo); + ok(hr == DISP_E_BADINDEX, "GetTypeInfo(1) returned: %08lx\n", hr); + + hr = IDispatchEx_GetTypeInfo(obj_disp, 0, LOCALE_USER_DEFAULT, &typeinfo); + ok(hr == S_OK, "GetTypeInfo failed: %08lx\n", hr); + hr = IDispatchEx_GetTypeInfo(obj_disp, 0, LOCALE_USER_DEFAULT, &typeinfo2); + ok(hr == S_OK, "GetTypeInfo failed: %08lx\n", hr); + ok(typeinfo != typeinfo2, "TypeInfo was not supposed to be shared.\n"); + ITypeInfo_Release(typeinfo2); + + hr = ITypeInfo_GetDocumentation(typeinfo, MEMBERID_NIL, &bstr, NULL, NULL, NULL); + ok(hr == S_OK, "GetDocumentation(MEMBERID_NIL) failed: %08lx\n", hr); + ok(!lstrcmpW(bstr, L"C"), "Unexpected class name %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + hr = ITypeInfo_GetTypeAttr(typeinfo, &attr); + ok(hr == S_OK, "GetTypeAttr failed: %08lx\n", hr); + ok(attr->typekind == TKIND_DISPATCH, "Unexpected typekind %u\n", attr->typekind); + /* On Wine, oleaut32 CreateTypeLib2 currently adds 7 phantom inherited + * IDispatch methods to TKIND_DISPATCH typeinfos, inflating cFuncs + * from 4 to 11 and zeroing cImplTypes. */ + todo_wine ok(attr->cFuncs == 4, "Unexpected cFuncs %u\n", attr->cFuncs); + ok(attr->cVars == 1, "Unexpected cVars %u\n", attr->cVars); + todo_wine ok(attr->cImplTypes == 1, "Unexpected cImplTypes %u\n", attr->cImplTypes); + ok(attr->wTypeFlags == TYPEFLAG_FDISPATCHABLE, "Unexpected wTypeFlags 0x%x\n", attr->wTypeFlags); + ITypeInfo_ReleaseTypeAttr(typeinfo, attr); + + wcscpy(str, L"method"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames(method) failed: %08lx\n", hr); + hr = ITypeInfo_GetFuncDesc(typeinfo, 0, &funcdesc); + ok(hr == S_OK, "GetFuncDesc failed: %08lx\n", hr); + ok(funcdesc->funckind == FUNC_DISPATCH, "Unexpected funckind %u\n", funcdesc->funckind); + /* Wine's phantom IDispatch methods occupy slots 0-6, so func[0] is + * QueryInterface (invkind reported as PROPERTYGET due to a separate + * Wine bug) instead of our 'method'. */ + todo_wine ok(funcdesc->invkind == INVOKE_FUNC, "Unexpected invkind %u\n", funcdesc->invkind); + ITypeInfo_ReleaseFuncDesc(typeinfo, funcdesc); + + wcscpy(str, L"add"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames(add) failed: %08lx\n", hr); + hr = ITypeInfo_GetNames(typeinfo, memid, bstrs, ARRAY_SIZE(bstrs), &count); + ok(hr == S_OK, "GetNames(add) failed: %08lx\n", hr); + ok(count == 3, "Unexpected name count %u\n", count); + ok(!lstrcmpW(bstrs[0], L"add"), "Unexpected name[0] %s\n", wine_dbgstr_w(bstrs[0])); + ok(!lstrcmpW(bstrs[1], L"a"), "Unexpected name[1] %s\n", wine_dbgstr_w(bstrs[1])); + ok(!lstrcmpW(bstrs[2], L"b"), "Unexpected name[2] %s\n", wine_dbgstr_w(bstrs[2])); + SysFreeString(bstrs[0]); + SysFreeString(bstrs[1]); + SysFreeString(bstrs[2]); + + wcscpy(str, L"val"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames(val) failed: %08lx\n", hr); + hr = ITypeInfo_GetDocumentation(typeinfo, memid, &bstr, NULL, NULL, NULL); + ok(hr == S_OK, "GetDocumentation(val) failed: %08lx\n", hr); + ok(!lstrcmpW(bstr, L"val"), "Unexpected name %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + wcscpy(str, L"defprop"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames(defprop) failed: %08lx\n", hr); + hr = ITypeInfo_GetDocumentation(typeinfo, memid, &bstr, NULL, NULL, NULL); + ok(hr == S_OK, "GetDocumentation(defprop) failed: %08lx\n", hr); + ok(!lstrcmpW(bstr, L"defprop"), "Unexpected name %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + hr = ITypeInfo_GetFuncDesc(typeinfo, 4, &funcdesc); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "GetFuncDesc(4) returned: %08lx\n", hr); + + hr = ITypeInfo_GetVarDesc(typeinfo, 0, &vardesc); + ok(hr == S_OK, "GetVarDesc(0) failed: %08lx\n", hr); + ok(vardesc->varkind == VAR_DISPATCH, "Unexpected varkind %u\n", vardesc->varkind); + ok(vardesc->elemdescVar.tdesc.vt == VT_VARIANT, "Unexpected var vt %d\n", vardesc->elemdescVar.tdesc.vt); + hr = ITypeInfo_GetDocumentation(typeinfo, vardesc->memid, &bstr, NULL, NULL, NULL); + ok(hr == S_OK, "GetDocumentation failed: %08lx\n", hr); + ok(!lstrcmpW(bstr, L"x"), "Unexpected var name %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + ITypeInfo_ReleaseVarDesc(typeinfo, vardesc); + + hr = ITypeInfo_GetVarDesc(typeinfo, 1, &vardesc); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "GetVarDesc(1) returned: %08lx\n", hr); + + wcscpy(str, L"METHOD"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames(METHOD) failed: %08lx\n", hr); + wcscpy(str, L"strret"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == DISP_E_UNKNOWNNAME, "GetIDsOfNames(strret) returned: %08lx\n", hr); + wcscpy(str, L"helper"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == DISP_E_UNKNOWNNAME, "GetIDsOfNames(helper) returned: %08lx\n", hr); + wcscpy(str, L"y"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == DISP_E_UNKNOWNNAME, "GetIDsOfNames(y) returned: %08lx\n", hr); + wcscpy(str, L"nonexistent"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == DISP_E_UNKNOWNNAME, "GetIDsOfNames(nonexistent) returned: %08lx\n", hr); + + wcscpy(str, L"method"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames(method) failed: %08lx\n", hr); + hr = ITypeInfo_GetNames(typeinfo, memid, bstrs, ARRAY_SIZE(bstrs), &count); + ok(hr == S_OK, "GetNames failed: %08lx\n", hr); + ok(count == 1, "Unexpected count %u\n", count); + ok(!lstrcmpW(bstrs[0], L"method"), "Unexpected name %s\n", wine_dbgstr_w(bstrs[0])); + SysFreeString(bstrs[0]); + + hr = ITypeInfo_GetImplTypeFlags(typeinfo, 0, &implTypeFlags); + ok(hr == S_OK, "GetImplTypeFlags failed: %08lx\n", hr); + ok(implTypeFlags == 0, "Unexpected implTypeFlags 0x%x\n", implTypeFlags); + hr = ITypeInfo_GetImplTypeFlags(typeinfo, 1, &implTypeFlags); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "GetImplTypeFlags(1) returned: %08lx\n", hr); + + /* IDispatch parent. Wine's CreateTypeLib2 returns success here + * but the resulting reftype isn't usable via GetRefTypeInfo. */ + hr = ITypeInfo_GetRefTypeOfImplType(typeinfo, 0, &reftype); + ok(hr == S_OK, "GetRefTypeOfImplType failed: %08lx\n", hr); + if(SUCCEEDED(hr)) { + hr = ITypeInfo_GetRefTypeInfo(typeinfo, reftype, &typeinfo2); + todo_wine ok(hr == S_OK, "GetRefTypeInfo failed: %08lx\n", hr); + if(SUCCEEDED(hr)) { + hr = ITypeInfo_GetDocumentation(typeinfo2, MEMBERID_NIL, &bstr, NULL, NULL, NULL); + ok(hr == S_OK, "GetDocumentation failed: %08lx\n", hr); + ok(!lstrcmpW(bstr, L"IDispatch"), "Unexpected TypeInfo name %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + ITypeInfo_Release(typeinfo2); + } + } + + hr = ITypeInfo_GetMops(typeinfo, MEMBERID_NIL, &bstr); + ok(hr == S_OK, "GetMops failed: %08lx\n", hr); + ok(!bstr, "Unexpected non-null string %s\n", wine_dbgstr_w(bstr)); + + obj = (void*)0xdeadbeef; + hr = ITypeInfo_CreateInstance(typeinfo, NULL, NULL, &obj); + /* Wine returns E_INVALIDARG instead of TYPE_E_BADMODULEKIND. */ + todo_wine ok(hr == TYPE_E_BADMODULEKIND, "CreateInstance returned: %08lx\n", hr); + ok(!obj, "Unexpected non-null obj %p.\n", obj); + obj = (void*)0xdeadbeef; + wcscpy(str, L"method"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &names, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames failed: %08lx\n", hr); + hr = ITypeInfo_AddressOfMember(typeinfo, memid, INVOKE_FUNC, &obj); + ok(hr == TYPE_E_BADMODULEKIND, "AddressOfMember returned: %08lx\n", hr); + /* Wine doesn't zero obj on failure. */ + todo_wine ok(!obj, "Unexpected non-null obj %p.\n", obj); + bstr = (BSTR)0xdeadbeef; + hr = ITypeInfo_GetDllEntry(typeinfo, memid, INVOKE_FUNC, &bstr, NULL, NULL); + ok(hr == TYPE_E_BADMODULEKIND, "GetDllEntry returned: %08lx\n", hr); + ok(!bstr, "Unexpected non-null str %p.\n", bstr); + + ITypeInfo_Release(typeinfo); + IDispatchEx_Release(obj_disp); + +done: + IActiveScriptParse_Release(parser); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hr = IActiveScript_Close(vbscript); + ok(hr == S_OK, "Close failed: %08lx\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScript_Release(vbscript); +} + static void test_vbscript(void) { IActiveScriptParseProcedure2 *parse_proc; @@ -3130,6 +3390,7 @@ START_TEST(vbscript) test_scriptdisp(); test_code_persistence(); test_script_typeinfo(); + test_vbdisp_typeinfo(); test_RegExp(); test_RegExp_Replace(); }else { diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index f1d1bb3d4e9..1ea228c8aca 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -424,12 +424,148 @@ static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pcti return S_OK; } +/* Map function_t::type to ITypeInfo INVOKEKIND. */ +static INVOKEKIND invoke_kind_for_func(function_type_t t) +{ + switch(t) { + case FUNC_PROPGET: return INVOKE_PROPERTYGET; + case FUNC_PROPLET: return INVOKE_PROPERTYPUT; + case FUNC_PROPSET: return INVOKE_PROPERTYPUTREF; + default: return INVOKE_FUNC; + } +} + +static HRESULT add_class_member_func(ICreateTypeInfo *cti, unsigned slot, DISPID memid, + const vbdisp_funcprop_desc_t *fp, vbdisp_invoke_type_t which) +{ + function_t *fn = fp->entries[which]; + FUNCDESC fd; + ELEMDESC params_storage[16]; + ELEMDESC *params = NULL; + BSTR names[17]; + unsigned cnames, j; + HRESULT hr; + + if(!fn) return S_OK; + + memset(&fd, 0, sizeof(fd)); + fd.memid = memid; + fd.funckind = FUNC_DISPATCH; + fd.invkind = invoke_kind_for_func(fn->type); + fd.callconv = CC_STDCALL; + fd.cParams = fn->arg_cnt < 16 ? fn->arg_cnt : 16; + fd.elemdescFunc.tdesc.vt = VT_VARIANT; + + if(fd.cParams) { + memset(params_storage, 0, sizeof(params_storage)); + for(j = 0; j < fd.cParams; j++) { + params_storage[j].tdesc.vt = VT_VARIANT; + params_storage[j].paramdesc.wParamFlags = PARAMFLAG_FIN; + } + params = params_storage; + } + fd.lprgelemdescParam = params; + + hr = ICreateTypeInfo_AddFuncDesc(cti, slot, &fd); + if(FAILED(hr)) return hr; + + cnames = 1 + fd.cParams; + names[0] = SysAllocString(fp->name); + for(j = 0; j < fd.cParams; j++) + names[j+1] = fn->args[j].name ? SysAllocString(fn->args[j].name) : SysAllocString(L""); + hr = ICreateTypeInfo_SetFuncAndParamNames(cti, slot, names, cnames); + for(j = 0; j < cnames; j++) SysFreeString(names[j]); + return hr; +} + +static HRESULT build_class_typeinfo(class_desc_t *desc, ITypeInfo **out) +{ + ICreateTypeLib2 *ctl = NULL; + ICreateTypeInfo *cti = NULL; + ITypeLib *tl = NULL; + HRESULT hr; + unsigned i, slot; + + *out = NULL; + + hr = CreateTypeLib2(SYS_WIN64, L"vbscript.tlb", &ctl); + if(FAILED(hr)) return hr; + + hr = ICreateTypeLib2_CreateTypeInfo(ctl, (LPOLESTR)desc->name, TKIND_DISPATCH, &cti); + if(FAILED(hr)) goto done; + + ICreateTypeInfo_SetTypeFlags(cti, TYPEFLAG_FDISPATCHABLE); + + /* One FUNCDESC per public named func/property (matching native). + * Property get/let/set collapse into a single entry; pick the get + * entry when present, else let, else set. */ + slot = 0; + for(i = 0; i < desc->func_cnt; i++) { + const vbdisp_funcprop_desc_t *fp = &desc->funcs[i]; + vbdisp_invoke_type_t which; + if(!fp->is_public || !fp->name) continue; + if(desc->class_initialize_id && i == desc->class_initialize_id) continue; + if(desc->class_terminate_id && i == desc->class_terminate_id) continue; + + if(fp->entries[VBDISP_CALLGET]) which = VBDISP_CALLGET; + else if(fp->entries[VBDISP_LET]) which = VBDISP_LET; + else if(fp->entries[VBDISP_SET]) which = VBDISP_SET; + else continue; + + hr = add_class_member_func(cti, slot, i, fp, which); + if(FAILED(hr)) goto done; + slot++; + } + + /* Public properties (Dim'd vars). */ + slot = 0; + for(i = 0; i < desc->prop_cnt; i++) { + VARDESC vd; + BSTR vname; + if(!desc->props[i].is_public || !desc->props[i].name) continue; + + memset(&vd, 0, sizeof(vd)); + vd.memid = desc->func_cnt + i; + vd.varkind = VAR_DISPATCH; + vd.elemdescVar.tdesc.vt = VT_VARIANT; + hr = ICreateTypeInfo_AddVarDesc(cti, slot, &vd); + if(FAILED(hr)) goto done; + + vname = SysAllocString(desc->props[i].name); + hr = ICreateTypeInfo_SetVarName(cti, slot, vname); + SysFreeString(vname); + if(FAILED(hr)) goto done; + slot++; + } + + hr = ICreateTypeInfo_LayOut(cti); + if(FAILED(hr)) goto done; + + hr = ICreateTypeLib2_QueryInterface(ctl, &IID_ITypeLib, (void**)&tl); + if(FAILED(hr)) goto done; + + hr = ITypeLib_GetTypeInfo(tl, 0, out); + +done: + if(tl) ITypeLib_Release(tl); + if(cti) ICreateTypeInfo_Release(cti); + if(ctl) ICreateTypeLib2_Release(ctl); + return hr; +} + static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { vbdisp_t *This = impl_from_IDispatchEx(iface); - FIXME("(%p)->(%u %lu %p)\n", This, iTInfo, lcid, ppTInfo); - return E_NOTIMPL; + + TRACE("(%p)->(%u %lu %p)\n", This, iTInfo, lcid, ppTInfo); + + if(!ppTInfo) return E_INVALIDARG; + *ppTInfo = NULL; + if(iTInfo) return DISP_E_BADINDEX; + if(!This->desc) return E_UNEXPECTED; + + return build_class_typeinfo((class_desc_t*)This->desc, ppTInfo); } static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10851
There was push-back on the TODO fixes so this is ready for review. https://gitlab.winehq.org/wine/wine/-/merge_requests/10853#note_139549 -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10851#note_139679
This merge request was approved by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10851
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)