/*
 * Copyright 2015 Jacek Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define COBJMACROS

#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"
#include "wine/list.h"

WINE_DEFAULT_DEBUG_CHANNEL(msscript);

#ifdef _WIN64

#define IActiveScriptParse_Release IActiveScriptParse64_Release
#define IActiveScriptParse_InitNew IActiveScriptParse64_InitNew
#define IActiveScriptParse_ParseScriptText IActiveScriptParse64_ParseScriptText

#else

#define IActiveScriptParse_Release IActiveScriptParse32_Release
#define IActiveScriptParse_InitNew IActiveScriptParse32_InitNew
#define IActiveScriptParse_ParseScriptText IActiveScriptParse32_ParseScriptText

#endif

struct ScriptControl;
typedef struct ConnectionPoint ConnectionPoint;
typedef struct ScriptProcedureCollection ScriptProcedureCollection;
typedef struct ScriptHost ScriptHost;

struct ConnectionPoint {
    IConnectionPoint IConnectionPoint_iface;
    ScriptControl *control;
    const IID *riid;
    ConnectionPoint *next;
};

struct named_item {
    struct list entry;
    BSTR name;
    IDispatch *disp;
};

struct module_enum {
    IEnumVARIANT IEnumVARIANT_iface;
    LONG ref;

    UINT pos;
    ScriptHost *host;
    ScriptControl *control;
};

typedef struct {
    IScriptModule IScriptModule_iface;
    LONG ref;

    BSTR name;
    ScriptHost *host;
    IDispatch *script_dispatch;
    ITypeInfo *script_typeinfo;
    ITypeComp *script_typecomp;

    ScriptProcedureCollection *procedures;
} ScriptModule;

typedef struct {
    IScriptProcedure IScriptProcedure_iface;
    LONG ref;

    ULONG hash;
    struct list entry;

    BSTR name;
    USHORT num_args;
    VARTYPE ret_type;
} ScriptProcedure;

struct ScriptProcedureCollection {
    IScriptProcedureCollection IScriptProcedureCollection_iface;
    LONG ref;

    LONG count;
    ScriptModule *module;
    struct list hash_table[43];
};

struct procedure_enum {
    IEnumVARIANT IEnumVARIANT_iface;
    LONG ref;

    WORD pos;
    WORD count;
    ScriptProcedureCollection *procedures;
};

typedef struct {
    IScriptError IScriptError_iface;
    IActiveScriptError *object;
    LONG ref;

    HRESULT number;
    BSTR text;
    BSTR source;
    BSTR desc;
    BSTR help_file;
    DWORD help_context;
    ULONG line;
    LONG column;

    BOOLEAN info_filled;
    BOOLEAN text_filled;
    BOOLEAN pos_filled;
} ScriptError;

struct ScriptHost {
    IActiveScriptSite IActiveScriptSite_iface;
    IActiveScriptSiteWindow IActiveScriptSiteWindow_iface;
    IServiceProvider IServiceProvider_iface;
    LONG ref;

    IActiveScript *script;
    IActiveScriptParse *parse;
    ScriptError *error;
    HWND site_hwnd;
    SCRIPTSTATE script_state;
    CLSID clsid;

    unsigned int module_count;
    struct list named_items;
};

struct ScriptControl {
    IScriptControl IScriptControl_iface;
    IPersistStreamInit IPersistStreamInit_iface;
    IOleObject IOleObject_iface;
    IOleControl IOleControl_iface;
    IQuickActivate IQuickActivate_iface;
    IViewObjectEx IViewObjectEx_iface;
    IPointerInactive IPointerInactive_iface;
    IConnectionPointContainer IConnectionPointContainer_iface;
    LONG ref;
    IOleClientSite *site;
    HWND site_hwnd;
    SIZEL extent;
    LONG timeout;
    VARIANT_BOOL allow_ui;
    VARIANT_BOOL use_safe_subset;

    /* connection points */
    ConnectionPoint *cp_list;
    ConnectionPoint cp_scsource;
    ConnectionPoint cp_propnotif;

    /* IViewObject sink */
    IAdviseSink *view_sink;
    DWORD view_sink_flags;

    /* modules */
    ScriptModule **modules;
    IScriptModuleCollection IScriptModuleCollection_iface;

    ScriptHost *host;
    ScriptError *error;
};

static HINSTANCE msscript_instance;

typedef enum tid_t {
    IScriptControl_tid,
    IScriptError_tid,
    IScriptModuleCollection_tid,
    IScriptModule_tid,
    IScriptProcedureCollection_tid,
    IScriptProcedure_tid,
    LAST_tid
} tid_t;

static ITypeLib *typelib;
static ITypeInfo *typeinfos[LAST_tid];

static REFIID tid_ids[] = {
    &IID_IScriptControl,
    &IID_IScriptError,
    &IID_IScriptModuleCollection,
    &IID_IScriptModule,
    &IID_IScriptProcedureCollection,
    &IID_IScriptProcedure
};

static HRESULT load_typelib(void)
{
    HRESULT hres;
    ITypeLib *tl;

    hres = LoadRegTypeLib(&LIBID_MSScriptControl, 1, 0, LOCALE_SYSTEM_DEFAULT, &tl);
    if(FAILED(hres)) {
        ERR("LoadRegTypeLib failed: %08x\n", hres);
        return hres;
    }

    if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
        ITypeLib_Release(tl);
    return hres;
}

static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
{
    HRESULT hres;

    if (!typelib)
        hres = load_typelib();
    if (!typelib)
        return hres;

    if(!typeinfos[tid]) {
        ITypeInfo *ti;

        hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti);
        if(FAILED(hres)) {
            ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres);
            return hres;
        }

        if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL))
            ITypeInfo_Release(ti);
    }

    *typeinfo = typeinfos[tid];
    ITypeInfo_AddRef(typeinfos[tid]);
    return S_OK;
}

static void release_typelib(void)
{
    unsigned i;

    if(!typelib)
        return;

    for(i = 0; i < ARRAY_SIZE(typeinfos); i++)
        if(typeinfos[i])
            ITypeInfo_Release(typeinfos[i]);

    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;
    LIST_FOR_EACH_ENTRY_SAFE(item, item1, &host->named_items, struct named_item, entry) {
       list_remove(&item->entry);
       SysFreeString(item->name);
       IDispatch_Release(item->disp);
       heap_free(item);
    }
}

static struct named_item *host_get_named_item(ScriptHost *host, const WCHAR *nameW)
{
    struct named_item *item;

    LIST_FOR_EACH_ENTRY(item, &host->named_items, struct named_item, entry) {
        if (!wcscmp(item->name, nameW))
            return item;
    }

    return NULL;
}

static HRESULT get_script_dispatch(ScriptModule *module, IDispatch **disp)
{
    if (!module->script_dispatch)
    {
        HRESULT hr = IActiveScript_GetScriptDispatch(module->host->script,
                                                     module->name, &module->script_dispatch);
        if (FAILED(hr)) return hr;
    }
    *disp = module->script_dispatch;
    return S_OK;
}

static HRESULT get_script_typeinfo(ScriptModule *module, ITypeInfo **typeinfo)
{
    IDispatch *disp;
    HRESULT hr;

    if (!module->script_typeinfo)
    {
        hr = get_script_dispatch(module, &disp);
        if (FAILED(hr)) return hr;

        hr = IDispatch_GetTypeInfo(disp, 0, LOCALE_USER_DEFAULT, &module->script_typeinfo);
        if (FAILED(hr)) return hr;
    }
    *typeinfo = module->script_typeinfo;
    return S_OK;
}

static HRESULT get_script_typecomp(ScriptModule *module, ITypeInfo *typeinfo, ITypeComp **typecomp)
{
    HRESULT hr;

    if (!module->script_typecomp)
    {
        hr = ITypeInfo_QueryInterface(typeinfo, &IID_ITypeComp, (void**)&module->script_typecomp);
        if (FAILED(hr)) return hr;
    }
    *typecomp = module->script_typecomp;
    return S_OK;
}

static void uncache_module_objects(ScriptModule *module)
{
    if (module->script_dispatch)
    {
        IDispatch_Release(module->script_dispatch);
        module->script_dispatch = NULL;
    }
    if (module->script_typeinfo)
    {
        ITypeInfo_Release(module->script_typeinfo);
        module->script_typeinfo = NULL;
    }
    if (module->script_typecomp)
    {
        ITypeComp_Release(module->script_typecomp);
        module->script_typecomp = 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 HRESULT start_script(ScriptModule *module)
{
    HRESULT hr = S_OK;

    if (module->host->script_state != SCRIPTSTATE_STARTED)
    {
        hr = set_script_state(module->host, SCRIPTSTATE_STARTED);
        if (SUCCEEDED(hr)) uncache_module_objects(module);
    }

    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;
    HRESULT hr;

    hr = start_script(module);
    if (FAILED(hr)) return hr;

    uncache_module_objects(module);
    if (module->procedures)
        module->procedures->count = -1;

    hr = IActiveScriptParse_ParseScriptText(module->host->parse, script_text, module->name,
                                            NULL, NULL, 0, 1, flag, res, &excepinfo);
    /* FIXME: more error handling */
    return hr;
}

static HRESULT WINAPI sp_caller_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
{
    if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IServiceProvider, riid))
        *obj = iface;
    else
    {
        FIXME("(%p)->(%s)\n", iface, debugstr_guid(riid));
        *obj = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*obj);
    return S_OK;
}

static ULONG WINAPI sp_caller_AddRef(IServiceProvider *iface)
{
    return 2;
}

static ULONG WINAPI sp_caller_Release(IServiceProvider *iface)
{
    return 1;
}

static HRESULT WINAPI sp_caller_QueryService(IServiceProvider *iface, REFGUID service, REFIID riid, void **obj)
{
    FIXME("(%p)->(%s %s %p): semi-stub\n", iface, debugstr_guid(service), debugstr_guid(riid), obj);

    *obj = NULL;
    if (IsEqualGUID(&SID_GetCaller, service))
        return S_OK;

    return E_NOINTERFACE;
}

static const IServiceProviderVtbl sp_caller_vtbl = {
    sp_caller_QueryInterface,
    sp_caller_AddRef,
    sp_caller_Release,
    sp_caller_QueryService
};

static IServiceProvider sp_caller = { &sp_caller_vtbl };

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);
    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, &sp_caller);
            IDispatchEx_Release(dispex);
        }
    }
    heap_free(dp.rgvarg);

    return hr;
}

static inline ScriptControl *impl_from_IScriptControl(IScriptControl *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IScriptControl_iface);
}

static inline ScriptControl *impl_from_IOleObject(IOleObject *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IOleObject_iface);
}

static inline ScriptControl *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IPersistStreamInit_iface);
}

static inline ScriptControl *impl_from_IOleControl(IOleControl *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IOleControl_iface);
}

static inline ScriptControl *impl_from_IQuickActivate(IQuickActivate *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IQuickActivate_iface);
}

static inline ScriptControl *impl_from_IViewObjectEx(IViewObjectEx *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IViewObjectEx_iface);
}

static inline ScriptControl *impl_from_IPointerInactive(IPointerInactive *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IPointerInactive_iface);
}

static inline ScriptControl *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IConnectionPointContainer_iface);
}

static inline ScriptProcedure *impl_from_IScriptProcedure(IScriptProcedure *iface)
{
    return CONTAINING_RECORD(iface, ScriptProcedure, IScriptProcedure_iface);
}

static inline ScriptProcedureCollection *impl_from_IScriptProcedureCollection(IScriptProcedureCollection *iface)
{
    return CONTAINING_RECORD(iface, ScriptProcedureCollection, IScriptProcedureCollection_iface);
}

static inline ScriptControl *impl_from_IScriptModuleCollection(IScriptModuleCollection *iface)
{
    return CONTAINING_RECORD(iface, ScriptControl, IScriptModuleCollection_iface);
}

static inline ScriptModule *impl_from_IScriptModule(IScriptModule *iface)
{
    return CONTAINING_RECORD(iface, ScriptModule, IScriptModule_iface);
}

static inline ScriptError *impl_from_IScriptError(IScriptError *iface)
{
    return CONTAINING_RECORD(iface, ScriptError, IScriptError_iface);
}

static inline ConnectionPoint *impl_from_IConnectionPoint(IConnectionPoint *iface)
{
    return CONTAINING_RECORD(iface, ConnectionPoint, IConnectionPoint_iface);
}

static inline ScriptHost *impl_from_IActiveScriptSite(IActiveScriptSite *iface)
{
    return CONTAINING_RECORD(iface, ScriptHost, IActiveScriptSite_iface);
}

static inline ScriptHost *impl_from_IActiveScriptSiteWindow(IActiveScriptSiteWindow *iface)
{
    return CONTAINING_RECORD(iface, ScriptHost, IActiveScriptSiteWindow_iface);
}

static inline ScriptHost *impl_from_IServiceProvider(IServiceProvider *iface)
{
    return CONTAINING_RECORD(iface, ScriptHost, IServiceProvider_iface);
}

static inline struct module_enum *module_enum_from_IEnumVARIANT(IEnumVARIANT *iface)
{
    return CONTAINING_RECORD(iface, struct module_enum, IEnumVARIANT_iface);
}

static inline struct procedure_enum *procedure_enum_from_IEnumVARIANT(IEnumVARIANT *iface)
{
    return CONTAINING_RECORD(iface, struct procedure_enum, IEnumVARIANT_iface);
}

/* IActiveScriptSite */
static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = &This->IActiveScriptSite_iface;
    }else if(IsEqualGUID(&IID_IActiveScriptSite, riid)) {
        TRACE("(%p)->(IID_IActiveScriptSite %p)\n", This, ppv);
        *ppv = &This->IActiveScriptSite_iface;
    }else if(IsEqualGUID(&IID_IActiveScriptSiteWindow, riid)) {
        TRACE("(%p)->(IID_IActiveScriptSiteWindow %p)\n", This, ppv);
        *ppv = &This->IActiveScriptSiteWindow_iface;
    }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
        TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
        *ppv = &This->IServiceProvider_iface;
    }else {
        FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI ActiveScriptSite_AddRef(IActiveScriptSite *iface)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI ActiveScriptSite_Release(IActiveScriptSite *iface)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if(!ref) {
        clear_named_items(This);
        heap_free(This);
    }

    return ref;
}

static HRESULT WINAPI ActiveScriptSite_GetLCID(IActiveScriptSite *iface, LCID *lcid)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    TRACE("(%p, %p)\n", This, lcid);

    *lcid = GetUserDefaultLCID();
    return S_OK;
}

static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface, LPCOLESTR name, DWORD mask,
    IUnknown **unk, ITypeInfo **ti)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);
    struct named_item *item;

    TRACE("(%p, %s, %#x, %p, %p)\n", This, debugstr_w(name), mask, unk, ti);

    item = host_get_named_item(This, name);
    if (!item)
        return TYPE_E_ELEMENTNOTFOUND;

    if (mask != SCRIPTINFO_IUNKNOWN) {
        FIXME("mask %#x is not supported\n", mask);
        return E_NOTIMPL;
    }

    *unk = (IUnknown*)item->disp;
    IUnknown_AddRef(*unk);

    return S_OK;
}

static HRESULT WINAPI ActiveScriptSite_GetDocVersionString(IActiveScriptSite *iface, BSTR *version)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    FIXME("(%p, %p)\n", This, version);

    return E_NOTIMPL;
}

static HRESULT WINAPI ActiveScriptSite_OnScriptTerminate(IActiveScriptSite *iface, const VARIANT *result,
    const EXCEPINFO *ei)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    FIXME("(%p, %s, %p)\n", This, debugstr_variant(result), ei);

    return E_NOTIMPL;
}

static HRESULT WINAPI ActiveScriptSite_OnStateChange(IActiveScriptSite *iface, SCRIPTSTATE state)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    FIXME("(%p, %d)\n", This, state);

    return E_NOTIMPL;
}

static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface, IActiveScriptError *script_error)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    TRACE("(%p, %p)\n", This, script_error);

    if (This->error)
    {
        IScriptError_Clear(&This->error->IScriptError_iface);
        IActiveScriptError_AddRef(script_error);
        This->error->object = script_error;
    }
    return S_FALSE;
}

static HRESULT WINAPI ActiveScriptSite_OnEnterScript(IActiveScriptSite *iface)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    FIXME("(%p)\n", This);

    return E_NOTIMPL;
}

static HRESULT WINAPI ActiveScriptSite_OnLeaveScript(IActiveScriptSite *iface)
{
    ScriptHost *This = impl_from_IActiveScriptSite(iface);

    FIXME("(%p)\n", This);

    return E_NOTIMPL;
}

static const IActiveScriptSiteVtbl ActiveScriptSiteVtbl = {
    ActiveScriptSite_QueryInterface,
    ActiveScriptSite_AddRef,
    ActiveScriptSite_Release,
    ActiveScriptSite_GetLCID,
    ActiveScriptSite_GetItemInfo,
    ActiveScriptSite_GetDocVersionString,
    ActiveScriptSite_OnScriptTerminate,
    ActiveScriptSite_OnStateChange,
    ActiveScriptSite_OnScriptError,
    ActiveScriptSite_OnEnterScript,
    ActiveScriptSite_OnLeaveScript
};

/* IActiveScriptSiteWindow */
static HRESULT WINAPI ActiveScriptSiteWindow_QueryInterface(IActiveScriptSiteWindow *iface, REFIID riid, void **obj)
{
    ScriptHost *This = impl_from_IActiveScriptSiteWindow(iface);
    return IActiveScriptSite_QueryInterface(&This->IActiveScriptSite_iface, riid, obj);
}

static ULONG WINAPI ActiveScriptSiteWindow_AddRef(IActiveScriptSiteWindow *iface)
{
    ScriptHost *This = impl_from_IActiveScriptSiteWindow(iface);
    return IActiveScriptSite_AddRef(&This->IActiveScriptSite_iface);
}

static ULONG WINAPI ActiveScriptSiteWindow_Release(IActiveScriptSiteWindow *iface)
{
    ScriptHost *This = impl_from_IActiveScriptSiteWindow(iface);
    return IActiveScriptSite_Release(&This->IActiveScriptSite_iface);
}

static HRESULT WINAPI ActiveScriptSiteWindow_GetWindow(IActiveScriptSiteWindow *iface, HWND *hwnd)
{
    ScriptHost *This = impl_from_IActiveScriptSiteWindow(iface);

    TRACE("(%p, %p)\n", This, hwnd);

    if (!hwnd) return E_POINTER;
    if (This->site_hwnd == ((HWND)-1)) return E_FAIL;

    *hwnd = This->site_hwnd;
    return S_OK;
}

static HRESULT WINAPI ActiveScriptSiteWindow_EnableModeless(IActiveScriptSiteWindow *iface, BOOL enable)
{
    ScriptHost *This = impl_from_IActiveScriptSiteWindow(iface);

    FIXME("(%p, %d): stub\n", This, enable);

    return S_OK;
}

static const IActiveScriptSiteWindowVtbl ActiveScriptSiteWindowVtbl = {
    ActiveScriptSiteWindow_QueryInterface,
    ActiveScriptSiteWindow_AddRef,
    ActiveScriptSiteWindow_Release,
    ActiveScriptSiteWindow_GetWindow,
    ActiveScriptSiteWindow_EnableModeless
};

/* IServiceProvider */
static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
{
    ScriptHost *This = impl_from_IServiceProvider(iface);
    return IActiveScriptSite_QueryInterface(&This->IActiveScriptSite_iface, riid, obj);
}

static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
{
    ScriptHost *This = impl_from_IServiceProvider(iface);
    return IActiveScriptSite_AddRef(&This->IActiveScriptSite_iface);
}

static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
{
    ScriptHost *This = impl_from_IServiceProvider(iface);
    return IActiveScriptSite_Release(&This->IActiveScriptSite_iface);
}

static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface, REFGUID service,
    REFIID riid, void **obj)
{
    ScriptHost *This = impl_from_IServiceProvider(iface);

    FIXME("(%p)->(%s %s %p)\n", This, debugstr_guid(service), debugstr_guid(riid), obj);

    return E_NOTIMPL;
}

static const IServiceProviderVtbl ServiceProviderVtbl = {
    ServiceProvider_QueryInterface,
    ServiceProvider_AddRef,
    ServiceProvider_Release,
    ServiceProvider_QueryService
};

static HRESULT WINAPI ScriptProcedure_QueryInterface(IScriptProcedure *iface, REFIID riid, void **ppv)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);

    if (IsEqualGUID(&IID_IDispatch, riid) || IsEqualGUID(&IID_IUnknown, riid) ||
        IsEqualGUID(&IID_IScriptProcedure, riid))
    {
        *ppv = &This->IScriptProcedure_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 ScriptProcedure_AddRef(IScriptProcedure *iface)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI ScriptProcedure_Release(IScriptProcedure *iface)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if (!ref)
    {
        list_remove(&This->entry);
        SysFreeString(This->name);
        heap_free(This);
    }
    return ref;
}

static HRESULT WINAPI ScriptProcedure_GetTypeInfoCount(IScriptProcedure *iface, UINT *pctinfo)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);

    TRACE("(%p)->(%p)\n", This, pctinfo);

    *pctinfo = 1;
    return S_OK;
}

static HRESULT WINAPI ScriptProcedure_GetTypeInfo(IScriptProcedure *iface, UINT iTInfo,
        LCID lcid, ITypeInfo **ppTInfo)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);

    TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);

    return get_typeinfo(IScriptProcedure_tid, ppTInfo);
}

static HRESULT WINAPI ScriptProcedure_GetIDsOfNames(IScriptProcedure *iface, REFIID riid,
        LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);
    ITypeInfo *typeinfo;
    HRESULT hr;

    TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);

    hr = get_typeinfo(IScriptProcedure_tid, &typeinfo);
    if (SUCCEEDED(hr))
    {
        hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
        ITypeInfo_Release(typeinfo);
    }

    return hr;
}

static HRESULT WINAPI ScriptProcedure_Invoke(IScriptProcedure *iface, DISPID dispIdMember,
        REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(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(IScriptProcedure_tid, &typeinfo);
    if(SUCCEEDED(hr))
    {
        hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
                              pDispParams, pVarResult, pExcepInfo, puArgErr);
        ITypeInfo_Release(typeinfo);
    }

    return hr;
}

static HRESULT WINAPI ScriptProcedure_get_Name(IScriptProcedure *iface, BSTR *pbstrName)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);

    TRACE("(%p)->(%p)\n", This, pbstrName);

    if (!pbstrName) return E_POINTER;

    *pbstrName = SysAllocString(This->name);
    return *pbstrName ? S_OK : E_OUTOFMEMORY;
}

static HRESULT WINAPI ScriptProcedure_get_NumArgs(IScriptProcedure *iface, LONG *pcArgs)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);

    TRACE("(%p)->(%p)\n", This, pcArgs);

    if (!pcArgs) return E_POINTER;

    *pcArgs = This->num_args;
    return S_OK;
}

static HRESULT WINAPI ScriptProcedure_get_HasReturnValue(IScriptProcedure *iface, VARIANT_BOOL *pfHasReturnValue)
{
    ScriptProcedure *This = impl_from_IScriptProcedure(iface);

    TRACE("(%p)->(%p)\n", This, pfHasReturnValue);

    if (!pfHasReturnValue) return E_POINTER;

    *pfHasReturnValue = (This->ret_type == VT_VOID) ? VARIANT_FALSE : VARIANT_TRUE;
    return S_OK;
}

static const IScriptProcedureVtbl ScriptProcedureVtbl = {
    ScriptProcedure_QueryInterface,
    ScriptProcedure_AddRef,
    ScriptProcedure_Release,
    ScriptProcedure_GetTypeInfoCount,
    ScriptProcedure_GetTypeInfo,
    ScriptProcedure_GetIDsOfNames,
    ScriptProcedure_Invoke,
    ScriptProcedure_get_Name,
    ScriptProcedure_get_NumArgs,
    ScriptProcedure_get_HasReturnValue
};

