Adding Debug facilities to an Active Scripting Host

Introduction

This article follows on from the Active Script Hosting discussions of Andrew Garbuzov. Before you start on adding debugger support to your active script it's probably a good idea to have active scripts up and running....

The code in this article builds on the Microsoft sample : "MFCAXS.EXE: MFCAXS Implements an Active Script Host Using MFC". This sample on its own is really a very good place to discover lots about how to implement Active Script Hosting. It also contains an excellent extension to CCmdTarget, CCmdTargetPlus, that allows you to easily write automation servers with events without having to use the COleControl class.

To experiment with the debugging interfaces, you will need an Active Script debugger installed like the IE4 one available from Microsoft's scripting site

A reference for the Active Script debugging interfaces was once provided in the online reference but this now seems to have been moved off the Microsoft site. Even with this documentation, implementing script debugging is far from trivial and the main aim of this article is to give you a working sample to play with.

Warning - Because the Active Script Debugging interfaces do not seem to be fully documented yet by Microsoft - in particular they have not provided any header files or type libraries, the sample project attached to this article includes a hand built header file, activscp_plus.h, which should be replaced with an offical MS version as soon as they provide one.


Debugging in Action

Before you start please Download demo project and extract and run mfcaxscrb.exe

and also have the Microsoft Active Script Engines and the IE4 debugger installed.

To view the debugger in action:

  1. Run the debugger
  2. Run the sample application. In this application type and run a script
    
    BroCon.Navigate ""http://www.codeguru.com%22"
    ??>www.codeguru.com"
    
    MsgBox "Hello World"
    
    Sub AButton_OnClick()
      MsgBox "Button A"
    End Sub
    
    Sub BButton_OnClick()
      MsgBox "Button B"
    End Sub
    
  3. The application will appear as:

  4. In the "Running Documents" view of the IE4 debugger, click down the hierarchy of the "MFC Scripting Documents" application and double-click on the "VBScript - Scripter script block"
  5. The IE4 debugger should now display some of the text of your script - in this you can now place breakpoints to test the link between the debugger and the application. For example, set a breakpoint in the AButton_OnClick handler and then click the AButton button in the MFC application
  6. Feel free to play with the debugger's "commands" window to view and alter values in your script's code. For example, you can use the command window to print the value of the edit control (use '? BroCon.LocationURL') or to set values or call methods (try 'BroCon.Navigate "www.dilbert.com"')

Note: Microsoft have set up the script debugging so that Visual Interdev will always be used in preference to the IE4 debugger. If you have Visual Interdev installed then you can attach to the script using "Debug|Processes" and then choosing the process "mfcaxsrvb.exe" in the dialog. The Interdev debugger gices you the benefits of watchpoints and a watch window, a "Set Next Statement" command, and generally better editing facilites.


Adding debugger support to an Active Script Host

To add debugging support to an Active Script Host, you need to perform the following steps:

1. Before you initialise any of the IActiveScript classes, manually create an IProcessDebugManager - this manager will control the debug process for you.


// Initialise the pdm
hr = CoCreateInstance(CLSID_ProcessDebugManager, 
                      NULL, 
                      CLSCTX_INPROC_SERVER | /*CLSCTX_INPROC_HANLDER | */CLSCTX_LOCAL_SERVER,
                      IID_IProcessDebugManager,
                      (void**)&m_pdm);

2. Manually create an IDebugApplication, set its name and then add this application to the process manager.


hr = m_pdm->CreateApplication(&m_pDebugApp);
if (!SUCCEEDED(hr))
{
   // ....
}

ASSERT(m_pDebugApp);
hr = m_pDebugApp->SetName(L"MFC Scripting Application");
if (!SUCCEEDED(hr))
{
   // ....
}

hr = m_pdm->AddApplication(m_pDebugApp, &m_dwAppCookie);
if (!SUCCEEDED(hr))
{
   // ....
}

3. For each document (normally each script) you wish to run, create an IDebugDocumentHelper to wrap the document. Define the short and long names of the document and attach the document to your application


hr = m_pdm->CreateDebugDocumentHelper(NULL, &m_pDebugDocHelper);
if (!SUCCEEDED(hr))
{
   // ...
}

hr = m_pDebugDocHelper->Init(m_pDebugApp, L"Doc Name", L"Long Doc Name", TEXT_DOC_ATTR_READONLY);
if (!SUCCEEDED(hr))
{
   // ...
}

