Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
So it's near the other helpers and will be needed for later patches in the series.
dlls/msscript.ocx/msscript.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index c4c6485..6d098cd 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -237,6 +237,20 @@ static HRESULT start_script(struct ScriptControl *control) return hr; }
+static HRESULT parse_script_text(ScriptControl *control, BSTR script_text, DWORD flag, VARIANT *res) +{ + EXCEPINFO excepinfo; + HRESULT hr; + + hr = start_script(control); + if (FAILED(hr)) return hr; + + hr = IActiveScriptParse_ParseScriptText(control->host->parse, script_text, NULL, + NULL, NULL, 0, 1, flag, res, &excepinfo); + /* FIXME: more error handling */ + return hr; +} + static inline ScriptControl *impl_from_IScriptControl(IScriptControl *iface) { return CONTAINING_RECORD(iface, ScriptControl, IScriptControl_iface); @@ -1008,20 +1022,6 @@ static HRESULT WINAPI ScriptControl_Reset(IScriptControl *iface) return set_script_state(This->host, SCRIPTSTATE_INITIALIZED); }
-static HRESULT parse_script_text(ScriptControl *control, BSTR script_text, DWORD flag, VARIANT *res) -{ - EXCEPINFO excepinfo; - HRESULT hr; - - hr = start_script(control); - if (FAILED(hr)) return hr; - - hr = IActiveScriptParse_ParseScriptText(control->host->parse, script_text, NULL, - NULL, NULL, 0, 1, flag, res, &excepinfo); - /* FIXME: more error handling */ - return hr; -} - static HRESULT WINAPI ScriptControl_AddCode(IScriptControl *iface, BSTR code) { ScriptControl *This = impl_from_IScriptControl(iface);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 166 ++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 3 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 6d098cd..7a18a84 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -106,6 +106,9 @@ struct ScriptControl { IAdviseSink *view_sink; DWORD view_sink_flags;
+ /* modules */ + IScriptModuleCollection IScriptModuleCollection_iface; + ScriptHost *host; };
@@ -113,6 +116,7 @@ static HINSTANCE msscript_instance;
typedef enum tid_t { IScriptControl_tid, + IScriptModuleCollection_tid, LAST_tid } tid_t;
@@ -120,7 +124,8 @@ static ITypeLib *typelib; static ITypeInfo *typeinfos[LAST_tid];
static REFIID tid_ids[] = { - &IID_IScriptControl + &IID_IScriptControl, + &IID_IScriptModuleCollection, };
static HRESULT load_typelib(void) @@ -291,6 +296,11 @@ static inline ScriptControl *impl_from_IConnectionPointContainer(IConnectionPoin return CONTAINING_RECORD(iface, ScriptControl, IConnectionPointContainer_iface); }
+static inline ScriptControl *impl_from_IScriptModuleCollection(IScriptModuleCollection *iface) +{ + return CONTAINING_RECORD(iface, ScriptControl, IScriptModuleCollection_iface); +} + static inline ConnectionPoint *impl_from_IConnectionPoint(IConnectionPoint *iface) { return CONTAINING_RECORD(iface, ConnectionPoint, IConnectionPoint_iface); @@ -565,6 +575,151 @@ static const IServiceProviderVtbl ServiceProviderVtbl = { ServiceProvider_QueryService };
+static HRESULT WINAPI ScriptModuleCollection_QueryInterface(IScriptModuleCollection *iface, REFIID riid, void **ppv) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + if (IsEqualGUID(&IID_IDispatch, riid) || IsEqualGUID(&IID_IUnknown, riid) || + IsEqualGUID(&IID_IScriptModuleCollection, riid)) + { + *ppv = &This->IScriptModuleCollection_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 ScriptModuleCollection_AddRef(IScriptModuleCollection *iface) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + return IScriptControl_AddRef(&This->IScriptControl_iface); +} + +static ULONG WINAPI ScriptModuleCollection_Release(IScriptModuleCollection *iface) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + return IScriptControl_Release(&This->IScriptControl_iface); +} + +static HRESULT WINAPI ScriptModuleCollection_GetTypeInfoCount(IScriptModuleCollection *iface, UINT *pctinfo) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI ScriptModuleCollection_GetTypeInfo(IScriptModuleCollection *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + + return get_typeinfo(IScriptModuleCollection_tid, ppTInfo); +} + +static HRESULT WINAPI ScriptModuleCollection_GetIDsOfNames(IScriptModuleCollection *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); + + hr = get_typeinfo(IScriptModuleCollection_tid, &typeinfo); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); + ITypeInfo_Release(typeinfo); + } + + return hr; +} + +static HRESULT WINAPI ScriptModuleCollection_Invoke(IScriptModuleCollection *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + hr = get_typeinfo(IScriptModuleCollection_tid, &typeinfo); + if(SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); + ITypeInfo_Release(typeinfo); + } + + return hr; +} + +static HRESULT WINAPI ScriptModuleCollection_get__NewEnum(IScriptModuleCollection *iface, IUnknown **ppenumContexts) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + FIXME("(%p)->(%p)\n", This, ppenumContexts); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModuleCollection_get_Item(IScriptModuleCollection *iface, VARIANT index, + IScriptModule **ppmod) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + FIXME("(%p)->(%s %p)\n", This, wine_dbgstr_variant(&index), ppmod); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModuleCollection_get_Count(IScriptModuleCollection *iface, LONG *plCount) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + FIXME("(%p)->(%p)\n", This, plCount); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModuleCollection_Add(IScriptModuleCollection *iface, BSTR name, + VARIANT *object, IScriptModule **ppmod) +{ + ScriptControl *This = impl_from_IScriptModuleCollection(iface); + + FIXME("(%p)->(%s %s %p)\n", This, wine_dbgstr_w(name), wine_dbgstr_variant(object), ppmod); + + return E_NOTIMPL; +} + +static const IScriptModuleCollectionVtbl ScriptModuleCollectionVtbl = { + ScriptModuleCollection_QueryInterface, + ScriptModuleCollection_AddRef, + ScriptModuleCollection_Release, + ScriptModuleCollection_GetTypeInfoCount, + ScriptModuleCollection_GetTypeInfo, + ScriptModuleCollection_GetIDsOfNames, + ScriptModuleCollection_Invoke, + ScriptModuleCollection_get__NewEnum, + ScriptModuleCollection_get_Item, + ScriptModuleCollection_get_Count, + ScriptModuleCollection_Add +}; + static HRESULT init_script_host(const CLSID *clsid, ScriptHost **ret) { IObjectSafety *objsafety; @@ -936,8 +1091,12 @@ static HRESULT WINAPI ScriptControl_put_UseSafeSubset(IScriptControl *iface, VAR static HRESULT WINAPI ScriptControl_get_Modules(IScriptControl *iface, IScriptModuleCollection **p) { ScriptControl *This = impl_from_IScriptControl(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + + TRACE("(%p)->(%p)\n", This, p); + + *p = &This->IScriptModuleCollection_iface; + IScriptControl_AddRef(iface); + return S_OK; }
static HRESULT WINAPI ScriptControl_get_Error(IScriptControl *iface, IScriptError **p) @@ -2031,6 +2190,7 @@ static HRESULT WINAPI ScriptControl_CreateInstance(IClassFactory *iface, IUnknow script_control->IViewObjectEx_iface.lpVtbl = &ViewObjectExVtbl; script_control->IPointerInactive_iface.lpVtbl = &PointerInactiveVtbl; script_control->IConnectionPointContainer_iface.lpVtbl = &ConnectionPointContainerVtbl; + script_control->IScriptModuleCollection_iface.lpVtbl = &ScriptModuleCollectionVtbl; script_control->ref = 1; script_control->site = NULL; script_control->cp_list = NULL;
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 6d098cd..7a18a84 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -106,6 +106,9 @@ struct ScriptControl { IAdviseSink *view_sink; DWORD view_sink_flags;
- /* modules */
- IScriptModuleCollection IScriptModuleCollection_iface;
};ScriptHost *host;
It looks like it could be a separated object (which could potentially help with your references problem).
Jacek
On 29/05/2020 19:53, Jacek Caban wrote:
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 6d098cd..7a18a84 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -106,6 +106,9 @@ struct ScriptControl { IAdviseSink *view_sink; DWORD view_sink_flags; + /* modules */ + IScriptModuleCollection IScriptModuleCollection_iface;
ScriptHost *host; };
It looks like it could be a separated object (which could potentially help with your references problem).
Jacek
It makes no difference if it's a separate object in this case. The module collection is tied to the control: updating the control (via put_Language for example) will also update an existing module collection, not just newly obtained ones, as tests show.
Modules are a different thing, though: they get detached and operations are not valid anymore (but obviously still in memory so using them doesn't cause a use-after-free), hence the issue.
I will explain more in the reply to the other message.
On 01.06.2020 14:17, Gabriel Ivăncescu wrote:
On 29/05/2020 19:53, Jacek Caban wrote:
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 6d098cd..7a18a84 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -106,6 +106,9 @@ struct ScriptControl { IAdviseSink *view_sink; DWORD view_sink_flags; + /* modules */ + IScriptModuleCollection IScriptModuleCollection_iface;
ScriptHost *host; };
It looks like it could be a separated object (which could potentially help with your references problem).
Jacek
It makes no difference if it's a separate object in this case. The module collection is tied to the control: updating the control (via put_Language for example) will also update an existing module collection, not just newly obtained ones, as tests show.
It may make a difference, depending on implementation details. The potentially problematic part of your solution is that collection shares reference counter with control instead of having a separated ref count. It means that collection keeps the whole control alive (which may or may not be desired, it should be testable).
Thanks,
Jacek
On 01/06/2020 17:54, Jacek Caban wrote:
On 01.06.2020 14:17, Gabriel Ivăncescu wrote:
On 29/05/2020 19:53, Jacek Caban wrote:
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 6d098cd..7a18a84 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -106,6 +106,9 @@ struct ScriptControl { IAdviseSink *view_sink; DWORD view_sink_flags; + /* modules */ + IScriptModuleCollection IScriptModuleCollection_iface;
ScriptHost *host; };
It looks like it could be a separated object (which could potentially help with your references problem).
Jacek
It makes no difference if it's a separate object in this case. The module collection is tied to the control: updating the control (via put_Language for example) will also update an existing module collection, not just newly obtained ones, as tests show.
It may make a difference, depending on implementation details. The potentially problematic part of your solution is that collection shares reference counter with control instead of having a separated ref count. It means that collection keeps the whole control alive (which may or may not be desired, it should be testable).
Yes but it already is the case that the module collection keeps the control alive, and is only really closed when the collection is closed.
To answer the other message since it's related: It's the same with non-detached modules. I did have tests for that, although not for the collection (I'll add that now).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
We have to treat a ref count of 0 in a special manner when they are linked to the script control, because the modules must exist even when they have no external refs (as the script control can still use them).
However, as evidenced by the tests, they must *not* keep a ref to the script control in this case, as it would create a circular ref and make no sense, anyway.
The alternative solution to allocate separate data for modules would require copying the data to multiple places (one for Module Collection, and one for orphaned Modules) which is more complicated, in my opinion.
dlls/msscript.ocx/msscript.c | 279 ++++++++++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 7a18a84..6bf41ff 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -65,6 +65,14 @@ struct named_item { IDispatch *disp; };
+typedef struct ScriptModule { + IScriptModule IScriptModule_iface; + LONG ref; + + BSTR name; + ScriptControl *control; +} ScriptModule; + typedef struct ScriptHost { IActiveScriptSite IActiveScriptSite_iface; IActiveScriptSiteWindow IActiveScriptSiteWindow_iface; @@ -107,6 +115,8 @@ struct ScriptControl { DWORD view_sink_flags;
/* modules */ + UINT num_modules; + ScriptModule **modules; IScriptModuleCollection IScriptModuleCollection_iface;
ScriptHost *host; @@ -117,6 +127,7 @@ static HINSTANCE msscript_instance; typedef enum tid_t { IScriptControl_tid, IScriptModuleCollection_tid, + IScriptModule_tid, LAST_tid } tid_t;
@@ -126,6 +137,7 @@ static ITypeInfo *typeinfos[LAST_tid]; static REFIID tid_ids[] = { &IID_IScriptControl, &IID_IScriptModuleCollection, + &IID_IScriptModule };
static HRESULT load_typelib(void) @@ -301,6 +313,11 @@ static inline ScriptControl *impl_from_IScriptModuleCollection(IScriptModuleColl return CONTAINING_RECORD(iface, ScriptControl, IScriptModuleCollection_iface); }
+static inline ScriptModule *impl_from_IScriptModule(IScriptModule *iface) +{ + return CONTAINING_RECORD(iface, ScriptModule, IScriptModule_iface); +} + static inline ConnectionPoint *impl_from_IConnectionPoint(IConnectionPoint *iface) { return CONTAINING_RECORD(iface, ConnectionPoint, IConnectionPoint_iface); @@ -575,6 +592,242 @@ static const IServiceProviderVtbl ServiceProviderVtbl = { ServiceProvider_QueryService };
+static void destroy_module(ScriptModule *module) +{ + SysFreeString(module->name); + heap_free(module); +} + +static void release_modules(ScriptControl *control) +{ + UINT i = control->num_modules - 1; + + if (!control->modules) return; + do + { + /* Unlink it from the script control if it's held */ + if (control->modules[i]->ref) + { + control->modules[i]->control = NULL; + IScriptControl_Release(&control->IScriptControl_iface); + } + else + destroy_module(control->modules[i]); + } while (i--); + + heap_free(control->modules); +} + +static HRESULT WINAPI ScriptModule_QueryInterface(IScriptModule *iface, REFIID riid, void **ppv) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + if (IsEqualGUID(&IID_IDispatch, riid) || IsEqualGUID(&IID_IUnknown, riid) || + IsEqualGUID(&IID_IScriptModule, riid)) + { + *ppv = &This->IScriptModule_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 ScriptModule_AddRef(IScriptModule *iface) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (ref == 1) + IScriptControl_AddRef(&This->control->IScriptControl_iface); + + return ref; +} + +static ULONG WINAPI ScriptModule_Release(IScriptModule *iface) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (!ref) + { + if (This->control) + IScriptControl_Release(&This->control->IScriptControl_iface); + else + destroy_module(This); + } + + return ref; +} + +static HRESULT WINAPI ScriptModule_GetTypeInfoCount(IScriptModule *iface, UINT *pctinfo) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI ScriptModule_GetTypeInfo(IScriptModule *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + + return get_typeinfo(IScriptModule_tid, ppTInfo); +} + +static HRESULT WINAPI ScriptModule_GetIDsOfNames(IScriptModule *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); + + hr = get_typeinfo(IScriptModule_tid, &typeinfo); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); + ITypeInfo_Release(typeinfo); + } + + return hr; +} + +static HRESULT WINAPI ScriptModule_Invoke(IScriptModule *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + hr = get_typeinfo(IScriptModule_tid, &typeinfo); + if(SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); + ITypeInfo_Release(typeinfo); + } + + return hr; +} + +static HRESULT WINAPI ScriptModule_get_Name(IScriptModule *iface, BSTR *pbstrName) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%p)\n", This, pbstrName); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModule_get_CodeObject(IScriptModule *iface, IDispatch **ppdispObject) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%p)\n", This, ppdispObject); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModule_get_Procedures(IScriptModule *iface, IScriptProcedureCollection **ppdispProcedures) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%p)\n", This, ppdispProcedures); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModule_AddCode(IScriptModule *iface, BSTR code) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%s)\n", This, debugstr_w(code)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModule_Eval(IScriptModule *iface, BSTR expression, VARIANT *res) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%s, %p)\n", This, debugstr_w(expression), res); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModule_ExecuteStatement(IScriptModule *iface, BSTR statement) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%s)\n", This, debugstr_w(statement)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptModule_Run(IScriptModule *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res) +{ + ScriptModule *This = impl_from_IScriptModule(iface); + + FIXME("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res); + + return E_NOTIMPL; +} + +static const IScriptModuleVtbl ScriptModuleVtbl = { + ScriptModule_QueryInterface, + ScriptModule_AddRef, + ScriptModule_Release, + ScriptModule_GetTypeInfoCount, + ScriptModule_GetTypeInfo, + ScriptModule_GetIDsOfNames, + ScriptModule_Invoke, + ScriptModule_get_Name, + ScriptModule_get_CodeObject, + ScriptModule_get_Procedures, + ScriptModule_AddCode, + ScriptModule_Eval, + ScriptModule_ExecuteStatement, + ScriptModule_Run +}; + +static ScriptModule *alloc_module(ScriptControl *control, BSTR name) +{ + ScriptModule *module = heap_alloc_zero(sizeof(*module)); + + if (!module) return NULL; + if (name && !(module->name = SysAllocString(name))) + { + heap_free(module); + return NULL; + } + + /* Don't keep a ref unless it's externally held, to match Windows behavior */ + module->IScriptModule_iface.lpVtbl = &ScriptModuleVtbl; + module->control = control; + + return module; +} + static HRESULT WINAPI ScriptModuleCollection_QueryInterface(IScriptModuleCollection *iface, REFIID riid, void **ppv) { ScriptControl *This = impl_from_IScriptModuleCollection(iface); @@ -691,9 +944,12 @@ static HRESULT WINAPI ScriptModuleCollection_get_Count(IScriptModuleCollection * { ScriptControl *This = impl_from_IScriptModuleCollection(iface);
- FIXME("(%p)->(%p)\n", This, plCount); + TRACE("(%p)->(%p)\n", This, plCount);
- return E_NOTIMPL; + if (!plCount) return E_POINTER; + + *plCount = This->num_modules; + return S_OK; }
static HRESULT WINAPI ScriptModuleCollection_Add(IScriptModuleCollection *iface, BSTR name, @@ -864,6 +1120,7 @@ static ULONG WINAPI ScriptControl_Release(IScriptControl *iface) IOleClientSite_Release(This->site); if (This->host) release_script_engine(This->host); + release_modules(This); heap_free(This); }
@@ -965,6 +1222,21 @@ static HRESULT WINAPI ScriptControl_put_Language(IScriptControl *iface, BSTR lan This->host = NULL; }
+ release_modules(This); + + /* Alloc the global module */ + This->modules = heap_alloc(sizeof(*This->modules)); + if (!This->modules) return E_OUTOFMEMORY; + + This->modules[0] = alloc_module(This, NULL); + if (!This->modules[0]) + { + heap_free(This->modules); + This->modules = NULL; + return E_OUTOFMEMORY; + } + This->num_modules = 1; + if (!language) return S_OK;
@@ -1094,6 +1366,8 @@ static HRESULT WINAPI ScriptControl_get_Modules(IScriptControl *iface, IScriptMo
TRACE("(%p)->(%p)\n", This, p);
+ if (!This->modules) return E_FAIL; + *p = &This->IScriptModuleCollection_iface; IScriptControl_AddRef(iface); return S_OK; @@ -2201,6 +2475,7 @@ static HRESULT WINAPI ScriptControl_CreateInstance(IClassFactory *iface, IUnknow script_control->allow_ui = VARIANT_TRUE; script_control->use_safe_subset = VARIANT_FALSE; script_control->state = Initialized; + script_control->modules = NULL;
ConnectionPoint_Init(&script_control->cp_scsource, script_control, &DIID_DScriptControlSource); ConnectionPoint_Init(&script_control->cp_propnotif, script_control, &IID_IPropertyNotifySink);
Hi Gabriel,
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@gmail.com
We have to treat a ref count of 0 in a special manner when they are linked to the script control, because the modules must exist even when they have no external refs (as the script control can still use them).
However, as evidenced by the tests, they must*not* keep a ref to the script control in this case, as it would create a circular ref and make no sense, anyway.
The alternative solution to allocate separate data for modules would require copying the data to multiple places (one for Module Collection, and one for orphaned Modules) which is more complicated, in my opinion.
There must be a cleaner way. If you need too much special treatment for orphaned modules, it means that the design needs to be improved. One thing that is often useful for similar cases are weak reference: never reference control from module and make sure that control always invalidates the module->control pointer when it actually releases the object.
Thanks,
Jacek
On 29.05.2020 18:52, Jacek Caban wrote:
Hi Gabriel,
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@gmail.com
We have to treat a ref count of 0 in a special manner when they are linked to the script control, because the modules must exist even when they have no external refs (as the script control can still use them).
However, as evidenced by the tests, they must*not* keep a ref to the script control in this case, as it would create a circular ref and make no sense, anyway.
The alternative solution to allocate separate data for modules would require copying the data to multiple places (one for Module Collection, and one for orphaned Modules) which is more complicated, in my opinion.
There must be a cleaner way. If you need too much special treatment for orphaned modules, it means that the design needs to be improved. One thing that is often useful for similar cases are weak reference: never reference control from module and make sure that control always invalidates the module->control pointer when it actually releases the object.
Or maybe script module should just store ScriptHost reference.
Jacek
Hi Jacek,
On 29/05/2020 21:46, Jacek Caban wrote:
On 29.05.2020 18:52, Jacek Caban wrote:
Hi Gabriel,
On 26.05.2020 14:41, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@gmail.com
We have to treat a ref count of 0 in a special manner when they are linked to the script control, because the modules must exist even when they have no external refs (as the script control can still use them).
However, as evidenced by the tests, they must*not* keep a ref to the script control in this case, as it would create a circular ref and make no sense, anyway.
The alternative solution to allocate separate data for modules would require copying the data to multiple places (one for Module Collection, and one for orphaned Modules) which is more complicated, in my opinion.
There must be a cleaner way. If you need too much special treatment for orphaned modules, it means that the design needs to be improved. One thing that is often useful for similar cases are weak reference: never reference control from module and make sure that control always invalidates the module->control pointer when it actually releases the object.
Ok so, I'll give some examples to illustrate why it has to be done either this way (with refcount checks), or by allocating separate objects for modules when requesting an external ref (on top of the objects stored in the collection).
I will send a patch series with the latter (separate objects) to show the alternative solution, but it does complicate the code a bit and the enumerator as I mentioned initially... that's what I meant the first time and why I went with the refcount checks.
So, the tests show that the behavior is like this:
Module Collection is tied to Control. Obtaining a ref to the collection, then updating the control via put_Language, and using the older collection ref will refer to the new control (i.e. if it had 5 modules, then put_Language resets it back to 1, the old collection will also report 1 module and refer to it). So keeping it on the control is the easiest as it avoids pointless allocations in the code.
Modules are, obviously, tied to the collection (in an array for random access by index). A collection needs module names, for example. The control also needs access to the first (global) module to simplify the code. So modules always exist after put_Language, even if there's no explicit ref to them. In this case, they can't ref anything since the caller might only hold a ref to the control.
However, consider what happens if the caller/user obtains a ref to a module. First of all, this keeps the control alive, so releasing the ref to the script control in this case will not destroy it, you can still use the module ref to do stuff with it. So, a module obtained externally needs to hold a ref to the control.
Secondly, put_Language detaches the modules from the control (or collection). They lose their ref to the control, and operations on them return E_FAIL.
What I did with this patch series is to hold a refcount of 0 when no external (caller/user) refs are held to the module, but only the control/collection can access them (and their names). Increasing refcount to above 0 will obtain a ref to the control, since it means they have an external ref.
put_Language would detach them from the module, releasing the control ref only if the ref was above 0 (i.e. they had external ref), otherwise it would simply destroy the module, as it means they had no external ref.
The alternative I will send later works like this: keep an array of modules (without the interface) that keeps the module data in the control/collection. When an external ref is requested, we allocate (if it's not already, otherwise we share it) a module interface and have it keep a ref to the control and point to the module data in the array. put_Language would detach all the allocated objects from the control.
The end result is the same, but personally, I believe it introduces more complexity to the code, as we have more allocations to deal with, and more error handling (even in the enumerator). :-/
That's why I preferred the refcount check implementation, but anyway, I'll do the alternative so you can see how it looks like and if you prefer it.
Or maybe script module should just store ScriptHost reference.
It wouldn't change much and we need a ref to the control anyway (to start the script for example). Note that modules that haven't been detached by put_Language must keep the control alive if they have external refs.
Thanks, Gabriel
On 01.06.2020 14:21, Gabriel Ivăncescu wrote:
Or maybe script module should just store ScriptHost reference.
It wouldn't change much and we need a ref to the control anyway (to start the script for example).
I would need a deeper look to be sure, but it seems to me that it would solve all your problems by not having to store control reference in modules. Starting script doesn't really need control. It currently needs it only to access script host and check control state. Control state could be reworked to not require it (test_State() on emulated script engine would be interesting for such rework).
Thanks,
Jacek
Hi Gabriel,
On 01.06.2020 16:51, Jacek Caban wrote:
On 01.06.2020 14:21, Gabriel Ivăncescu wrote:
Or maybe script module should just store ScriptHost reference.
It wouldn't change much and we need a ref to the control anyway (to start the script for example).
I would need a deeper look to be sure, but it seems to me that it would solve all your problems by not having to store control reference in modules. Starting script doesn't really need control. It currently needs it only to access script host and check control state. Control state could be reworked to not require it (test_State() on emulated script engine would be interesting for such rework).
I tested it myself and, as suspected, we don't need control object to start the script engine. I send patches for that. I hope it helps.
Jacek
Hi Jacek,
On 12/06/2020 18:14, Jacek Caban wrote:
Hi Gabriel,
On 01.06.2020 16:51, Jacek Caban wrote:
On 01.06.2020 14:21, Gabriel Ivăncescu wrote:
Or maybe script module should just store ScriptHost reference.
It wouldn't change much and we need a ref to the control anyway (to start the script for example).
I would need a deeper look to be sure, but it seems to me that it would solve all your problems by not having to store control reference in modules. Starting script doesn't really need control. It currently needs it only to access script host and check control state. Control state could be reworked to not require it (test_State() on emulated script engine would be interesting for such rework).
I tested it myself and, as suspected, we don't need control object to start the script engine. I send patches for that. I hope it helps.
Jacek
On second thought, it actually might help with the circular refcounting—because the script engine is released in put_Language, so we can just remove the weak refs to the host from the detached modules.
I'm not 100% sure yet, will do more testing. But I hope it works to pass all tests once I rebase it.
Thanks, Gabriel
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 6bf41ff..91e6dfa 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -71,6 +71,7 @@ typedef struct ScriptModule {
BSTR name; ScriptControl *control; + IDispatch *script_dispatch; } ScriptModule;
typedef struct ScriptHost { @@ -81,7 +82,6 @@ typedef struct ScriptHost {
IActiveScript *script; IActiveScriptParse *parse; - IDispatch *script_dispatch; SCRIPTSTATE script_state; CLSID clsid;
@@ -220,14 +220,15 @@ static struct named_item *host_get_named_item(ScriptHost *host, const WCHAR *nam return NULL; }
-static HRESULT get_script_dispatch(struct ScriptControl *control, IDispatch **disp) +static HRESULT get_script_dispatch(ScriptModule *module, IDispatch **disp) { - if (!control->host->script_dispatch) + if (!module->script_dispatch) { - HRESULT hr = IActiveScript_GetScriptDispatch(control->host->script, NULL, &control->host->script_dispatch); + HRESULT hr = IActiveScript_GetScriptDispatch(module->control->host->script, + module->name, &module->script_dispatch); if (FAILED(hr)) return hr; } - *disp = control->host->script_dispatch; + *disp = module->script_dispatch; return S_OK; }
@@ -384,10 +385,7 @@ static void release_script_engine(ScriptHost *host)
if (host->parse) IActiveScriptParse_Release(host->parse); - if (host->script_dispatch) - IDispatch_Release(host->script_dispatch);
- host->script_dispatch = NULL; host->parse = NULL; host->script = NULL;
@@ -594,6 +592,8 @@ static const IServiceProviderVtbl ServiceProviderVtbl = {
static void destroy_module(ScriptModule *module) { + if (module->script_dispatch) + IDispatch_Release(module->script_dispatch); SysFreeString(module->name); heap_free(module); } @@ -994,7 +994,6 @@ static HRESULT init_script_host(const CLSID *clsid, ScriptHost **ret) host->ref = 1; host->script = NULL; host->parse = NULL; - host->script_dispatch = NULL; host->clsid = *clsid; list_init(&host->named_items);
@@ -1505,11 +1504,12 @@ static HRESULT WINAPI ScriptControl_Run(IScriptControl *iface, BSTR procedure_na V_VT(res) = VT_EMPTY; if (sa->cDims == 0) return DISP_E_BADINDEX; if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE; + if (!This->modules) return E_FAIL;
hr = start_script(This); if (FAILED(hr)) return hr;
- hr = get_script_dispatch(This, &disp); + hr = get_script_dispatch(This->modules[0], &disp); if (FAILED(hr)) return hr;
hr = IDispatch_GetIDsOfNames(disp, &IID_NULL, &procedure_name, 1, LOCALE_USER_DEFAULT, &dispid);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 91e6dfa..393f39e 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -255,15 +255,17 @@ static HRESULT start_script(struct ScriptControl *control) return hr; }
-static HRESULT parse_script_text(ScriptControl *control, BSTR script_text, DWORD flag, VARIANT *res) +static HRESULT parse_script_text(ScriptModule *module, BSTR script_text, DWORD flag, VARIANT *res) { EXCEPINFO excepinfo; HRESULT hr;
- hr = start_script(control); + if (!module->control) return E_FAIL; + + hr = start_script(module->control); if (FAILED(hr)) return hr;
- hr = IActiveScriptParse_ParseScriptText(control->host->parse, script_text, NULL, + hr = IActiveScriptParse_ParseScriptText(module->control->host->parse, script_text, module->name, NULL, NULL, 0, 1, flag, res, &excepinfo); /* FIXME: more error handling */ return hr; @@ -761,27 +763,31 @@ static HRESULT WINAPI ScriptModule_AddCode(IScriptModule *iface, BSTR code) { ScriptModule *This = impl_from_IScriptModule(iface);
- FIXME("(%p)->(%s)\n", This, debugstr_w(code)); + TRACE("(%p)->(%s)\n", This, debugstr_w(code));
- return E_NOTIMPL; + return parse_script_text(This, code, SCRIPTTEXT_ISVISIBLE, NULL); }
static HRESULT WINAPI ScriptModule_Eval(IScriptModule *iface, BSTR expression, VARIANT *res) { ScriptModule *This = impl_from_IScriptModule(iface);
- FIXME("(%p)->(%s, %p)\n", This, debugstr_w(expression), res); + TRACE("(%p)->(%s, %p)\n", This, debugstr_w(expression), res);
- return E_NOTIMPL; + if (!res) + return E_POINTER; + V_VT(res) = VT_EMPTY; + + return parse_script_text(This, expression, SCRIPTTEXT_ISEXPRESSION, res); }
static HRESULT WINAPI ScriptModule_ExecuteStatement(IScriptModule *iface, BSTR statement) { ScriptModule *This = impl_from_IScriptModule(iface);
- FIXME("(%p)->(%s)\n", This, debugstr_w(statement)); + TRACE("(%p)->(%s)\n", This, debugstr_w(statement));
- return E_NOTIMPL; + return parse_script_text(This, statement, 0, NULL); }
static HRESULT WINAPI ScriptModule_Run(IScriptModule *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res) @@ -1460,7 +1466,9 @@ static HRESULT WINAPI ScriptControl_AddCode(IScriptControl *iface, BSTR code)
TRACE("(%p)->(%s).\n", This, debugstr_w(code));
- return parse_script_text(This, code, SCRIPTTEXT_ISVISIBLE, NULL); + if (!This->modules) + return E_FAIL; + return parse_script_text(This->modules[0], code, SCRIPTTEXT_ISVISIBLE, NULL); }
static HRESULT WINAPI ScriptControl_Eval(IScriptControl *iface, BSTR expression, VARIANT *res) @@ -1473,7 +1481,9 @@ static HRESULT WINAPI ScriptControl_Eval(IScriptControl *iface, BSTR expression, return E_POINTER; V_VT(res) = VT_EMPTY;
- return parse_script_text(This, expression, SCRIPTTEXT_ISEXPRESSION, res); + if (!This->modules) + return E_FAIL; + return parse_script_text(This->modules[0], expression, SCRIPTTEXT_ISEXPRESSION, res); }
static HRESULT WINAPI ScriptControl_ExecuteStatement(IScriptControl *iface, BSTR statement) @@ -1482,7 +1492,9 @@ static HRESULT WINAPI ScriptControl_ExecuteStatement(IScriptControl *iface, BSTR
TRACE("(%p)->(%s)\n", This, debugstr_w(statement));
- return parse_script_text(This, statement, 0, NULL); + if (!This->modules) + return E_FAIL; + return parse_script_text(This->modules[0], statement, 0, NULL); }
static HRESULT WINAPI ScriptControl_Run(IScriptControl *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 393f39e..3cb0215 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -736,9 +736,13 @@ static HRESULT WINAPI ScriptModule_get_Name(IScriptModule *iface, BSTR *pbstrNam { ScriptModule *This = impl_from_IScriptModule(iface);
- FIXME("(%p)->(%p)\n", This, pbstrName); + TRACE("(%p)->(%p)\n", This, pbstrName);
- return E_NOTIMPL; + if (!pbstrName) return E_POINTER; + if (!This->control) return E_FAIL; + + *pbstrName = SysAllocString(This->name ? This->name : L"Global"); + return *pbstrName ? S_OK : E_OUTOFMEMORY; }
static HRESULT WINAPI ScriptModule_get_CodeObject(IScriptModule *iface, IDispatch **ppdispObject)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 96 +++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 45 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 3cb0215..3cbc7a4 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -271,6 +271,56 @@ static HRESULT parse_script_text(ScriptModule *module, BSTR script_text, DWORD f return hr; }
+static HRESULT run_procedure(ScriptModule *module, BSTR procedure_name, SAFEARRAY *args, VARIANT *res) +{ + IDispatchEx *dispex; + IDispatch *disp; + DISPPARAMS dp; + DISPID dispid; + HRESULT hr; + UINT i; + + hr = start_script(module->control); + if (FAILED(hr)) return hr; + + hr = get_script_dispatch(module, &disp); + if (FAILED(hr)) return hr; + + hr = IDispatch_GetIDsOfNames(disp, &IID_NULL, &procedure_name, 1, LOCALE_USER_DEFAULT, &dispid); + if (FAILED(hr)) return hr; + + dp.cArgs = args->rgsabound[0].cElements; + dp.rgdispidNamedArgs = NULL; + dp.cNamedArgs = 0; + dp.rgvarg = heap_alloc(dp.cArgs * sizeof(*dp.rgvarg)); + if (!dp.rgvarg) return E_OUTOFMEMORY; + + hr = SafeArrayLock(args); + if (SUCCEEDED(hr)) + { + /* The DISPPARAMS are stored in reverse order */ + for (i = 0; i < dp.cArgs; i++) + dp.rgvarg[i] = *(VARIANT*)((char*)(args->pvData) + (dp.cArgs - i - 1) * args->cbElements); + SafeArrayUnlock(args); + + hr = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if (FAILED(hr)) + { + hr = IDispatch_Invoke(disp, dispid, &IID_NULL, LOCALE_USER_DEFAULT, + DISPATCH_METHOD, &dp, res, NULL, NULL); + } + else + { + hr = IDispatchEx_InvokeEx(dispex, dispid, LOCALE_USER_DEFAULT, + DISPATCH_METHOD, &dp, res, NULL, NULL); + IDispatchEx_Release(dispex); + } + } + heap_free(dp.rgvarg); + + return hr; +} + static inline ScriptControl *impl_from_IScriptControl(IScriptControl *iface) { return CONTAINING_RECORD(iface, ScriptControl, IScriptControl_iface); @@ -1504,13 +1554,7 @@ static HRESULT WINAPI ScriptControl_ExecuteStatement(IScriptControl *iface, BSTR static HRESULT WINAPI ScriptControl_Run(IScriptControl *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res) { ScriptControl *This = impl_from_IScriptControl(iface); - IDispatchEx *dispex; - IDispatch *disp; SAFEARRAY *sa; - DISPPARAMS dp; - DISPID dispid; - HRESULT hr; - UINT i;
TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res);
@@ -1522,45 +1566,7 @@ static HRESULT WINAPI ScriptControl_Run(IScriptControl *iface, BSTR procedure_na if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE; if (!This->modules) return E_FAIL;
- hr = start_script(This); - if (FAILED(hr)) return hr; - - hr = get_script_dispatch(This->modules[0], &disp); - if (FAILED(hr)) return hr; - - hr = IDispatch_GetIDsOfNames(disp, &IID_NULL, &procedure_name, 1, LOCALE_USER_DEFAULT, &dispid); - if (FAILED(hr)) return hr; - - dp.cArgs = sa->rgsabound[0].cElements; - dp.rgdispidNamedArgs = NULL; - dp.cNamedArgs = 0; - dp.rgvarg = heap_alloc(dp.cArgs * sizeof(*dp.rgvarg)); - if (!dp.rgvarg) return E_OUTOFMEMORY; - - hr = SafeArrayLock(sa); - if (SUCCEEDED(hr)) - { - /* The DISPPARAMS are stored in reverse order */ - for (i = 0; i < dp.cArgs; i++) - dp.rgvarg[i] = *(VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i - 1) * sa->cbElements); - SafeArrayUnlock(sa); - - hr = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); - if (FAILED(hr)) - { - hr = IDispatch_Invoke(disp, dispid, &IID_NULL, LOCALE_USER_DEFAULT, - DISPATCH_METHOD, &dp, res, NULL, NULL); - } - else - { - hr = IDispatchEx_InvokeEx(dispex, dispid, LOCALE_USER_DEFAULT, - DISPATCH_METHOD, &dp, res, NULL, NULL); - IDispatchEx_Release(dispex); - } - } - heap_free(dp.rgvarg); - - return hr; + return run_procedure(This->modules[0], procedure_name, sa, res); }
static const IScriptControlVtbl ScriptControlVtbl = {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 3cbc7a4..d5fdca7 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -847,10 +847,19 @@ static HRESULT WINAPI ScriptModule_ExecuteStatement(IScriptModule *iface, BSTR s static HRESULT WINAPI ScriptModule_Run(IScriptModule *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res) { ScriptModule *This = impl_from_IScriptModule(iface); + SAFEARRAY *sa;
- FIXME("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res); + TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res);
- return E_NOTIMPL; + if (!parameters || !res) return E_POINTER; + if (!(sa = *parameters)) return E_POINTER; + + V_VT(res) = VT_EMPTY; + if (sa->cDims == 0) return DISP_E_BADINDEX; + if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE; + if (!This->control) return E_FAIL; + + return run_procedure(This, procedure_name, sa, res); }
static const IScriptModuleVtbl ScriptModuleVtbl = {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 59 +++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 24 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index d5fdca7..0745e09 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -255,6 +255,40 @@ static HRESULT start_script(struct ScriptControl *control) return hr; }
+static HRESULT add_script_object(ScriptHost *host, BSTR name, IDispatch *object, DWORD flags) +{ + struct named_item *item; + HRESULT hr; + + if (host_get_named_item(host, name)) + return E_INVALIDARG; + + item = heap_alloc(sizeof(*item)); + if (!item) + return E_OUTOFMEMORY; + + item->name = SysAllocString(name); + if (!item->name) + { + heap_free(item); + return E_OUTOFMEMORY; + } + IDispatch_AddRef(item->disp = object); + list_add_tail(&host->named_items, &item->entry); + + hr = IActiveScript_AddNamedItem(host->script, name, flags); + if (FAILED(hr)) + { + list_remove(&item->entry); + IDispatch_Release(item->disp); + SysFreeString(item->name); + heap_free(item); + return hr; + } + + return hr; +} + static HRESULT parse_script_text(ScriptModule *module, BSTR script_text, DWORD flag, VARIANT *res) { EXCEPINFO excepinfo; @@ -1473,8 +1507,6 @@ static HRESULT WINAPI ScriptControl_AddObject(IScriptControl *iface, BSTR name, { ScriptControl *This = impl_from_IScriptControl(iface); DWORD flags = SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE; - struct named_item *item; - HRESULT hr;
TRACE("(%p)->(%s %p %x)\n", This, debugstr_w(name), object, add_members);
@@ -1484,30 +1516,9 @@ static HRESULT WINAPI ScriptControl_AddObject(IScriptControl *iface, BSTR name, if (!This->host) return E_FAIL;
- if (host_get_named_item(This->host, name)) - return E_INVALIDARG; - - item = heap_alloc(sizeof(*item)); - if (!item) - return E_OUTOFMEMORY; - - item->name = SysAllocString(name); - IDispatch_AddRef(item->disp = object); - list_add_tail(&This->host->named_items, &item->entry); - if (add_members) flags |= SCRIPTITEM_GLOBALMEMBERS; - hr = IActiveScript_AddNamedItem(This->host->script, name, flags); - if (FAILED(hr)) { - list_remove(&item->entry); - IDispatch_Release(item->disp); - SysFreeString(item->name); - heap_free(item); - return hr; - } - - - return hr; + return add_script_object(This->host, name, object, flags); }
static HRESULT WINAPI ScriptControl_Reset(IScriptControl *iface)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 41 ++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 0745e09..e3b7b86 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -704,6 +704,23 @@ static void release_modules(ScriptControl *control) heap_free(control->modules); }
+static UINT find_module(ScriptControl *control, BSTR name) +{ + UINT i, len = SysStringLen(name); + + if (len == sizeof("Global") - 1 && !wcsicmp(name, L"Global")) + return 0; + + for (i = 1; i < control->num_modules; i++) + { + BSTR modname = control->modules[i]->name; + if (SysStringLen(modname) == len && !wcsicmp(name, modname)) + return i; + } + + return ~0; +} + static HRESULT WINAPI ScriptModule_QueryInterface(IScriptModule *iface, REFIID riid, void **ppv) { ScriptModule *This = impl_from_IScriptModule(iface); @@ -1037,10 +1054,30 @@ static HRESULT WINAPI ScriptModuleCollection_get_Item(IScriptModuleCollection *i IScriptModule **ppmod) { ScriptControl *This = impl_from_IScriptModuleCollection(iface); + HRESULT hr; + UINT i;
- FIXME("(%p)->(%s %p)\n", This, wine_dbgstr_variant(&index), ppmod); + TRACE("(%p)->(%s %p)\n", This, wine_dbgstr_variant(&index), ppmod);
- return E_NOTIMPL; + if (!ppmod) return E_POINTER; + + if (V_VT(&index) == VT_BSTR) + { + i = find_module(This, V_BSTR(&index)); + if (i == ~0) return CTL_E_ILLEGALFUNCTIONCALL; + } + else + { + hr = VariantChangeType(&index, &index, 0, VT_INT); + if (FAILED(hr)) return hr; + + i = V_INT(&index) - 1; + if (i >= This->num_modules) return 0x800a0009; + } + + *ppmod = &This->modules[i]->IScriptModule_iface; + IScriptModule_AddRef(*ppmod); + return S_OK; }
static HRESULT WINAPI ScriptModuleCollection_get_Count(IScriptModuleCollection *iface, LONG *plCount)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 42 ++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index e3b7b86..f0a4853 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -197,6 +197,11 @@ static void release_typelib(void) ITypeLib_Release(typelib); }
+static inline BOOL is_power_of_2(unsigned x) +{ + return !(x & (x - 1)); +} + static void clear_named_items(ScriptHost *host) { struct named_item *item, *item1; @@ -1096,10 +1101,43 @@ static HRESULT WINAPI ScriptModuleCollection_Add(IScriptModuleCollection *iface, VARIANT *object, IScriptModule **ppmod) { ScriptControl *This = impl_from_IScriptModuleCollection(iface); + ScriptModule *mod, **mods; + HRESULT hr;
- FIXME("(%p)->(%s %s %p)\n", This, wine_dbgstr_w(name), wine_dbgstr_variant(object), ppmod); + TRACE("(%p)->(%s %s %p)\n", This, wine_dbgstr_w(name), wine_dbgstr_variant(object), ppmod);
- return E_NOTIMPL; + if (!ppmod) return E_POINTER; + if (!name || V_VT(object) != VT_DISPATCH || find_module(This, name) != ~0) + return E_INVALIDARG; + if (!This->host) return E_FAIL; + + /* See if we need to grow the array */ + if (is_power_of_2(This->num_modules)) + { + mods = heap_realloc(This->modules, This->num_modules * 2 * sizeof(*This->modules)); + if (!mods) return E_OUTOFMEMORY; + This->modules = mods; + } + + mod = alloc_module(This, name); + if (!mod) return E_OUTOFMEMORY; + + /* If no object, Windows only calls AddNamedItem without adding a NULL object */ + if (V_DISPATCH(object)) + hr = add_script_object(This->host, name, V_DISPATCH(object), 0); + else + hr = IActiveScript_AddNamedItem(This->host->script, name, SCRIPTITEM_CODEONLY); + + if (FAILED(hr)) + { + destroy_module(mod); + return hr; + } + This->modules[This->num_modules++] = mod; + + *ppmod = &mod->IScriptModule_iface; + IScriptModule_AddRef(*ppmod); + return S_OK; }
static const IScriptModuleCollectionVtbl ScriptModuleCollectionVtbl = {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 150 ++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index f0a4853..a691413 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -65,6 +65,13 @@ struct named_item { IDispatch *disp; };
+struct script_mod_enum { + IEnumVARIANT IEnumVARIANT_iface; + LONG ref; + UINT pos; + ScriptControl *control; +}; + typedef struct ScriptModule { IScriptModule IScriptModule_iface; LONG ref; @@ -430,6 +437,11 @@ static inline ScriptHost *impl_from_IServiceProvider(IServiceProvider *iface) return CONTAINING_RECORD(iface, ScriptHost, IServiceProvider_iface); }
+static inline struct script_mod_enum *script_mod_enum_from_IEnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, struct script_mod_enum, IEnumVARIANT_iface); +} + /* IActiveScriptSite */ static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv) { @@ -935,6 +947,127 @@ static const IScriptModuleVtbl ScriptModuleVtbl = { ScriptModule_Run };
+static HRESULT WINAPI script_mod_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + struct script_mod_enum *This = script_mod_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 script_mod_enum_AddRef(IEnumVARIANT *iface) +{ + struct script_mod_enum *This = script_mod_enum_from_IEnumVARIANT(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI script_mod_enum_Release(IEnumVARIANT *iface) +{ + struct script_mod_enum *This = script_mod_enum_from_IEnumVARIANT(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (!ref) + { + IScriptControl_Release(&This->control->IScriptControl_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI script_mod_enum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + struct script_mod_enum *This = script_mod_enum_from_IEnumVARIANT(iface); + UINT i, num; + + TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched); + + if (!rgVar) return E_POINTER; + + num = min(celt, This->control->num_modules - 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 script_mod_enum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + struct script_mod_enum *This = script_mod_enum_from_IEnumVARIANT(iface); + + TRACE("(%p)->(%u)\n", This, celt); + + if (This->control->num_modules - This->pos < celt) + { + This->pos = This->control->num_modules; + return S_FALSE; + } + This->pos += celt; + return S_OK; +} + +static HRESULT WINAPI script_mod_enum_Reset(IEnumVARIANT *iface) +{ + struct script_mod_enum *This = script_mod_enum_from_IEnumVARIANT(iface); + + TRACE("(%p)\n", This); + + This->pos = 0; + return S_OK; +} + +static HRESULT WINAPI script_mod_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + struct script_mod_enum *This = script_mod_enum_from_IEnumVARIANT(iface); + struct script_mod_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; + IScriptControl_AddRef(&This->control->IScriptControl_iface); + + *ppEnum = &clone->IEnumVARIANT_iface; + return S_OK; +} + +static const IEnumVARIANTVtbl script_mod_enum_vtbl = { + script_mod_enum_QueryInterface, + script_mod_enum_AddRef, + script_mod_enum_Release, + script_mod_enum_Next, + script_mod_enum_Skip, + script_mod_enum_Reset, + script_mod_enum_Clone +}; + static ScriptModule *alloc_module(ScriptControl *control, BSTR name) { ScriptModule *module = heap_alloc_zero(sizeof(*module)); @@ -1049,10 +1182,23 @@ static HRESULT WINAPI ScriptModuleCollection_Invoke(IScriptModuleCollection *ifa static HRESULT WINAPI ScriptModuleCollection_get__NewEnum(IScriptModuleCollection *iface, IUnknown **ppenumContexts) { ScriptControl *This = impl_from_IScriptModuleCollection(iface); + struct script_mod_enum *mod_enum;
- FIXME("(%p)->(%p)\n", This, ppenumContexts); + TRACE("(%p)->(%p)\n", This, ppenumContexts);
- return E_NOTIMPL; + if (!ppenumContexts) return E_POINTER; + + if (!(mod_enum = heap_alloc(sizeof(*mod_enum)))) + return E_OUTOFMEMORY; + + mod_enum->IEnumVARIANT_iface.lpVtbl = &script_mod_enum_vtbl; + mod_enum->ref = 1; + mod_enum->pos = 0; + mod_enum->control = This; + IScriptControl_AddRef(&This->IScriptControl_iface); + + *ppenumContexts = (IUnknown*)&mod_enum->IEnumVARIANT_iface; + return S_OK; }
static HRESULT WINAPI ScriptModuleCollection_get_Item(IScriptModuleCollection *iface, VARIANT index,
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/tests/msscript.c | 360 ++++++++++++++++++++++++++++- 1 file changed, 356 insertions(+), 4 deletions(-)
diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c index 22db28e..2466547 100644 --- a/dlls/msscript.ocx/tests/msscript.c +++ b/dlls/msscript.ocx/tests/msscript.c @@ -150,6 +150,7 @@ static HRESULT WINAPI ActiveScriptParse_AddScriptlet(IActiveScriptParse *iface, return E_NOTIMPL; }
+static const WCHAR *parse_item_name; static DWORD parse_flags = 0;
static HRESULT WINAPI ActiveScriptParse_ParseScriptText(IActiveScriptParse *iface, @@ -158,7 +159,8 @@ static HRESULT WINAPI ActiveScriptParse_ParseScriptText(IActiveScriptParse *ifac DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo) { ok(!!pstrCode, "got wrong pointer: %p.\n", pstrCode); - ok(!pstrItemName, "got wrong pointer: %p.\n", pstrItemName); + ok(!lstrcmpW(pstrItemName, parse_item_name), "got wrong item name: %s (expected %s).\n", + wine_dbgstr_w(pstrItemName), wine_dbgstr_w(parse_item_name)); ok(!punkContext, "got wrong pointer: %p.\n", punkContext); ok(!pstrDelimiter, "got wrong pointer: %p.\n", pstrDelimiter); ok(!dwSourceContextCookie, "got wrong value: %s.\n", wine_dbgstr_longlong(dwSourceContextCookie)); @@ -522,12 +524,13 @@ static HRESULT WINAPI ActiveScript_Close(IActiveScript *iface) return E_NOTIMPL; }
+static const WCHAR *AddNamedItem_expected_name; +static DWORD AddNamedItem_expected_flags; static HRESULT WINAPI ActiveScript_AddNamedItem(IActiveScript *iface, LPCOLESTR name, DWORD flags) { - static const WCHAR oW[] = {'o',0}; CHECK_EXPECT(AddNamedItem); - ok(!lstrcmpW(name, oW), "got name %s\n", wine_dbgstr_w(name)); - ok(flags == (SCRIPTITEM_ISVISIBLE|SCRIPTITEM_ISSOURCE|SCRIPTITEM_GLOBALMEMBERS), "got flags %#x\n", flags); + ok(!lstrcmpW(name, AddNamedItem_expected_name), "got name %s\n", wine_dbgstr_w(name)); + ok(flags == AddNamedItem_expected_flags, "got flags %#x\n", flags); return S_OK; }
@@ -1413,6 +1416,8 @@ static void test_AddObject(void) ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
SET_EXPECT(AddNamedItem); + AddNamedItem_expected_name = objname; + AddNamedItem_expected_flags = SCRIPTITEM_ISVISIBLE|SCRIPTITEM_ISSOURCE|SCRIPTITEM_GLOBALMEMBERS; hr = IScriptControl_AddObject(sc, objname, &testdisp, VARIANT_TRUE); ok(hr == S_OK, "got 0x%08x\n", hr); CHECK_CALLED(AddNamedItem); @@ -1693,6 +1698,7 @@ static void test_IScriptControl_Eval(void)
SET_EXPECT(SetScriptState_STARTED); SET_EXPECT(ParseScriptText); + parse_item_name = NULL; parse_flags = SCRIPTTEXT_ISEXPRESSION; script_str = SysAllocString(L"var1 = 1 + 1"); V_VT(&var) = VT_NULL; @@ -1806,6 +1812,7 @@ static void test_IScriptControl_AddCode(void)
SET_EXPECT(SetScriptState_STARTED); SET_EXPECT(ParseScriptText); + parse_item_name = NULL; parse_flags = SCRIPTTEXT_ISVISIBLE; code_str = SysAllocString(L"1 + 1"); hr = IScriptControl_AddCode(sc, code_str); @@ -1823,6 +1830,7 @@ static void test_IScriptControl_AddCode(void)
/* Call Eval() after AddCode() for checking if it will call SetScriptState() again. */ SET_EXPECT(ParseScriptText); + parse_item_name = NULL; parse_flags = SCRIPTTEXT_ISEXPRESSION; code_str = SysAllocString(L"var2 = 10 + var1"); V_VT(&var) = VT_NULL; @@ -1905,6 +1913,7 @@ static void test_IScriptControl_ExecuteStatement(void)
SET_EXPECT(SetScriptState_STARTED); SET_EXPECT(ParseScriptText); + parse_item_name = NULL; parse_flags = 0; str = SysAllocString(L"1 + 1"); hr = IScriptControl_ExecuteStatement(sc, str); @@ -2125,6 +2134,348 @@ static void test_IScriptControl_Run(void) SafeArrayDestroy(params); }
+static void test_IScriptControl_get_Modules(void) +{ + IEnumVARIANT *enumvar, *enumvar2; + IScriptModuleCollection *mods; + VARIANT var, vars[3]; + IScriptModule *mod; + IScriptControl *sc; + IUnknown *unknown; + 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); + ok(hr == S_OK, "Failed to create IScriptControl interface: 0x%08x.\n", hr); + + hr = IScriptControl_get_Modules(sc, &mods); + ok(hr == E_FAIL, "IScriptControl_get_Modules returned: 0x%08x.\n", hr); + + str = SysAllocString(L"jscript"); + hr = IScriptControl_put_Language(sc, str); + ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr); + SysFreeString(str); + + hr = IScriptControl_get_Modules(sc, &mods); + ok(hr == S_OK, "IScriptControl_get_Modules failed: 0x%08x.\n", hr); + + hr = IScriptModuleCollection_get_Count(mods, NULL); + ok(hr == E_POINTER, "IScriptModuleCollection_get_Count returned: 0x%08x.\n", hr); + hr = IScriptModuleCollection_get_Count(mods, &count); + ok(hr == S_OK, "IScriptModuleCollection_get_Count failed: 0x%08x.\n", hr); + ok(count == 1, "count is not 1, got %d.\n", count); + + V_VT(&var) = VT_I4; + V_I4(&var) = -1; + hr = IScriptModuleCollection_get_Item(mods, var, NULL); + ok(hr == E_POINTER, "IScriptModuleCollection_get_Item returned: 0x%08x.\n", hr); + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == 0x800a0009, "IScriptModuleCollection_get_Item returned: 0x%08x.\n", hr); + + V_VT(&var) = VT_EMPTY; + str = SysAllocString(L"foobar"); + hr = IScriptModuleCollection_Add(mods, str, &var, &mod); + ok(hr == E_INVALIDARG, "IScriptModuleCollection_Add returned: 0x%08x.\n", hr); + hr = IScriptModuleCollection_Add(mods, str, &var, NULL); + ok(hr == E_POINTER, "IScriptModuleCollection_Add returned: 0x%08x.\n", hr); + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = NULL; + hr = IScriptModuleCollection_Add(mods, NULL, &var, &mod); + ok(hr == E_INVALIDARG, "IScriptModuleCollection_Add returned: 0x%08x.\n", hr); + hr = IScriptModuleCollection_Add(mods, str, &var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_Add failed: 0x%08x.\n", hr); + 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); + IScriptModule_Release(mod); + SysFreeString(str); + + /* Adding a module with the same name is invalid (case insensitive) */ + str = SysAllocString(L"FooBar"); + hr = IScriptModuleCollection_Add(mods, str, &var, &mod); + ok(hr == E_INVALIDARG, "IScriptModuleCollection_Add failed: 0x%08x.\n", hr); + SysFreeString(str); + + hr = IScriptModuleCollection_get_Count(mods, &count); + ok(hr == S_OK, "IScriptModuleCollection_get_Count failed: 0x%08x.\n", hr); + ok(count == 3, "count is not 3, got %d.\n", count); + V_VT(&var) = VT_I4; + V_I4(&var) = count + 1; + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == 0x800a0009, "IScriptModuleCollection_get_Item returned: 0x%08x.\n", hr); + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(L"non-existent module"); + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == CTL_E_ILLEGALFUNCTIONCALL, "IScriptModuleCollection_get_Item returned: 0x%08x.\n", hr); + ok(V_VT(&var) == VT_BSTR, "var type not BSTR, got %d.\n", V_VT(&var)); + VariantClear(&var); + + V_VT(&var) = VT_I4; + V_I4(&var) = 1; + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_get_Item failed: 0x%08x.\n", hr); + hr = IScriptModule_get_Name(mod, NULL); + ok(hr == E_POINTER, "IScriptModule_get_Name returned: 0x%08x.\n", hr); + hr = IScriptModule_get_Name(mod, &str); + ok(hr == S_OK, "IScriptModule_get_Name failed: 0x%08x.\n", hr); + ok(!lstrcmpW(str, L"Global"), "got %s.\n", wine_dbgstr_w(str)); + SysFreeString(str); + str = SysAllocString(L"function add(a, b) { return a + b; }\n"); + hr = IScriptModule_AddCode(mod, str); + ok(hr == S_OK, "IScriptModule_AddCode failed: 0x%08x.\n", hr); + IScriptModule_Release(mod); + SysFreeString(str); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(L"some other module"); + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_get_Item failed: 0x%08x.\n", hr); + ok(V_VT(&var) == VT_BSTR, "var type not BSTR, got %d.\n", V_VT(&var)); + VariantClear(&var); + hr = IScriptModule_get_Name(mod, &str); + ok(hr == S_OK, "IScriptModule_get_Name failed: 0x%08x.\n", hr); + ok(!lstrcmpW(str, L"some other Module"), "got %s.\n", wine_dbgstr_w(str)); + IScriptModule_Release(mod); + SysFreeString(str); + + V_VT(&var) = VT_R8; + V_R8(&var) = 2.0; + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_get_Item failed: 0x%08x.\n", hr); + hr = IScriptModule_get_Name(mod, &str); + ok(hr == S_OK, "IScriptModule_get_Name failed: 0x%08x.\n", hr); + ok(!lstrcmpW(str, L"foobar"), "got %s.\n", wine_dbgstr_w(str)); + SysFreeString(str); + str = SysAllocString(L"function sub(a, b) { return a - b; }\n"); + hr = IScriptModule_AddCode(mod, str); + ok(hr == S_OK, "IScriptModule_AddCode failed: 0x%08x.\n", hr); + 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); + IEnumVARIANT_Release(enumvar); + + 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); + ok(hr == S_OK, "IScriptControl_Eval failed: 0x%08x.\n", hr); + ok(V_VT(&var) == VT_I4 && V_I4(&var) == 15, "V_VT(var) = %d, V_I4(var) = %d.\n", V_VT(&var), V_I4(&var)); + SysFreeString(str); + str = SysAllocString(L"sub(10, 5)"); + hr = IScriptControl_Eval(sc, str, &var); + ok(FAILED(hr), "IScriptControl_Eval succeeded: 0x%08x.\n", hr); + SysFreeString(str); + + /* Grab a module ref and change the language to something valid */ + V_VT(&var) = VT_I2; + V_I2(&var) = 3; + hr = IScriptModuleCollection_get_Item(mods, var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_get_Item failed: 0x%08x.\n", hr); + str = SysAllocString(L"vbscript"); + hr = IScriptControl_put_Language(sc, str); + ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr); + SysFreeString(str); + + /* The module collection changes and module ref is invalid */ + hr = IScriptModuleCollection_get_Count(mods, &count); + ok(hr == S_OK, "IScriptModuleCollection_get_Count failed: 0x%08x.\n", hr); + ok(count == 1, "count is not 1, got %d.\n", count); + hr = IScriptModule_get_Name(mod, &str); + ok(hr == E_FAIL, "IScriptModule_get_Name returned: 0x%08x.\n", hr); + str = SysAllocString(L"function closed() { }\n"); + hr = IScriptModule_AddCode(mod, str); + ok(hr == E_FAIL, "IScriptModule_AddCode failed: 0x%08x.\n", hr); + SysFreeString(str); + str = SysAllocString(L"sub closed\nend sub"); + hr = IScriptModule_AddCode(mod, str); + ok(hr == E_FAIL, "IScriptModule_AddCode failed: 0x%08x.\n", hr); + IScriptModule_Release(mod); + SysFreeString(str); + + IScriptModuleCollection_Release(mods); + IScriptControl_Release(sc); + + /* custom script engine */ + if (have_custom_engine) + { + BSTR code_str; + + 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); + + /* Add a module with a non-null object and add some code to it */ + SET_EXPECT(AddNamedItem); + str = SysAllocString(L"modname"); + AddNamedItem_expected_name = str; + AddNamedItem_expected_flags = 0; + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = &testdisp; + hr = IScriptModuleCollection_Add(mods, str, &var, &mod); + ok(hr == S_OK, "IScriptModuleCollection_Add failed: 0x%08x.\n", hr); + VariantClear(&var); + CHECK_CALLED(AddNamedItem); + + unknown = (IUnknown*)0xdeadbeef; + hr = IActiveScriptSite_GetItemInfo(site, str, SCRIPTINFO_IUNKNOWN, &unknown, NULL); + ok(hr == S_OK, "IActiveScriptSite_GetItemInfo failed: 0x%08x.\n", hr); + ok(unknown == (IUnknown*)&testdisp, "Unexpected IUnknown for the item: %p.\n", unknown); + IUnknown_Release(unknown); + + SET_EXPECT(SetScriptState_STARTED); + SET_EXPECT(ParseScriptText); + parse_item_name = str; + parse_flags = SCRIPTTEXT_ISVISIBLE; + code_str = SysAllocString(L"some code"); + hr = IScriptModule_AddCode(mod, code_str); + ok(hr == S_OK, "IScriptControl_AddCode failed: 0x%08x.\n", hr); + CHECK_CALLED(SetScriptState_STARTED); + CHECK_CALLED(ParseScriptText); + SysFreeString(code_str); + SysFreeString(str); + + /* Keep the module ref before changing the language */ + SET_EXPECT(Close); + hr = IScriptControl_put_Language(sc, NULL); + ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr); + CHECK_CALLED(Close); + IScriptModuleCollection_Release(mods); + IActiveScriptSite_Release(site); + IScriptControl_Release(sc); + IScriptModule_Release(mod); + + /* Now try holding a module ref while closing the script */ + 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); + + SET_EXPECT(AddNamedItem); + str = SysAllocString(L"foo"); + 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); + VariantClear(&var); + CHECK_CALLED(AddNamedItem); + + unknown = (IUnknown*)0xdeadbeef; + hr = IActiveScriptSite_GetItemInfo(site, str, SCRIPTINFO_IUNKNOWN, &unknown, NULL); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "IActiveScriptSite_GetItemInfo returned: 0x%08x.\n", hr); + IScriptModuleCollection_Release(mods); + IActiveScriptSite_Release(site); + IScriptControl_Release(sc); + + SET_EXPECT(SetScriptState_STARTED); + SET_EXPECT(ParseScriptText); + parse_item_name = str; + parse_flags = SCRIPTTEXT_ISVISIBLE; + code_str = SysAllocString(L"code after close"); + hr = IScriptModule_AddCode(mod, code_str); + ok(hr == S_OK, "IScriptControl_AddCode failed: 0x%08x.\n", hr); + CHECK_CALLED(SetScriptState_STARTED); + CHECK_CALLED(ParseScriptText); + SysFreeString(code_str); + SysFreeString(str); + + SET_EXPECT(Close); + IScriptModule_Release(mod); + CHECK_CALLED(Close); + } +} + START_TEST(msscript) { IUnknown *unk; @@ -2162,6 +2513,7 @@ START_TEST(msscript) test_IScriptControl_AddCode(); test_IScriptControl_ExecuteStatement(); test_IScriptControl_Run(); + test_IScriptControl_get_Modules();
init_registry(FALSE);
On 26.05.2020 14:42, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@gmail.com
dlls/msscript.ocx/tests/msscript.c | 360 ++++++++++++++++++++++++++++- 1 file changed, 356 insertions(+), 4 deletions(-)
Please send tests earlier in the series. This really makes reviewing easier. If some parts are not easy to mark as todo_wine, you can always split tests into more patches.
Thanks,
Jacek