mail icon Contact me

C++ implementation

Remember, the general strategy is to create a component that:

  • pretends to be a language
  • does nothing even vaguely like a language but
  • merely records method calls from the scripting host to a file.

So the core part is actually writing the code that records method calls. Here we go.

The constructor and the destructor take care of member variables:

CNullScript::CNullScript()
:  m_scriptState(SCRIPTSTATE_UNINITIALIZED),
  m_site(NULL)
{
  m_fp = fopen ("c:\\NullScript.txt", "w");
}
CNullScript::~CNullScript()
{
  if (NULL != m_fp)
  {
    fclose (m_fp);
    m_fp = NULL;
  }
  if (NULL != m_site)
  {
    m_site->Release ();
    m_site = NULL;
  }
}

The constructor opens a FILE named NullScript.txt for write. This foreshadows what's to come: lots of code that logs method calls to this file.

The IDispatch methods report their method calls:

HRESULT CNullScript::GetIDsOfNames( 
  REFIID  riid,                  
  OLECHAR FAR* FAR*  rgszNames,  
  unsigned int  cNames,          
  LCID   lcid,                   
  DISPID FAR*  rgDispId)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IDispatch::GetIDsOfNames\n");
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::GetTypeInfo( 
  unsigned int  iTInfo,         
  LCID  lcid,                   
  ITypeInfo FAR* FAR*  ppTInfo)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IDispatch::GetTypeInfo\n");
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::GetTypeInfoCount( 
  unsigned int FAR*  pctinfo)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IDispatch::GetTypeInfoCount\n");
  fflush (m_fp);
  *pctinfo = 1;
  return hr;
}
HRESULT CNullScript::Invoke( 
  DISPID  dispIdMember,      
  REFIID  riid,              
  LCID  lcid,                
  WORD  wFlags,              
  DISPPARAMS FAR*  pDispParams,  
  VARIANT FAR*  pVarResult,  
  EXCEPINFO FAR*  pExcepInfo,  
  unsigned int FAR*  puArgErr)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IDispatch::Invoke\n");
  fflush (m_fp);
  return hr;
}

The IActiveScript declare their method calls. Since the SetScriptState controls the start of the scripting engine, requesting it to start or stop execution, I did some extra stuff to record the state. Adding named items is also important, so I dumped the flags sent from the scripting host.

HRESULT CNullScript::SetScriptSite(
  /* [in] */  IActiveScriptSite *pass)
{
  fprintf (m_fp, "IActiveScript::SetScriptSite\n");
  fflush (m_fp);
  HRESULT hr = pass->QueryInterface (IID_IActiveScriptSite,
    reinterpret_cast<void **>(&m_site));
  return hr;
}
HRESULT CNullScript::GetScriptSite(
  /* [in] */                REFIID riid,
  /*[out, iid_is(riid)]*/ void **ppvObject)
{
  fprintf (m_fp, "IActiveScript::GetScriptSite\n");
  fflush (m_fp);
  *ppvObject = NULL;
  HRESULT hr = m_site->QueryInterface (riid, ppvObject);
  return hr;
}
HRESULT CNullScript::SetScriptState(
  /* [in] */  SCRIPTSTATE ss)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::SetScriptState(%08lx)\n",
    (DWORD) ss);
  fflush (m_fp);
  m_scriptState = ss;
  return hr;
}
HRESULT CNullScript::GetScriptState(
  /* [out] */ SCRIPTSTATE *pssState)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::GetScriptState\n");
  fflush (m_fp);
  *pssState = m_scriptState;
  return hr;
}
HRESULT CNullScript::Close(void)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::Close\n");
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::AddNamedItem(
  /* [in] */  LPCOLESTR pstrName,
  /* [in] */  DWORD     dwFlags)
{
  USES_CONVERSION;
  HRESULT hr = S_OK;
  const TCHAR *cp = OLE2T(pstrName);
  typedef struct
  {
    DWORD f;
    char *name;
  } Flags;
  const Flags flags[] =
  {
    {SCRIPTITEM_CODEONLY, "SCRIPTITEM_CODEONLY"},
    {SCRIPTITEM_GLOBALMEMBERS, "SCRIPTITEM_GLOBALMEMBERS"},
    {SCRIPTITEM_ISPERSISTENT, "SCRIPTITEM_ISPERSISTENT"},
    {SCRIPTITEM_ISSOURCE, "SCRIPTITEM_ISSOURCE"},
    {SCRIPTITEM_ISVISIBLE, "SCRIPTITEM_ISVISIBLE"},
    {SCRIPTITEM_NOCODE, "SCRIPTITEM_NOCODE"},
  };
  fprintf (m_fp, "IActiveScript::AddNamedItem('%s', %08lx)\n",
    cp, dwFlags);
  for (unsigned i = 0; i < sizeof(flags)/sizeof(flags[0]); i++)
  {
    if (dwFlags & flags[i].f)
      fprintf (m_fp, "\t%s\n", flags[i].name);
  }
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::AddTypeLib(
  /* [in] */  REFGUID   rguidTypeLib,
  /* [in] */  DWORD     dwMajor,
  /* [in] */  DWORD     dwMinor,
  /* [in] */  DWORD     dwFlags)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::AddTypeLib\n");
  fflush (m_fp);
  return hr;
}

