Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 194 ++++++++++++++++++++++++++++- dlls/msscript.ocx/tests/msscript.c | 80 ++++++++++++ 2 files changed, 272 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index a339e70..d493065 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -110,6 +110,15 @@ struct ScriptProcedureCollection { struct list hash_table[43]; };
+struct procedure_enum { + IEnumVARIANT IEnumVARIANT_iface; + LONG ref; + + WORD pos; + WORD count; + ScriptProcedureCollection *procedures; +}; + struct ScriptHost { IActiveScriptSite IActiveScriptSite_iface; IActiveScriptSiteWindow IActiveScriptSiteWindow_iface; @@ -532,6 +541,11 @@ static inline struct module_enum *module_enum_from_IEnumVARIANT(IEnumVARIANT *if return CONTAINING_RECORD(iface, struct module_enum, IEnumVARIANT_iface); }
+static inline struct procedure_enum *procedure_enum_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, struct procedure_enum, IEnumVARIANT_iface); +} + /* IActiveScriptSite */ static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv) { @@ -976,6 +990,151 @@ done: return hr; }
+static HRESULT WINAPI procedure_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + + if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IEnumVARIANT, riid)) + { + *ppv = &This->IEnumVARIANT_iface; + } + else + { + WARN("unsupported interface: (%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI procedure_enum_AddRef(IEnumVARIANT *iface) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI procedure_enum_Release(IEnumVARIANT *iface) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (!ref) + { + IScriptProcedureCollection_Release(&This->procedures->IScriptProcedureCollection_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI procedure_enum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + FUNCDESC *desc; + ITypeInfo *ti; + UINT i, num; + HRESULT hr; + + TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched); + + if (!rgVar) return E_POINTER; + if (!This->procedures->module->host) return E_FAIL; + + hr = start_script(This->procedures->module->host); + if (FAILED(hr)) return hr; + + hr = get_script_typeinfo(This->procedures->module, &ti); + if (FAILED(hr)) return hr; + + num = min(celt, This->count - This->pos); + for (i = 0; i < num; i++) + { + hr = ITypeInfo_GetFuncDesc(ti, This->pos + i, &desc); + if (FAILED(hr)) break; + + hr = get_script_procedure(This->procedures, ti, desc, (IScriptProcedure**)&V_DISPATCH(rgVar + i)); + if (FAILED(hr)) break; + + V_VT(rgVar + i) = VT_DISPATCH; + } + + if (FAILED(hr)) + { + while (i--) + VariantClear(rgVar + i); + if (pCeltFetched) *pCeltFetched = 0; + return hr; + } + + This->pos += i; + + if (pCeltFetched) *pCeltFetched = i; + return i == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI procedure_enum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%u)\n", This, celt); + + if (This->count - This->pos < celt) + { + This->pos = This->count; + return S_FALSE; + } + This->pos += celt; + return S_OK; +} + +static HRESULT WINAPI procedure_enum_Reset(IEnumVARIANT *iface) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + + TRACE("(%p)\n", This); + + This->pos = 0; + return S_OK; +} + +static HRESULT WINAPI procedure_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface); + struct procedure_enum *clone; + + TRACE("(%p)->(%p)\n", This, ppEnum); + + if (!ppEnum) return E_POINTER; + + if (!(clone = heap_alloc(sizeof(*clone)))) + return E_OUTOFMEMORY; + + *clone = *This; + clone->ref = 1; + IScriptProcedureCollection_AddRef(&This->procedures->IScriptProcedureCollection_iface); + + *ppEnum = &clone->IEnumVARIANT_iface; + return S_OK; +} + +static const IEnumVARIANTVtbl procedure_enum_vtbl = { + procedure_enum_QueryInterface, + procedure_enum_AddRef, + procedure_enum_Release, + procedure_enum_Next, + procedure_enum_Skip, + procedure_enum_Reset, + procedure_enum_Clone +}; + static HRESULT WINAPI ScriptProcedureCollection_QueryInterface(IScriptProcedureCollection *iface, REFIID riid, void **ppv) { ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface); @@ -1091,10 +1250,41 @@ static HRESULT WINAPI ScriptProcedureCollection_Invoke(IScriptProcedureCollectio static HRESULT WINAPI ScriptProcedureCollection_get__NewEnum(IScriptProcedureCollection *iface, IUnknown **ppenumProcedures) { ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface); + struct procedure_enum *proc_enum; + TYPEATTR *attr; + ITypeInfo *ti; + UINT count; + HRESULT hr;
- FIXME("(%p)->(%p)\n", This, ppenumProcedures); + TRACE("(%p)->(%p)\n", This, ppenumProcedures);
- return E_NOTIMPL; + if (!ppenumProcedures) return E_POINTER; + if (!This->module->host) return E_FAIL; + + hr = start_script(This->module->host); + if (FAILED(hr)) return hr; + + hr = get_script_typeinfo(This->module, &ti); + if (FAILED(hr)) return hr; + + hr = ITypeInfo_GetTypeAttr(ti, &attr); + if (FAILED(hr)) return hr; + + count = attr->cFuncs; + ITypeInfo_ReleaseTypeAttr(ti, attr); + + if (!(proc_enum = heap_alloc(sizeof(*proc_enum)))) + return E_OUTOFMEMORY; + + proc_enum->IEnumVARIANT_iface.lpVtbl = &procedure_enum_vtbl; + proc_enum->ref = 1; + proc_enum->pos = 0; + proc_enum->count = count; + proc_enum->procedures = This; + IScriptProcedureCollection_AddRef(&This->IScriptProcedureCollection_iface); + + *ppenumProcedures = (IUnknown*)&proc_enum->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI ScriptProcedureCollection_get_Item(IScriptProcedureCollection *iface, VARIANT index, diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c index 7233af5..085913a 100644 --- a/dlls/msscript.ocx/tests/msscript.c +++ b/dlls/msscript.ocx/tests/msscript.c @@ -3293,9 +3293,12 @@ static void test_IScriptControl_get_CodeObject(void) static void test_IScriptControl_get_Procedures(void) { IScriptProcedureCollection *procs, *procs2; + IEnumVARIANT *enumvar, *enumvar2; IScriptProcedure *proc, *proc2; IScriptControl *sc; + IUnknown *unknown; VARIANT_BOOL vbool; + ULONG fetched; VARIANT var; LONG count; HRESULT hr; @@ -3548,6 +3551,44 @@ static void test_IScriptControl_get_Procedures(void) ok(hr == E_INVALIDARG, "IScriptProcedureCollection_get_Item returned: 0x%08x.\n", hr); CHECK_CALLED(GetFuncDesc);
+ /* _NewEnum never caches the function count */ + hr = IScriptProcedureCollection_get__NewEnum(procs, NULL); + ok(hr == E_POINTER, "IScriptProcedureCollection_get__NewEnum returned: 0x%08x.\n", hr); + SET_EXPECT(GetTypeAttr); + SET_EXPECT(ReleaseTypeAttr); + hr = IScriptProcedureCollection_get__NewEnum(procs, &unknown); + ok(hr == S_OK, "IScriptProcedureCollection_get__NewEnum failed: 0x%08x.\n", hr); + CHECK_CALLED(GetTypeAttr); + CHECK_CALLED(ReleaseTypeAttr); + hr = IUnknown_QueryInterface(unknown, &IID_IEnumVARIANT, (void**)&enumvar); + ok(hr == S_OK, "Failed to query for IEnumVARIANT: 0x%08x.\n", hr); + ok((char*)unknown == (char*)enumvar, "unknown and enumvar are not the same (%p vs %p).\n", unknown, enumvar); + IEnumVARIANT_Release(enumvar); + IUnknown_Release(unknown); + SET_EXPECT(GetTypeAttr); + SET_EXPECT(ReleaseTypeAttr); + hr = IScriptProcedureCollection_get__NewEnum(procs, &unknown); + ok(hr == S_OK, "IScriptProcedureCollection_get__NewEnum failed: 0x%08x.\n", hr); + CHECK_CALLED(GetTypeAttr); + CHECK_CALLED(ReleaseTypeAttr); + hr = IUnknown_QueryInterface(unknown, &IID_IEnumVARIANT, (void**)&enumvar); + ok(hr == S_OK, "Failed to query for IEnumVARIANT: 0x%08x.\n", hr); + IUnknown_Release(unknown); + + fetched = 0xdeadbeef; + hr = IEnumVARIANT_Next(enumvar, 0, NULL, NULL); + ok(hr == E_POINTER, "IEnumVARIANT_Next returned: 0x%08x.\n", hr); + hr = IEnumVARIANT_Next(enumvar, 0, NULL, &fetched); + ok(hr == E_POINTER, "IEnumVARIANT_Next failed: 0x%08x.\n", hr); + ok(fetched == 0xdeadbeef, "got %u.\n", fetched); + hr = IEnumVARIANT_Next(enumvar, 0, &var, &fetched); + ok(hr == S_OK, "IEnumVARIANT_Next returned: 0x%08x.\n", hr); + ok(fetched == 0, "got %u.\n", fetched); + hr = IEnumVARIANT_Next(enumvar, 0, &var, NULL); + ok(hr == S_OK, "IEnumVARIANT_Next returned: 0x%08x.\n", hr); + hr = IEnumVARIANT_Clone(enumvar, NULL); + ok(hr == E_POINTER, "IEnumVARIANT_Clone failed: 0x%08x.\n", hr); + for (i = 0; i < ARRAY_SIZE(custom_engine_funcs); i++) { /* Querying by index still goes through the Bind process */ @@ -3583,6 +3624,25 @@ static void test_IScriptControl_get_Procedures(void) CHECK_CALLED(GetNames); CHECK_CALLED(ReleaseFuncDesc);
+ /* Compare with the enumerator */ + SET_EXPECT(GetFuncDesc); + SET_EXPECT(GetNames); + SET_EXPECT(ReleaseFuncDesc); + hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched); + ok(hr == S_OK, "IEnumVARIANT_Next for index %u failed: 0x%08x.\n", i, hr); + ok(fetched == 1, "got %u.\n", fetched); + ok(V_VT(&var) == VT_DISPATCH, "V_VT(var) = %d.\n", V_VT(&var)); + CHECK_CALLED(GetFuncDesc); + CHECK_CALLED(GetNames); + CHECK_CALLED(ReleaseFuncDesc); + hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IScriptProcedure, (void**)&proc2); + ok(hr == S_OK, "Failed to query IScriptProcedure for index %u: 0x%08x.\n", i, hr); + VariantClear(&var); + + ok(proc == proc2, "proc and proc2 are not the same for %s and enum index %u.\n", + wine_dbgstr_w(custom_engine_funcs[i].name), i); + IScriptProcedure_Release(proc2); + /* Verify the properties */ hr = IScriptProcedure_get_Name(proc, &str); ok(hr == S_OK, "get_Name for %s failed: 0x%08x.\n", wine_dbgstr_w(custom_engine_funcs[i].name), hr); @@ -3601,6 +3661,26 @@ static void test_IScriptControl_get_Procedures(void) IScriptProcedure_Release(proc); }
+ hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched); + ok(hr == S_FALSE, "IEnumVARIANT_Next failed: 0x%08x.\n", hr); + ok(fetched == 0, "got %u.\n", fetched); + hr = IEnumVARIANT_Skip(enumvar, 0); + ok(hr == S_OK, "IEnumVARIANT_Skip failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Skip(enumvar, 1); + ok(hr == S_FALSE, "IEnumVARIANT_Skip failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Reset(enumvar); + ok(hr == S_OK, "IEnumVARIANT_Reset failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Skip(enumvar, ARRAY_SIZE(custom_engine_funcs) - 1); + ok(hr == S_OK, "IEnumVARIANT_Skip failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Clone(enumvar, &enumvar2); + ok(hr == S_OK, "IEnumVARIANT_Clone failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Skip(enumvar2, 1); + ok(hr == S_OK, "IEnumVARIANT_Skip failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Skip(enumvar2, 1); + ok(hr == S_FALSE, "IEnumVARIANT_Skip failed: 0x%08x.\n", hr); + + IEnumVARIANT_Release(enumvar2); + IEnumVARIANT_Release(enumvar); IScriptProcedureCollection_Release(procs); IActiveScriptSite_Release(site);