Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
These helpers will be needed by more methods and other helpers (such as the next patch) so it's better to place them at the top and avoid pointless forward declarations.
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 816a99d..9cb8282 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -200,6 +200,16 @@ static struct named_item *host_get_named_item(ScriptHost *host, const WCHAR *nam return NULL; }
+static HRESULT set_script_state(ScriptHost *host, SCRIPTSTATE state) +{ + HRESULT hr; + + hr = IActiveScript_SetScriptState(host->script, state); + if (SUCCEEDED(hr)) + host->script_state = state; + return hr; +} + static inline ScriptControl *impl_from_IScriptControl(IScriptControl *iface) { return CONTAINING_RECORD(iface, ScriptControl, IScriptControl_iface); @@ -954,16 +964,6 @@ static HRESULT WINAPI ScriptControl_AddObject(IScriptControl *iface, BSTR name, return hr; }
-static HRESULT set_script_state(ScriptHost *host, SCRIPTSTATE state) -{ - HRESULT hr; - - hr = IActiveScript_SetScriptState(host->script, state); - if (SUCCEEDED(hr)) - host->script_state = state; - return hr; -} - static HRESULT WINAPI ScriptControl_Reset(IScriptControl *iface) { ScriptControl *This = impl_from_IScriptControl(iface);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/msscript.c | 112 +++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 11 deletions(-)
diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c index 9cb8282..9319b5e 100644 --- a/dlls/msscript.ocx/msscript.c +++ b/dlls/msscript.ocx/msscript.c @@ -20,12 +20,14 @@
#include "windows.h" #include "initguid.h" +#include "dispex.h" #include "ole2.h" #include "olectl.h" #include "objsafe.h" #include "activscp.h" #include "rpcproxy.h" #include "msscript.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
#include "wine/debug.h" #include "wine/heap.h" @@ -71,6 +73,7 @@ typedef struct ScriptHost {
IActiveScript *script; IActiveScriptParse *parse; + IDispatch *script_dispatch; SCRIPTSTATE script_state; CLSID clsid;
@@ -200,6 +203,17 @@ 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) +{ + if (!control->host->script_dispatch) + { + HRESULT hr = IActiveScript_GetScriptDispatch(control->host->script, NULL, &control->host->script_dispatch); + if (FAILED(hr)) return hr; + } + *disp = control->host->script_dispatch; + return S_OK; +} + static HRESULT set_script_state(ScriptHost *host, SCRIPTSTATE state) { HRESULT hr; @@ -210,6 +224,19 @@ static HRESULT set_script_state(ScriptHost *host, SCRIPTSTATE state) return hr; }
+static HRESULT start_script(struct ScriptControl *control) +{ + HRESULT hr = S_OK; + + if (!control->host || control->state != Initialized) + return E_FAIL; + + if (control->host->script_state != SCRIPTSTATE_STARTED) + hr = set_script_state(control->host, SCRIPTSTATE_STARTED); + + return hr; +} + static inline ScriptControl *impl_from_IScriptControl(IScriptControl *iface) { return CONTAINING_RECORD(iface, ScriptControl, IScriptControl_iface); @@ -316,7 +343,10 @@ 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;
@@ -539,6 +569,7 @@ 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);
@@ -982,15 +1013,8 @@ static HRESULT parse_script_text(ScriptControl *control, BSTR script_text, DWORD EXCEPINFO excepinfo; HRESULT hr;
- if (!control->host || control->state != Initialized) - return E_FAIL; - - if (control->host->script_state != SCRIPTSTATE_STARTED) - { - hr = set_script_state(control->host, SCRIPTSTATE_STARTED); - if (FAILED(hr)) - return 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); @@ -1032,8 +1056,74 @@ 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); - FIXME("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res); - return E_NOTIMPL; + IDispatchEx *dispex; + IDispatch *disp; + SAFEARRAY *sa; + DISPPARAMS dp; + DISPID dispid; + HRESULT hr; + UINT i = 0; + + TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res); + + if (!parameters || !res) return E_POINTER; + if (!(sa = *parameters)) return E_POINTER; + + if (sa->cDims == 0) return DISP_E_BADINDEX; + if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE; + + hr = start_script(This); + 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 (FAILED(hr)) goto err; + + for (i = 0; i < dp.cArgs; i++) + { + /* The DISPPARAMS are stored in reverse order */ + VARIANT *src = (VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i - 1) * sa->cbElements); + + V_VT(&dp.rgvarg[i]) = VT_EMPTY; + hr = VariantCopy(&dp.rgvarg[i], src); + if (FAILED(hr)) + { + SafeArrayUnlock(sa); + goto err; + } + } + SafeArrayUnlock(sa); + + hr = get_script_dispatch(This, &disp); + if (FAILED(hr)) goto err; + + hr = IDispatch_GetIDsOfNames(disp, &IID_NULL, &procedure_name, 1, LOCALE_USER_DEFAULT, &dispid); + if (FAILED(hr)) goto err; + + 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); + } + +err: + while (i--) + VariantClear(&dp.rgvarg[i]); + heap_free(dp.rgvarg); + return hr; }
static const IScriptControlVtbl ScriptControlVtbl = {
Hi Gabriel,
On 9/26/19 4:43 PM, Gabriel Ivăncescu wrote:
static HRESULT WINAPI ScriptControl_Run(IScriptControl *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res) { ScriptControl *This = impl_from_IScriptControl(iface);
- FIXME("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res);
- return E_NOTIMPL;
- IDispatchEx *dispex;
- IDispatch *disp;
- SAFEARRAY *sa;
- DISPPARAMS dp;
- DISPID dispid;
- HRESULT hr;
- UINT i = 0;
- TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res);
- if (!parameters || !res) return E_POINTER;
- if (!(sa = *parameters)) return E_POINTER;
- if (sa->cDims == 0) return DISP_E_BADINDEX;
Shouldn't it be '!= 1'? Are cDims > 1 expected to work?
- if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE;
- hr = start_script(This);
- 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 (FAILED(hr)) goto err;
Error case will try to clean array elements that are not yet initialized.
- for (i = 0; i < dp.cArgs; i++)
- {
/* The DISPPARAMS are stored in reverse order */
VARIANT *src = (VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i - 1) * sa->cbElements);
V_VT(&dp.rgvarg[i]) = VT_EMPTY;
hr = VariantCopy(&dp.rgvarg[i], src);
Do we really need VariantCopy here? I think that simple assignment should be enough in this case would would simplify the code.
Thanks,
Jacek
Hi Jacek,
Thanks for the review.
On 9/27/19 5:15 PM, Jacek Caban wrote:
Hi Gabriel,
On 9/26/19 4:43 PM, Gabriel Ivăncescu wrote:
static HRESULT WINAPI ScriptControl_Run(IScriptControl *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res) { ScriptControl *This = impl_from_IScriptControl(iface); - FIXME("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res); - return E_NOTIMPL; + IDispatchEx *dispex; + IDispatch *disp; + SAFEARRAY *sa; + DISPPARAMS dp; + DISPID dispid; + HRESULT hr; + UINT i = 0;
+ TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(procedure_name), parameters, res);
+ if (!parameters || !res) return E_POINTER; + if (!(sa = *parameters)) return E_POINTER;
+ if (sa->cDims == 0) return DISP_E_BADINDEX;
Shouldn't it be '!= 1'? Are cDims > 1 expected to work?
Yeah, they're supposed to "work", Windows seems to just ignore the other dimensions and only read the first one, without giving errors (I did add tests for it, so no problem there).
+ if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE;
+ hr = start_script(This); + 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 (FAILED(hr)) goto err;
Error case will try to clean array elements that are not yet initialized.
It counts down from 'i' (wherever it stopped at), not the entire array, so it should be safe, unless I'm missing something?
+ for (i = 0; i < dp.cArgs; i++) + { + /* The DISPPARAMS are stored in reverse order */ + VARIANT *src = (VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i
- sa->cbElements);
+ V_VT(&dp.rgvarg[i]) = VT_EMPTY; + hr = VariantCopy(&dp.rgvarg[i], src);
Do we really need VariantCopy here? I think that simple assignment should be enough in this case would would simplify the code.
Actually, I'm not sure. :-)
I'll do some more tests with this and see what happens.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/msscript.ocx/tests/msscript.c | 560 ++++++++++++++++++++++++++++- 1 file changed, 558 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c index 95b2a69..091d882 100644 --- a/dlls/msscript.ocx/tests/msscript.c +++ b/dlls/msscript.ocx/tests/msscript.c @@ -29,6 +29,7 @@
#include "msscript.h" #include "wine/test.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
#define TESTSCRIPT_CLSID "{178fc164-f585-4e24-9c13-4bb7faf80746}" static const GUID CLSID_TestScript = @@ -92,12 +93,17 @@ DEFINE_EXPECT(CreateInstance); DEFINE_EXPECT(SetInterfaceSafetyOptions); DEFINE_EXPECT(InitNew); DEFINE_EXPECT(Close); +DEFINE_EXPECT(QI_IDispatchEx); +DEFINE_EXPECT(GetIDsOfNames); +DEFINE_EXPECT(Invoke); +DEFINE_EXPECT(InvokeEx); DEFINE_EXPECT(SetScriptSite); DEFINE_EXPECT(QI_IActiveScriptParse); DEFINE_EXPECT(SetScriptState_INITIALIZED); DEFINE_EXPECT(SetScriptState_STARTED); DEFINE_EXPECT(ParseScriptText); DEFINE_EXPECT(AddNamedItem); +DEFINE_EXPECT(GetScriptDispatch);
#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) static void _expect_ref(IUnknown* obj, ULONG ref, int line) @@ -226,6 +232,317 @@ static const IObjectSafetyVtbl ObjectSafetyVtbl = {
static IObjectSafety ObjectSafety = { &ObjectSafetyVtbl };
+static void dispparams_release(DISPPARAMS *dp) +{ + while (dp->cArgs--) + VariantClear(&dp->rgvarg[dp->cArgs]); + HeapFree(GetProcessHeap(), 0, dp->rgvarg); +} + +static HRESULT safearray_to_dispparams(SAFEARRAY *sa, DISPPARAMS *out) +{ + DISPPARAMS dp; + HRESULT hr; + UINT i; + + if (sa->cDims == 0 || !(sa->fFeatures & FADF_VARIANT)) + return E_FAIL; + + dp.cArgs = sa->rgsabound[0].cElements; + dp.rgdispidNamedArgs = NULL; + dp.cNamedArgs = 0; + + dp.rgvarg = HeapAlloc(GetProcessHeap(), 0, dp.cArgs * sizeof(*dp.rgvarg)); + if (!dp.rgvarg) return E_OUTOFMEMORY; + + hr = SafeArrayLock(sa); + if (FAILED(hr)) + { + HeapFree(GetProcessHeap(), 0, dp.rgvarg); + return hr; + } + + for (i = 0; i < dp.cArgs; i++) + { + /* The DISPPARAMS are stored in reverse order */ + VARIANT *src = (VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i - 1) * sa->cbElements); + + V_VT(&dp.rgvarg[i]) = VT_EMPTY; + hr = VariantCopy(&dp.rgvarg[i], src); + if (FAILED(hr)) + { + dp.cArgs = i; + dispparams_release(&dp); + goto err; + } + } + *out = dp; + +err: + SafeArrayUnlock(sa); + return hr; +} + +static struct +{ + UINT line; + const WCHAR *name; + DISPPARAMS dp; +} Dispatch_expect; + +#define set_Dispatch_expect(n,sa) do { \ + Dispatch_expect.line = __LINE__; \ + Dispatch_expect.name = (n); \ + ok(safearray_to_dispparams((sa), &Dispatch_expect.dp) == S_OK, "safearray_to_dispparams failed.\n"); \ +} while(0) + +static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface) +{ + return 2; +} + +static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) +{ + return 1; +} + +static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, + DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + UINT line = Dispatch_expect.line, i; + + CHECK_EXPECT(InvokeEx); + ok_(__FILE__,line)(lcid == LOCALE_USER_DEFAULT, "unexpected lcid %u\n", lcid); + ok_(__FILE__,line)(wFlags == DISPATCH_METHOD, "unexpected wFlags %u\n", wFlags); + ok_(__FILE__,line)(id == 0xdeadbeef, "unexpected id %d\n", id); + ok_(__FILE__,line)(pdp->cNamedArgs == Dispatch_expect.dp.cNamedArgs, + "unexpected number of named args %u (expected %u)\n", pdp->cNamedArgs, Dispatch_expect.dp.cNamedArgs); + ok_(__FILE__,line)(!pdp->rgdispidNamedArgs, + "unexpected (non-NULL) named args array %p\n", pdp->rgdispidNamedArgs); + ok_(__FILE__,line)(pdp->cArgs == Dispatch_expect.dp.cArgs, + "unexpected number of args %u (expected %u)\n", pdp->cArgs, Dispatch_expect.dp.cArgs); + if (pdp->rgvarg) + { + if (pdp->cArgs == Dispatch_expect.dp.cArgs) + for (i = 0; i < Dispatch_expect.dp.cArgs; i++) + { + VARIANT *v1 = pdp->rgvarg + i, *v2 = Dispatch_expect.dp.rgvarg + i; + + ok_(__FILE__,line)(V_VT(v1) == V_VT(v2) && VarCmp(v1, v2, lcid, 0) == VARCMP_EQ, + "unexpected arg %u: V_VT = %d, V_I4 = %d (expected V_VT = %d, V_I4 = %d)\n", i, + V_VT(v1), V_I4(v1), V_VT(v2), V_I4(v2)); + } + } + else if (Dispatch_expect.dp.rgvarg) + ok_(__FILE__,line)(0, "unexpected NULL args array\n"); + + V_VT(pvarRes) = VT_I2; + V_I2(pvarRes) = 42; + return S_OK; +} + +static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, + DWORD *pgrfdex) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const IDispatchExVtbl DispatchExVtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + DispatchEx_InvokeEx, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static IDispatchEx DispatchEx = { &DispatchExVtbl }; + +static BOOL DispatchEx_available = FALSE; +static HRESULT WINAPI Dispatch_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + + if (IsEqualGUID(&IID_IDispatchEx, riid)) + { + CHECK_EXPECT(QI_IDispatchEx); + if (DispatchEx_available) + { + *ppv = &DispatchEx; + return S_OK; + } + return E_NOINTERFACE; + } + + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI Dispatch_AddRef(IDispatch *iface) +{ + return 2; +} + +static ULONG WINAPI Dispatch_Release(IDispatch *iface) +{ + return 1; +} + +static HRESULT WINAPI Dispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + UINT line = Dispatch_expect.line; + + CHECK_EXPECT(GetIDsOfNames); + ok_(__FILE__,line)(IsEqualGUID(&IID_NULL, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid)); + ok_(__FILE__,line)(lcid == LOCALE_USER_DEFAULT, "unexpected lcid %u\n", lcid); + ok_(__FILE__,line)(cNames == 1, "unexpected cNames %u\n", cNames); + ok_(__FILE__,line)(!lstrcmpW(rgszNames[0], Dispatch_expect.name), + "unexpected name: %s\n", wine_dbgstr_w(rgszNames[0])); + + *rgDispId = 0xdeadbeef; + return S_OK; +} + +static HRESULT WINAPI Dispatch_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + UINT line = Dispatch_expect.line, i; + + CHECK_EXPECT(Invoke); + ok_(__FILE__,line)(IsEqualGUID(&IID_NULL, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid)); + ok_(__FILE__,line)(lcid == LOCALE_USER_DEFAULT, "unexpected lcid %u\n", lcid); + ok_(__FILE__,line)(wFlags == DISPATCH_METHOD, "unexpected wFlags %u\n", wFlags); + ok_(__FILE__,line)(dispIdMember == 0xdeadbeef, "unexpected dispIdMember %d\n", dispIdMember); + ok_(__FILE__,line)(pDispParams->cNamedArgs == Dispatch_expect.dp.cNamedArgs, + "unexpected number of named args %u (expected %u)\n", pDispParams->cNamedArgs, Dispatch_expect.dp.cNamedArgs); + ok_(__FILE__,line)(!pDispParams->rgdispidNamedArgs, + "unexpected (non-NULL) named args array %p\n", pDispParams->rgdispidNamedArgs); + ok_(__FILE__,line)(pDispParams->cArgs == Dispatch_expect.dp.cArgs, + "unexpected number of args %u (expected %u)\n", pDispParams->cArgs, Dispatch_expect.dp.cArgs); + if (pDispParams->rgvarg) + { + if (pDispParams->cArgs == Dispatch_expect.dp.cArgs) + for (i = 0; i < Dispatch_expect.dp.cArgs; i++) + { + VARIANT *v1 = pDispParams->rgvarg + i, *v2 = Dispatch_expect.dp.rgvarg + i; + + ok_(__FILE__,line)(V_VT(v1) == V_VT(v2) && VarCmp(v1, v2, lcid, 0) == VARCMP_EQ, + "unexpected arg %u: V_VT = %d, V_I4 = %d (expected V_VT = %d, V_I4 = %d)\n", i, + V_VT(v1), V_I4(v1), V_VT(v2), V_I4(v2)); + } + } + else if (Dispatch_expect.dp.rgvarg) + ok_(__FILE__,line)(0, "unexpected NULL args array\n"); + + V_VT(pVarResult) = VT_R8; + V_R8(pVarResult) = 4.2; + return S_OK; +} + +static const IDispatchVtbl DispatchVtbl = { + Dispatch_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + Dispatch_Invoke +}; + +static IDispatch Dispatch = { &DispatchVtbl }; + static HRESULT WINAPI ActiveScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv) { *ppv = NULL; @@ -362,8 +679,12 @@ static HRESULT WINAPI ActiveScript_AddTypeLib(IActiveScript *iface, REFGUID rgui static HRESULT WINAPI ActiveScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName, IDispatch **ppdisp) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + CHECK_EXPECT(GetScriptDispatch); + ok(!pstrItemName, "pstrItemName not NULL, got %s.\n", wine_dbgstr_w(pstrItemName)); + + *ppdisp = &Dispatch; + + return S_OK; }
static HRESULT WINAPI ActiveScript_GetCurrentScriptThreadID(IActiveScript *iface, @@ -1757,6 +2078,240 @@ static void test_IScriptControl_ExecuteStatement(void) } }
+static void test_IScriptControl_Run(void) +{ + SAFEARRAYBOUND bnd[] = { { 2, 0 }, { 2, 0 } }; + LONG idx0_0[] = { 0, 0 }; + LONG idx0_1[] = { 1, 0 }; + LONG idx1_0[] = { 0, 1 }; + LONG idx1_1[] = { 1, 1 }; + IScriptControl *sc; + SAFEARRAY *params; + VARIANT var; + HRESULT hr; + BSTR 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); + + params = NULL; + str = a2bstr("identifier"); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr); + + params = SafeArrayCreate(VT_VARIANT, 1, bnd); + ok(params != NULL, "Failed to create SafeArray.\n"); + + V_VT(&var) = VT_I4; + V_I4(&var) = 10; + SafeArrayPutElement(params, idx0_0, &var); + V_I4(&var) = 3; + SafeArrayPutElement(params, idx0_1, &var); + + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == E_FAIL, "IScriptControl_Run returned: 0x%08x.\n", hr); + + hr = IScriptControl_Run(sc, str, NULL, &var); + ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr); + + hr = IScriptControl_Run(sc, str, ¶ms, NULL); + ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr); + SysFreeString(str); + + hr = IScriptControl_Run(sc, NULL, ¶ms, &var); + ok(hr == E_FAIL, "IScriptControl_Run returned: 0x%08x.\n", hr); + + str = a2bstr("jscript"); + hr = IScriptControl_put_Language(sc, str); + ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr); + SysFreeString(str); + + str = a2bstr("foobar"); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_UNKNOWNNAME, "IScriptControl_Run failed: 0x%08x.\n", hr); + todo_wine CHECK_ERROR(sc, 0); + SysFreeString(str); + + str = a2bstr("function subtract(a, b) { return a - b; }\n"); + hr = IScriptControl_AddCode(sc, str); + ok(hr == S_OK, "IScriptControl_AddCode failed: 0x%08x.\n", hr); + todo_wine CHECK_ERROR(sc, 0); + SysFreeString(str); + + str = a2bstr("Subtract"); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_UNKNOWNNAME, "IScriptControl_Run failed: 0x%08x.\n", hr); + SysFreeString(str); + + str = a2bstr("subtract"); + hr = IScriptControl_Run(sc, str, ¶ms, NULL); + ok(hr == E_POINTER, "IScriptControl_Run failed: 0x%08x.\n", hr); + todo_wine CHECK_ERROR(sc, 0); + + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + ok((V_VT(&var) == VT_I4) && (V_I4(&var) == 7), "V_VT(var) = %d, V_I4(var) = %d.\n", V_VT(&var), V_I4(&var)); + todo_wine CHECK_ERROR(sc, 0); + VariantClear(&var); + SafeArrayDestroy(params); + + /* The array must be of VT_VARIANT type, else it either + crashes on Windows, or returns DISP_E_BADVARTYPE. */ + if (!broken(1)) + { + params = SafeArrayCreate(VT_I4, 1, bnd); + ok(params != NULL, "Failed to create SafeArray.\n"); + + V_I4(&var) = 10; + SafeArrayPutElement(params, idx0_0, &V_I4(&var)); + V_I4(&var) = 3; + SafeArrayPutElement(params, idx0_1, &V_I4(&var)); + + V_VT(&var) = VT_EMPTY; + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_BADVARTYPE, "IScriptControl_Run returned: 0x%08x.\n", hr); + ok(V_VT(&var) == VT_EMPTY, "V_VT(var) = %d.\n", V_VT(&var)); + SafeArrayDestroy(params); + } + + /* The array's other dimensions are ignored */ + params = SafeArrayCreate(VT_VARIANT, 2, bnd); + ok(params != NULL, "Failed to create SafeArray.\n"); + + V_VT(&var) = VT_I4; + V_I4(&var) = 10; + SafeArrayPutElement(params, idx0_0, &var); + V_I4(&var) = 3; + SafeArrayPutElement(params, idx0_1, &var); + V_I4(&var) = 90; + SafeArrayPutElement(params, idx1_0, &var); + V_I4(&var) = 80; + SafeArrayPutElement(params, idx1_1, &var); + + VariantClear(&var); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + ok((V_VT(&var) == VT_I4) && (V_I4(&var) == 7), "V_VT(var) = %d, V_I4(var) = %d.\n", V_VT(&var), V_I4(&var)); + + /* Hack the array's dimensions to 0 */ + VariantClear(&var); + params->cDims = 0; + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_BADINDEX, "IScriptControl_Run returned: 0x%08x.\n", hr); + ok(V_VT(&var) == VT_EMPTY, "V_VT(var) = %d.\n", V_VT(&var)); + params->cDims = 2; + VariantClear(&var); + SysFreeString(str); + IScriptControl_Release(sc); + + /* custom script engine */ + if (have_custom_engine) + { + 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 = a2bstr("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); + + SET_EXPECT(SetScriptState_STARTED); + SET_EXPECT(GetScriptDispatch); + SET_EXPECT(QI_IDispatchEx); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(Invoke); + str = a2bstr("function"); + set_Dispatch_expect(str, params); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + ok((V_VT(&var) == VT_R8) && (V_R8(&var) == 4.2), "V_VT(var) = %d, V_R8(var) = %lf.\n", V_VT(&var), V_R8(&var)); + VariantClear(&var); + SysFreeString(str); + dispparams_release(&Dispatch_expect.dp); + CHECK_CALLED(SetScriptState_STARTED); + CHECK_CALLED(GetScriptDispatch); + CHECK_CALLED(QI_IDispatchEx); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(Invoke); + + /* GetScriptDispatch is cached and not called again */ + CLEAR_CALLED(GetScriptDispatch); + SET_EXPECT(QI_IDispatchEx); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(Invoke); + str = a2bstr("BarFoo"); + set_Dispatch_expect(str, params); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + VariantClear(&var); + SysFreeString(str); + dispparams_release(&Dispatch_expect.dp); + CHECK_NOT_CALLED(GetScriptDispatch); + CHECK_CALLED(QI_IDispatchEx); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(Invoke); + + /* Make DispatchEx available */ + DispatchEx_available = TRUE; + CLEAR_CALLED(GetScriptDispatch); + SET_EXPECT(QI_IDispatchEx); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(InvokeEx); + str = a2bstr("FooBar"); + set_Dispatch_expect(str, params); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + ok((V_VT(&var) == VT_I2) && (V_I2(&var) == 42), "V_VT(var) = %d, V_I2(var) = %d.\n", V_VT(&var), V_I2(&var)); + VariantClear(&var); + SysFreeString(str); + dispparams_release(&Dispatch_expect.dp); + CHECK_NOT_CALLED(GetScriptDispatch); + CHECK_CALLED(QI_IDispatchEx); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(InvokeEx); + + /* QueryInterface for IDispatchEx is always called and not cached */ + CLEAR_CALLED(GetScriptDispatch); + SET_EXPECT(QI_IDispatchEx); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(InvokeEx); + str = a2bstr("FooBar"); + set_Dispatch_expect(str, params); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + VariantClear(&var); + SysFreeString(str); + dispparams_release(&Dispatch_expect.dp); + CHECK_NOT_CALLED(GetScriptDispatch); + CHECK_CALLED(QI_IDispatchEx); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(InvokeEx); + DispatchEx_available = FALSE; + + IActiveScriptSite_Release(site); + + SET_EXPECT(Close); + IScriptControl_Release(sc); + CHECK_CALLED(Close); + } + + SafeArrayDestroy(params); +} + START_TEST(msscript) { IUnknown *unk; @@ -1793,6 +2348,7 @@ START_TEST(msscript) test_IScriptControl_Eval(); test_IScriptControl_AddCode(); test_IScriptControl_ExecuteStatement(); + test_IScriptControl_Run();
init_registry(FALSE);
On 9/26/19 4:43 PM, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com
dlls/msscript.ocx/tests/msscript.c | 560 ++++++++++++++++++++++++++++- 1 file changed, 558 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c index 95b2a69..091d882 100644 --- a/dlls/msscript.ocx/tests/msscript.c +++ b/dlls/msscript.ocx/tests/msscript.c @@ -29,6 +29,7 @@
#include "msscript.h" #include "wine/test.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
#define TESTSCRIPT_CLSID "{178fc164-f585-4e24-9c13-4bb7faf80746}" static const GUID CLSID_TestScript = @@ -92,12 +93,17 @@ DEFINE_EXPECT(CreateInstance); DEFINE_EXPECT(SetInterfaceSafetyOptions); DEFINE_EXPECT(InitNew); DEFINE_EXPECT(Close); +DEFINE_EXPECT(QI_IDispatchEx); +DEFINE_EXPECT(GetIDsOfNames); +DEFINE_EXPECT(Invoke); +DEFINE_EXPECT(InvokeEx); DEFINE_EXPECT(SetScriptSite); DEFINE_EXPECT(QI_IActiveScriptParse); DEFINE_EXPECT(SetScriptState_INITIALIZED); DEFINE_EXPECT(SetScriptState_STARTED); DEFINE_EXPECT(ParseScriptText); DEFINE_EXPECT(AddNamedItem); +DEFINE_EXPECT(GetScriptDispatch);
#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) static void _expect_ref(IUnknown* obj, ULONG ref, int line) @@ -226,6 +232,317 @@ static const IObjectSafetyVtbl ObjectSafetyVtbl = {
static IObjectSafety ObjectSafety = { &ObjectSafetyVtbl };
+static void dispparams_release(DISPPARAMS *dp) +{
- while (dp->cArgs--)
VariantClear(&dp->rgvarg[dp->cArgs]);
- HeapFree(GetProcessHeap(), 0, dp->rgvarg);
+}
+static HRESULT safearray_to_dispparams(SAFEARRAY *sa, DISPPARAMS *out) +{
- DISPPARAMS dp;
- HRESULT hr;
- UINT i;
- if (sa->cDims == 0 || !(sa->fFeatures & FADF_VARIANT))
return E_FAIL;
- dp.cArgs = sa->rgsabound[0].cElements;
- dp.rgdispidNamedArgs = NULL;
- dp.cNamedArgs = 0;
- dp.rgvarg = HeapAlloc(GetProcessHeap(), 0, dp.cArgs * sizeof(*dp.rgvarg));
- if (!dp.rgvarg) return E_OUTOFMEMORY;
- hr = SafeArrayLock(sa);
- if (FAILED(hr))
- {
HeapFree(GetProcessHeap(), 0, dp.rgvarg);
return hr;
- }
- for (i = 0; i < dp.cArgs; i++)
- {
/* The DISPPARAMS are stored in reverse order */
VARIANT *src = (VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i - 1) * sa->cbElements);
V_VT(&dp.rgvarg[i]) = VT_EMPTY;
hr = VariantCopy(&dp.rgvarg[i], src);
if (FAILED(hr))
{
dp.cArgs = i;
dispparams_release(&dp);
goto err;
}
- }
- *out = dp;
+err:
- SafeArrayUnlock(sa);
- return hr;
+}
+static struct +{
- UINT line;
- const WCHAR *name;
- DISPPARAMS dp;
+} Dispatch_expect;
+#define set_Dispatch_expect(n,sa) do { \
- Dispatch_expect.line = __LINE__; \
- Dispatch_expect.name = (n); \
- ok(safearray_to_dispparams((sa), &Dispatch_expect.dp) == S_OK, "safearray_to_dispparams failed.\n"); \
+} while(0)
That part seems to be more complicated than needed. Since you always pass the same arguments anyway, you could just as well hardcode expected arguments in Invoke[Ex] implementation. If you really need something more flexible, how about storing DISPPARAMS passed to Invoke[Ex] somewhere in global variable and testing it in the caller, after Run() call returns?
+static const IDispatchExVtbl DispatchExVtbl = {
- DispatchEx_QueryInterface,
- DispatchEx_AddRef,
- DispatchEx_Release,
- DispatchEx_GetTypeInfoCount,
- DispatchEx_GetTypeInfo,
- DispatchEx_GetIDsOfNames,
- DispatchEx_Invoke,
- DispatchEx_GetDispID,
- DispatchEx_InvokeEx,
- DispatchEx_DeleteMemberByName,
- DispatchEx_DeleteMemberByDispID,
- DispatchEx_GetMemberProperties,
- DispatchEx_GetMemberName,
- DispatchEx_GetNextDispID,
- DispatchEx_GetNameSpaceParent
+};
+static IDispatchEx DispatchEx = { &DispatchExVtbl };
+static BOOL DispatchEx_available = FALSE; +static HRESULT WINAPI Dispatch_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{
- *ppv = NULL;
- if (IsEqualGUID(&IID_IDispatchEx, riid))
- {
CHECK_EXPECT(QI_IDispatchEx);
if (DispatchEx_available)
{
*ppv = &DispatchEx;
return S_OK;
}
return E_NOINTERFACE;
- }
- ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid));
- return E_NOINTERFACE;
+}
You don't really need separated vtbls for IDispatch and IDispatchEx. You could have a single IDispatchEx implementation and simply not expose IDispatchEx from QI in cases when you want to test plain IDispatch.
@@ -1757,6 +2078,240 @@ static void test_IScriptControl_ExecuteStatement(void) } }
+static void test_IScriptControl_Run(void) +{
- SAFEARRAYBOUND bnd[] = { { 2, 0 }, { 2, 0 } };
- LONG idx0_0[] = { 0, 0 };
- LONG idx0_1[] = { 1, 0 };
- LONG idx1_0[] = { 0, 1 };
- LONG idx1_1[] = { 1, 1 };
- IScriptControl *sc;
- SAFEARRAY *params;
- VARIANT var;
- HRESULT hr;
- BSTR 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);
- params = NULL;
- str = a2bstr("identifier");
- hr = IScriptControl_Run(sc, str, ¶ms, &var);
- ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr);
- params = SafeArrayCreate(VT_VARIANT, 1, bnd);
- ok(params != NULL, "Failed to create SafeArray.\n");
- V_VT(&var) = VT_I4;
- V_I4(&var) = 10;
- SafeArrayPutElement(params, idx0_0, &var);
- V_I4(&var) = 3;
- SafeArrayPutElement(params, idx0_1, &var);
- hr = IScriptControl_Run(sc, str, ¶ms, &var);
- ok(hr == E_FAIL, "IScriptControl_Run returned: 0x%08x.\n", hr);
- hr = IScriptControl_Run(sc, str, NULL, &var);
- ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr);
- hr = IScriptControl_Run(sc, str, ¶ms, NULL);
- ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr);
- SysFreeString(str);
- hr = IScriptControl_Run(sc, NULL, ¶ms, &var);
- ok(hr == E_FAIL, "IScriptControl_Run returned: 0x%08x.\n", hr);
- str = a2bstr("jscript");
- hr = IScriptControl_put_Language(sc, str);
- ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr);
- SysFreeString(str);
- str = a2bstr("foobar");
- hr = IScriptControl_Run(sc, str, ¶ms, &var);
- ok(hr == DISP_E_UNKNOWNNAME, "IScriptControl_Run failed: 0x%08x.\n", hr);
- todo_wine CHECK_ERROR(sc, 0);
- SysFreeString(str);
- str = a2bstr("function subtract(a, b) { return a - b; }\n");
- hr = IScriptControl_AddCode(sc, str);
- ok(hr == S_OK, "IScriptControl_AddCode failed: 0x%08x.\n", hr);
- todo_wine CHECK_ERROR(sc, 0);
- SysFreeString(str);
- str = a2bstr("Subtract");
- hr = IScriptControl_Run(sc, str, ¶ms, &var);
- ok(hr == DISP_E_UNKNOWNNAME, "IScriptControl_Run failed: 0x%08x.\n", hr);
- SysFreeString(str);
- str = a2bstr("subtract");
- hr = IScriptControl_Run(sc, str, ¶ms, NULL);
- ok(hr == E_POINTER, "IScriptControl_Run failed: 0x%08x.\n", hr);
- todo_wine CHECK_ERROR(sc, 0);
- hr = IScriptControl_Run(sc, str, ¶ms, &var);
- ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr);
- ok((V_VT(&var) == VT_I4) && (V_I4(&var) == 7), "V_VT(var) = %d, V_I4(var) = %d.\n", V_VT(&var), V_I4(&var));
- todo_wine CHECK_ERROR(sc, 0);
- VariantClear(&var);
You don't need to clear VT_I4 variant.
- SafeArrayDestroy(params);
- /* The array must be of VT_VARIANT type, else it either
crashes on Windows, or returns DISP_E_BADVARTYPE. */
- if (!broken(1))
- {
params = SafeArrayCreate(VT_I4, 1, bnd);
ok(params != NULL, "Failed to create SafeArray.\n");
V_I4(&var) = 10;
SafeArrayPutElement(params, idx0_0, &V_I4(&var));
V_I4(&var) = 3;
SafeArrayPutElement(params, idx0_1, &V_I4(&var));
V_VT(&var) = VT_EMPTY;
hr = IScriptControl_Run(sc, str, ¶ms, &var);
ok(hr == DISP_E_BADVARTYPE, "IScriptControl_Run returned: 0x%08x.\n", hr);
ok(V_VT(&var) == VT_EMPTY, "V_VT(var) = %d.\n", V_VT(&var));
SafeArrayDestroy(params);
- }
This is not really an interesting test and having it run only on Wine may be misleading. Please remove it.
Jacek
On 9/27/19 5:27 PM, Jacek Caban wrote:
On 9/26/19 4:43 PM, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com
dlls/msscript.ocx/tests/msscript.c | 560 ++++++++++++++++++++++++++++- 1 file changed, 558 insertions(+), 2 deletions(-)
diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c index 95b2a69..091d882 100644 --- a/dlls/msscript.ocx/tests/msscript.c +++ b/dlls/msscript.ocx/tests/msscript.c @@ -29,6 +29,7 @@ #include "msscript.h" #include "wine/test.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); #define TESTSCRIPT_CLSID "{178fc164-f585-4e24-9c13-4bb7faf80746}" static const GUID CLSID_TestScript = @@ -92,12 +93,17 @@ DEFINE_EXPECT(CreateInstance); DEFINE_EXPECT(SetInterfaceSafetyOptions); DEFINE_EXPECT(InitNew); DEFINE_EXPECT(Close); +DEFINE_EXPECT(QI_IDispatchEx); +DEFINE_EXPECT(GetIDsOfNames); +DEFINE_EXPECT(Invoke); +DEFINE_EXPECT(InvokeEx); DEFINE_EXPECT(SetScriptSite); DEFINE_EXPECT(QI_IActiveScriptParse); DEFINE_EXPECT(SetScriptState_INITIALIZED); DEFINE_EXPECT(SetScriptState_STARTED); DEFINE_EXPECT(ParseScriptText); DEFINE_EXPECT(AddNamedItem); +DEFINE_EXPECT(GetScriptDispatch); #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) static void _expect_ref(IUnknown* obj, ULONG ref, int line) @@ -226,6 +232,317 @@ static const IObjectSafetyVtbl ObjectSafetyVtbl = { static IObjectSafety ObjectSafety = { &ObjectSafetyVtbl }; +static void dispparams_release(DISPPARAMS *dp) +{ + while (dp->cArgs--) + VariantClear(&dp->rgvarg[dp->cArgs]); + HeapFree(GetProcessHeap(), 0, dp->rgvarg); +}
+static HRESULT safearray_to_dispparams(SAFEARRAY *sa, DISPPARAMS *out) +{ + DISPPARAMS dp; + HRESULT hr; + UINT i;
+ if (sa->cDims == 0 || !(sa->fFeatures & FADF_VARIANT)) + return E_FAIL;
+ dp.cArgs = sa->rgsabound[0].cElements; + dp.rgdispidNamedArgs = NULL; + dp.cNamedArgs = 0;
+ dp.rgvarg = HeapAlloc(GetProcessHeap(), 0, dp.cArgs * sizeof(*dp.rgvarg)); + if (!dp.rgvarg) return E_OUTOFMEMORY;
+ hr = SafeArrayLock(sa); + if (FAILED(hr)) + { + HeapFree(GetProcessHeap(), 0, dp.rgvarg); + return hr; + }
+ for (i = 0; i < dp.cArgs; i++) + { + /* The DISPPARAMS are stored in reverse order */ + VARIANT *src = (VARIANT*)((char*)(sa->pvData) + (dp.cArgs - i
- sa->cbElements);
+ V_VT(&dp.rgvarg[i]) = VT_EMPTY; + hr = VariantCopy(&dp.rgvarg[i], src); + if (FAILED(hr)) + { + dp.cArgs = i; + dispparams_release(&dp); + goto err; + } + } + *out = dp;
+err: + SafeArrayUnlock(sa); + return hr; +}
+static struct +{ + UINT line; + const WCHAR *name; + DISPPARAMS dp; +} Dispatch_expect;
+#define set_Dispatch_expect(n,sa) do { \ + Dispatch_expect.line = __LINE__; \ + Dispatch_expect.name = (n); \ + ok(safearray_to_dispparams((sa), &Dispatch_expect.dp) == S_OK, "safearray_to_dispparams failed.\n"); \ +} while(0)
That part seems to be more complicated than needed. Since you always pass the same arguments anyway, you could just as well hardcode expected arguments in Invoke[Ex] implementation. If you really need something more flexible, how about storing DISPPARAMS passed to Invoke[Ex] somewhere in global variable and testing it in the caller, after Run() call returns?
Oh sure I'll do it that way. Though, I'd have to copy the arg pointer array for comparison purposes, since it would otherwise be freed, but shouldn't be a problem, right?
+static const IDispatchExVtbl DispatchExVtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + DispatchEx_InvokeEx, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +};
+static IDispatchEx DispatchEx = { &DispatchExVtbl };
+static BOOL DispatchEx_available = FALSE; +static HRESULT WINAPI Dispatch_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + *ppv = NULL;
+ if (IsEqualGUID(&IID_IDispatchEx, riid)) + { + CHECK_EXPECT(QI_IDispatchEx); + if (DispatchEx_available) + { + *ppv = &DispatchEx; + return S_OK; + } + return E_NOINTERFACE; + }
+ ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +}
You don't really need separated vtbls for IDispatch and IDispatchEx. You could have a single IDispatchEx implementation and simply not expose IDispatchEx from QI in cases when you want to test plain IDispatch.
Right. I forgot it derives from it. :-)
@@ -1757,6 +2078,240 @@ static void test_IScriptControl_ExecuteStatement(void) } } +static void test_IScriptControl_Run(void) +{ + SAFEARRAYBOUND bnd[] = { { 2, 0 }, { 2, 0 } }; + LONG idx0_0[] = { 0, 0 }; + LONG idx0_1[] = { 1, 0 }; + LONG idx1_0[] = { 0, 1 }; + LONG idx1_1[] = { 1, 1 }; + IScriptControl *sc; + SAFEARRAY *params; + VARIANT var; + HRESULT hr; + BSTR 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);
+ params = NULL; + str = a2bstr("identifier"); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr);
+ params = SafeArrayCreate(VT_VARIANT, 1, bnd); + ok(params != NULL, "Failed to create SafeArray.\n");
+ V_VT(&var) = VT_I4; + V_I4(&var) = 10; + SafeArrayPutElement(params, idx0_0, &var); + V_I4(&var) = 3; + SafeArrayPutElement(params, idx0_1, &var);
+ hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == E_FAIL, "IScriptControl_Run returned: 0x%08x.\n", hr);
+ hr = IScriptControl_Run(sc, str, NULL, &var); + ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr);
+ hr = IScriptControl_Run(sc, str, ¶ms, NULL); + ok(hr == E_POINTER, "IScriptControl_Run returned: 0x%08x.\n", hr); + SysFreeString(str);
+ hr = IScriptControl_Run(sc, NULL, ¶ms, &var); + ok(hr == E_FAIL, "IScriptControl_Run returned: 0x%08x.\n", hr);
+ str = a2bstr("jscript"); + hr = IScriptControl_put_Language(sc, str); + ok(hr == S_OK, "IScriptControl_put_Language failed: 0x%08x.\n", hr); + SysFreeString(str);
+ str = a2bstr("foobar"); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_UNKNOWNNAME, "IScriptControl_Run failed: 0x%08x.\n", hr); + todo_wine CHECK_ERROR(sc, 0); + SysFreeString(str);
+ str = a2bstr("function subtract(a, b) { return a - b; }\n"); + hr = IScriptControl_AddCode(sc, str); + ok(hr == S_OK, "IScriptControl_AddCode failed: 0x%08x.\n", hr); + todo_wine CHECK_ERROR(sc, 0); + SysFreeString(str);
+ str = a2bstr("Subtract"); + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_UNKNOWNNAME, "IScriptControl_Run failed: 0x%08x.\n", hr); + SysFreeString(str);
+ str = a2bstr("subtract"); + hr = IScriptControl_Run(sc, str, ¶ms, NULL); + ok(hr == E_POINTER, "IScriptControl_Run failed: 0x%08x.\n", hr); + todo_wine CHECK_ERROR(sc, 0);
+ hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == S_OK, "IScriptControl_Run failed: 0x%08x.\n", hr); + ok((V_VT(&var) == VT_I4) && (V_I4(&var) == 7), "V_VT(var) = %d, V_I4(var) = %d.\n", V_VT(&var), V_I4(&var)); + todo_wine CHECK_ERROR(sc, 0); + VariantClear(&var);
You don't need to clear VT_I4 variant.
+ SafeArrayDestroy(params);
+ /* The array must be of VT_VARIANT type, else it either + crashes on Windows, or returns DISP_E_BADVARTYPE. */ + if (!broken(1)) + { + params = SafeArrayCreate(VT_I4, 1, bnd); + ok(params != NULL, "Failed to create SafeArray.\n");
+ V_I4(&var) = 10; + SafeArrayPutElement(params, idx0_0, &V_I4(&var)); + V_I4(&var) = 3; + SafeArrayPutElement(params, idx0_1, &V_I4(&var));
+ V_VT(&var) = VT_EMPTY; + hr = IScriptControl_Run(sc, str, ¶ms, &var); + ok(hr == DISP_E_BADVARTYPE, "IScriptControl_Run returned: 0x%08x.\n", hr); + ok(V_VT(&var) == VT_EMPTY, "V_VT(var) = %d.\n", V_VT(&var)); + SafeArrayDestroy(params); + }
This is not really an interesting test and having it run only on Wine may be misleading. Please remove it.
Noted.