//  S_FALSE 
//  The scripting engine does not support a dispatch object;
//  the ppdisp parameter is set to NULL.  
HRESULT CNullScript::GetScriptDispatch(
  /* [in] */  LPCOLESTR pstrItemName,
  /* [out] */ IDispatch **ppdisp)
{
  USES_CONVERSION;
  HRESULT hr = S_OK;
  TCHAR *cp = OLE2T(pstrItemName);
  if (NULL == cp)
    cp = "NULL";
  fprintf (m_fp, "IActiveScript::GetScriptDispatch(%s)\n", cp);
  fflush (m_fp);
  hr = this->QueryInterface (IID_IDispatch, 
    reinterpret_cast<void **>(ppdisp));
  return hr;
}
HRESULT CNullScript::GetCurrentScriptThreadID(
  /* [out] */ SCRIPTTHREADID *pstidThread)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::GetCurrentScriptThreadID\n");
  fflush (m_fp);
  *pstidThread = 0;
  return hr;
}
HRESULT CNullScript::GetScriptThreadID(
  /* [in] */  DWORD             dwWin32ThreadId,
  /* [out] */ SCRIPTTHREADID    *pstidThread)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::GetScriptThreadID\n");
  fflush (m_fp);
  *pstidThread = 0;
  return hr;
}
HRESULT CNullScript::GetScriptThreadState(
  /* [in] */  SCRIPTTHREADID    stidThread,
  /* [out] */ SCRIPTTHREADSTATE *pstsState)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::GetScriptThreadState\n");
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::InterruptScriptThread(
  /* [in] */  SCRIPTTHREADID    stidThread,
  /* [in] */  const EXCEPINFO   *pexcepinfo,
  /* [in] */  DWORD dwFlags)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScript::InterruptScriptThread\n");
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::Clone(
  /* [out] */ IActiveScript     **ppscript)
{
  fprintf (m_fp, "IActiveScript::Clone\n");
  fflush (m_fp);
  HRESULT hr = this->QueryInterface (IID_IActiveScript,
    reinterpret_cast<void **>(ppscript));
  return hr;
}

The IActiveScriptParse methods report their method calls. Since they have important parameters passed to them, we record the parameters, too:

HRESULT CNullScript::InitNew(void)
{
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScriptParse::InitNew\n");
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::AddScriptlet(
  /* [in] */  LPCOLESTR         pstrDefaultName,
  /* [in] */  LPCOLESTR         pstrCode,
  /* [in] */  LPCOLESTR         pstrItemName,
  /* [in] */  LPCOLESTR         pstrSubItemName,
  /* [in] */  LPCOLESTR         pstrEventName,
  /* [in] */  LPCOLESTR         pstrDelimiter,
  /* [in] */  DWORD             dwSourceContextCookie,
  /* [in] */  ULONG             ulStartingLineNumber,
  /* [in] */  DWORD             dwFlags,
  /* [out] */ BSTR              *pbstrName,
  /* [out] */ EXCEPINFO         *pexcepinfo)
{
  USES_CONVERSION;
  TCHAR *cp = OLE2T(pstrCode);
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScriptParse::AddScriptlet(%s)\n", cp);
  fflush (m_fp);
  return hr;
}
HRESULT CNullScript::ParseScriptText(
  /* [in] */  LPCOLESTR         pstrCode,
  /* [in] */  LPCOLESTR         pstrItemName,
  /* [in] */  IUnknown          *punkContext,
  /* [in] */  LPCOLESTR         pstrDelimiter,
  /* [in] */  DWORD             dwSourceContextCookie,
  /* [in] */  ULONG             ulStartingLineNumber,
  /* [in] */  DWORD             dwFlags,
  /* [out] */ VARIANT           *pvarResult,
  /* [out] */ EXCEPINFO         *pexcepinfo)
{
  USES_CONVERSION;
  TCHAR *cp = OLE2T(pstrCode);
  HRESULT hr = S_OK;
  fprintf (m_fp, "IActiveScriptParse::ParseScriptText(%s)\n", cp);
  fflush (m_fp);
  return hr;
}

Now we're almost ready. We just have to set up some infrastructure and we're done.

Next: RGS file

© 2001-8 Hugh Brown. All rights reserved.