Scripting OLE Automation Objects in Mathcad and MathConnex

1.	Overview
MathConnex and Mathcad expose a component which allows users to script an OLE automation object for use within a computational environment. This component uses the Microsoft ActiveX Scripting specification to expose scripting languages which can be used to “drive” the automation object. Although these languages provide features which allow the author to communicate with the OLE object, they do not provide any intrinsic facilities for communicating with the outside environment (in this case, either MathConnex or Mathcad). In order for a scripted object to be useful in a computation, it must have a mechanism for processing inputs and sending outputs to and from the host environment. The scripting object model solves this problem by providing a framework for scripting languages to interact within a computational environment.

2.	Scripting
2.1	Event-Driven vs. Procedural
Most programming languages are structured to be executed as a series of statements: either in a top-down fashion starting with the first statement encountered, or with a predefined entry point (such as main() in the C language). This procedural approach assumes a program which will be executed linearly, continuously executing statements until the last statement is reached. While this works well in a variety of contexts, it’s not as useful in an environment which is event-driven. In an event driven environment, the program doesn’t execute in such a continuous fashion, but rather is called upon to do work based on the triggering of an outside event. This is typically accomplished by defining one or more event functions which are called by the event controller whenever an appropriate event is triggered. 

2.2	Events
The scripting component has adopted an event-driven model to serve as the framework for scripting components. This model defines three basic events which are triggered by the outside (host) environment:
2.2.1	Start
The “Start” event is fired by the host environment whenever a computation is started. This is a good place for scripts to set initialization variables, open files for reading or writing, etc. This is called once before the computation actually begins.
2.2.2	Exec
This event is fired when the host environment supplies inputs and requests any outputs from the script. There are two parameters defined for the Exec function: inputs and outputs. These parameters are objects of type DataArray. See the description of DataArray for details.

NOTE: It is possible for this event function to be called many times after a single “Start” event is triggered. This can happen in environments where repeated computation is required during a single recalculation of the host environment. (MathConnex is an example of this kind of environment, since the user presses “Run” and repeated computations may occur before the worksheet is stopped.) Script writers should put any “one-time” initialization code in the Start event so that the script is able to handle repeated calls to Exec for processing of input and output data.
2.2.3	Stop
This event is fired to tell the script that the current computation has stopped. It will only be called after the Start event has been called. This is a good place for cleanup of data, closing any open files, etc.

NOTE: It is possible for the Start and Stop event to be triggered one after the other without the Exec event ever being triggered. This can happen in cases where the computation begins but the input data is never available to the script component, preventing it from executing.

2.3	Event Function Implementation
The actual event function syntax is language-specific. Most scripting languages provide a standard mechanism for event functions, usually a combination of the object which defines the event (sometimes referred to as the object which sources the event) and the name of the event. When a script contains a function which matches the naming convention used for the event, the language “attaches” the function to the event so that it is called whenever the event is fired. VBScript, for example, uses the name of the event source, followed by an underscore, followed by the event name. So if an object called “MyEventSrc” defined an event handler called “Event1”, then the VBScript function would look something like this:

Sub MyEventSrc_Event1( )
REM do something when the event is trigerred
End Sub

The event functions are defined in an OLE object’s type library. The script writer must be aware that the events exist and the names used for them in order to provide appropriate event handler functions in the script.

2.4	JScript caveats
The JScript language is a relatively recent addition to the scripting support provided by Microsoft, and is a language originally developed by Netscape for scripting of web pages. Due to this history, JScript doesn’t always follow the common conventions for automation languages. The following summarizes the main differences between an “automation” language such as VBScript and JScript:

2.4.1	Events
JScript has two main differences with a language such as VBScript when it comes to providing event handlers in the scripting language. First, JScript is case sensitive, whereas VBScript is not. As a result, care must be taken when writing event handling functions in JScript since the names “foo()” and “Foo()” are considered two different functions.

