Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 163 ++++++++++++++++++++++++++++- dlls/msscript.ocx/tests/msscript.c | 134 +++++++++++++++++++++++- 2 files changed, 294 insertions(+), 3 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 7fc7b95..352bcab 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -66,6 +66,15 @@ struct named_item { IDispatch *disp; };
+struct module_enum { + IEnumVARIANT IEnumVARIANT_iface; + LONG ref; + + UINT pos; + ScriptHost *host; + ScriptControl *control; +}; + typedef struct { IScriptModule IScriptModule_iface; LONG ref; @@ -375,6 +384,11 @@ static inline ScriptHost *impl_from_IServiceProvider(IServiceProvider *iface) return CONTAINING_RECORD(iface, ScriptHost, IServiceProvider_iface); }
+static inline struct module_enum *module_enum_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, struct module_enum, IEnumVARIANT_iface); +} + /* IActiveScriptSite */ static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv) { @@ -844,6 +858,135 @@ static const IScriptModuleVtbl ScriptModuleVtbl = { ScriptModule_Run };
+static HRESULT WINAPI module_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + struct module_enum *This = module_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 module_enum_AddRef(IEnumVARIANT *iface) +{ + struct module_enum *This = module_enum_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI module_enum_Release(IEnumVARIANT *iface) +{ + struct module_enum *This = module_enum_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (!ref) + { + IActiveScriptSite_Release(&This->host->IActiveScriptSite_iface); + IScriptControl_Release(&This->control->IScriptControl_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI module_enum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + struct module_enum *This = module_enum_from_IEnumVARIANT(iface); + unsigned int i, num; + + TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched); + + if (!rgVar) return E_POINTER; + if (This->host != This->control->host) return E_FAIL; + + num = min(celt, This->host->module_count - This->pos); + for (i = 0; i < num; i++) + { + V_VT(rgVar + i) = VT_DISPATCH; + V_DISPATCH(rgVar + i) = (IDispatch*)(&This->control->modules[This->pos++]->IScriptModule_iface); + IDispatch_AddRef(V_DISPATCH(rgVar + i)); + } + + if (pCeltFetched) *pCeltFetched = i; + return i == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI module_enum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + struct module_enum *This = module_enum_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%u)\n", This, celt); + + if (This->host != This->control->host) return E_FAIL; + + if (This->host->module_count - This->pos < celt) + { + This->pos = This->host->module_count; + return S_FALSE; + } + This->pos += celt; + return S_OK; +} + +static HRESULT WINAPI module_enum_Reset(IEnumVARIANT *iface) +{ + struct module_enum *This = module_enum_from_IEnumVARIANT(iface); + + TRACE("(%p)\n", This); + + if (This->host != This->control->host) return E_FAIL; + + This->pos = 0; + return S_OK; +} + +static HRESULT WINAPI module_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + struct module_enum *This = module_enum_from_IEnumVARIANT(iface); + struct module_enum *clone; + + TRACE("(%p)->(%p)\n", This, ppEnum); + + if (!ppEnum) return E_POINTER; + if (This->host != This->control->host) return E_FAIL; + + if (!(clone = heap_alloc(sizeof(*clone)))) + return E_OUTOFMEMORY; + + *clone = *This; + clone->ref = 1; + IActiveScriptSite_AddRef(&This->host->IActiveScriptSite_iface); + IScriptControl_AddRef(&This->control->IScriptControl_iface); + + *ppEnum = &clone->IEnumVARIANT_iface; + return S_OK; +} + +static const IEnumVARIANTVtbl module_enum_vtbl = { + module_enum_QueryInterface, + module_enum_AddRef, + module_enum_Release, + module_enum_Next, + module_enum_Skip, + module_enum_Reset, + module_enum_Clone +}; + static ScriptModule *create_module(ScriptHost *host, BSTR name) { ScriptModule *module; @@ -987,10 +1130,26 @@ static HRESULT WINAPI ScriptModuleCollection_Invoke(IScriptModuleCollection *ifa static HRESULT WINAPI ScriptModuleCollection_get__NewEnum(IScriptModuleCollection *iface, IUnknown **ppenumContexts) { ScriptControl *This = impl_from_IScriptModuleCollection(iface); + struct module_enum *module_enum;
- FIXME("(%p)->(%p)\n", This, ppenumContexts); + TRACE("(%p)->(%p)\n", This, ppenumContexts);
- return E_NOTIMPL; + if (!ppenumContexts) return E_POINTER; + if (!This->host) return E_FAIL; + + if (!(module_enum = heap_alloc(sizeof(*module_enum)))) + return E_OUTOFMEMORY; + + module_enum->IEnumVARIANT_iface.lpVtbl = &module_enum_vtbl; + module_enum->ref = 1; + module_enum->pos = 0; + module_enum->host = This->host; + module_enum->control = This; + IActiveScriptSite_AddRef(&This->host->IActiveScriptSite_iface); + IScriptControl_AddRef(&This->IScriptControl_iface); + + *ppenumContexts = (IUnknown*)&module_enum->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI ScriptModuleCollection_get_Item(IScriptModuleCollection *iface, VARIANT index, diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c index 807f82e..9986fbf 100644 --- a/dlls/msscript.ocx/tests/msscript.c +++ b/dlls/msscript.ocx/tests/msscript.c @@ -2307,14 +2307,17 @@ static void test_IScriptControl_Run(void)
static void test_IScriptControl_get_Modules(void) { + IEnumVARIANT *enumvar, *enumvar2; IScriptModuleCollection *mods; + VARIANT var, vars[3]; IScriptModule *mod; IScriptControl *sc; IUnknown *unknown; - VARIANT var; + ULONG fetched; LONG count; HRESULT hr; BSTR str; + UINT i;
hr = CoCreateInstance(&CLSID_ScriptControl, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, &IID_IScriptControl, (void**)&sc); @@ -2360,6 +2363,16 @@ static void test_IScriptControl_get_Modules(void) IScriptModule_Release(mod); SysFreeString(str);
+ /* Grab an enumerator before we add another module */ + hr = IScriptModuleCollection_get__NewEnum(mods, NULL); + ok(hr == E_POINTER, "IScriptModuleCollection_get__NewEnum returned: 0x%08x.\n", hr); + hr = IScriptModuleCollection_get__NewEnum(mods, &unknown); + ok(hr == S_OK, "IScriptModuleCollection_get__NewEnum failed: 0x%08x.\n", hr); + 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); + IUnknown_Release(unknown); + str = SysAllocString(L"some other Module"); hr = IScriptModuleCollection_Add(mods, str, &var, &mod); ok(hr == S_OK, "IScriptModuleCollection_Add failed: 0x%08x.\n", hr); @@ -2428,6 +2441,46 @@ static void test_IScriptControl_get_Modules(void) IScriptModule_Release(mod); SysFreeString(str);
+ /* Test the enumerator, should be updated */ + 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); + + hr = IEnumVARIANT_Next(enumvar, ARRAY_SIZE(vars), vars, &fetched); + ok(hr == S_OK, "IEnumVARIANT_Next failed: 0x%08x.\n", hr); + ok(fetched == ARRAY_SIZE(vars), "got %u.\n", fetched); + 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_Clone(enumvar, &enumvar2); + ok(hr == S_OK, "IEnumVARIANT_Clone failed: 0x%08x.\n", hr); + hr = IEnumVARIANT_Skip(enumvar2, 1); + ok(hr == S_FALSE, "IEnumVARIANT_Skip failed: 0x%08x.\n", hr); + IEnumVARIANT_Release(enumvar2); + + for (i = 0; i < ARRAY_SIZE(vars); i++) + { + ok(V_VT(&vars[i]) == VT_DISPATCH, "V_VT(vars[%u]) = %d.\n", i, V_VT(&vars[i])); + hr = IDispatch_QueryInterface(V_DISPATCH(&vars[i]), &IID_IScriptModule, (void**)&mod); + ok(hr == S_OK, "Failed to query IScriptModule from vars[%u]: 0x%08x.\n", i, hr); + IScriptModule_Release(mod); + VariantClear(&vars[i]); + } + /* The 'Global' module is the same as the script control */ str = SysAllocString(L"add(10, 5)"); hr = IScriptControl_Eval(sc, str, &var); @@ -2465,6 +2518,17 @@ static void test_IScriptControl_get_Modules(void) IScriptModule_Release(mod); SysFreeString(str);
+ /* The enumerator is also invalid */ + hr = IEnumVARIANT_Reset(enumvar); + ok(hr == E_FAIL, "IEnumVARIANT_Skip returned: 0x%08x.\n", hr); + hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched); + ok(hr == E_FAIL, "IEnumVARIANT_Next returned: 0x%08x.\n", hr); + hr = IEnumVARIANT_Skip(enumvar, 0); + ok(hr == E_FAIL, "IEnumVARIANT_Skip returned: 0x%08x.\n", hr); + hr = IEnumVARIANT_Clone(enumvar, &enumvar2); + ok(hr == E_FAIL, "IEnumVARIANT_Clone returned: 0x%08x.\n", hr); + IEnumVARIANT_Release(enumvar); + hr = IScriptControl_put_Language(sc, NULL); ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr);
@@ -2480,6 +2544,8 @@ static void test_IScriptControl_get_Modules(void) hr = IScriptModuleCollection_Add(mods, str, &var, &mod); ok(hr == E_FAIL, "IScriptModuleCollection_Add returned: 0x%08x.\n", hr); SysFreeString(str); + hr = IScriptModuleCollection_get__NewEnum(mods, &unknown); + ok(hr == E_FAIL, "IScriptModuleCollection_get__NewEnum returned: 0x%08x.\n", hr);
IScriptModuleCollection_Release(mods); hr = IScriptControl_get_Modules(sc, &mods); @@ -2640,6 +2706,72 @@ static void test_IScriptControl_get_Modules(void) SET_EXPECT(Close); IScriptModule_Release(mod); CHECK_CALLED(Close); + + /* Hold an enumerator while releasing the script control */ + hr = CoCreateInstance(&CLSID_ScriptControl, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, + &IID_IScriptControl, (void**)&sc); + ok(hr == S_OK, "Failed to create IScriptControl interface: 0x%08x.\n", hr); + + SET_EXPECT(CreateInstance); + SET_EXPECT(SetInterfaceSafetyOptions); + SET_EXPECT(SetScriptSite); + SET_EXPECT(QI_IActiveScriptParse); + SET_EXPECT(InitNew); + str = SysAllocString(L"testscript"); + hr = IScriptControl_put_Language(sc, str); + ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr); + SysFreeString(str); + CHECK_CALLED(CreateInstance); + CHECK_CALLED(SetInterfaceSafetyOptions); + CHECK_CALLED(SetScriptSite); + CHECK_CALLED(QI_IActiveScriptParse); + CHECK_CALLED(InitNew); + + hr = IScriptControl_get_Modules(sc, &mods); + ok(hr == S_OK, "IScriptControl_get_Modules failed: 0x%08x.\n", hr); + IScriptControl_Release(sc); + + SET_EXPECT(AddNamedItem); + str = SysAllocString(L"bar"); + AddNamedItem_expected_name = str; + AddNamedItem_expected_flags = SCRIPTITEM_CODEONLY; + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = NULL; + hr = IScriptModuleCollection_Add(mods, str, &var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_Add failed: 0x%08x.\n", hr); + IScriptModule_Release(mod); + VariantClear(&var); + SysFreeString(str); + CHECK_CALLED(AddNamedItem); + + hr = IScriptModuleCollection_get__NewEnum(mods, &unknown); + ok(hr == S_OK, "IScriptModuleCollection_get__NewEnum failed: 0x%08x.\n", hr); + hr = IUnknown_QueryInterface(unknown, &IID_IEnumVARIANT, (void**)&enumvar); + ok(hr == S_OK, "Failed to query for IEnumVARIANT: 0x%08x.\n", hr); + IUnknown_Release(unknown); + + IScriptModuleCollection_Release(mods); + IActiveScriptSite_Release(site); + + hr = IEnumVARIANT_Next(enumvar, ARRAY_SIZE(vars), vars, &fetched); + ok(hr == S_FALSE, "IEnumVARIANT_Next failed: 0x%08x.\n", hr); + ok(fetched == 2, "got %u.\n", fetched); + for (i = 0; i < fetched; i++) + { + ok(V_VT(&vars[i]) == VT_DISPATCH, "V_VT(vars[%u]) = %d.\n", i, V_VT(&vars[i])); + hr = IDispatch_QueryInterface(V_DISPATCH(&vars[i]), &IID_IScriptModule, (void**)&mod); + ok(hr == S_OK, "Failed to query IScriptModule from vars[%u]: 0x%08x.\n", i, hr); + hr = IScriptModule_get_Name(mod, &str); + ok(hr == S_OK, "IScriptModule_get_Name failed for vars[%u]: 0x%08x.\n", i, hr); + ok(!lstrcmpW(str, i ? L"bar" : L"Global"), "wrong name for vars[%u]: %s.\n", i, wine_dbgstr_w(str)); + IScriptModule_Release(mod); + VariantClear(&vars[i]); + SysFreeString(str); + } + + SET_EXPECT(Close); + IEnumVARIANT_Release(enumvar); + CHECK_CALLED(Close); } }