Channels ▼
RSS

Active Scripting Newsletter


Dr. Dobb's Active Scripting Newsletter - 07/30/01

by Mark M. Baker

 

Scripting Chat

This month I received an e-mail from a developer regarding a problem he was having with IActiveScriptParse::ParseScriptText blocking and not returning when a script was parsed. I thought it would be useful to cover this method in some more detail since it is highly used in Active Scripting, but not necessarily well understood.

First off, some background on the method for those newer to Active Scripting. When constructing an Active Scripting "host" (the code module that interacts with the scripting "engine" and manages the process of feeding script to the engine, handling events from the engine, etc.), one of the first steps after constructing the appropriate engine (VBScript, JScript, etc) is to feed script text into the engine itself. This is done by using IActiveScriptParse::ParseScriptText. The IActiveScriptParse interface is obtained from the engine via a normal COM IUnknown::QueryInterface call.

The ParseScriptText method itself has a number of parameters that must be either used or defaulted depending upon the style of use. Let's take a look at these parameters in more detail:

HRESULT ParseScriptText(
    LPCOLESTR pstrCode,
    LPCOLESTR pstrItemName,
    IUnknown *punkContext,
    LPCOLESTR pstrDelimiter,
    DWORD dwSourceContextCookie,
    ULONG ulStartingLineNumber,
    DWORD dwFlags,
    VARIANT *pvarResult,
    EXCEPINFO *pexcepinfo );

pstrCode
[in] A pointer to the script text to be parsed by the engine. This text can be a group of one or more functions, global script code such as variables, etc.

pstrItemName
[in] A pointer to the name of a context (aka named context) in which the script should be evaluated. If NULL, the context is the global context. A named context can be thought of as similar to a C++ namespace in that it wraps a cluster of code with a name. This named context can then later be accessed with IActiveScript::GetScriptDispatch() to fetch only those functions within a particular namespace.

punkContext
[in] A pointer to a context normally used in a debugging environment. This context would be the IDebugDocumentContext and would represent a subsection of a running script. Under most circumstances, this parm would be set to NULL.

pstrDelimiter
[in] A end-of-document delimiter. This could be used to parse only a subset of the entire script passed with pstrCode and could be any character. However, under most circumstances, this parm would be NULL, indicating that parsing should continue until a NULL character is reached in the passed string (end of the string).

dwSourceContextCookie
[in] A value used later in a debugging host (smart host) to interact with the script document to set breakpoints, locate subsections of the document, etc. This value is obtained through a prior call to IDebugDocumentHelper::DefineScriptBlock. If creating a basic host without the more advanced debugging support, this value can be 0.

ulStartingLineNumber
[in] The line of script at which parsing will begin. Lines are defined by carriage return/line feed pairs (\r\n). Normally this would be set to 0.

dwFlags
[in] There are 3 choices here: SCRIPTTEXT_ISEXPRESSION, SCRIPTTEXT_ISVISIBLE, SCRIPTTEXT_ISPERSISTENT.

SCRIPTTEXT_ISEXPRESSION is used to evaluate a small block of script as an expression and have the result returned immediately. For example, if you needed the result from a
script that performed a numeric calculation. This could be used in a web page that provides a simple calculator, or for adding up numbers in an on-screen form. This flag is infrequently used.

SCRIPTTEXT_ISVISIBLE is used when the script text is to be callable within the global namespace. This flag is normally included; however, it could be excluded in a case where the host wishes to add multiple sets of script text, but where one or more of the sets is not to be visible outside the script itself. In essence, this allows a form of code hiding.

SCRIPTTEXT_ISPERSISTENT is used to ensure that any parsed code is retained when moving back to the initialized state or when cloning the engine. Cloning is often done to restart an engine that was interrupted. By adding this flag, you ensure that you don't have to recall ParseScriptText to re-add the script text. However, you might find special circumstances where script text is meant to be run once and not again. This flag ensures that the script text is not stored in the engine unless you wish it to be.

pvarResult
[out] A pointer to a VARIANT that contains the result of an expression evaluation. Passing in a non-NULL value only makes sense if you also included the flag SCRIPTTEXT_ISEXPRESSION.

pexcepInfo
[out] A pointer to a EXCEPINFO structure that will contain exceptions encountered while trying to process the script text. It's important to understand that the EXCEPINFO structure should be initialized to NULL (cleared out) when passed, and checked for any non-NULL members when ParseScriptText returns. Any BSTRs that appear in the structure must be released by calling SysFreeString(). An easy way to ensure you don't forget to do this is by creating a C++ class called CExcepInfo that derives from EXCEPINFO. In the constructor, you'd call ZeroMemory(this,sizeof(EXCEPINFO)). In the destructor, you'd check each BSTR member and call SysFreeString as appropriate. When calling ParseScriptText, you'd pass in a pointer to the CExcepInfo class rather than a EXCEPINFO structure. The CExcepInfo class will then make sure cleanup and initialization are done when needed. The EXCEPINFO structure will contain useful information about possible parsing problems that can be relayed back to the user, particularly if the user was the one who wrote the script.

It's important to understand that ParseScriptText blocks when evaluating an expression, and doesn't block when evaluating normal script text. The developer controls this by passing in the appropriate flags to the method. In the case of non-blocking script text, the script is actually executed when a call is made to IActiveScript::SetScriptState. The problem I think developers have with this method is its multiple use configuration. It is trying to serve two different roles, and it is often unclear which flags to pass in, when to pass them in, or which flags/values conflict with others. Ideally, Microsoft would have broken out the method into two or more methods that were narrow and easier to understand. But the interfaces are here to stay and it's important to understand the operation of this critical method in Active Scripting.

Side Dish

Microsoft's Andrew Clinick has been writing a superb series of articles regarding scripting in the .NET world. I highly recommend reading the Scripting Clinick column section on http://msdn.microsoft.com when you have time.


The Active Scripting FAQ

Here's a new entry in the upcoming FAQ release:

Q: How can I safely interrupt a running script?

A: To interrupt a running script, you call IActiveScript::InterruptScriptThread(). This is not guaranteed to immediately interrupt the script. Rather, it sets a flag that the script should stop, and the script engine will stop at the next opportunity. InterruptScriptThread is one of the only two methods you can call on a script engine from outside its base thread (the other being SetScriptSite). Since the thread where the script engine is running is obviously blocked while the script is in progress, you must call InterruptScriptThread from a different thread. This would also apply for reacting to the ESC key to call InterruptScriptThread. Since the thread isn't pumping messages while it's waiting for the script to complete, it won't detect that the ESC key has been pressed. This sample code assumes you've marshalled an IActiveScript interface pointer to the second thread and named it pScriptEngine.

//Interrupt the script engine. You can either pass NULL for the second
//parameter, or you can pass an initialized EXCEPINFO structure. Passing
//an uninitialized EXCEPINFO structure may result in a crash.
EXCEPINFO ei;
memset( &ei, 0, sizeof(EXCEPINFO) );
ei.scode = 0x800a3328;
ei.bstrDescription = SysAllocString( L"Joel's custom error" );
ei.bstrSource = SysAllocString(L"InterruptScriptThread" );
HRESULT hr = pActiveScript->InterruptScriptThread( SCRIPTTHREADID_BASE, &ei, 0 );

Source: Joel Alley, Microsoft Developer Support


Do You Have a Passion for Scripting?

Check out the Dr. Dobb's Journal Alternative & Scripting Languages web site (http://www.ddj.com/topics/altlang/) for more information on a variety of scripting and alternative languages. This site changes regularly, so check back often.

Be sure to bookmark Microsoft Program Manager Andrew Clinick's Scripting Clinick monthly column on the in's-and-out's of Active Scripting and the new Visual Studio for Applications (VSA) .NET technology.


Looking for back issues of the Active Scripting newsletter?

Head over to the Dr. Dobb's Journal site (http://www.ddj.com/maillists/active/) to read any of the back issues online. The Active Scripting newsletter has now been in publication for over a year and you will find a wealth of information, commentary and insight that will make using and understanding Active Scripting easier and more productive.


Final Thoughts

I hope you will find this newsletter a timely and helpful addition to your knowledge base about Active Scripting. Please feel free to email me at mark_baker@mindspring.com and let me know what you think of it, ways to improve on content, format, etc., and how this newsletter is helping you.

Until next month,

Cheers!

Mark

 


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video