Secondly, JScript’s event mechanism is supposed to be similar to VBScript in that you specify the event by the object name followed by an underscore followed by the event name (respecting upper-lower case conventions). However, due to limitations in the current implementation of JScript, the string “__JSEventHandler__” must be prepended to any function names in order for the scripting engine to correctly attach the even to the function. Thus the following event handlers would be valid event handlers in VBScript and JScript respectively:

VBScript:
Sub MyEventSrc_Event1()
	REM TODO: add your code here
End Sub

JScript:
function __JSEventHandler__MyEventSrc_Event1()
{
	// TODO: add your code here
}

2.4.2	Collections
Collections are natively supported in VBScript using the For Each…Next syntax. Although JScript provides a similar for (x in y) type of mechanism, the current implementation does not appear to work with automation collections. This is only a convenience, since collections can still be accessed with the standard for loop syntax in both languages.
2.4.3	Arrays 
2.4.3.1	Comparison with Variant Arrays
Arrays in VBScript follow the standard automation VARIANT object. That is, if you create a VARIANT array used in automation, the array elements can be directly accessed via VBScript. JScript uses an internal representation for arrays and therefore will not be able to access VARIANT arrays. This is a serious limitation, since most automation objects which deal in arrays will represent them as VARIANT arrays. To avoid this limitation for the arrays represented in a DataValue object (see description below), the Value and IValue methods of the DataValue object are overloaded to allow a column, row index to be supplied. This means that scripting languages that do not support VARIANT arrays natively can still access data values through the use of indexing.
2.4.3.2	Multi-Dimensional Arrays
JScript’s internal array creation mechanism only supports single-dimension arrays. Therefore you can declare an array of 5 elements (x=new Array(5)), but you cannot directly declare a 2D array. To circumvent this restriction, JScript allows “nested” arrays to achieve multiple dimensions. An example of this would be the following code which creates a 2x2 2D array:

var js2DArray = new Array(2);  
for (var j = 0; j < js2DArray.length; j++)  
{  // Prepare to fill it with rows.
    	var aRow = new Array(2);  // Create a row.
    	for (var i = 0; i < aRow.length; i++)  
{  // fill the row with zeroes.
    		aRow[i] = 0
    	}
js2DArray[j] = aRow;  // Put the row into the outer array.
}

Once a nested array is declared, elements in the array can be accessed using multiple sets of brackets ([]).. For example, js2DArray[col][row] is valid for the example above, while js2DArray[col,row] would generate an error.
2.4.3.3	Array assignment
JScript does allow the assignment of VARIANT array values from one object to another. This means that a VARIANT array can be passed from an input DataValue to a scripted object, then back to an output DataValue as long as JScript is not used to access the elements in the VARIANT array. Examples are provided at the end of this document to show the syntax for accessing the DataValue object with each language.

NOTE: Internally created JScript arrays (i.e. x = new Array()) cannot be assigned to the methods or properties of automation objects which expect VARIANT arrays, since JScript represents arrays in a different format. Therefore the following sequence of statements is not valid in JScript:

var jsArray = new Array(5) ;
Outputs(0).Value = jsArray;

3.	Scripting Object Model
The following describes the methods, properties and objects exposed for scripts written with the scripting component. For convenience, the examples and syntax below use VBScript style syntax unless otherwise noted.

3.1	Global Functions
The script component exposes the following global methods which may be used by script writers.

3.1.1	alert
Syntax:
alert( string )

Description:
This function is provided mainly for compatibility with the JScript language. This function takes a single parameter as a string which will be presented to the user as a standard modal Windows message box with an OK button. VBScript users will probably want to use the more extensive MsgBox function -- which is provided natively by the VBScript engine -- rather than the alert method.

Example:
function __JSEventHandler__MyEventSrc_Event1( )
{
	// will pop up a message box with the following text
	alert(“In MyEventSrc_Event1!”);
}

Note: the example above uses the JScript language syntax.

3.1.2	errmsg
Syntax:
errmsg( msg, [src])

Description:
This function is provided mainly for compatibility with the JScript language. This function will display an error message from within the script and cause the script to stop execution. In some environments, this error message may include line number information for the source of the error.

The first parameter is the error message text. There is a second, optional parameter which can be used to display the source of the error.

Example:
	// will pop up a message box with the following text
	// and cause the script to stop execution
	errmsg(“An error has occured!”);

	// same effect, but with second parameter specified
	errmsg(“An error has occured”, “My Script”);

Note: the example above uses the JScript language syntax.

3.2	DataValues Collection
Collections are an automation concept which allows a set of associated elements to be bundled together. Standard methods and properties are provided to allow access to each of the elements in the collection. Collections are directly supported in some languages, allowing easy iteration over the elements in the collection. VBScript supports collections through the for each …next construct. Languages that do not directly support collections can still iterate through the elements using the standard properties and methods described below.

The DataValues collection is used in the Exec event which takes two DataValues collections as parameters. The first parameter (named “Inputs”) is a collection of inputs. Each input element in the collection is an object of type DataValue (see below). Script authors can iterate through elements and query the total number of elements in the collection. The “Outputs” parameter is also a collection of DataValue objects representing each output provided by the component. Script authors may set the data for each output object in the collection. These output values are then used by the host environment when the event function returns.

3.2.1	Properties
3.2.1.1	Count
Syntax:
x = Obj.Count

Description:
This read-only property can be used to query the total number of elements in the collection. 

Example:
	for i= 0 to Inputs.Count-1
		val = Inputs(i)
	next

3.2.2	Methods
3.2.2.1	Item
Syntax:
Obj.Item( index )
Obj( index )

Description:
The Item method is used to specify an individual element in the collection. The DataValues collection is zero-based, so the item numbers range from 0 to Count-1.

The Item method is the default method for this object; for languages that support it, this means that the method name can be dropped, in which case the default method is implied. 

Example:
	for i= 0 to Inputs.Count-1
		val = Inputs.Item(i)
	next

or, since Item is the default property, the following is also valid:

	for i= 0 to Inputs.Count-1
		val = Inputs(i)
	next

Note: Although the example above shows VBScript, both VBScript and JScript support this shorthand approach for default methods and properties.

3.3	DataValue Object
3.3.1	Properties
3.3.1.1	Value
Syntax:
x = Obj.Value ( [row, col] )
Obj.Value ( [row, col] ) = x

Description:
The Value property is used to access the real portion of the data in the DataValue object. Note that the row and col parameters are optional. If the row and col parameters are omitted, then the value is returned as a 2D VARIANT array of double values or as a 1D VARIANT array of double values or as a scalar, depending on the type of data contained in the DataValue object. If the row and col parameters are specified, a scalar value at the specified row/column is returned.

NOTE: when the Value property is used on the left hand side of an assignment, the array automatically grows (if necessary) to fit the data. For example, if Outputs(0) initially contains no data and the script writer does the following :

Outputs(0).Value(1,2) = 1

A 2 row, 3 column data value is created which contains the value “1” at row 1, col 2. If a value already exists and an assignment is made which exceeds the current maximum row or column, the value is automatically re-dimensioned to fit the new data. This feature of the Value property allows languages that do not directly support VARIANT arrays (such as JScript) to create and access DataValue  arrays.

Example:
	for i= 0 to Inputs.Count-1
		valmatrix = Inputs(i).Value ‘ returns a whole matrix
		valscalar = Inputs(i).Value(0,0) ‘ returns element at 0,0
	next

3.3.1.2	IValue
Syntax:
x = Obj.IValue ( [row, col] )
Obj.IValue ( [row, col] ) = x

Description:
The IValue property is used to access the imaginary portion of the data in the DataValue object. Note that the row and col parameters are optional. If the row and col parameters are omitted, then the value is returned as a 2D VARIANT array of double values or as a 1D VARIANT array of double values or as a scalar, depending on the type of data contained in the DataValue object. If the row and col parameters are specified, a scalar value at the specified column/row representing the imaginary portion of the data is returned. Note that the DataValue object may not contain an imaginary value (e.g. it is a real number), in which case the IValue portion will return NIL.

Example:
	for i= 0 to Inputs.Count-1
		valmatrix = Inputs(i).IValue 		‘ returns a whole matrix
 		if (valmatrix != nil) then
			valscalar = Inputs(i).IValue(1,0) 	‘ returns element
end if 				‘ in second row, first column
	next

3.3.1.3	Rows
Syntax:
x = Obj.Rows

Description:
The Rows property is a read-only value which returns the number of rows in the DataValue object.

Example:
NumRows = Inputs(0).Rows

3.3.1.4	Cols
Syntax:
x = Obj. Cols

Description:
The Cols property is a read-only value which returns the number of columns in the DataValue object.

Example:
	NumCols = Inputs(0).Cols

3.3.1.5	IsComplex
Syntax:
if (Obj. IsComplex) then…

Description:
The IsComplex property is a read-only value which returns TRUE if the value has a valid imaginary portion (which can be accessed through the IValue property). If IsComplex returns FALSE, it can be assumed that the value has only a real portion.

Example:
	if (Inputs(0).IsComplex ) then
MsgBox( “Complex Value!” )
else
MsgBox( “Real Value!” )
end if
3.3.2	Methods
None.

4.	Script Examples
The following shows sample scripts using two popular scripting engines within a MathConnex or Mathcad environment. Note that these examples are intended to give an impression of the syntactical conventions and how they apply to various object methods and properties, rather than a truly useful script.

The scripts below all accomplish the same task. They iterate through the available inputs, display the value of the first element in a popup message box, assign the input to the output, and finally add one to the first element of each output.
4.1	VBScript
This version of the script will iterate through each available input using the For Each…Next construct. 

Sub ScriptObjEvent_Start()
  REM TODO: Add your code here
End Sub

Sub ScriptObjEvent_Exec(Inputs,Outputs)
  Dim i
  i=0

  Dim dataval
  for each dataval in Inputs
    val = dataval.Value

    REM popup a message box to show progress through collection
    MsgBox(val(0,0))

    REM just pass inputs on to the outputs, incrementing
    REM the first element by 1
    if i < Outputs.Count then
        val(0,0) = val(0,0)+1
   	Outputs(i).Value = val
    end if

    i = i + 1
  next
End Sub

Sub ScriptObjEvent_Stop()
  REM TODO: Add your code here
End Sub

The second version accomplishes the same task without using the For Each…Next construct:

Sub ScriptObjEvent_Start()
  REM TODO: Add your code here
End Sub

Sub ScriptObjEvent_Exec(Inputs,Outputs)
  Dim dataval
  for i=0 to Inputs.Count-1
    set dataval = Inputs(i)
    val = dataval.Value

    REM popup a message box to show progress through collection
    MsgBox(val(0,0))

    REM just pass inputs on to the outputs, incrementing
    REM the first element by 1
    if i < Outputs.Count then
        val(0,0) = val(0,0)+1
   	Outputs(i).Value = val
    end if
  next
End Sub

Sub ScriptObjEvent_Stop()
  REM TODO: Add your code here
End Sub

4.2	JScript
The JScript version to accomplish the same result:

function __JSEventHandler__ScriptObjEvent_Start()
{
  // TODO: Add code here
}

function __JSEventHandler__ScriptObjEvent_Exec(inputs,outputs)
{
  var dataval;
  for ( i=0; i<inputs.Count; i++ )
  {
    // look at each dataval in the collection
    dataval = Inputs(i)

    // show what we found
    alert(dataval.Value(0,0).toString());

    // pass inputs on to outputs, incrementing 
    // first element by one
    if ( i<Outputs.Count )
    {
	Outputs(i).Value = dataval.Value;
        Outputs(i).Value(0,0) += 1 ;
    }
  }
}

function __JSEventHandler__ScriptObjEvent_Stop()
{
  // TODO: Add code here
}

(10/27/97 – revised 11/02/98)