hr = m_pDebugDocHelper->Attach(NULL);
if (!SUCCEEDED(hr))
{
   // ...
}

4. Support the IActiveScriptSiteDebug in your script host. This interface provides a number of methods allowing a debugger to get the structure of the scripts in your documents and to get the text of those documents.


 
    
BEGIN_INTERFACE_MAP(CMfcaxscrvbDlg, CDialog) 
    ...
    INTERFACE_PART(CMfcaxscrvbDlg,IID_IActiveScriptSiteDebug,ActiveScriptSiteDebug)
    ...
END_INTERFACE_MAP()

... 

/////////////////////////////////////////////////////////////////////////////
// IActiveScriptSiteDebug Implementation
STDMETHODIMP_(ULONG) CMfcaxscrvbDlg::XActiveScriptSiteDebug::AddRef()
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)
   return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CMfcaxscrvbDlg::XActiveScriptSiteDebug::Release()
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)
   return pThis->ExternalRelease();
}

STDMETHODIMP CMfcaxscrvbDlg::XActiveScriptSiteDebug::QueryInterface(REFIID iid, LPVOID* ppvObj)
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)
   return pThis->ExternalQueryInterface(&iid, ppvObj);
}

// Used by the language engine to delegate IDebugCodeContext::GetSourceContext. 
STDMETHODIMP CMfcaxscrvbDlg::XActiveScriptSiteDebug::GetDocumentContextFromPosition(
      DWORD dwSourceContext,// As provided to ParseScriptText 
                            // or AddScriptlet 
      ULONG uCharacterOffset,// character offset relative 
                             // to start of script block or scriptlet 
      ULONG uNumChars,// Number of characters in context 
                      // Returns the document context corresponding to this character-position range. 
      IDebugDocumentContext **ppsc)
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)

   ULONG ulStartPos = 0;
   HRESULT hr;

   if (pThis->m_pDebugDocHelper)
   {
      hr = pThis->m_pDebugDocHelper->GetScriptBlockInfo(dwSourceContext, NULL, &ulStartPos, NULL);
      hr = pThis->m_pDebugDocHelper->CreateDebugDocumentContext(ulStartPos + uCharacterOffset, uNumChars, ppsc);
   }
   else
   {
      hr = E_NOTIMPL;
   }

	return hr;
}

// Returns the debug application object associated with this script site. Provides 
// a means for a smart host to define what application object each script belongs to. 
// Script engines should attempt to call this method to get their containing application 
// and resort to IProcessDebugManager::GetDefaultApplication if this fails. 
STDMETHODIMP CMfcaxscrvbDlg::XActiveScriptSiteDebug::GetApplication( 
      IDebugApplication **ppda)
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)
   if (!ppda)
   {
      return E_INVALIDARG;
   }

   // bugbug - should addref to this ?
   if (pThis->m_pDebugApp)
   {
      ULONG ul = pThis->m_pDebugApp->AddRef();
   }

   *ppda = pThis->m_pDebugApp;

	return S_OK;
}

// Gets the application node under which script documents should be added 
// can return NULL if script documents should be top-level. 
STDMETHODIMP CMfcaxscrvbDlg::XActiveScriptSiteDebug::GetRootApplicationNode( 
      IDebugApplicationNode **ppdanRoot)
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)

   if (!ppdanRoot)
   {
      return E_INVALIDARG;
   }

   if (pThis->m_pDebugDocHelper)
   {
      return pThis->m_pDebugDocHelper->GetDebugApplicationNode(ppdanRoot);
   }

   return E_NOTIMPL;
}

// Allows a smart host to control the handling of runtime errors 
STDMETHODIMP CMfcaxscrvbDlg::XActiveScriptSiteDebug::OnScriptErrorDebug( 
      // the runtime error that occurred 
      IActiveScriptErrorDebug *pErrorDebug, 
      // whether to pass the error to the debugger to do JIT debugging 
      BOOL*pfEnterDebugger, 
      // whether to call IActiveScriptSite::OnScriptError() when the user 
      // decides to continue without debugging 
      BOOL *pfCallOnScriptErrorWhenContinuing)
{
   METHOD_PROLOGUE_EX_(CMfcaxscrvbDlg, ActiveScriptSiteDebug)
   if (pfEnterDebugger)
   {
      *pfEnterDebugger = TRUE;
   }
   if (pfCallOnScriptErrorWhenContinuing)
   {
      *pfCallOnScriptErrorWhenContinuing = TRUE;
   }
   return S_OK;
}

