October 1999
Figure 1   IActiveScript

IActiveScript : IUnknown
{
HRESULT SetScriptSite(IActiveScriptSite* pass) = 0;
HRESULT GetScriptSite(REFIID riid, void** ppvObject) = 0;
HRESULT SetScriptState(SCRIPTSTATE ss) = 0;
HRESULT GetScriptState(SCRIPTSTATE* pssState) = 0;
HRESULT Close( void) = 0;
HRESULT AddNamedItem(LPCOLESTR pstrName,
                     DWORD dwFlags) = 0;
HRESULT AddTypeLib(REFGUID rguidTypeLib,
                   DWORD dwMajor,
                   DWORD dwMinor,
                   DWORD dwFlags) = 0;
HRESULT GetScriptDispatch(LPCOLESTR pstrItemName,
                          IDispatch** ppdisp) = 0;
HRESULT GetCurrentScriptThreadID(SCRIPTTHREADID* pstidThread)=0;
HRESULT GetScriptThreadID(DWORD dwWin32ThreadId,
                          SCRIPTTHREADID* pstidThread) = 0;
HRESULT GetScriptThreadState(SCRIPTTHREADID stidThread,
                             SCRIPTTHREADSTATE* pstsState) = 0;
HRESULT InterruptScriptThread(SCRIPTTHREADID stidThread,
                              EXCEPINFO* pexcepinfo,    
                              DWORD dwFlags) = 0;
HRESULT Clone(IActiveScript** ppscript) = 0;
};

Figure 2   IActiveScriptParse


IActiveScriptParse : IUnknown
{
    HRESULT InitNew( void) = 0;
    HRESULT AddScriptlet( 
        LPCOLESTR pstrDefaultName,    
        LPCOLESTR pstrCode,
        LPCOLESTR pstrItemName,
        LPCOLESTR pstrSubItemName,    
        LPCOLESTR pstrEventName,    
        LPCOLESTR pstrDelimiter,
        DWORD dwSourceContextCookie,
        ULONG ulStartingLineNumber,
        DWORD dwFlags,
        BSTR* pbstrName,
        EXCEPINFO* pexcepinfo) = 0;
        
    HRESULT ParseScriptText( 
        LPCOLESTR pstrCode,
        LPCOLESTR pstrItemName,
        IUnknown* punkContext,
        LPCOLESTR pstrDelimiter,
        DWORD dwSourceContextCookie,
        ULONG ulStartingLineNumber,
        DWORD dwFlags,
        VARIANT* pvarResult,
        EXCEPINFO* pexcepinfo) = 0;
};

Figure 3   IActiveScriptSite


IActiveScriptSite : IUnknown
{
HRESULT GetLCID(LCID* plcid) = 0;
HRESULT GetItemInfo(LPCOLESTR pstrName,
                    DWORD dwReturnMask,
                    IUnknown** ppiunkItem,
                    ITypeInfo** ppti) = 0;
HRESULT GetDocVersionString(BSTR* pbstrVersion) = 0;
HRESULT OnScriptTerminate(VARIANT* pvarResult,
                          EXCEPINFO* pexcepinfo) = 0;
HRESULT OnStateChange(SCRIPTSTATE ssScriptState) = 0;
HRESULT OnScriptError(IActiveScriptError* pscripterror) = 0;
HRESULT OnEnterScript(void) = 0;
HRESULT OnLeaveScript(void) = 0;
};

Figure 5   CActiveScriptSite Highlights


class CActiveScriptSite : 
    public IActiveScriptSite,
{
protected:
    /////////////////////////////////////////////////////////////
    // These interfaces come from Microsoft. We'll cache them.
    IActiveScript* m_pActiveScript;
    IActiveScriptParse* m_pActiveScriptParse;

    /////////////////////////////////////////////////////////////
    // Lists of named items and macro information. 
    CNamedItems* m_pitems;
    CMacroInfos* m_pmacros;

    /////////////////////////////////////////////////////////////
    // Prototypes for the functions found in the interfaces
    //  QueryInterface, AddRef, Release, GetItemInfo, etc.
}; 

Figure 6   InitScriptEngine


////////////////////////////////////////////////////////////////////
// Called by the client to start up the script engine...
//
STDMETHODIMP InitScriptEngine(REFCLSID clsid)
{
    ULONG ul = 0;
    HRESULT hResult = E_FAIL;
    IActiveScriptSite* pActiveScriptSite = NULL;

    hResult = CoCreateInstance(clsid, NULL, 
                        CLSCTX_INPROC_SERVER, 
                        IID_IActiveScript,
                        (void**)&m_pActiveScript);
    if(SUCCEEDED(hResult))
    {
        hResult = m_pActiveScript->QueryInterface(
            IID_IActiveScriptParse, (void**)&m_pActiveScriptParse);
        
        if(FAILED(hResult))
        {
            goto err;
        }
    }
    
    if( FAILED( hResult ) )
    {
        goto err;
    }

    hResult = QueryInterface(IID_IActiveScriptSite, 
                             (void**)&pActiveScriptSite);
  
    if(SUCCEEDED(hResult))
    {
        hResult = m_pActiveScript->SetScriptSite(
            pActiveScriptSite);
        pActiveScriptSite->Release();
    } else
    {
        goto err;
    }

    if(FAILED( hResult ))
    {
        goto err;
    }

    hResult = m_pActiveScriptParse->InitNew();
    if( FAILED( hResult ) )
    {
        goto err;
    }

    m_bIsInitialized = true;
    return S_OK;

err:

    if(m_pActiveScriptParse)
    {
        m_pActiveScriptParse->Release();
        m_pActiveScriptParse = NULL;
    }

    if(m_pActiveScript)
    {
        m_pActiveScript->Release();
        m_pActiveScript = NULL;
    }

    return hResult;
}

Figure 7   Adding a Named Item


STDMETHODIMP AddNamedItem(BSTR bstrName, IUnknown* pUnkItem)
{
    if(!m_pitems)
    {
        m_pitems = new CNamedItems;

        /////////////////////////////////////////////////////////
        // First add the named item to our own internal collection
        NamedItem* pNamedItem = NULL;

        pNamedItem = new NamedItem(bstrName, pUnkItem);
        m_pitems->push_back(pNamedItem);
        ///////////////////////////////////////////////////////
        // Then add the item to the active script engine...
        HRESULT hr = m_pActiveScript->AddNamedItem(bstrName, 
            SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
    }

    return S_OK;
}

Figure 8   IActiveScriptSite::OnStateChange


////////////////////////////////////////////////////////////////////////////
//
//  Called by active script engine when the state changes...
//
STDMETHODIMP OnStateChange(tagSCRIPTSTATE ssScriptState)
{
    OutputDebugString("OnStateChange\n");

    switch(ssScriptState)
    {
        case SCRIPTSTATE_UNINITIALIZED:
            OutputDebugString( "\tSCRIPTSTATE_UNINITIALIZED\n" );
            break;

        case SCRIPTSTATE_INITIALIZED:
            OutputDebugString( "\tSCRIPTSTATE_INITIALIZED\n" );
            break;

        case SCRIPTSTATE_STARTED:
            OutputDebugString( "\tSCRIPTSTATE_STARTED\n" );
            break;

        case SCRIPTSTATE_CONNECTED:
            OutputDebugString( "\tSCRIPTSTATE_CONNECTED\n" );
            break;

        case SCRIPTSTATE_DISCONNECTED:
            OutputDebugString( "\tSCRIPTSTATE_DISCONNECTED\n" );
            break;

        case SCRIPTSTATE_CLOSED:
            OutputDebugString( "\tSCRIPTSTATE_CLOSED\n" );
            break;

        default:
            OutputDebugString( "\tUnknown SCRIPTSTATE value\n" );
            break;
    }
    return S_OK;
}


Figure 9   Script Engine States

State
Description
SCRIPTSTATE_UNINITIALIZED
Script has just been created, but has not yet been initialized using an IPersist interface and IActiveScript::SetScriptSite.
SCRIPTSTATE_INITIALIZED
Script has been initialized, but is not running (connecting to other objects or sinking events) or executing any code. Code can be queried for execution by calling the IActiveScriptParse::ParseScriptText method.
SCRIPTSTATE_STARTED
Script can execute code, but is not yet sinking the events of objects added by the IActiveScript::AddNamedItem method.
SCRIPTSTATE_CONNECTED
Script is loaded and connected for sinking events.
SCRIPTSTATE_DISCONNECTED
Script is loaded and has a runtime execution state, but is temporarily disconnected from sinking events.
SCRIPTSTATE_CLOSED
Script has been closed. The scripting engine no longer works and returns errors for most methods.


Figure 10   IActiveScriptSite::GetItemInfo


/////////////////////////////////////////////////////////////////
// 
// This function is called by the COM scripting engine. 
//
STDMETHODIMP GetItemInfo(LPCOLESTR pstrName, 
                         ULONG dwReturnMask, 
                         IUnknown** ppiunkItem, 
                         ITypeInfo** ppti)
{
    OutputDebugString( "GetItemInfo\n" );

    if( ppiunkItem != NULL )
    {
        *ppiunkItem = NULL;
}

if( ppti != NULL )
{
*ppti = NULL;
    }

    HRESULT hr = S_OK;
    bool bFound = false;

    std::vector<NamedItem*>::iterator iter;
    //Find the named item...
    if(m_pitems)
    {
        for (iter = m_pitems->begin(); 
             iter < m_pitems->end(); 
             iter++) 
        {
            NamedItem* pNamedItem = *iter;

            bFound = (wcscmp(pNamedItem->m_bstrName,
                      pstrName) == 0);

            if (bFound) 
            {
                break;
            }
        }
    }

    if(!bFound)
    {
        return TYPE_E_ELEMENTNOTFOUND;
    }

    if( dwReturnMask&SCRIPTINFO_IUNKNOWN )
    {    
        OutputDebugString( "Looking for item's unknown\n" );
 
        NamedItem* pNamedItem = *iter;
        pNamedItem->m_pUnk->AddRef();
        *ppiunkItem = pNamedItem->m_pUnk;
    }

    if( dwReturnMask&SCRIPTINFO_ITYPEINFO )
    {
        OutputDebugString( "Looking for item's type info\n" );
        NamedItem* pNamedItem = *iter;
        if(pNamedItem->m_pTypeInfo)
        {
            pNamedItem->m_pTypeInfo->AddRef();
        }
        *ppti = pNamedItem->m_pTypeInfo;
    }
            
    return S_OK;
}

Figure 11   Invoking a Scripting Function


STDMETHODIMP RunMacro(BSTR bstrMacroName)
{
CMacroInfos::iterator iter;

    bool bFound = false;
    CMacroInfo* pMacroInfo = 0;

    if(m_pmacros)
    {
        for (iter = m_pmacros->begin(); 
             iter < m_pmacros->end(); 
             iter++) 
        {
            pMacroInfo = *iter;

            bFound = (wcscmp(pMacroInfo->m_bstrMacroName, 
                      bstrMacroName)
                      == 0);

            if (bFound) 
        {
                break;
            }
        }

        if(bFound)
        {
            IDispatch* pDispatch = 0;
            HRESULT hResult;
            hresult = m_pActiveScript->GetScriptDispatch(NULL, &pDispatch);
            if(SUCCEEDED(hResult))
            {

                DISPPARAMS dp = {0, 0};
                VARIANT v;
                VariantInit(&v);
                EXCEPINFO ei;

                unsigned int nArgError = 0;

                memset(&ei, sizeof(EXCEPINFO), 0);

                HRESULT hr;
                hr = pDispatch->Invoke(pMacroInfo->m_dispID,
                                       IID_NULL,
                                       0, DISPATCH_METHOD,
                                       &dp,
                                       &v,
                                       &ei,
                                       &nArgError);

                pDispatch->Release();
            }
        }
    }

    return S_OK;
}