/* This function always releases the FUNCDESC passed in */
static HRESULT get_script_procedure(ScriptProcedureCollection *procedures, ITypeInfo *typeinfo,
        FUNCDESC *desc, IScriptProcedure **procedure)
{
    struct list *proc_list;
    ScriptProcedure *proc;
    ULONG hash;
    HRESULT hr;
    BSTR str;
    UINT len;

    hr = ITypeInfo_GetNames(typeinfo, desc->memid, &str, 1, &len);
    if (FAILED(hr)) goto done;

    len = SysStringLen(str);
    hash = LHashValOfNameSys(sizeof(void*) == 8 ? SYS_WIN64 : SYS_WIN32, LOCALE_USER_DEFAULT, str);
    proc_list = &procedures->hash_table[hash % ARRAY_SIZE(procedures->hash_table)];

    /* Try to find it in the hash table */
    LIST_FOR_EACH_ENTRY(proc, proc_list, ScriptProcedure, entry)
    {
        if (proc->hash == hash && SysStringLen(proc->name) == len &&
            !memcmp(proc->name, str, len * sizeof(*str)))
        {
            SysFreeString(str);
            IScriptProcedure_AddRef(&proc->IScriptProcedure_iface);
            *procedure = &proc->IScriptProcedure_iface;
            goto done;
        }
    }

    if (!(proc = heap_alloc(sizeof(*proc))))
    {
        hr = E_OUTOFMEMORY;
        SysFreeString(str);
        goto done;
    }

    proc->IScriptProcedure_iface.lpVtbl = &ScriptProcedureVtbl;
    proc->ref = 1;
    proc->hash = hash;
    proc->name = str;
    proc->num_args = desc->cParams + desc->cParamsOpt;
    proc->ret_type = desc->elemdescFunc.tdesc.vt;
    list_add_tail(proc_list, &proc->entry);

    *procedure = &proc->IScriptProcedure_iface;

done:
    ITypeInfo_ReleaseFuncDesc(typeinfo, desc);
    return hr;
}

static HRESULT WINAPI procedure_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);

    if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IEnumVARIANT, riid))
    {
        *ppv = &This->IEnumVARIANT_iface;
    }
    else
    {
        WARN("unsupported interface: (%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI procedure_enum_AddRef(IEnumVARIANT *iface)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI procedure_enum_Release(IEnumVARIANT *iface)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if (!ref)
    {
        IScriptProcedureCollection_Release(&This->procedures->IScriptProcedureCollection_iface);
        heap_free(This);
    }

    return ref;
}

static HRESULT WINAPI procedure_enum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);
    FUNCDESC *desc;
    ITypeInfo *ti;
    UINT i, num;
    HRESULT hr;

    TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched);

    if (!rgVar) return E_POINTER;
    if (!This->procedures->module->host) return E_FAIL;

    hr = start_script(This->procedures->module);
    if (FAILED(hr)) return hr;

    hr = get_script_typeinfo(This->procedures->module, &ti);
    if (FAILED(hr)) return hr;

    num = min(celt, This->count - This->pos);
    for (i = 0; i < num; i++)
    {
        hr = ITypeInfo_GetFuncDesc(ti, This->pos + i, &desc);
        if (FAILED(hr)) break;

        hr = get_script_procedure(This->procedures, ti, desc, (IScriptProcedure**)&V_DISPATCH(rgVar + i));
        if (FAILED(hr)) break;

        V_VT(rgVar + i) = VT_DISPATCH;
    }

    if (FAILED(hr))
    {
        while (i--)
            VariantClear(rgVar + i);
        if (pCeltFetched) *pCeltFetched = 0;
        return hr;
    }

    This->pos += i;

    if (pCeltFetched) *pCeltFetched = i;
    return i == celt ? S_OK : S_FALSE;
}

static HRESULT WINAPI procedure_enum_Skip(IEnumVARIANT *iface, ULONG celt)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);

    TRACE("(%p)->(%u)\n", This, celt);

    if (This->count - This->pos < celt)
    {
        This->pos = This->count;
        return S_FALSE;
    }
    This->pos += celt;
    return S_OK;
}

static HRESULT WINAPI procedure_enum_Reset(IEnumVARIANT *iface)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);

    TRACE("(%p)\n", This);

    This->pos = 0;
    return S_OK;
}

static HRESULT WINAPI procedure_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
{
    struct procedure_enum *This = procedure_enum_from_IEnumVARIANT(iface);
    struct procedure_enum *clone;

    TRACE("(%p)->(%p)\n", This, ppEnum);

    if (!ppEnum) return E_POINTER;

    if (!(clone = heap_alloc(sizeof(*clone))))
        return E_OUTOFMEMORY;

    *clone = *This;
    clone->ref = 1;
    IScriptProcedureCollection_AddRef(&This->procedures->IScriptProcedureCollection_iface);

    *ppEnum = &clone->IEnumVARIANT_iface;
    return S_OK;
}

static const IEnumVARIANTVtbl procedure_enum_vtbl = {
    procedure_enum_QueryInterface,
    procedure_enum_AddRef,
    procedure_enum_Release,
    procedure_enum_Next,
    procedure_enum_Skip,
    procedure_enum_Reset,
    procedure_enum_Clone
};

static HRESULT WINAPI ScriptProcedureCollection_QueryInterface(IScriptProcedureCollection *iface, REFIID riid, void **ppv)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);

    if (IsEqualGUID(&IID_IDispatch, riid) || IsEqualGUID(&IID_IUnknown, riid) ||
        IsEqualGUID(&IID_IScriptProcedureCollection, riid))
    {
        *ppv = &This->IScriptProcedureCollection_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 ScriptProcedureCollection_AddRef(IScriptProcedureCollection *iface)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI ScriptProcedureCollection_Release(IScriptProcedureCollection *iface)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
    LONG ref = InterlockedDecrement(&This->ref);
    UINT i;

    TRACE("(%p) ref=%d\n", This, ref);

    if (!ref)
    {
        /* Unlink any dangling items from the hash table */
        for (i = 0; i < ARRAY_SIZE(This->hash_table); i++)
            list_remove(&This->hash_table[i]);

        This->module->procedures = NULL;
        IScriptModule_Release(&This->module->IScriptModule_iface);
        heap_free(This);
    }
    return ref;
}

static HRESULT WINAPI ScriptProcedureCollection_GetTypeInfoCount(IScriptProcedureCollection *iface, UINT *pctinfo)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);

    TRACE("(%p)->(%p)\n", This, pctinfo);

    *pctinfo = 1;
    return S_OK;
}

static HRESULT WINAPI ScriptProcedureCollection_GetTypeInfo(IScriptProcedureCollection *iface, UINT iTInfo,
        LCID lcid, ITypeInfo **ppTInfo)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);

    TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);

    return get_typeinfo(IScriptProcedureCollection_tid, ppTInfo);
}

static HRESULT WINAPI ScriptProcedureCollection_GetIDsOfNames(IScriptProcedureCollection *iface, REFIID riid,
        LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
    ITypeInfo *typeinfo;
    HRESULT hr;

    TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);

    hr = get_typeinfo(IScriptProcedureCollection_tid, &typeinfo);
    if (SUCCEEDED(hr))
    {
        hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
        ITypeInfo_Release(typeinfo);
    }

    return hr;
}

static HRESULT WINAPI ScriptProcedureCollection_Invoke(IScriptProcedureCollection *iface, DISPID dispIdMember,
        REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(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(IScriptProcedureCollection_tid, &typeinfo);
    if(SUCCEEDED(hr))
    {
        hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
                              pDispParams, pVarResult, pExcepInfo, puArgErr);
        ITypeInfo_Release(typeinfo);
    }

    return hr;
}

static HRESULT WINAPI ScriptProcedureCollection_get__NewEnum(IScriptProcedureCollection *iface, IUnknown **ppenumProcedures)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
    struct procedure_enum *proc_enum;
    TYPEATTR *attr;
    ITypeInfo *ti;
    UINT count;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, ppenumProcedures);

    if (!ppenumProcedures) return E_POINTER;
    if (!This->module->host) return E_FAIL;

    hr = start_script(This->module);
    if (FAILED(hr)) return hr;

    hr = get_script_typeinfo(This->module, &ti);
    if (FAILED(hr)) return hr;

    hr = ITypeInfo_GetTypeAttr(ti, &attr);
    if (FAILED(hr)) return hr;

    count = attr->cFuncs;
    ITypeInfo_ReleaseTypeAttr(ti, attr);

    if (!(proc_enum = heap_alloc(sizeof(*proc_enum))))
        return E_OUTOFMEMORY;

    proc_enum->IEnumVARIANT_iface.lpVtbl = &procedure_enum_vtbl;
    proc_enum->ref = 1;
    proc_enum->pos = 0;
    proc_enum->count = count;
    proc_enum->procedures = This;
    IScriptProcedureCollection_AddRef(&This->IScriptProcedureCollection_iface);

    *ppenumProcedures = (IUnknown*)&proc_enum->IEnumVARIANT_iface;
    return S_OK;
}

static HRESULT WINAPI ScriptProcedureCollection_get_Item(IScriptProcedureCollection *iface, VARIANT index,
        IScriptProcedure **ppdispProcedure)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
    ITypeInfo *typeinfo;
    FUNCDESC *desc;
    HRESULT hr;

    TRACE("(%p)->(%s %p)\n", This, wine_dbgstr_variant(&index), ppdispProcedure);

    if (!ppdispProcedure) return E_POINTER;
    if (!This->module->host) return E_FAIL;

    hr = start_script(This->module);
    if (FAILED(hr)) return hr;

    hr = get_script_typeinfo(This->module, &typeinfo);
    if (FAILED(hr)) return hr;

    if (V_VT(&index) == VT_BSTR)
    {
        struct list *proc_list;
        ScriptProcedure *proc;
        ITypeComp *comp;
        BINDPTR bindptr;
        DESCKIND kind;
        ULONG hash;
        UINT len;

        len = SysStringLen(V_BSTR(&index));
        hash = LHashValOfNameSys(sizeof(void*) == 8 ? SYS_WIN64 : SYS_WIN32, LOCALE_USER_DEFAULT, V_BSTR(&index));
        proc_list = &This->hash_table[hash % ARRAY_SIZE(This->hash_table)];

        /* Try to find it in the hash table */
        LIST_FOR_EACH_ENTRY(proc, proc_list, ScriptProcedure, entry)
        {
            if (proc->hash == hash && SysStringLen(proc->name) == len &&
                !memcmp(proc->name, V_BSTR(&index), len * sizeof(WCHAR)))
            {
                IScriptProcedure_AddRef(&proc->IScriptProcedure_iface);
                *ppdispProcedure = &proc->IScriptProcedure_iface;
                return S_OK;
            }
        }

        hr = get_script_typecomp(This->module, typeinfo, &comp);
        if (FAILED(hr)) return hr;

        hr = ITypeComp_Bind(comp, V_BSTR(&index), hash, INVOKE_FUNC, &typeinfo, &kind, &bindptr);
        if (FAILED(hr)) return hr;

        switch (kind)
        {
        case DESCKIND_FUNCDESC:
            hr = get_script_procedure(This, typeinfo, bindptr.lpfuncdesc, ppdispProcedure);
            ITypeInfo_Release(typeinfo);
            return hr;
        case DESCKIND_IMPLICITAPPOBJ:
        case DESCKIND_VARDESC:
            ITypeInfo_ReleaseVarDesc(typeinfo, bindptr.lpvardesc);
            ITypeInfo_Release(typeinfo);
            break;
        case DESCKIND_TYPECOMP:
            ITypeComp_Release(bindptr.lptcomp);
            break;
        default:
            break;
        }
        return CTL_E_ILLEGALFUNCTIONCALL;
    }

    hr = VariantChangeType(&index, &index, 0, VT_INT);
    if (FAILED(hr)) return hr;
    if (V_INT(&index) <= 0) return 0x800a0009;

    hr = ITypeInfo_GetFuncDesc(typeinfo, V_INT(&index) - 1, &desc);
    if (FAILED(hr)) return hr;

    return get_script_procedure(This, typeinfo, desc, ppdispProcedure);
}

static HRESULT WINAPI ScriptProcedureCollection_get_Count(IScriptProcedureCollection *iface, LONG *plCount)
{
    ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
    TYPEATTR *attr;
    ITypeInfo *ti;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, plCount);

    if (!plCount) return E_POINTER;
    if (!This->module->host) return E_FAIL;

    if (This->count == -1)
    {
        hr = start_script(This->module);
        if (FAILED(hr)) return hr;

        hr = get_script_typeinfo(This->module, &ti);
        if (FAILED(hr)) return hr;

        hr = ITypeInfo_GetTypeAttr(ti, &attr);
        if (FAILED(hr)) return hr;

        This->count = attr->cFuncs;
        ITypeInfo_ReleaseTypeAttr(ti, attr);
    }

    *plCount = This->count;
    return S_OK;
}

static const IScriptProcedureCollectionVtbl ScriptProcedureCollectionVtbl = {
    ScriptProcedureCollection_QueryInterface,
    ScriptProcedureCollection_AddRef,
    ScriptProcedureCollection_Release,
    ScriptProcedureCollection_GetTypeInfoCount,
    ScriptProcedureCollection_GetTypeInfo,
    ScriptProcedureCollection_GetIDsOfNames,
    ScriptProcedureCollection_Invoke,
    ScriptProcedureCollection_get__NewEnum,
    ScriptProcedureCollection_get_Item,
    ScriptProcedureCollection_get_Count
};

static void detach_script_host(ScriptHost *host)
{
    if (--host->module_count)
        return;

    if (host->script) {
        IActiveScript_Close(host->script);
        IActiveScript_Release(host->script);
    }

    if (host->parse)
        IActiveScriptParse_Release(host->parse);

    if (host->error)
        IScriptError_Release(&host->error->IScriptError_iface);

    host->parse = NULL;
    host->error = NULL;
    host->script = NULL;
}

static void detach_module(ScriptModule *module)
{
    ScriptHost *host = module->host;

    if (host) {
        module->host = NULL;
        detach_script_host(host);
        IActiveScriptSite_Release(&host->IActiveScriptSite_iface);
    }
}

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);

    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)
    {
        detach_module(This);
        SysFreeString(This->name);
        uncache_module_objects(This);
        heap_free(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);

    TRACE("(%p)->(%p)\n", This, pbstrName);

    if (!pbstrName) return E_POINTER;
    if (!This->host) 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)
{
    ScriptModule *This = impl_from_IScriptModule(iface);
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, ppdispObject);

    if (!This->host) return E_FAIL;

    hr = start_script(This);
    if (FAILED(hr)) return hr;

    hr = get_script_dispatch(This, ppdispObject);
    if (FAILED(hr)) return hr;

    IDispatch_AddRef(*ppdispObject);
    return hr;
}

static HRESULT WINAPI ScriptModule_get_Procedures(IScriptModule *iface, IScriptProcedureCollection **ppdispProcedures)
{
    ScriptModule *This = impl_from_IScriptModule(iface);

    TRACE("(%p)->(%p)\n", This, ppdispProcedures);

    if (!This->host)
        return E_FAIL;

    if (This->procedures)
        IScriptProcedureCollection_AddRef(&This->procedures->IScriptProcedureCollection_iface);
    else
    {
        ScriptProcedureCollection *procs;
        UINT i;

        if (!(procs = heap_alloc(sizeof(*procs))))
            return E_OUTOFMEMORY;

        procs->IScriptProcedureCollection_iface.lpVtbl = &ScriptProcedureCollectionVtbl;
        procs->ref = 1;
        procs->count = -1;
        procs->module = This;
        for (i = 0; i < ARRAY_SIZE(procs->hash_table); i++)
            list_init(&procs->hash_table[i]);

        This->procedures = procs;
        IScriptModule_AddRef(&This->IScriptModule_iface);
    }

    *ppdispProcedures = &This->procedures->IScriptProcedureCollection_iface;
    return S_OK;
}

static HRESULT WINAPI ScriptModule_AddCode(IScriptModule *iface, BSTR code)
{
    ScriptModule *This = impl_from_IScriptModule(iface);

    TRACE("(%p)->(%s)\n", This, debugstr_w(code));

    if (!This->host)
        return E_FAIL;

    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);

    TRACE("(%p)->(%s, %p)\n", This, debugstr_w(expression), res);

    if (!res)
        return E_POINTER;
    V_VT(res) = VT_EMPTY;
    if (!This->host)
        return E_FAIL;

    return parse_script_text(This, expression, SCRIPTTEXT_ISEXPRESSION, res);
}

static HRESULT WINAPI ScriptModule_ExecuteStatement(IScriptModule *iface, BSTR statement)
{
    ScriptModule *This = impl_from_IScriptModule(iface);

    TRACE("(%p)->(%s)\n", This, debugstr_w(statement));

    if (!This->host)
        return E_FAIL;

    return parse_script_text(This, statement, 0, NULL);
}

static HRESULT WINAPI ScriptModule_Run(IScriptModule *iface, BSTR procedure_name, SAFEARRAY **parameters, VARIANT *res)
{
    ScriptModule *This = impl_from_IScriptModule(iface);
    SAFEARRAY *sa;

    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;

    V_VT(res) = VT_EMPTY;
    if (sa->cDims == 0) return DISP_E_BADINDEX;
    if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE;
    if (!This->host) return E_FAIL;

    return run_procedure(This, procedure_name, sa, res);
}

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 HRESULT WINAPI module_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);

    if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IEnumVARIANT, riid))
    {
        *ppv = &This->IEnumVARIANT_iface;
    }
    else
    {
        WARN("unsupported interface: (%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI module_enum_AddRef(IEnumVARIANT *iface)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI module_enum_Release(IEnumVARIANT *iface)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if (!ref)
    {
        IActiveScriptSite_Release(&This->host->IActiveScriptSite_iface);
        IScriptControl_Release(&This->control->IScriptControl_iface);
        heap_free(This);
    }

    return ref;
}

static HRESULT WINAPI module_enum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);
    unsigned int i, num;

    TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched);

    if (!rgVar) return E_POINTER;
    if (This->host != This->control->host) return E_FAIL;

    num = min(celt, This->host->module_count - This->pos);
    for (i = 0; i < num; i++)
    {
        V_VT(rgVar + i) = VT_DISPATCH;
        V_DISPATCH(rgVar + i) = (IDispatch*)(&This->control->modules[This->pos++]->IScriptModule_iface);
        IDispatch_AddRef(V_DISPATCH(rgVar + i));
    }

    if (pCeltFetched) *pCeltFetched = i;
    return i == celt ? S_OK : S_FALSE;
}

static HRESULT WINAPI module_enum_Skip(IEnumVARIANT *iface, ULONG celt)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);

    TRACE("(%p)->(%u)\n", This, celt);

    if (This->host != This->control->host) return E_FAIL;

    if (This->host->module_count - This->pos < celt)
    {
        This->pos = This->host->module_count;
        return S_FALSE;
    }
    This->pos += celt;
    return S_OK;
}

static HRESULT WINAPI module_enum_Reset(IEnumVARIANT *iface)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);

    TRACE("(%p)\n", This);

    if (This->host != This->control->host) return E_FAIL;

    This->pos = 0;
    return S_OK;
}

static HRESULT WINAPI module_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
{
    struct module_enum *This = module_enum_from_IEnumVARIANT(iface);
    struct module_enum *clone;

    TRACE("(%p)->(%p)\n", This, ppEnum);

    if (!ppEnum) return E_POINTER;
    if (This->host != This->control->host) return E_FAIL;

    if (!(clone = heap_alloc(sizeof(*clone))))
        return E_OUTOFMEMORY;

    *clone = *This;
    clone->ref = 1;
    IActiveScriptSite_AddRef(&This->host->IActiveScriptSite_iface);
    IScriptControl_AddRef(&This->control->IScriptControl_iface);

    *ppEnum = &clone->IEnumVARIANT_iface;
    return S_OK;
}

static const IEnumVARIANTVtbl module_enum_vtbl = {
    module_enum_QueryInterface,
    module_enum_AddRef,
    module_enum_Release,
    module_enum_Next,
    module_enum_Skip,
    module_enum_Reset,
    module_enum_Clone
};

static ScriptModule *create_module(ScriptHost *host, BSTR name)
{
    ScriptModule *module;

    if (!(module = heap_alloc_zero(sizeof(*module)))) return NULL;

    module->IScriptModule_iface.lpVtbl = &ScriptModuleVtbl;
    module->ref = 1;
    if (name && !(module->name = SysAllocString(name)))
    {
        heap_free(module);
        return NULL;
    }
    module->host = host;
    IActiveScriptSite_AddRef(&host->IActiveScriptSite_iface);
    return module;
}

static void release_modules(ScriptControl *control, BOOL force_detach)
{
    unsigned int i, module_count = control->host->module_count;

    for (i = 0; i < module_count; i++) {
        if (force_detach) detach_module(control->modules[i]);
        IScriptModule_Release(&control->modules[i]->IScriptModule_iface);
    }

    heap_free(control->modules);
}

static ScriptModule *find_module(ScriptControl *control, BSTR name)
{
    unsigned int i;

    if (!wcsicmp(name, L"Global"))
        return control->modules[0];

    for (i = 1; i < control->host->module_count; i++)
    {
        if (!wcsicmp(name, control->modules[i]->name))
            return control->modules[i];
    }
    return NULL;
}

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);
    struct module_enum *module_enum;

    TRACE("(%p)->(%p)\n", This, ppenumContexts);

    if (!ppenumContexts) return E_POINTER;
    if (!This->host) return E_FAIL;

    if (!(module_enum = heap_alloc(sizeof(*module_enum))))
        return E_OUTOFMEMORY;

    module_enum->IEnumVARIANT_iface.lpVtbl = &module_enum_vtbl;
    module_enum->ref = 1;
    module_enum->pos = 0;
    module_enum->host = This->host;
    module_enum->control = This;
    IActiveScriptSite_AddRef(&This->host->IActiveScriptSite_iface);
    IScriptControl_AddRef(&This->IScriptControl_iface);

    *ppenumContexts = (IUnknown*)&module_enum->IEnumVARIANT_iface;
    return S_OK;
}

static HRESULT WINAPI ScriptModuleCollection_get_Item(IScriptModuleCollection *iface, VARIANT index,
        IScriptModule **ppmod)
{
    ScriptControl *This = impl_from_IScriptModuleCollection(iface);
    ScriptModule *module;
    unsigned int i;
    HRESULT hr;

    TRACE("(%p)->(%s %p)\n", This, wine_dbgstr_variant(&index), ppmod);

    if (!ppmod) return E_POINTER;
    if (!This->host) return E_FAIL;

    if (V_VT(&index) == VT_BSTR)
    {
        module = find_module(This, V_BSTR(&index));
        if (!module) 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->host->module_count) return 0x800a0009;

        module = This->modules[i];
    }

    *ppmod = &module->IScriptModule_iface;
    IScriptModule_AddRef(*ppmod);
    return S_OK;
}

static HRESULT WINAPI ScriptModuleCollection_get_Count(IScriptModuleCollection *iface, LONG *plCount)
{
    ScriptControl *This = impl_from_IScriptModuleCollection(iface);

    TRACE("(%p)->(%p)\n", This, plCount);

    if (!plCount) return E_POINTER;
    if (!This->host) return E_FAIL;

    *plCount = This->host->module_count;
    return S_OK;
}

static HRESULT WINAPI ScriptModuleCollection_Add(IScriptModuleCollection *iface, BSTR name,
        VARIANT *object, IScriptModule **ppmod)
{
    ScriptControl *This = impl_from_IScriptModuleCollection(iface);
    ScriptModule *module, **modules;
    ScriptHost *host = This->host;
    HRESULT hr;

    TRACE("(%p)->(%s %s %p)\n", This, wine_dbgstr_w(name), wine_dbgstr_variant(object), ppmod);

    if (!ppmod) return E_POINTER;
    if (!name || V_VT(object) != VT_DISPATCH) return E_INVALIDARG;
    if (!host) return E_FAIL;
    if (find_module(This, name)) return E_INVALIDARG;

    /* See if we need to grow the array */
    if (is_power_of_2(host->module_count))
    {
        modules = heap_realloc(This->modules, host->module_count * 2 * sizeof(*This->modules));
        if (!modules) return E_OUTOFMEMORY;
        This->modules = modules;
    }

    if (!(module = create_module(host, name)))
        return E_OUTOFMEMORY;

    /* If no object, Windows only calls AddNamedItem without adding a NULL object */
    if (V_DISPATCH(object))
        hr = add_script_object(host, name, V_DISPATCH(object), 0);
    else
        hr = IActiveScript_AddNamedItem(host->script, name, SCRIPTITEM_CODEONLY);

    if (FAILED(hr))
    {
        IScriptModule_Release(&module->IScriptModule_iface);
        return hr;
    }
    This->modules[host->module_count++] = module;

    *ppmod = &module->IScriptModule_iface;
    IScriptModule_AddRef(*ppmod);
    return S_OK;
}

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 void fill_error_info(ScriptError *error)
{
    EXCEPINFO info;

    if (error->info_filled) return;
    error->info_filled = TRUE;

    if (!error->object)
        return;
    if (FAILED(IActiveScriptError_GetExceptionInfo(error->object, &info)))
        return;
    if (info.pfnDeferredFillIn)
        info.pfnDeferredFillIn(&info);

    error->number = info.scode;
    error->source = info.bstrSource;
    error->desc = info.bstrDescription;
    error->help_file = info.bstrHelpFile;
    error->help_context = info.dwHelpContext;
}

static void fill_error_text(ScriptError *error)
{
    if (error->text_filled) return;
    error->text_filled = TRUE;

    if (error->object)
        IActiveScriptError_GetSourceLineText(error->object, &error->text);
}

static void fill_error_pos(ScriptError *error)
{
    DWORD context;
    LONG column;
    ULONG line;

    if (error->pos_filled) return;
    error->pos_filled = TRUE;

    if (!error->object)
        return;
    if (FAILED(IActiveScriptError_GetSourcePosition(error->object, &context, &line, &column)))
        return;

    error->line = line;
    error->column = column;
}

static HRESULT WINAPI ScriptError_QueryInterface(IScriptError *iface, REFIID riid, void **ppv)
{
    ScriptError *This = impl_from_IScriptError(iface);

    if (IsEqualGUID(&IID_IDispatch, riid) || IsEqualGUID(&IID_IUnknown, riid) ||
        IsEqualGUID(&IID_IScriptError, riid))
    {
        *ppv = &This->IScriptError_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 ScriptError_AddRef(IScriptError *iface)
{
    ScriptError *This = impl_from_IScriptError(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI ScriptError_Release(IScriptError *iface)
{
    ScriptError *This = impl_from_IScriptError(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if (!ref)
    {
        IScriptError_Clear(&This->IScriptError_iface);
        heap_free(This);
    }

    return ref;
}

static HRESULT WINAPI ScriptError_GetTypeInfoCount(IScriptError *iface, UINT *pctinfo)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, pctinfo);

    *pctinfo = 1;
    return S_OK;
}

static HRESULT WINAPI ScriptError_GetTypeInfo(IScriptError *iface, UINT iTInfo,
        LCID lcid, ITypeInfo **ppTInfo)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);

    return get_typeinfo(IScriptError_tid, ppTInfo);
}

static HRESULT WINAPI ScriptError_GetIDsOfNames(IScriptError *iface, REFIID riid,
        LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
    ScriptError *This = impl_from_IScriptError(iface);
    ITypeInfo *typeinfo;
    HRESULT hr;

    TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);

    hr = get_typeinfo(IScriptError_tid, &typeinfo);
    if (SUCCEEDED(hr))
    {
        hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
        ITypeInfo_Release(typeinfo);
    }

    return hr;
}

static HRESULT WINAPI ScriptError_Invoke(IScriptError *iface, DISPID dispIdMember,
        REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    ScriptError *This = impl_from_IScriptError(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(IScriptError_tid, &typeinfo);
    if(SUCCEEDED(hr))
    {
        hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
                              pDispParams, pVarResult, pExcepInfo, puArgErr);
        ITypeInfo_Release(typeinfo);
    }

    return hr;
}

static HRESULT WINAPI ScriptError_get_Number(IScriptError *iface, LONG *plNumber)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, plNumber);

    fill_error_info(This);
    *plNumber = This->number;
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_Source(IScriptError *iface, BSTR *pbstrSource)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, pbstrSource);

    fill_error_info(This);
    *pbstrSource = SysAllocString(This->source);
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_Description(IScriptError *iface, BSTR *pbstrDescription)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, pbstrDescription);

    fill_error_info(This);
    *pbstrDescription = SysAllocString(This->desc);
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_HelpFile(IScriptError *iface, BSTR *pbstrHelpFile)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, pbstrHelpFile);

    fill_error_info(This);
    *pbstrHelpFile = SysAllocString(This->help_file);
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_HelpContext(IScriptError *iface, LONG *plHelpContext)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, plHelpContext);

    fill_error_info(This);
    *plHelpContext = This->help_context;
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_Text(IScriptError *iface, BSTR *pbstrText)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, pbstrText);

    fill_error_text(This);
    *pbstrText = SysAllocString(This->text);
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_Line(IScriptError *iface, LONG *plLine)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, plLine);

    fill_error_pos(This);
    *plLine = This->line;
    return S_OK;
}

static HRESULT WINAPI ScriptError_get_Column(IScriptError *iface, LONG *plColumn)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->(%p)\n", This, plColumn);

    fill_error_pos(This);
    *plColumn = This->column;
    return S_OK;
}

static HRESULT WINAPI ScriptError_Clear(IScriptError *iface)
{
    ScriptError *This = impl_from_IScriptError(iface);

    TRACE("(%p)->()\n", This);

    if (This->object)
    {
        IActiveScriptError_Release(This->object);
        This->object = NULL;
    }
    SysFreeString(This->text);
    SysFreeString(This->source);
    SysFreeString(This->desc);
    SysFreeString(This->help_file);

    This->number = 0;
    This->text = NULL;
    This->source = NULL;
    This->desc = NULL;
    This->help_file = NULL;
    This->help_context = 0;
    This->line = 0;
    This->column = 0;

    This->info_filled = FALSE;
    This->text_filled = FALSE;
    This->pos_filled = FALSE;
    return S_OK;
}

static const IScriptErrorVtbl ScriptErrorVtbl = {
    ScriptError_QueryInterface,
    ScriptError_AddRef,
    ScriptError_Release,
    ScriptError_GetTypeInfoCount,
    ScriptError_GetTypeInfo,
    ScriptError_GetIDsOfNames,
    ScriptError_Invoke,
    ScriptError_get_Number,
    ScriptError_get_Source,
    ScriptError_get_Description,
    ScriptError_get_HelpFile,
    ScriptError_get_HelpContext,
    ScriptError_get_Text,
    ScriptError_get_Line,
    ScriptError_get_Column,
    ScriptError_Clear
};

static HRESULT set_safety_opts(IActiveScript *script, VARIANT_BOOL use_safe_subset)
{
    IObjectSafety *objsafety;
    HRESULT hr;

    hr = IActiveScript_QueryInterface(script, &IID_IObjectSafety, (void**)&objsafety);
    if (FAILED(hr)) {
        FIXME("Could not get IObjectSafety, %#x\n", hr);
        return hr;
    }

    hr = IObjectSafety_SetInterfaceSafetyOptions(objsafety, &IID_IActiveScriptParse, INTERFACESAFE_FOR_UNTRUSTED_DATA,
                                                 use_safe_subset ? INTERFACESAFE_FOR_UNTRUSTED_DATA : 0);
    IObjectSafety_Release(objsafety);
    if (FAILED(hr)) {
        FIXME("SetInterfaceSafetyOptions failed, %#x\n", hr);
        return hr;
    }

    return hr;
}

static HRESULT init_script_host(ScriptControl *control, const CLSID *clsid, ScriptHost **ret)
{
    ScriptHost *host;
    HRESULT hr;

    *ret = NULL;

    host = heap_alloc(sizeof(*host));
    if (!host)
        return E_OUTOFMEMORY;

    host->IActiveScriptSite_iface.lpVtbl = &ActiveScriptSiteVtbl;
    host->IActiveScriptSiteWindow_iface.lpVtbl = &ActiveScriptSiteWindowVtbl;
    host->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl;
    host->ref = 1;
    host->script = NULL;
    host->parse = NULL;
    host->clsid = *clsid;
    host->module_count = 1;
    list_init(&host->named_items);

    hr = CoCreateInstance(&host->clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
            &IID_IActiveScript, (void**)&host->script);
    if (FAILED(hr)) {
        WARN("Failed to create an instance for %s, %#x\n", debugstr_guid(clsid), hr);
        goto failed;
    }

    hr = set_safety_opts(host->script, control->use_safe_subset);
    if (FAILED(hr)) goto failed;

    hr = IActiveScript_SetScriptSite(host->script, &host->IActiveScriptSite_iface);
    if (FAILED(hr)) {
        WARN("SetScriptSite failed, %#x\n", hr);
        goto failed;
    }

    hr = IActiveScript_QueryInterface(host->script, &IID_IActiveScriptParse, (void**)&host->parse);
    if (FAILED(hr)) {
        WARN("Failed to get IActiveScriptParse, %#x\n", hr);
        goto failed;
    }

    hr = IActiveScriptParse_InitNew(host->parse);
    if (FAILED(hr)) {
        WARN("InitNew failed, %#x\n", hr);
        goto failed;
    }
    host->script_state = SCRIPTSTATE_INITIALIZED;
    host->site_hwnd = control->allow_ui ? control->site_hwnd : ((HWND)-1);
    host->error = control->error;
    IScriptError_AddRef(&host->error->IScriptError_iface);

    *ret = host;
    return S_OK;

failed:
    detach_script_host(host);
    return hr;
}

static HRESULT WINAPI ScriptControl_QueryInterface(IScriptControl *iface, REFIID riid, void **ppv)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = &This->IScriptControl_iface;
    }else if(IsEqualGUID(&IID_IDispatch, riid)) {
        TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
        *ppv = &This->IScriptControl_iface;
    }else if(IsEqualGUID(&IID_IScriptControl, riid)) {
        TRACE("(%p)->(IID_IScriptControl %p)\n", This, ppv);
        *ppv = &This->IScriptControl_iface;
    }else if(IsEqualGUID(&IID_IOleObject, riid)) {
        TRACE("(%p)->(IID_IOleObject %p)\n", This, ppv);
        *ppv = &This->IOleObject_iface;
    }else if(IsEqualGUID(&IID_IPersistStreamInit, riid)) {
        TRACE("(%p)->(IID_IPersistStreamInit %p)\n", This, ppv);
        *ppv = &This->IPersistStreamInit_iface;
    }else if(IsEqualGUID(&IID_IPersist, riid)) {
        TRACE("(%p)->(IID_IPersist %p)\n", This, ppv);
        *ppv = &This->IPersistStreamInit_iface;
    }else if(IsEqualGUID(&IID_IOleControl, riid)) {
        TRACE("(%p)->(IID_IOleControl %p)\n", This, ppv);
        *ppv = &This->IOleControl_iface;
    }else if(IsEqualGUID(&IID_IQuickActivate, riid)) {
        TRACE("(%p)->(IID_IQuickActivate %p)\n", This, ppv);
        *ppv = &This->IQuickActivate_iface;
    }else if(IsEqualGUID(&IID_IViewObject, riid)) {
        TRACE("(%p)->(IID_IViewObject %p)\n", This, ppv);
        *ppv = &This->IViewObjectEx_iface;
    }else if(IsEqualGUID(&IID_IViewObject2, riid)) {
        TRACE("(%p)->(IID_IViewObject2 %p)\n", This, ppv);
        *ppv = &This->IViewObjectEx_iface;
    }else if(IsEqualGUID(&IID_IViewObjectEx, riid)) {
        TRACE("(%p)->(IID_IViewObjectEx %p)\n", This, ppv);
        *ppv = &This->IViewObjectEx_iface;
    }else if(IsEqualGUID(&IID_IPointerInactive, riid)) {
        TRACE("(%p)->(IID_IPointerInactive %p)\n", This, ppv);
        *ppv = &This->IPointerInactive_iface;
    }else if(IsEqualGUID(&IID_IConnectionPointContainer, riid)) {
        TRACE("(%p)->(IID_IConnectionPointContainer %p)\n", This, ppv);
        *ppv = &This->IConnectionPointContainer_iface;
    }else {
        FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI ScriptControl_AddRef(IScriptControl *iface)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI ScriptControl_Release(IScriptControl *iface)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if(!ref) {
        if (This->site)
            IOleClientSite_Release(This->site);
        if (This->host)
        {
            release_modules(This, FALSE);
            IActiveScriptSite_Release(&This->host->IActiveScriptSite_iface);
        }
        IScriptError_Release(&This->error->IScriptError_iface);
        heap_free(This);
    }

    return ref;
}