5. Before you parse the script text using IActiveScriptParse, add the script text to the IDebugDocumentHelper and define the script as a text.


hr = m_pDebugDocHelper->AddDBCSText(strScriptText);
if (!SUCCEEDED(hr))
{
   // ....
}

DWORD dw;
hr = m_pDebugDocHelper->DefineScriptBlock(0, strScriptText.GetLength(), m_axs, FALSE, &dw);
if (!SUCCEEDED(hr))
{
   // ....
}

6. When you have finished with the script, be sure to detach and release the IDebugDocumentHelper.


m_pDebugDocHelper->Detach();m_pDebugDocHelper->Release();
m_pDebugDocHelper = NULL;

7. [optional] The sample code included with this article also provides a simple IDebugDocumentHost implementation. This interface does not get used to its full extent by the IE4 debugger, but other Active Debuggers may use it more fully. Among the capabilities of this interface are customisation of script syntax coloring and provision of file path names and methods to react to the debugger changing document text.


Download source and Executable- 86 KB




Comments

  • I met problem on setting the breakpoints during debugging the script

    Posted by Legacy on 08/05/2002 12:00am

    Originally posted by: Yu Chen

    I have added the debug facilities to my active scripting host as the sample. But when I set breakpoint in the script, the breakpoint's appearance is not the same as the standar, there is a '?' in it. When I see the breakpoint's properties, the information show "The breakpoint will not currently be hit". And also the breakpoint didn't take effect.
    How to solve it?
    Thanks a lot!!!

    Reply
  • IProcessDebugManager not registered

    Posted by Legacy on 05/19/2002 12:00am

    Originally posted by: Martin Sedlmair

    Hello,

    I'm trying to get the IProcessDebugManager interface, but somehow it's not registered on my system (on both windows 2000 and winxp). is there something i have to do to get the interface registered or do i have to install something?

    thanks,
    martin

    Reply
  • Where the OnScriptErrorDebug event has gone?

    Posted by Legacy on 02/14/2000 12:00am

    Originally posted by: Behcet Saribatir


    I used the sample code above. I can see the code under MS and Interdev Debuggers, etc.

    However, when I have intentional errors in the script code, the OnScriptErrorDebug method is never called.

    The previous method of handling errors (OnScriptError method) is not called unless I fire up a debugger and look at the source window for the script with the error in it.

    Where have I gone wrong??

    Reply
  • Script debugger doesn't start up

    Posted by Legacy on 02/11/2000 12:00am

    Originally posted by: Michael

    When I run the sample application on a machine with just NT 4 SP4, IE 5 and the MS Script debugger installed (thus without Visual Studio), the debugger won't start when I push the 'Debugger' button.
    When run on a machine with Visual Studio installed, the VisStd debugger will start.

    What am I missing?

    Reply
  • A way of NOT debugging with MSSCRIPT.OCX?

    Posted by Legacy on 11/03/1999 12:00am

    Originally posted by: oscar

    I'm developing a non-MFC project (ATL) with MSSCRIPT.OCX, and I don't want to have the "Run-time error" window. I know I can use the "On error resume next" statement, but then I lost the error information (sometimes, in certain errors). Can anyone help me?
    

    Reply
  • ATL

    Posted by Legacy on 11/02/1999 12:00am

    Originally posted by: oscar

    I'm looking to implement this in ATL. Have you an example?
    

    Reply
  • IActiveScriptSiteDebug::GetDocumentContextFromPosition not called (and solution)

    Posted by Legacy on 04/09/1999 12:00am

    Originally posted by: Karim Ratib

    In the sample, IActiveScriptSiteDebug::GetDocumentContextFromPosition is never called. This is a problem because the host cannot control the appearance of the source displayed in the debugger. To fix this, just specify the flag SCRIPTTEXT_HOSTMANAGESSOURCE when calling IActiveScriptParse::ParseScriptText.

    Reply
  • Active Debugging header files

    Posted by Legacy on 01/11/1999 12:00am

    Originally posted by: Juraj Rojko

    Active Debugging header files (ActivDbg.h etc.) are part of Internet Explorer 4.01 Refresh of the Internet Client SDK. This refresh can be downloaded from
    http://support.microsoft.com/support/downloads/dp2928.asp

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • QA teams don't have time to test everything yet they can't afford to ship buggy code. Learn how Coverity can help organizations shrink their testing cycles and reduce regression risk by focusing their manual and automated testing based on the impact of change.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds