// IActiveScriptSite.c
//
// This file contains our MyRealIActiveScriptSite object. This is
// a multiple interface object that has a standard IActiveScriptSite
// as the base object, and a standard IActiveScriptSiteWindow as a
// sub-object.
#include <windows.h>
#include <stddef.h>
#include <activscp.h>
#include "IActiveScriptSite.h"
#include "Extern.h"
// Our IActiveScriptSite VTable. NOTE: We declare the first arg to be a
// pointer to a MyRealIActiveScriptSite (because that's what it really
// is, and what we want). But the definition of an IActiveScriptSite
// VTable in Microsoft's include file defines it as an IActiveScriptSite
// pointer. So the compiler will likely give us warnings, but they are
// trivial
static STDMETHODIMP QueryInterface(MyRealIActiveScriptSite *, REFIID, void **);
static STDMETHODIMP_(ULONG) AddRef(MyRealIActiveScriptSite *);
static STDMETHODIMP_(ULONG) Release(MyRealIActiveScriptSite *);
static STDMETHODIMP GetLCID(MyRealIActiveScriptSite *, LCID *);
static STDMETHODIMP GetItemInfo(MyRealIActiveScriptSite *, LPCOLESTR, DWORD, IUnknown **, ITypeInfo **);
static STDMETHODIMP GetDocVersionString(MyRealIActiveScriptSite *, BSTR *);
static STDMETHODIMP OnScriptTerminate(MyRealIActiveScriptSite *, const VARIANT *, const EXCEPINFO *);
static STDMETHODIMP OnStateChange(MyRealIActiveScriptSite *, SCRIPTSTATE);
static STDMETHODIMP OnScriptError(MyRealIActiveScriptSite *, IActiveScriptError *);
static STDMETHODIMP OnEnterScript(MyRealIActiveScriptSite *);
static STDMETHODIMP OnLeaveScript(MyRealIActiveScriptSite *);
static const IActiveScriptSiteVtbl SiteTable = {
QueryInterface,
AddRef,
Release,
GetLCID,
GetItemInfo,
GetDocVersionString,
OnScriptTerminate,
OnStateChange,
OnScriptError,
OnEnterScript,
OnLeaveScript};
// IActiveScriptSiteWindow VTable
static STDMETHODIMP siteWnd_QueryInterface(IActiveScriptSiteWindow *, REFIID, void **);
static STDMETHODIMP_(ULONG) siteWnd_AddRef(IActiveScriptSiteWindow *);
static STDMETHODIMP_(ULONG) siteWnd_Release(IActiveScriptSiteWindow *);
static STDMETHODIMP GetSiteWindow(IActiveScriptSiteWindow *, HWND *);
static STDMETHODIMP EnableModeless(IActiveScriptSiteWindow *, BOOL);
static const IActiveScriptSiteWindowVtbl SiteWindowTable = {
siteWnd_QueryInterface,
siteWnd_AddRef,
siteWnd_Release,
GetSiteWindow,
EnableModeless};
// Since we're going to support using only 1 script engine at a time,
// we need only one IActiveScriptSite. We'll declare it globally to
// make this easy (instead of GlobalAlloc'ing it)
MyRealIActiveScriptSite MyActiveScriptSite;
static const wchar_t ErrorStr[] = L"Error";
static const wchar_t EmptyStr[] = L"";
//===========================================================================
//============================ Helper functions =============================
//===========================================================================
/********************* allocIActiveScriptSiteObject() *******************
* Initializes an MyRealIActiveScriptSite object. Called once at the
* start of our program.
*/
void initIActiveScriptSiteObject(void)
{
// Initialize the lpVtbl members of our IActiveScriptSite and
// IActiveScriptSiteWindow sub-objects
MyActiveScriptSite.site.lpVtbl = (IActiveScriptSiteVtbl *)&SiteTable;
MyActiveScriptSite.siteWnd.lpVtbl = (IActiveScriptSiteWindowVtbl *)&SiteWindowTable;
}
//===========================================================================
//======================= IActiveScriptSite functions =======================
//===========================================================================
static STDMETHODIMP QueryInterface(MyRealIActiveScriptSite *this, REFIID riid, void **ppv)
{
// An ActiveX Script Host is supposed to provide an object
// with multiple interfaces, where the base object is an
// IActiveScriptSite, and there is an IActiveScriptSiteWindow
// sub-object. Therefore, a caller can pass an IUnknown or
// IActiveScript VTable GUID if he wants our IActiveScriptSite.
// Or, the caller can pass a IActiveScriptSiteWindow VTable
// GUID if he wants our IActiveScriptSiteWindow sub-object
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IActiveScriptSite))
*ppv = this;
else if (IsEqualIID(riid, &IID_IActiveScriptSiteWindow))
*ppv = ((unsigned char *)this + offsetof(MyRealIActiveScriptSite, siteWnd));
else
{
*ppv = 0;
return(E_NOINTERFACE);
}
AddRef(this);
return(S_OK);
}
static STDMETHODIMP_(ULONG) AddRef(MyRealIActiveScriptSite *this)
{
// Since our MyRealIActiveScriptSite is not allocated, but instead
// declared statically, we have no need to maintain a reference
// count
return(1);
}
static STDMETHODIMP_(ULONG) Release(MyRealIActiveScriptSite *this)
{
// Since our MyRealIActiveScriptSite is not allocated, but instead
// declared statically, we have no need to maintain a reference
// count, nor free it.
//
// NOTE: If our MyRealIActiveScriptSite did contain some resources
// that we had to free, then we'd need to maintain a reference
// count, and free those resources when the reference count was
// zero
return(1);
}
// Called by the script engine to get any pointers to our own host-defined
// objects whose functions a script may directly call. We don't implement
// any such objects here, so this is just a stub.
static STDMETHODIMP GetItemInfo(MyRealIActiveScriptSite *this, LPCOLESTR objectName, DWORD dwReturnMask, IUnknown **objPtr, ITypeInfo **typeInfo)
{
if (dwReturnMask & SCRIPTINFO_IUNKNOWN) *objPtr = 0;
if (dwReturnMask & SCRIPTINFO_ITYPEINFO) *typeInfo = 0;
return(E_FAIL);
}
// Called by the script engine when there is an error running/parsing a script.
// The script engine passes us its IActiveScriptError object whose functions
// we can call to get information about the error, such as the line/character
// position (in the script) where the error occurred, an error message we can
// display to the user, etc. Typically, our OnScriptError will get the error
// message and display it to the user.
static STDMETHODIMP OnScriptError(MyRealIActiveScriptSite *this, IActiveScriptError *scriptError)
{
ULONG lineNumber;
BSTR desc;
EXCEPINFO ei;
OLECHAR wszOutput[1024]; // NOTE: We really should allocate this, to ensure it's big enough
// Call GetSourcePosition() to retrieve the line # where
// the error occurred in the script
scriptError->lpVtbl->GetSourcePosition(scriptError, 0, &lineNumber, 0);
// Call GetSourceLineText() to retrieve the line in the script that
// has an error.
//
// Note: The IActiveScriptError is supposed to clear our BSTR pointer
// if it can't return the line. So we shouldn't have to do
// that first. But you may need to uncomment the following line
// if a script engine isn't properly written. Unfortunately, too many
// engines are not written properly, so we uncomment it
desc = 0;
scriptError->lpVtbl->GetSourceLineText(scriptError, &desc);
// Call GetExceptionInfo() to fill in our EXCEPINFO struct with more
// information.
//
// Note: The IActiveScriptError is supposed to zero out any fields of
// our EXCEPINFO that it doesn't fill in. So we shouldn't have to do
// that first. But you may need to uncomment the following line
// if a script engine isn't properly written
// ZeroMemory(&ei, sizeof(ei));
scriptError->lpVtbl->GetExceptionInfo(scriptError, &ei);
// Format the message we'll display to the user
wsprintfW(&wszOutput[0], L"%s\nLine %u: %s\n%s", ei.bstrSource, lineNumber + 1, ei.bstrDescription, desc ? desc : &EmptyStr[0]);
// Free what we got from the IActiveScriptError functions
SysFreeString(desc);
SysFreeString(ei.bstrSource);
SysFreeString(ei.bstrDescription);
SysFreeString(ei.bstrHelpFile);
// Display the message
MessageBoxW(0, &wszOutput[0], &ErrorStr[0], MB_SETFOREGROUND|MB_OK|MB_ICONEXCLAMATION);
return(S_OK);
}
// Called when the script engine wants to know what language ID our
// program is using.
static STDMETHODIMP GetLCID(MyRealIActiveScriptSite *this, LCID *lcid)
{
*lcid = LOCALE_USER_DEFAULT;
return(S_OK);
}
// Called when the script engine wishes to retrieve the version number
// (as a string) for the current document. We are expected to return a
// SysAllocString()'ed BSTR copy of this version string.
//
// Here's what I believe this is for. An engine may implement some
// IPersist object. When we AddScriptlet or ParseScriptText some
// script with the SCRIPTTEXT_ISPERSISTENT flag, then the script
// engine will save that script to disk for us, when we call the
// engine IPersist's Save() function. But maybe the engine wants to
// minimize unnecessary saving to disk. So, it fetches this version
// string and caches it when it does the first save to disk. Then,
// when we subsequently call IPersist Save again, the engine fetches
// this version string again, and compares it to the previous version
// string. If the same string, then the engine assumes the script doesn't
// need to be resaved. If different, then the engine assumes our
// document has changed and therefore it should resave the scripts.
// In other words, this is used simply as an "IsDirty" flag by IPersist
// Save to see if a persistant script needs to be resaved. I don't
// believe that engines without any IPersist object bother with this,
// and I have seen example engines _with_ an IPersist that don't
// bother with it.
static STDMETHODIMP GetDocVersionString(MyRealIActiveScriptSite *this, BSTR *version)
{
// We have no document versions
*version = 0;
// If an engine chokes on the above, try this instead:
// if (!(*version = SysAllocString(&EmptyStr[0])))
// return(E_OUTOFMEMORY);
return(S_OK);
}
// Called when the engine has completely finished running scripts and
// has returned to INITIALIZED state. In many engines, this is not
// called because an engine alone can't determine when we are going
// to stop running/adding scripts to it.
static STDMETHODIMP OnScriptTerminate(MyRealIActiveScriptSite *this, const VARIANT *pvr, const EXCEPINFO *pei)
{
return(S_OK);
}
// Called when the script engine's state is changed (for example, by
// us calling the engine IActiveScript->SetScriptState). We're passed
// the new state.
static STDMETHODIMP OnStateChange(MyRealIActiveScriptSite *this, SCRIPTSTATE state)
{
return(S_OK);
}
// Called right before the script engine executes/interprets each
// script added via the engine IActiveScriptParse->ParseScriptText()
// or AddScriptlet(). This is also called when our IApp object
// calls some function in the script.
static STDMETHODIMP OnEnterScript(MyRealIActiveScriptSite *this)
{
return(S_OK);
}
// Called immediately after the script engine executes/interprets
// each script.
static STDMETHODIMP OnLeaveScript(MyRealIActiveScriptSite *this)
{
return(S_OK);
}
//===========================================================================
//==================== IActiveScriptSiteWindow functions ====================
//===========================================================================
static STDMETHODIMP siteWnd_QueryInterface(IActiveScriptSiteWindow *this, REFIID riid, void **ppv)
{
// Since our IActiveScriptSiteWindow is a sub-object embedded inside of our
// MyRealIActiveScriptSite object (ie, our MyRealIActiveScriptSite has "multiple
// interfaces" and our IActiveScriptSiteWindow happens to be a sub-object),
// then we just delegate to our base object's (IActiveScriptSite's) QueryInterface
// to do the real work. We do this by substituting our IActiveScriptSite object for
// the "this" pointer. That's easy to do because both our IActiveScriptSiteWindow
// and IActiveScriptSite objects are embedded inside of our one MyRealIActiveScriptSite.
// So a little pointer arithmatic gets us what we want
this = (IActiveScriptSiteWindow *)(((unsigned char *)this - offsetof(MyRealIActiveScriptSite, siteWnd)));
return(QueryInterface((MyRealIActiveScriptSite *)this, riid, ppv));
}
static STDMETHODIMP_(ULONG) siteWnd_AddRef(IActiveScriptSiteWindow *this)
{
this = (IActiveScriptSiteWindow *)(((unsigned char *)this - offsetof(MyRealIActiveScriptSite, siteWnd)));
return(AddRef((MyRealIActiveScriptSite *)this));
}
static STDMETHODIMP_(ULONG) siteWnd_Release(IActiveScriptSiteWindow *this)
{
this = (IActiveScriptSiteWindow *)(((unsigned char *)this - offsetof(MyRealIActiveScriptSite, siteWnd)));
return(Release((MyRealIActiveScriptSite *)this));
}
// Called by the script engine when it wants to know what window it should
// use as the owner of any dialog box the engine presents.
static STDMETHODIMP GetSiteWindow(IActiveScriptSiteWindow *this, HWND *phwnd)
{
// We have no app window
*phwnd = 0;
return(S_OK);
}
// Called when the script engine wants us to enable/disable all of our open
// windows.
static STDMETHODIMP EnableModeless(IActiveScriptSiteWindow *this, BOOL enable)
{
// We have no open windows
return(S_OK);
}