static HRESULT WINAPI ScriptControl_GetTypeInfoCount(IScriptControl *iface, UINT *pctinfo)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%p)\n", This, pctinfo);
    *pctinfo = 1;
    return S_OK;
}

static HRESULT WINAPI ScriptControl_GetTypeInfo(IScriptControl *iface, UINT iTInfo,
        LCID lcid, ITypeInfo **ppTInfo)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
    return get_typeinfo(IScriptControl_tid, ppTInfo);
}

static HRESULT WINAPI ScriptControl_GetIDsOfNames(IScriptControl *iface, REFIID riid,
        LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    ITypeInfo *typeinfo;
    HRESULT hres;

    TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);

    hres = get_typeinfo(IScriptControl_tid, &typeinfo);
    if(SUCCEEDED(hres)) {
        hres = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
        ITypeInfo_Release(typeinfo);
    }

    return hres;
}

static HRESULT WINAPI ScriptControl_Invoke(IScriptControl *iface, DISPID dispIdMember,
        REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    ITypeInfo *typeinfo;
    HRESULT hres;

    TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);

    hres = get_typeinfo(IScriptControl_tid, &typeinfo);
    if(SUCCEEDED(hres)) {
        hres = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
                pDispParams, pVarResult, pExcepInfo, puArgErr);
        ITypeInfo_Release(typeinfo);
    }

    return hres;
}

static HRESULT WINAPI ScriptControl_get_Language(IScriptControl *iface, BSTR *p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    LPOLESTR progidW;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, p);

    if (!p)
        return E_POINTER;

    *p = NULL;

    if (!This->host)
        return S_OK;

    hr = ProgIDFromCLSID(&This->host->clsid, &progidW);
    if (FAILED(hr))
        return hr;

    *p = SysAllocString(progidW);
    CoTaskMemFree(progidW);
    return *p ? S_OK : E_OUTOFMEMORY;
}

static HRESULT WINAPI ScriptControl_put_Language(IScriptControl *iface, BSTR language)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    CLSID clsid;
    HRESULT hres;

    TRACE("(%p)->(%s)\n", This, debugstr_w(language));

    if (language && FAILED(CLSIDFromProgID(language, &clsid)))
        return CTL_E_INVALIDPROPERTYVALUE;

    if (This->host) {
        release_modules(This, TRUE);
        IActiveScriptSite_Release(&This->host->IActiveScriptSite_iface);
        This->host = NULL;
    }

    if (!language)
        return S_OK;

    hres = init_script_host(This, &clsid, &This->host);
    if (FAILED(hres))
        return hres;

    /* Alloc global module */
    This->modules = heap_alloc_zero(sizeof(*This->modules));
    if (This->modules) {
        This->modules[0] = create_module(This->host, NULL);
        if (!This->modules[0]) {
            heap_free(This->modules);
            This->modules = NULL;
            hres = E_OUTOFMEMORY;
        }
    }
    else
        hres = E_OUTOFMEMORY;

    if (FAILED(hres)) {
        detach_script_host(This->host);
        This->host = NULL;
    }
    return hres;
}

static HRESULT WINAPI ScriptControl_get_State(IScriptControl *iface, ScriptControlStates *p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    SCRIPTSTATE state;
    HRESULT hres;

    TRACE("(%p)->(%p)\n", This, p);

    if(!p)
        return E_POINTER;

    if(!This->host)
        return E_FAIL;

    hres = IActiveScript_GetScriptState(This->host->script, &state);
    if (FAILED(hres)) return hres;

    switch (state)
    {
    case SCRIPTSTATE_INITIALIZED:
    case SCRIPTSTATE_STARTED:
        *p = Initialized;
        break;
    case SCRIPTSTATE_CONNECTED:
        *p = Connected;
        break;
    default:
        WARN("unexpected state %d\n", state);
        return E_FAIL;
    }
    return S_OK;
}

static HRESULT WINAPI ScriptControl_put_State(IScriptControl *iface, ScriptControlStates state)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%x)\n", This, state);

    if(!This->host)
        return E_FAIL;

    if(state != Initialized && state != Connected)
        return CTL_E_INVALIDPROPERTYVALUE;

    return IActiveScript_SetScriptState(This->host->script, state == Connected ? SCRIPTSTATE_CONNECTED : SCRIPTSTATE_STARTED);
}

static HRESULT WINAPI ScriptControl_put_SitehWnd(IScriptControl *iface, LONG hwnd)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%x)\n", This, hwnd);

    if (hwnd && !IsWindow(LongToHandle(hwnd)))
        return CTL_E_INVALIDPROPERTYVALUE;

    This->site_hwnd = LongToHandle(hwnd);
    if (This->host)
        This->host->site_hwnd = This->allow_ui ? This->site_hwnd : ((HWND)-1);
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_SitehWnd(IScriptControl *iface, LONG *p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%p)\n", This, p);

    if (!p) return E_POINTER;

    *p = HandleToLong(This->site_hwnd);
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_Timeout(IScriptControl *iface, LONG *p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%p)\n", This, p);

    if (!p)
        return E_POINTER;

    *p = This->timeout;
    return S_OK;
}

static HRESULT WINAPI ScriptControl_put_Timeout(IScriptControl *iface, LONG timeout)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%d)\n", This, timeout);

    if (timeout < -1)
        return CTL_E_INVALIDPROPERTYVALUE;

    if (timeout != -1)
        FIXME("execution timeout ignored\n");

    This->timeout = timeout;
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_AllowUI(IScriptControl *iface, VARIANT_BOOL *p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%p)\n", This, p);

    if(!p)
        return E_POINTER;

    *p = This->allow_ui;
    return S_OK;
}

static HRESULT WINAPI ScriptControl_put_AllowUI(IScriptControl *iface, VARIANT_BOOL allow_ui)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%x)\n", This, allow_ui);

    This->allow_ui = allow_ui;
    if (This->host)
        This->host->site_hwnd = allow_ui ? This->site_hwnd : ((HWND)-1);
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_UseSafeSubset(IScriptControl *iface, VARIANT_BOOL *p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%p)\n", This, p);

    if(!p)
        return E_POINTER;

    *p = This->use_safe_subset;
    return S_OK;
}

static HRESULT WINAPI ScriptControl_put_UseSafeSubset(IScriptControl *iface, VARIANT_BOOL use_safe_subset)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    TRACE("(%p)->(%x)\n", This, use_safe_subset);

    if (This->host && This->use_safe_subset != use_safe_subset)
        set_safety_opts(This->host->script, use_safe_subset);

    This->use_safe_subset = use_safe_subset;
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_Modules(IScriptControl *iface, IScriptModuleCollection **p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%p)\n", This, p);

    if (!This->host) return E_FAIL;

    *p = &This->IScriptModuleCollection_iface;
    IScriptControl_AddRef(iface);
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_Error(IScriptControl *iface, IScriptError **p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%p)\n", This, p);

    if (!p) return E_POINTER;

    *p = &This->error->IScriptError_iface;
    IScriptError_AddRef(*p);
    return S_OK;
}

static HRESULT WINAPI ScriptControl_get_CodeObject(IScriptControl *iface, IDispatch **p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%p)\n", This, p);

    if (!This->host) return E_FAIL;

    return IScriptModule_get_CodeObject(&This->modules[0]->IScriptModule_iface, p);
}

static HRESULT WINAPI ScriptControl_get_Procedures(IScriptControl *iface, IScriptProcedureCollection **p)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%p)\n", This, p);

    if (!This->host) return E_FAIL;

    return IScriptModule_get_Procedures(&This->modules[0]->IScriptModule_iface, p);
}

static HRESULT WINAPI ScriptControl__AboutBox(IScriptControl *iface)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI ScriptControl_AddObject(IScriptControl *iface, BSTR name, IDispatch *object, VARIANT_BOOL add_members)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    DWORD flags = SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE;

    TRACE("(%p)->(%s %p %x)\n", This, debugstr_w(name), object, add_members);

    if (!object)
        return E_INVALIDARG;

    if (!This->host)
        return E_FAIL;

    if (add_members)
        flags |= SCRIPTITEM_GLOBALMEMBERS;
    return add_script_object(This->host, name, object, flags);
}

static HRESULT WINAPI ScriptControl_Reset(IScriptControl *iface)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)\n", This);

    if (!This->host)
        return E_FAIL;

    clear_named_items(This->host);
    return set_script_state(This->host, SCRIPTSTATE_INITIALIZED);
}

static HRESULT WINAPI ScriptControl_AddCode(IScriptControl *iface, BSTR code)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%s).\n", This, debugstr_w(code));

    if (!This->host)
        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)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%s, %p).\n", This, debugstr_w(expression), res);

    if (!res)
        return E_POINTER;
    V_VT(res) = VT_EMPTY;
    if (!This->host)
        return E_FAIL;

    return parse_script_text(This->modules[0], expression, SCRIPTTEXT_ISEXPRESSION, res);
}

static HRESULT WINAPI ScriptControl_ExecuteStatement(IScriptControl *iface, BSTR statement)
{
    ScriptControl *This = impl_from_IScriptControl(iface);

    TRACE("(%p)->(%s)\n", This, debugstr_w(statement));

    if (!This->host)
        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)
{
    ScriptControl *This = impl_from_IScriptControl(iface);
    SAFEARRAY *sa;

    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;

    V_VT(res) = VT_EMPTY;
    if (sa->cDims == 0) return DISP_E_BADINDEX;
    if (!(sa->fFeatures & FADF_VARIANT)) return DISP_E_BADVARTYPE;
    if (!This->host) return E_FAIL;

    return run_procedure(This->modules[0], procedure_name, sa, res);
}

static const IScriptControlVtbl ScriptControlVtbl = {
    ScriptControl_QueryInterface,
    ScriptControl_AddRef,
    ScriptControl_Release,
    ScriptControl_GetTypeInfoCount,
    ScriptControl_GetTypeInfo,
    ScriptControl_GetIDsOfNames,
    ScriptControl_Invoke,
    ScriptControl_get_Language,
    ScriptControl_put_Language,
    ScriptControl_get_State,
    ScriptControl_put_State,
    ScriptControl_put_SitehWnd,
    ScriptControl_get_SitehWnd,
    ScriptControl_get_Timeout,
    ScriptControl_put_Timeout,
    ScriptControl_get_AllowUI,
    ScriptControl_put_AllowUI,
    ScriptControl_get_UseSafeSubset,
    ScriptControl_put_UseSafeSubset,
    ScriptControl_get_Modules,
    ScriptControl_get_Error,
    ScriptControl_get_CodeObject,
    ScriptControl_get_Procedures,
    ScriptControl__AboutBox,
    ScriptControl_AddObject,
    ScriptControl_Reset,
    ScriptControl_AddCode,
    ScriptControl_Eval,
    ScriptControl_ExecuteStatement,
    ScriptControl_Run
};

static HRESULT WINAPI OleObject_QueryInterface(IOleObject *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IOleObject(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI OleObject_AddRef(IOleObject *iface)
{
    ScriptControl *This = impl_from_IOleObject(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI OleObject_Release(IOleObject *iface)
{
    ScriptControl *This = impl_from_IOleObject(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI OleObject_SetClientSite(IOleObject *iface, IOleClientSite *site)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    TRACE("(%p)->(%p)\n", This, site);

    if (This->site)
        IOleClientSite_Release(This->site);

    if ((This->site = site))
        IOleClientSite_AddRef(site);

    return S_OK;
}

static HRESULT WINAPI OleObject_GetClientSite(IOleObject *iface, IOleClientSite **site)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    TRACE("(%p)->(%p)\n", This, site);

    if (!site)
        return E_POINTER;

    if ((*site = This->site))
        IOleClientSite_AddRef(*site);

    return S_OK;
}

static HRESULT WINAPI OleObject_SetHostNames(IOleObject *iface, LPCOLESTR containerapp, LPCOLESTR containerobj)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%s %s)\n", This, debugstr_w(containerapp), debugstr_w(containerobj));

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_Close(IOleObject *iface, DWORD save)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d)\n", This, save);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_SetMoniker(IOleObject *iface, DWORD which, IMoniker *moniker)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d %p)\n", This, which, moniker);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_GetMoniker(IOleObject *iface, DWORD assign, DWORD which, IMoniker **moniker)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d %d %p)\n", This, assign, which, moniker);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_InitFromData(IOleObject *iface, IDataObject *dataobj, BOOL creation,
    DWORD reserved)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%p %d %d)\n", This, dataobj, creation, reserved);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_GetClipboardData(IOleObject *iface, DWORD reserved, IDataObject **dataobj)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d %p)\n", This, reserved, dataobj);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_DoVerb(IOleObject *iface, LONG verb, LPMSG msg, IOleClientSite *active_site,
    LONG index, HWND hwndParent, LPCRECT rect)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d %p %p %d %p %p)\n", This, verb, msg, active_site, index, hwndParent, rect);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_EnumVerbs(IOleObject *iface, IEnumOLEVERB **enumoleverb)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%p)\n", This, enumoleverb);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_Update(IOleObject *iface)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)\n", This);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_IsUpToDate(IOleObject *iface)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)\n", This);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_GetUserClassID(IOleObject *iface, CLSID *clsid)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%p)\n", This, clsid);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_GetUserType(IOleObject *iface, DWORD form_of_type, LPOLESTR *usertype)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d %p)\n", This, form_of_type, usertype);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_SetExtent(IOleObject *iface, DWORD aspect, SIZEL *size)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d %p)\n", This, aspect, size);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_GetExtent(IOleObject *iface, DWORD aspect, SIZEL *size)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    TRACE("(%p)->(%d %p)\n", This, aspect, size);

    if (aspect != DVASPECT_CONTENT)
        return DV_E_DVASPECT;

    *size = This->extent;
    return S_OK;
}

static HRESULT WINAPI OleObject_Advise(IOleObject *iface, IAdviseSink *sink, DWORD *connection)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%p %p)\n", This, sink, connection);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_Unadvise(IOleObject *iface, DWORD connection)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%d)\n", This, connection);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_EnumAdvise(IOleObject *iface, IEnumSTATDATA **enumadvise)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%p)\n", This, enumadvise);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleObject_GetMiscStatus(IOleObject *iface, DWORD aspect, DWORD *status)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    TRACE("(%p)->(%d %p)\n", This, aspect, status);

    return OleRegGetMiscStatus(&CLSID_ScriptControl, aspect, status);
}

static HRESULT WINAPI OleObject_SetColorScheme(IOleObject *iface, LOGPALETTE *logpal)
{
    ScriptControl *This = impl_from_IOleObject(iface);

    FIXME("(%p)->(%p)\n", This, logpal);

    return E_NOTIMPL;
}

static const IOleObjectVtbl OleObjectVtbl = {
    OleObject_QueryInterface,
    OleObject_AddRef,
    OleObject_Release,
    OleObject_SetClientSite,
    OleObject_GetClientSite,
    OleObject_SetHostNames,
    OleObject_Close,
    OleObject_SetMoniker,
    OleObject_GetMoniker,
    OleObject_InitFromData,
    OleObject_GetClipboardData,
    OleObject_DoVerb,
    OleObject_EnumVerbs,
    OleObject_Update,
    OleObject_IsUpToDate,
    OleObject_GetUserClassID,
    OleObject_GetUserType,
    OleObject_SetExtent,
    OleObject_GetExtent,
    OleObject_Advise,
    OleObject_Unadvise,
    OleObject_EnumAdvise,
    OleObject_GetMiscStatus,
    OleObject_SetColorScheme
};

static HRESULT WINAPI PersistStreamInit_QueryInterface(IPersistStreamInit *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI PersistStreamInit_AddRef(IPersistStreamInit *iface)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI PersistStreamInit_Release(IPersistStreamInit *iface)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI PersistStreamInit_GetClassID(IPersistStreamInit *iface, CLSID *clsid)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);

    FIXME("(%p)->(%p)\n", This, clsid);

    return E_NOTIMPL;
}

static HRESULT WINAPI PersistStreamInit_IsDirty(IPersistStreamInit *iface)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);

    FIXME("(%p)\n", This);

    return E_NOTIMPL;
}

static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, IStream *stream)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);

    FIXME("(%p)->(%p)\n", This, stream);

    return E_NOTIMPL;
}

static HRESULT WINAPI PersistStreamInit_Save(IPersistStreamInit *iface, IStream *stream, BOOL clear_dirty)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);

    FIXME("(%p)->(%p %d)\n", This, stream, clear_dirty);

    return E_NOTIMPL;
}

static HRESULT WINAPI PersistStreamInit_GetSizeMax(IPersistStreamInit *iface, ULARGE_INTEGER *size)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);

    FIXME("(%p)->(%p)\n", This, size);

    return E_NOTIMPL;
}

static HRESULT WINAPI PersistStreamInit_InitNew(IPersistStreamInit *iface)
{
    ScriptControl *This = impl_from_IPersistStreamInit(iface);

    FIXME("(%p)\n", This);

    return S_OK;
}

static const IPersistStreamInitVtbl PersistStreamInitVtbl = {
    PersistStreamInit_QueryInterface,
    PersistStreamInit_AddRef,
    PersistStreamInit_Release,
    PersistStreamInit_GetClassID,
    PersistStreamInit_IsDirty,
    PersistStreamInit_Load,
    PersistStreamInit_Save,
    PersistStreamInit_GetSizeMax,
    PersistStreamInit_InitNew
};

static HRESULT WINAPI OleControl_QueryInterface(IOleControl *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IOleControl(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI OleControl_AddRef(IOleControl *iface)
{
    ScriptControl *This = impl_from_IOleControl(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI OleControl_Release(IOleControl *iface)
{
    ScriptControl *This = impl_from_IOleControl(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI OleControl_GetControlInfo(IOleControl *iface, CONTROLINFO *info)
{
    ScriptControl *This = impl_from_IOleControl(iface);

    TRACE("(%p)->(%p)\n", This, info);

    if (!info)
        return E_POINTER;

    info->hAccel = NULL;
    info->cAccel = 0;

    return S_OK;
}

static HRESULT WINAPI OleControl_OnMnemonic(IOleControl *iface, MSG *msg)
{
    ScriptControl *This = impl_from_IOleControl(iface);

    FIXME("(%p)->(%p)\n", This, msg);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleControl_OnAmbientPropertyChange(IOleControl *iface, DISPID dispid)
{
    ScriptControl *This = impl_from_IOleControl(iface);

    FIXME("(%p)->(%#x)\n", This, dispid);

    return E_NOTIMPL;
}

static HRESULT WINAPI OleControl_FreezeEvents(IOleControl *iface, BOOL freeze)
{
    ScriptControl *This = impl_from_IOleControl(iface);

    FIXME("(%p)->(%d)\n", This, freeze);

    return E_NOTIMPL;
}

static const IOleControlVtbl OleControlVtbl = {
    OleControl_QueryInterface,
    OleControl_AddRef,
    OleControl_Release,
    OleControl_GetControlInfo,
    OleControl_OnMnemonic,
    OleControl_OnAmbientPropertyChange,
    OleControl_FreezeEvents
};

static HRESULT WINAPI QuickActivate_QueryInterface(IQuickActivate *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IQuickActivate(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI QuickActivate_AddRef(IQuickActivate *iface)
{
    ScriptControl *This = impl_from_IQuickActivate(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI QuickActivate_Release(IQuickActivate *iface)
{
    ScriptControl *This = impl_from_IQuickActivate(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI QuickActivate_QuickActivate(IQuickActivate *iface, QACONTAINER *container, QACONTROL *control)
{
    ScriptControl *This = impl_from_IQuickActivate(iface);

    FIXME("(%p)->(%p %p)\n", This, container, control);

    return E_NOTIMPL;
}

static HRESULT WINAPI QuickActivate_SetContentExtent(IQuickActivate *iface, SIZEL *size)
{
    ScriptControl *This = impl_from_IQuickActivate(iface);

    FIXME("(%p)->(%p)\n", This, size);

    return E_NOTIMPL;
}

static HRESULT WINAPI QuickActivate_GetContentExtent(IQuickActivate *iface, SIZEL *size)
{
    ScriptControl *This = impl_from_IQuickActivate(iface);

    FIXME("(%p)->(%p)\n", This, size);

    return E_NOTIMPL;
}

static const IQuickActivateVtbl QuickActivateVtbl = {
    QuickActivate_QueryInterface,
    QuickActivate_AddRef,
    QuickActivate_Release,
    QuickActivate_QuickActivate,
    QuickActivate_SetContentExtent,
    QuickActivate_GetContentExtent
};

static HRESULT WINAPI ViewObject_QueryInterface(IViewObjectEx *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI ViewObject_AddRef(IViewObjectEx *iface)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI ViewObject_Release(IViewObjectEx *iface)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI ViewObject_Draw(IViewObjectEx *iface, DWORD drawaspect, LONG index, void *aspect,
    DVTARGETDEVICE *device, HDC target_dev, HDC hdc_draw, const RECTL *bounds, const RECTL *win_bounds,
    BOOL (STDMETHODCALLTYPE *fn_continue)(ULONG_PTR cont), ULONG_PTR cont)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %d %p %p %p %p %p %p %p %lu)\n", This, drawaspect, index, aspect, device, target_dev,
        hdc_draw, bounds, win_bounds, fn_continue, cont);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_GetColorSet(IViewObjectEx *iface, DWORD drawaspect, LONG index, void *aspect,
    DVTARGETDEVICE *device, HDC hic_target, LOGPALETTE **colorset)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %d %p %p %p %p)\n", This, drawaspect, index, aspect, device, hic_target,
        colorset);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_Freeze(IViewObjectEx *iface, DWORD drawaspect, LONG index, void *aspect,
    DWORD *freeze)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %d %p %p)\n", This, drawaspect, index, aspect, freeze);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_Unfreeze(IViewObjectEx *iface, DWORD freeze)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d)\n", This, freeze);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_SetAdvise(IViewObjectEx *iface, DWORD aspects, DWORD flags, IAdviseSink *sink)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    TRACE("(%p)->(%d %#x %p)\n", This, aspects, flags, sink);

    if (aspects != DVASPECT_CONTENT)
        return DV_E_DVASPECT;

    This->view_sink_flags = flags;
    if (This->view_sink)
        IAdviseSink_Release(This->view_sink);
    This->view_sink = sink;
    if (This->view_sink)
        IAdviseSink_AddRef(This->view_sink);

    return S_OK;
}

static HRESULT WINAPI ViewObject_GetAdvise(IViewObjectEx *iface, DWORD *aspects, DWORD *flags, IAdviseSink **sink)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    TRACE("(%p)->(%p %p %p)\n", This, aspects, flags, sink);

    if (aspects)
        *aspects = DVASPECT_CONTENT;
    if (flags)
        *flags = This->view_sink_flags;
    if (sink) {
        *sink = This->view_sink;
        if (*sink)
            IAdviseSink_AddRef(*sink);
    }

    return S_OK;
}

static HRESULT WINAPI ViewObject_GetExtent(IViewObjectEx *iface, DWORD draw_aspect, LONG index,
    DVTARGETDEVICE *device, SIZEL *size)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %d %p %p)\n", This, draw_aspect, index, device, size);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_GetRect(IViewObjectEx *iface, DWORD aspect, RECTL *rect)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %p)\n", This, aspect, rect);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_GetViewStatus(IViewObjectEx *iface, DWORD *status)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    TRACE("(%p)->(%p)\n", This, status);

    *status = VIEWSTATUS_OPAQUE;
    return S_OK;
}

static HRESULT WINAPI ViewObject_QueryHitPoint(IViewObjectEx *iface, DWORD aspect, const RECT *bounds,
    POINT pt, LONG close_hint, DWORD *hit_result)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %s %s %d %p)\n", This, aspect, wine_dbgstr_rect(bounds), wine_dbgstr_point(&pt), close_hint, hit_result);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_QueryHitRect(IViewObjectEx *iface, DWORD aspect, const RECT *bounds,
    const RECT *loc, LONG close_hint, DWORD *hit_result)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %s %s %d %p)\n", This, aspect, wine_dbgstr_rect(bounds), wine_dbgstr_rect(loc), close_hint, hit_result);

    return E_NOTIMPL;
}

static HRESULT WINAPI ViewObject_GetNaturalExtent(IViewObjectEx *iface, DWORD aspect, LONG index,
    DVTARGETDEVICE *device, HDC target_hdc, DVEXTENTINFO *extent_info, SIZEL *size)
{
    ScriptControl *This = impl_from_IViewObjectEx(iface);

    FIXME("(%p)->(%d %d %p %p %p %p)\n", This, aspect, index, device, target_hdc, extent_info, size);

    return E_NOTIMPL;
}

static const IViewObjectExVtbl ViewObjectExVtbl = {
    ViewObject_QueryInterface,
    ViewObject_AddRef,
    ViewObject_Release,
    ViewObject_Draw,
    ViewObject_GetColorSet,
    ViewObject_Freeze,
    ViewObject_Unfreeze,
    ViewObject_SetAdvise,
    ViewObject_GetAdvise,
    ViewObject_GetExtent,
    ViewObject_GetRect,
    ViewObject_GetViewStatus,
    ViewObject_QueryHitPoint,
    ViewObject_QueryHitRect,
    ViewObject_GetNaturalExtent
};

static HRESULT WINAPI PointerInactive_QueryInterface(IPointerInactive *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IPointerInactive(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI PointerInactive_AddRef(IPointerInactive *iface)
{
    ScriptControl *This = impl_from_IPointerInactive(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI PointerInactive_Release(IPointerInactive *iface)
{
    ScriptControl *This = impl_from_IPointerInactive(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI PointerInactive_GetActivationPolicy(IPointerInactive *iface, DWORD *policy)
{
    ScriptControl *This = impl_from_IPointerInactive(iface);

    TRACE("(%p)->(%p)\n", This, policy);

    if (!policy)
        return E_POINTER;

    *policy = 0;
    return S_OK;
}

static HRESULT WINAPI PointerInactive_OnInactiveMouseMove(IPointerInactive *iface, const RECT *bounds,
    LONG x, LONG y, DWORD key_state)
{
    ScriptControl *This = impl_from_IPointerInactive(iface);

    FIXME("(%p)->(%s %d %d %#x)\n", This, wine_dbgstr_rect(bounds), x, y, key_state);

    return E_NOTIMPL;
}

static HRESULT WINAPI PointerInactive_OnInactiveSetCursor(IPointerInactive *iface, const RECT *bounds,
    LONG x, LONG y, DWORD msg, BOOL set_always)
{
    ScriptControl *This = impl_from_IPointerInactive(iface);

    FIXME("(%p)->(%s %d %d %#x %d)\n", This, wine_dbgstr_rect(bounds), x, y, msg, set_always);

    return E_NOTIMPL;
}

static const IPointerInactiveVtbl PointerInactiveVtbl = {
    PointerInactive_QueryInterface,
    PointerInactive_AddRef,
    PointerInactive_Release,
    PointerInactive_GetActivationPolicy,
    PointerInactive_OnInactiveMouseMove,
    PointerInactive_OnInactiveSetCursor
};

static HRESULT WINAPI ConnectionPointContainer_QueryInterface(IConnectionPointContainer *iface, REFIID riid, void **obj)
{
    ScriptControl *This = impl_from_IConnectionPointContainer(iface);
    return IScriptControl_QueryInterface(&This->IScriptControl_iface, riid, obj);
}

static ULONG WINAPI ConnectionPointContainer_AddRef(IConnectionPointContainer *iface)
{
    ScriptControl *This = impl_from_IConnectionPointContainer(iface);
    return IScriptControl_AddRef(&This->IScriptControl_iface);
}

static ULONG WINAPI ConnectionPointContainer_Release(IConnectionPointContainer *iface)
{
    ScriptControl *This = impl_from_IConnectionPointContainer(iface);
    return IScriptControl_Release(&This->IScriptControl_iface);
}

static HRESULT WINAPI ConnectionPointContainer_EnumConnectionPoints(IConnectionPointContainer *iface, IEnumConnectionPoints **enum_points)
{
    ScriptControl *This = impl_from_IConnectionPointContainer(iface);

    FIXME("(%p)->(%p)\n", This, enum_points);

    return E_NOTIMPL;
}

static HRESULT WINAPI ConnectionPointContainer_FindConnectionPoint(IConnectionPointContainer *iface, REFIID riid, IConnectionPoint **cp)
{
    ScriptControl *This = impl_from_IConnectionPointContainer(iface);
    ConnectionPoint *iter;

    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), cp);

    *cp = NULL;

    for (iter = This->cp_list; iter; iter = iter->next) {
        if (IsEqualIID(iter->riid, riid))
            *cp = &iter->IConnectionPoint_iface;
    }

    if (*cp) {
        IConnectionPoint_AddRef(*cp);
        return S_OK;
    }

    FIXME("unsupported connection point %s\n", debugstr_guid(riid));
    return CONNECT_E_NOCONNECTION;
}

static const IConnectionPointContainerVtbl ConnectionPointContainerVtbl = {
    ConnectionPointContainer_QueryInterface,
    ConnectionPointContainer_AddRef,
    ConnectionPointContainer_Release,
    ConnectionPointContainer_EnumConnectionPoints,
    ConnectionPointContainer_FindConnectionPoint
};

static HRESULT WINAPI ConnectionPoint_QueryInterface(IConnectionPoint *iface,
        REFIID riid, void **ppv)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = &This->IConnectionPoint_iface;
    }else if(IsEqualGUID(&IID_IConnectionPoint, riid)) {
        TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
        *ppv = &This->IConnectionPoint_iface;
    }else {
        FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI ConnectionPoint_AddRef(IConnectionPoint *iface)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
    return IConnectionPointContainer_AddRef(&This->control->IConnectionPointContainer_iface);
}

static ULONG WINAPI ConnectionPoint_Release(IConnectionPoint *iface)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
    return IConnectionPointContainer_Release(&This->control->IConnectionPointContainer_iface);
}

static HRESULT WINAPI ConnectionPoint_GetConnectionInterface(IConnectionPoint *iface, IID *iid)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    TRACE("(%p)->(%p)\n", This, iid);

    *iid = *This->riid;
    return S_OK;
}

static HRESULT WINAPI ConnectionPoint_GetConnectionPointContainer(IConnectionPoint *iface,
        IConnectionPointContainer **container)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    TRACE("(%p)->(%p)\n", This, container);

    if (!container)
        return E_POINTER;

    *container = &This->control->IConnectionPointContainer_iface;
    IConnectionPointContainer_AddRef(*container);

    return S_OK;
}

static HRESULT WINAPI ConnectionPoint_Advise(IConnectionPoint *iface, IUnknown *unk_sink,
        DWORD *cookie)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    FIXME("(%p)->(%p %p)\n", This, unk_sink, cookie);

    return E_NOTIMPL;
}

static HRESULT WINAPI ConnectionPoint_Unadvise(IConnectionPoint *iface, DWORD cookie)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    FIXME("(%p)->(%d)\n", This, cookie);

    return E_NOTIMPL;
}

static HRESULT WINAPI ConnectionPoint_EnumConnections(IConnectionPoint *iface,
        IEnumConnections **ppEnum)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    FIXME("(%p)->(%p): stub\n", This, ppEnum);

    return E_NOTIMPL;
}

static const IConnectionPointVtbl ConnectionPointVtbl =
{
    ConnectionPoint_QueryInterface,
    ConnectionPoint_AddRef,
    ConnectionPoint_Release,
    ConnectionPoint_GetConnectionInterface,
    ConnectionPoint_GetConnectionPointContainer,
    ConnectionPoint_Advise,
    ConnectionPoint_Unadvise,
    ConnectionPoint_EnumConnections
};

static void ConnectionPoint_Init(ConnectionPoint *cp, ScriptControl *sc, REFIID riid)
{
    cp->IConnectionPoint_iface.lpVtbl = &ConnectionPointVtbl;
    cp->control = sc;
    cp->riid = riid;

    cp->next = sc->cp_list;
    sc->cp_list = cp;
}

static HRESULT WINAPI ScriptControl_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
{
    ScriptControl *script_control;
    DWORD dpi_x, dpi_y;
    HRESULT hres;
    HDC hdc;

    TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv);

    script_control = heap_alloc_zero(sizeof(*script_control));
    if(!script_control)
        return E_OUTOFMEMORY;

    script_control->error = heap_alloc_zero(sizeof(*script_control->error));
    if(!script_control->error)
    {
        heap_free(script_control);
        return E_OUTOFMEMORY;
    }

    script_control->IScriptControl_iface.lpVtbl = &ScriptControlVtbl;
    script_control->IPersistStreamInit_iface.lpVtbl = &PersistStreamInitVtbl;
    script_control->IOleObject_iface.lpVtbl = &OleObjectVtbl;
    script_control->IOleControl_iface.lpVtbl = &OleControlVtbl;
    script_control->IQuickActivate_iface.lpVtbl = &QuickActivateVtbl;
    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->timeout = 10000;
    script_control->allow_ui = VARIANT_TRUE;
    script_control->use_safe_subset = VARIANT_FALSE;

    script_control->error->IScriptError_iface.lpVtbl = &ScriptErrorVtbl;
    script_control->error->ref = 1;

    ConnectionPoint_Init(&script_control->cp_scsource, script_control, &DIID_DScriptControlSource);
    ConnectionPoint_Init(&script_control->cp_propnotif, script_control, &IID_IPropertyNotifySink);

    hdc = GetDC(0);
    dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
    dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
    ReleaseDC(0, hdc);

    script_control->extent.cx = MulDiv(38, 2540, dpi_x);
    script_control->extent.cy = MulDiv(38, 2540, dpi_y);

    hres = IScriptControl_QueryInterface(&script_control->IScriptControl_iface, riid, ppv);
    IScriptControl_Release(&script_control->IScriptControl_iface);
    return hres;
}

/******************************************************************
 *              DllMain (msscript.ocx.@)
 */
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
{
    TRACE("(%p %d %p)\n", instance, reason, reserved);

    switch(reason) {
    case DLL_PROCESS_ATTACH:
        msscript_instance = instance;
        DisableThreadLibraryCalls(instance);
        break;
    case DLL_PROCESS_DETACH:
        if(!reserved)
            release_typelib();
        break;
    }

    return TRUE;
}

static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv)
{
    *ppv = NULL;

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv);
        *ppv = iface;
    }else if(IsEqualGUID(&IID_IClassFactory, riid)) {
        TRACE("(%p)->(IID_IClassFactory %p)\n", iface, ppv);
        *ppv = iface;
    }

    if(*ppv) {
        IUnknown_AddRef((IUnknown*)*ppv);
        return S_OK;
    }

    WARN("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppv);
    return E_NOINTERFACE;
}

static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
{
    TRACE("(%p)\n", iface);
    return 2;
}

static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
{
    TRACE("(%p)\n", iface);
    return 1;
}

static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
{
    TRACE("(%p)->(%x)\n", iface, fLock);
    return S_OK;
}

static const IClassFactoryVtbl ScriptControlFactoryVtbl = {
    ClassFactory_QueryInterface,
    ClassFactory_AddRef,
    ClassFactory_Release,
    ScriptControl_CreateInstance,
    ClassFactory_LockServer
};

static IClassFactory ScriptControlFactory = { &ScriptControlFactoryVtbl };

/***********************************************************************
 *		DllGetClassObject	(msscript.ocx.@)
 */
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
    if(IsEqualGUID(&CLSID_ScriptControl, rclsid)) {
        TRACE("(CLSID_ScriptControl %s %p)\n", debugstr_guid(riid), ppv);
        return IClassFactory_QueryInterface(&ScriptControlFactory, riid, ppv);
    }

    FIXME("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
    return CLASS_E_CLASSNOTAVAILABLE;
}

/***********************************************************************
 *          DllCanUnloadNow (msscript.ocx.@)
 */
HRESULT WINAPI DllCanUnloadNow(void)
{
    TRACE("\n");
    return S_FALSE;
}

/***********************************************************************
 *          DllRegisterServer (msscript.ocx.@)
 */
HRESULT WINAPI DllRegisterServer(void)
{
    TRACE("()\n");
    return __wine_register_resources(msscript_instance);
}

/***********************************************************************
 *          DllUnregisterServer (msscript.ocx.@)
 */
HRESULT WINAPI DllUnregisterServer(void)
{
    TRACE("()\n");
    return __wine_unregister_resources(msscript_instance);
}
