ScriptSite - ATL Active Script Engine Wrapper - VisionTech Limited

Download · View Source Code (link opens new window)

Microsoft did us a favour when they produced Active Scripting. We can readily add scripting facilities to our applications with practically no effort compared to what was required just a few years ago.

I’ve included scripting in a few applications and each time modified the previous copy of my code to fit the new requirements. Having decided this time to construct a more reusable wrapper and to make use of ATL, I decided to share the results of my efforts to save others some time.

At the heart of the code is the CScriptSiteImpl class. This is the base class from which you need to inherit for your project. I’ve included a minimal class CScriptSiteBasic (which you can use directly) that demonstrates what you need to do to use the code:

    <font color="#0000ff">class</font> ATL_NO_VTABLE CScriptSiteBasic :
        <font color="#0000ff">public</font> CComObjectRootEx<CComSingleThreadModel>,
        <font color="#0000ff">public</font> CScriptSiteImpl
    {
    <font color="#0000ff">public</font>:
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    BEGIN_COM_MAP(CScriptSiteBasic)
        COM_INTERFACE_ENTRY(IActiveScriptSite)
        COM_INTERFACE_ENTRY(IActiveScriptSiteWindow)
    END_COM_MAP()
    };

To make use of this class, just use code as follows:

    LPCTSTR strScriptCode; _// this points to the script code we want to run_
    ...
    CComObject* pBasic;
    CComQIPtr spUnk;
    HR(CComObject::CreateInstance(&pBasic));
    spUnk = pBasic; _// let CComQIPtr tidy up for us_
    HR(pBasic->Initiate(_T("jscript"),GetDesktopWindow()));
    HR(pBasic->AddScript(strScriptCode));
    HR(pBasic->Run());
    HR(pBasic->Terminate());</pre>

You will more than likely want to include your own object model within the script engine. To do this, you will need to create your own class derived from CScriptSiteImpl in the same fashion as CScriptSiteBasic above.

You will need to call CScriptSiteImpl::AddObject once for each object to tell the script engine the names used in your model. Next, you will need to provide your own implementation of LookupNamedItem as follows:

    _// Override this method in your implementation and return the desired object
    // or TYPE_E_ELEMENTNOTFOUND if the name doesn't match one of yours
    // (You must call CScriptSiteImpl::AddObject in the first place to tell
    // the script engine that your objects exist)._
    STDMETHOD(LookupNamedItem)(LPCOLESTR pstrName,LPUNKNOWN* ppunkItem)
    {
        return TYPE_E_ELEMENTNOTFOUND;
    }
You need to check the value of pstrName and return the corresponding IUnknown interface for that object. Of course, the object must implement IDispatch because the script engines are late bound. If the name doesn't match one of your objects, return TYPE_E_ELEMENTNOTFOUND. Finally, to include your own error handler, you need to override HandleScriptError:
    _// Override this method in your implementation to handle error messages_
    STDMETHOD(HandleScriptError)(EXCEPINFO* pei,ULONG ulLine,LONG lPos,BSTR src)
    {
        ...
        ...
        return NOERROR;
    }
pei points to the EXCEPINFO structure for the error, ulLine and lPos indicate the line and column in your source script where the error occurred, and src contains a copy of that source script line. [Download](/software/download/ScriptSite.zip) · [View Source Code](/software/download/ScriptSite.h.html) (link opens new window)