//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:       bscript.cxx
//
//  Contents:   Implementation of CBServerScript
//
//----------------------------------------------------------------------------

#include "headers.hxx"
CScriptHost::CScriptHost(CMTScript *   pMT,
                         BOOL         fPrimary,
                         BOOL         fDispatchOnly)
      : _pMT(pMT),
        _fIsPrimaryScript(fPrimary)
{
    _ulRefs     = 1;

    VariantInit(&_vPubCache);
    VariantInit(&_vPrivCache);

    Assert(_dwPublicSN == 0);
    Assert(_dwPrivateSN == 0);
}

CScriptHost::~CScriptHost()
{
    int i;

    // Any thread can call the dtor.
    WHEN_DBG(_dwThreadId = GetCurrentThreadId());
    AssertSz(PopScript() == S_FALSE,
             "Script object not closed properly!");

    VariantClear(&_vPubCache);
    VariantClear(&_vPrivCache);

    for (i = 0; i < _aryEvtSinks.Size(); i++)
    {
        _aryEvtSinks[i]->Disconnect();
    }

    _aryEvtSinks.ReleaseAll();

    ReleaseInterface(_pTypeInfoIGlobalMTScript);
    ReleaseInterface(_pTypeInfoCMTScript);
}

HRESULT
CScriptHost::QueryInterface(REFIID iid, void **ppvObj)
{
    if (iid == IID_IGlobalMTScript || iid == IID_IUnknown || iid == IID_IDispatch)
    {
        *ppvObj = (IGlobalMTScript *)this;
    }
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }

    ((IUnknown *)*ppvObj)->AddRef();
    return S_OK;
}

DWORD
CScriptHost::ThreadMain()
{
    HRESULT        hr;
    CStr           cstrScript;
    VARIANT        varParam;
    SCRIPT_PARAMS *pscrParams;

    VariantInit(&varParam);

    VERIFY_THREAD();

    pscrParams = (SCRIPT_PARAMS*)_pvParams;


    cstrScript.Set(pscrParams->pszPath);

#if DBG == 1
    char achBuf[10];
    cstrScript.GetMultiByte(achBuf, 10);
    SetName(achBuf);
#endif

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
                              COINIT_DISABLE_OLE1DDE   |
                              COINIT_SPEED_OVER_MEMORY);
    if (!SUCCEEDED(hr))
    {
        ThreadStarted(hr);  // Free our creating thread
        goto Cleanup;
    }

    if (pscrParams->pvarParams)
    {
        if (V_VT(pscrParams->pvarParams) == VT_DISPATCH)
        {
            // Unmarshal the IDispatch pointer being handed to us from the
            // other thread.

            IDispatch *pDisp;
            DWORD      dwCookie = V_I4(pscrParams->pvarParams);

            hr = _pMT->_pGIT->GetInterfaceFromGlobal(dwCookie,
                                                     IID_IDispatch,
                                                     (LPVOID*)&pDisp);
            if (!hr)
            {
                V_VT(&varParam) = VT_DISPATCH;
                V_DISPATCH(&varParam) = pDisp;
            }
        }
        else
        {
            VariantCopy(&varParam, pscrParams->pvarParams);
        }
    }

    // Hold a reference on ourself while the script is running
    AddRef();

    if (_fIsPrimaryScript)
    {

        hr = THR(LoadTypeLibrary());

        // Ensure that ScriptMain() completes before we fire any other events.
        _fDontHandleEvents = TRUE;
    }

    if (hr)
    {
        ThreadStarted(hr);
        goto Cleanup;
    }
    hr = ExecuteTopLevelScript(cstrScript, &varParam);
    if (hr)
    {
        ThreadStarted(hr);
        TraceTag((tagError, "Failed to execute script: %x", hr));
        AssertSz(!_fIsPrimaryScript, "Failed to execute script");

        PostQuitMessage(0);
        goto Cleanup;
    }
    ThreadStarted(hr);
    FireEvent(DISPID_MTScript_ScriptMain, 0, NULL);
    //
    // Secondary scripts go away as soon as they're done.
    //
    if (_fIsPrimaryScript)
    {
        DWORD dwRet;

        _fDontHandleEvents = FALSE;

        dwRet = MessageEventPump(TRUE);

        AssertSz(dwRet == MEP_EXIT, "NONFATAL: Invalid return value from MessageEventPump!");
    }
    else
    {
        CScriptHost *pThis = this;

        PostToThread(_pMT,
                     MD_SECONDARYSCRIPTTERMINATE,
                     (LPVOID)&pThis,
                     sizeof(CScriptHost*));
    }

Cleanup:
    CloseScripts();

    VariantClear(&varParam);

    if (_fIsPrimaryScript)
    {
        int i;

        for (i = 0; i < s_arySyncEvents.Size(); i++)
        {
            CloseHandle(s_arySyncEvents[i]._hEvent);
            s_arySyncEvents[i]._cstrName.Free();
        }

        s_arySyncEvents.DeleteAll();

        for (i = 0; i < (int)s_cThreadLocks; i++)
        {
            DeleteCriticalSection(&s_aThreadLocks[i]._csLock);
            s_aThreadLocks[i]._cstrName.Free();
        }

        memset(&s_aThreadLocks, 0, sizeof(s_aThreadLocks));
        s_cThreadLocks = 0;
    }

    Release();
    CoUninitialize();
    return 0;
}

//+---------------------------------------------------------------------------
//
//  Member:     CScriptHost::MessageEventPump, public
//
//  Synopsis:   Empties our message queues (both windows' and our private
//              threadcomm queue)
//
//  Arguments:  [fWait]     -- If TRUE, will not return until an event occurs
//              [cEvents]   -- Count of events to monitor
//              [pEvents]   -- Pointer to list of event handles
//              [fAll]      -- If TRUE, don't return until all handles in
//                               pEvents are signaled.
//              [dwTimeout] -- Timeout after this many ms if nothing signals
//              [fNoEvents] -- If TRUE, don't fire events while waiting
//
//  Returns:    MEP_TIMEOUT: The given timeout period expired without any
//                           event objects becoming signaled. Returned only
//                           if dwTimeout != INFINITE
//              MEP_EXIT: An event occurred which is causing this thread to
//                        terminate. The caller should clean up and finish
//                        what it's doing.
//              MEP_FALLTHROUGH: Indicates that no objects signaled.
//                               Returned only if fWait==FALSE.
//              MEP_EVENT_0: If one (or all if fAll==TRUE) of the passed-in
//                           event handles became signaled. The index of the
//                           signaled handle is added to MEP_EVENT_0. Returned
//                           only if one or more event handles were passed in.
//
//----------------------------------------------------------------------------

DWORD
CScriptHost::MessageEventPump(BOOL    fWait,
                              UINT    cEvents   /* = 0        */,
                              HANDLE *pEvents   /* = NULL     */,
                              BOOL    fAll      /* = FALSE    */,
                              DWORD   dwTimeout /* = INFINITE */,
                              BOOL    fNoEvents /* = FALSE    */)
{
    CStackPtrAry<HANDLE, 5> aryHandles;

    MSG   msg;
    DWORD dwRet;
    DWORD mepReturn = MEP_FALLTHROUGH;

    _int64 i64Freq = 0;
    _int64 i64Time;
    _int64 i64Goal  = 0;
    long   lTimeout = dwTimeout;
    BOOL   fTimeout = dwTimeout != INFINITE;

    if (cEvents)
    {
        aryHandles.CopyIndirect(cEvents, pEvents, FALSE);
    }

    if (_fMustExitThread)
    {
        return MEP_EXIT;
    }

    // WARNING! aryHandles will get rebuilt under certain conditions below.
    // If you add code which adds handles to the array, you must update the
    // code below as well!

    if (!fNoEvents && !_fDontHandleEvents)
    {
        aryHandles.Insert(0, _hCommEvent);
    }
    else if (fNoEvents)
    {
        _fDontHandleEvents = TRUE;
    }

    if (fTimeout)
    {
        QueryPerformanceFrequency((LARGE_INTEGER*)&i64Freq);
        QueryPerformanceCounter((LARGE_INTEGER*)&i64Time);

        // Resolution must be at least milliseconds
        Assert(i64Freq >= 1000);

        // Compute the time when the timer will be complete, converted to ms
        i64Goal = ((i64Time * 1000) / i64Freq) + lTimeout;
    }

    do
    {
        //
        // Purge out all window messages (primarily for OLE's benefit).
        //
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
            {
                _fMustExitThread = TRUE;
                return MEP_EXIT;
            }

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (_fMustExitThread)
        {
            AbortScripts();
            return MEP_EXIT;
        }

        dwRet = MsgWaitForMultipleObjects(aryHandles.Size(),
                                          aryHandles,
                                          FALSE,
                                          (fWait) ? (DWORD)lTimeout : 0,
                                          QS_ALLINPUT);

        if (dwRet == WAIT_OBJECT_0 && !_fDontHandleEvents)
        {
            //
            // Another thread is sending us a message.
            //
            HandleThreadMessage();
        }
        else if (dwRet < WAIT_OBJECT_0 + aryHandles.Size())
        {
            Assert(cEvents);

            int iEvent = dwRet - WAIT_OBJECT_0;

            //
            // One of the events the script is waiting for has been signaled.
            //
            if (fAll)
            {
                // They want to wait for all the events. Remove the signaled
                // event from the array and if it's the last one then we're
                // there!

                aryHandles.Delete(iEvent);

                if (aryHandles.Size() == ((_fDontHandleEvents) ? 0 : 1))
                {
                    // All the events have come back signaled. Check that none
                    // have become unsignaled.
                    if (WaitForMultipleObjects(cEvents, pEvents, TRUE, 0) == WAIT_TIMEOUT)
                    {
                        // Something became unsignaled. Start over! Rebuild
                        // the array of handles.

                        aryHandles.CopyIndirect(cEvents, pEvents, FALSE);

                        if (!_fDontHandleEvents)
                        {
                            aryHandles.Insert(0, _hCommEvent);
                        }
                    }
                    else
                    {
                        mepReturn = MEP_EVENT_0;

                        break;
                    }
                }
            }
            else
            {
                mepReturn = MEP_EVENT_0 + iEvent;

                if (!_fDontHandleEvents)
                {
                    mepReturn--;
                }

                break;
            }
        }
        else if (dwRet == WAIT_OBJECT_0 + aryHandles.Size())
        {
            //
            // A windows message came through. It will be handled at the
            // top of the loop.
            //
        }
        else if (dwRet == WAIT_FAILED)
        {
            TraceTag((tagError, "WaitForMultipleObjects failure (%d)", GetLastError()));

            AssertSz(FALSE, "WaitForMultipleObjects failure");

            _fMustExitThread = TRUE;

            mepReturn = MEP_EXIT;

            break;
        }
        else
        {
            Assert(dwRet == WAIT_TIMEOUT);

            mepReturn = MEP_TIMEOUT;

            break;
        }

        // Since any number of things could have brought us out of MWFMO,
        // we need to compute the remaining timeout for the next time around.

        if (fTimeout)
        {
            QueryPerformanceCounter((LARGE_INTEGER*)&i64Time);

            // Convert current time to milliseconds.
            i64Time = ((i64Time * 1000) / i64Freq);

            // Compute the delta between the current time and our goal
            lTimeout = (DWORD)(i64Goal - i64Time);

            // Are we timed out?
            if (lTimeout <= 0)
            {
                mepReturn = MEP_TIMEOUT;

                break;
            }
        }
    }
    while (fWait);  // Only do the loop once if fWait == FALSE

    if (fNoEvents)
    {
        _fDontHandleEvents = FALSE;
    }

    // MEP_FALLTHROUGH is not a valid return if fWait == TRUE
    Assert(!fWait || mepReturn != MEP_FALLTHROUGH);

    return mepReturn;
}

void
CScriptHost::HandleThreadMessage()
{
    VERIFY_THREAD();

    THREADMSG tm;
    BYTE      bData[MSGDATABUFSIZE];
    DWORD     cbData;

    if (_fDontHandleEvents)
        return;

    //
    //$ FUTURE: Add a way to filter messages so we can check for MD_PLEASEEXIT
    // without pulling off the event messages
    //
    while (GetNextMsg(&tm, (void **)bData, &cbData))
    {
        switch (tm)
        {
        case MD_PLEASEEXIT:
            //
            // We're being asked to terminate.
            //
            AbortScripts();
            PostQuitMessage(0);
            break;

        case MD_MACHINECONNECT:
            AssertSz(_fIsPrimaryScript, "Non-primary script got machine event!");

            _fDontHandleEvents = TRUE;

            FireEvent(DISPID_MTScript_OnMachineConnect, 0, NULL);

            _fDontHandleEvents = FALSE;
            break;

        case MD_MACHEVENTCALL:
            AssertSz(_fIsPrimaryScript, "Non-primary script got machine event!");
            Assert(cbData == sizeof(MACHPROC_EVENT_DATA*));

            _fDontHandleEvents = TRUE;

            // This call will set the event object in the
            // MACHPROC_EVENT_DATA struct when everything completes.
            FireMachineEvent(*(MACHPROC_EVENT_DATA**)bData, TRUE);

            _fDontHandleEvents = FALSE;
            break;

        case MD_PROCESSDATA:
            Assert(cbData == sizeof(MACHPROC_EVENT_DATA*));

            _fDontHandleEvents = TRUE;

            // This call will set the event object in the
            // MACHPROC_EVENT_DATA struct when everything completes.
            FireMachineEvent(*(MACHPROC_EVENT_DATA**)bData, FALSE);

            _fDontHandleEvents = FALSE;
            break;

        case MD_PROCESSEXITED:
        case MD_PROCESSTERMINATED:
        case MD_PROCESSCONNECTED:
        case MD_PROCESSCRASHED:
            Assert(cbData == sizeof(CProcessThread*));

            _fDontHandleEvents = TRUE;

            FireProcessEvent(tm, *(CProcessThread**)bData);

            _fDontHandleEvents = FALSE;

            break;

        default:
            AssertSz(FALSE, "CScriptHost got a message it couldn't handle!");
            break;
        }
    }
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::PushScript
//
//  Create a new script site/engine and push it on the script stack
//
//---------------------------------------------------------------------------

HRESULT
CScriptHost::PushScript(TCHAR *pchName)
{
    VERIFY_THREAD();

    HRESULT hr;
    CScriptSite * pScriptSite;
    TCHAR       * pchFile;

    hr = LoadTypeLibrary();
    if (hr)
        goto Cleanup;

    pScriptSite = new CScriptSite(this);
    if(!pScriptSite)
    {
        hr = E_OUTOFMEMORY;
        goto Cleanup;
    }

    pchFile = _tcsrchr(pchName, _T('\\'));
    if (!pchFile)
    {
        pchFile = pchName;
    }
    else
        pchFile++;

    hr = pScriptSite->Init(pchFile);
    if (hr)
    {
        delete pScriptSite;
        pScriptSite = NULL;
        goto Cleanup;
    }

    pScriptSite->_pScriptSitePrev = _pScriptSite;
    _pScriptSite = pScriptSite;

Cleanup:
    RRETURN(hr);
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::PopScript
//
//  Pop last script site/engine off the script stack
//
//---------------------------------------------------------------------------

HRESULT
CScriptHost::PopScript()
{
    VERIFY_THREAD();

    CScriptSite * pScriptSite = _pScriptSite;

    if(!_pScriptSite)
        return S_FALSE;

    _pScriptSite = _pScriptSite->_pScriptSitePrev;

    pScriptSite->Close();
    pScriptSite->Release();

    return S_OK;
}


//---------------------------------------------------------------------------
//
//  Member: CScriptHost::CloseScripts
//
//  Clear the stack of script engines
//
//---------------------------------------------------------------------------

HRESULT
CScriptHost::CloseScripts()
{
    VERIFY_THREAD();

    while(PopScript() == S_OK)
        ;

    AssertSz(_pScriptSite == NULL, "Should have released script site");

    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::AbortScripts
//
//  Clear the stack of script engines
//
//---------------------------------------------------------------------------

HRESULT
CScriptHost::AbortScripts()
{
    VERIFY_THREAD();

    // Make sure we're not stuck on MsgWaitForMultipleObjects and that we
    //   never will be again.

    _fMustExitThread = TRUE;
    SetEvent(_hCommEvent);

    CScriptSite * pScriptSite;

    pScriptSite = _pScriptSite;

    while (pScriptSite)
    {
        pScriptSite->Abort();

        pScriptSite = pScriptSite->_pScriptSitePrev;
    }

    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::ExecuteTopLevelScript
//
//  Close previous top level script engine then load and execute script
//  in a new top level script engine
//
//---------------------------------------------------------------------------

HRESULT
CScriptHost::ExecuteTopLevelScript(TCHAR * pchPath, VARIANT *pvarParams)
{
    VERIFY_THREAD();

    HRESULT hr;

    // Stablize reference count during script execution.
    // Script can hide window which decrements reference count.

    AddRef();

    // Getting read to close the scripts fire unload event.
    CloseScripts();

    hr = THR(PushScript(pchPath));
    if(hr)
        goto Cleanup;

    hr = THR(VariantCopy(&_pScriptSite->_varParam, pvarParams));
    if (hr)
        goto Cleanup;

    hr = THR(_pScriptSite->ExecuteScriptFile(pchPath));
    if(hr)
        goto Cleanup;

    hr = THR(_pScriptSite->SetScriptState(SCRIPTSTATE_CONNECTED));
    if (hr)
        goto Cleanup;

Cleanup:
    Release();
    RRETURN(hr);
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::ExecuteScriptlet
//
//  Add a scriptlet to the current top level script engine and execute it
//
//---------------------------------------------------------------------------

HRESULT
CScriptHost::ExecuteTopLevelScriptlet(TCHAR * pchScript)
{
    VERIFY_THREAD();

    HRESULT hr;

    // Stablize reference count during script execution.
    // Script can hide window which decrements reference count.

    AddRef();

    if(!_pScriptSite)
    {
        hr = THR(PushScript(_T("Scriptlet")));
        if(hr)
            goto Cleanup;
    }
    else
    {
        Assert(_pScriptSite->_pScriptSitePrev == NULL);
    }

    hr = THR(_pScriptSite->ExecuteScriptStr(pchScript));
    if (hr)
        goto Cleanup;

    hr = THR(_pScriptSite->SetScriptState(SCRIPTSTATE_CONNECTED));

Cleanup:
    Release();
    RRETURN(hr);
}

//+---------------------------------------------------------------------------
//
//  Member:     CScriptHost::FireProcessEvent, public
//
//  Synopsis:   Fires an OnProcessEvent event into the script
//
//----------------------------------------------------------------------------

void
CScriptHost::FireProcessEvent(THREADMSG tm, CProcessThread *pProc)
{
    VARIANTARG  varg[3];
    TCHAR      *pszMsg;
    DISPID      dispid = DISPID_MTScript_OnProcessEvent;

    VERIFY_THREAD();

    VariantInit(&varg[0]);
    VariantInit(&varg[1]);
    VariantInit(&varg[2]);

    // Parameters are in order from last to first

    V_VT(&varg[2]) = VT_I4;
    V_I4(&varg[2]) = pProc->ProcId();

    switch (tm)
    {
    case MD_PROCESSEXITED:
        pszMsg = _T("exited");

        V_VT(&varg[0]) = VT_I4;
        V_I4(&varg[0]) = pProc->GetExitCode();
        break;

    case MD_PROCESSCRASHED:
        pszMsg = _T("crashed");

        // 3rd parameter is empty
        break;

    case MD_PROCESSTERMINATED:
        pszMsg = _T("terminated");

        // 3rd parameter is empty
        break;

    case MD_PROCESSCONNECTED:
        pszMsg = _T("connected");

        // 3rd parameter is empty
        break;

    default:
        AssertSz(FALSE, "NONFATAL: Invalid THREADMSG value");
        return;
        break;
    }

    V_VT(&varg[1])   = VT_BSTR;
    V_BSTR(&varg[1]) = SysAllocString(pszMsg); // NULL is a valid value for BSTR

    FireEvent(dispid, 3, varg);

    VariantClear(&varg[0]);
    VariantClear(&varg[1]);
    VariantClear(&varg[2]);

    return;
}

long CScriptHost::FireScriptErrorEvent(
                                        TCHAR *bstrFile,
                                        long nLine,
                                        long nChar,
                                        TCHAR *bstrText,
                                        long sCode,
                                        TCHAR *bstrSource,
                                        TCHAR *bstrDescription)
{
    long      cSucceeded = 0;

    VERIFY_THREAD();

    // Parameters are in order from last to first
    AutoVariant varg[7];
    cSucceeded += varg[6].Set(bstrFile);
    cSucceeded += varg[5].Set(nLine);
    cSucceeded += varg[4].Set(nChar);
    cSucceeded += varg[3].Set(bstrText);
    cSucceeded += varg[2].Set(sCode);
    cSucceeded += varg[1].Set(bstrSource);
    cSucceeded += varg[0].Set(bstrDescription);

    if (cSucceeded != ARRAY_SIZE(varg))
        return 0; // Default return value

    AutoVariant varResult;
    FireEvent(DISPID_MTScript_OnScriptError, ARRAY_SIZE(varg), varg, &varResult);
    AutoVariant varResultInt;
    if (VariantChangeType(&varResultInt, &varResult, 0, VT_I4) == S_OK)
        return  V_I4(&varResultInt);

    return 0;
}
//---------------------------------------------------------------------------
//
//  Member: CScriptHost::FireMachineEvent
//
//  Notes:  Fires the OnRemoteExec event when a machine connected to us
//          remotely calls the Exec() method.
//
//---------------------------------------------------------------------------

void
CScriptHost::FireMachineEvent(MACHPROC_EVENT_DATA *pmed, BOOL fExec)
{

    VERIFY_THREAD();

    DISPID      dispid = (fExec)
                           ? DISPID_MTScript_OnRemoteExec
                           : DISPID_MTScript_OnProcessEvent;
    DISPPARAMS  dp;
    EXCEPINFO   ei;
    UINT        uArgErr = 0;
    VARIANTARG  vararg[3];
    VARIANTARG  varResult;
    HRESULT     hr = S_OK;

    pmed->hrReturn = S_OK;

    if (GetSite() && GetSite()->_pDispSink)
    {
        VariantInit(&vararg[0]);
        VariantInit(&vararg[1]);
        VariantInit(&vararg[2]);
        VariantInit(&varResult);

        // Params are in order from last to first in the array
        V_VT(&vararg[0]) = VT_BSTR;
        V_BSTR(&vararg[0]) = pmed->bstrParams;

        V_VT(&vararg[1]) = VT_BSTR;
        V_BSTR(&vararg[1]) = pmed->bstrCmd;

        if (!fExec)
        {
            V_VT(&vararg[2]) = VT_I4;
            V_I4(&vararg[2]) = pmed->dwProcId;
        }

        dp.rgvarg            = vararg;
        dp.rgdispidNamedArgs = NULL;
        dp.cArgs             = (fExec) ? 2 : 3;
        dp.cNamedArgs        = 0;

        hr = GetSite()->_pDispSink->Invoke(dispid,
                                           IID_NULL,
                                           0,
                                           DISPATCH_METHOD,
                                           &dp,
                                           &varResult,
                                           &ei,
                                           &uArgErr);
        pmed->hrReturn = hr;

        if (hr)
        {
            // If an error occurred, do nothing except return the error code.
        }
        // Check for data types which we don't support.
        else if (   V_ISBYREF(&varResult)
                 || V_ISARRAY(&varResult)
                 || V_ISVECTOR(&varResult)
                 || V_VT(&varResult) == VT_UNKNOWN)
        {
            // Do nothing. Return an empty result
            AssertSz(FALSE, "NONFATAL: Unsupported data type returned from OnRemoteExec event");
        }
        else if (V_VT(&varResult) == VT_DISPATCH)
        {
            if (fExec)
            {
                // Note that the return value is an IDispatch, but don't set the
                // pointer because it will need to be retrieved out of the GIT
                V_VT(pmed->pvReturn)       = VT_DISPATCH;
                V_DISPATCH(pmed->pvReturn) = NULL;

                hr =_pMT->_pGIT->RegisterInterfaceInGlobal(V_DISPATCH(&varResult),
                                                           IID_IDispatch,
                                                           &pmed->dwGITCookie);
                if (hr)
                {
                    pmed->hrReturn = hr;
                }
            }

            // Leave the result empty if they returned an IDispatch from an
            //   OnProcessEvent call.
        }
        else
        {
            VariantCopy(pmed->pvReturn, &varResult);
        }

        VariantClear(&varResult);
    }

    // Tell the calling thread we're done with the call and it can continue.
    SetEvent(pmed->hEvent);
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::FireEvent
//
//---------------------------------------------------------------------------

void
CScriptHost::FireEvent(DISPID dispid, UINT cArg, VARIANTARG *pvararg, VARIANTARG *pvarResult)
{
    VERIFY_THREAD();

    DISPPARAMS  dp;
    EXCEPINFO   ei;
    UINT        uArgErr = 0;

    if (GetSite() && GetSite()->_pDispSink)
    {
        dp.rgvarg            = pvararg;
        dp.rgdispidNamedArgs = NULL;
        dp.cArgs             = cArg;
        dp.cNamedArgs        = 0;

        GetSite()->_pDispSink->Invoke(
                dispid,
                IID_NULL,
                0,
                DISPATCH_METHOD,
                &dp,
                pvarResult,
                &ei,
                &uArgErr);
    }
}

void
CScriptHost::FireEvent(DISPID dispid, UINT carg, VARIANTARG *pvararg)
{
    FireEvent(dispid, carg, pvararg, NULL);
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::FireEvent
//
//---------------------------------------------------------------------------

void
CScriptHost::FireEvent(DISPID dispid, LPCTSTR pch)
{
    VERIFY_THREAD();

    VARIANT var;

    V_BSTR(&var) = SysAllocString(pch);
    V_VT(&var) = VT_BSTR;
    FireEvent(dispid, 1, &var);
    SysFreeString(V_BSTR(&var));
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::FireEvent
//
//---------------------------------------------------------------------------

void
CScriptHost::FireEvent(DISPID dispid, BOOL fArg)
{
    VERIFY_THREAD();

    VARIANT var;

    V_BOOL(&var) = fArg ? VARIANT_TRUE : VARIANT_FALSE;
    V_VT(&var) = VT_BOOL;
    FireEvent(dispid, 1, &var);
}

//---------------------------------------------------------------------------
//
//  Member: CScriptHost::FireEvent
//
//---------------------------------------------------------------------------

void
CScriptHost::FireEvent(DISPID dispid, IDispatch *pDisp)
{
    VERIFY_THREAD();

    VARIANT var;

    V_DISPATCH(&var) = pDisp;
    V_VT(&var) = VT_DISPATCH;
    FireEvent(dispid, 1, &var);
}

HRESULT
CScriptHost::LoadTypeLibrary()
{
    VERIFY_THREAD();

    HRESULT hr = S_OK;

    if (!_pTypeLibEXE)
    {
        // BUGBUG -- Is this valid, or does this need to be marshalled?

        _pTypeLibEXE = _pMT->_pTypeLibEXE;
    }

    if (!_pTypeInfoCMTScript)
    {
        hr = THR(_pTypeLibEXE->GetTypeInfoOfGuid(CLSID_LocalMTScript, &_pTypeInfoCMTScript));
        if (hr)
            goto Cleanup;
    }

    if (!_pTypeInfoIGlobalMTScript)
    {
        hr = THR(_pTypeLibEXE->GetTypeInfoOfGuid(IID_IGlobalMTScript, &_pTypeInfoIGlobalMTScript));
        if (hr)
            goto Cleanup;
    }

Cleanup:
    return hr;
}

void
CScriptHost::GetScriptPath(CStr *pcstrPath)
{
    _pMT->_options.GetScriptPath(pcstrPath);
}