Home page Home page Home page Home page
Pixel
Pixel Header R1 C1 Pixel
Pixel Header R2 C1 Pixel
Pixel Header R3 C1 Pixel
Pixel
By Sprezz | Friday, 15 November 2024 14:52 | 0 Comments

An interesting query made its way into the office this week. Our client wanted to be able to validate data using Regular Expressions (REGEXP) for Email validation (^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$ if you must know) and so unsurprisingly searched the documentation for REGEXP and came across its usage in RTI_JSON. This led to a series of mutual misunderstandings which led to a far greater understanding of what JSON actually is (see footnote) but still didn't help in actually validating using a REGEXP.

Fortunately, we have resources in the office who were able to advise that what we wanted to achieve was actually relatively simple using the OI ActiveX Scripting Host. This is something that is not yet "officially" documented in the product - but like quite a few things in OI, there is profuse documentation in the Equates. In version 10 this has been handily encapsulated for us the the RTI_AXSH function - the ActiveX Scripting Host.  ActiveX Scripting and Active Scripting are the same thing.  The X was dropped over the years. 

But first - what actually is a Scripting Host?

An Active Scripting Host is a technology used in Windows to support component-based scripting. It allows different scripting engines to be integrated into applications. This enables the execution of scripts written in various languages like VBScript, JScript (Microsoft’s implementation of JavaScript), Python, Perl and others. If we have a task we wish to execute that is better suited to say, JScript - performing a REGEXP for example - we can use RTI_AXSH to do this for us. 

To show how easy it is, we'll first provide a small sample program to do this, then explain the program and finally document the routine.

The Code

compile function test_revaxsh_regexp( void )

   $insert rti_AXSH_Equates
   $insert rti_SSP_Equates
   $insert logical

   call set_Status( FALSE$ )

   errStat = SETSTAT_OK$
   errText = ""
   hAXSH  = 0
   retVal = ""

   code =   're = new RegExp( "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" );'
   code :=  'function testRE( str ) {'
   code := '   return re.test( str );'
   code := '}'

   createParams = ""
   createParams<REVAXSH_CREATE_POS_LANGUAGE$> = "JScript"

   hAXSH = rti_AXSH( REVAXSH_MTD_CREATE$, createParams )

   errStat = get_Status( errText )

   if errStat then
      return ""
   end

   call rti_AXSH( REVAXSH_MTD_ADDCODE$, hAXSH, code )

   errStat = get_Status( errText )

   if errStat then
      return ""
   end

   string = "amcauley@sprezzatura"

   if get_Status() else
      retVal = rti_AXSH( REVAXSH_MTD_RUNEX$, hAXSH, 'testRE', string )
      errStat = get_Status( errText )
   end

   if ( hAXSH ) then
      call rti_AXSH( REVAXSH_MTD_DESTROY$, hAXSH )
   end

   if errStat then
      debug
   End

return retVal

The Explanation

The first thing that we need to do is insert RTI_AXSH_Equates. Once this has been done, we construct some simple JScript code in a variable called code. For those not fluent in JScript it's probably worth explaining how the code operates.

We start by create a global variable called "re". Think of it as being like labelled common. It'll be there until the scripting host is destroyed. Loading the code instantiates the re object as a REGEXP type.

We then define a JScript function called testRE so that we can call it from within Basic+.

We initialise our create parameter to tell OI which scripting language to use and then use the CREATE method to initialise our Script Hosting. Now that we have a container to put our code in, we can add it using the ADDCODE method. Assuming no errors we then use the RUNEX method, telling it the name of the function in our code we wish to execute (testRE). We now have the result we want to do with as we will.

Finally in an act of polite housekeeping we tell the system to DESTROY the scripting host we created so as not to leak resources.

We hope that this information makes the Scripting Host a little less scary!

The Documentation - taken from RTI_AXHS_EQUATES.

The RTI_AXSH routine provides a set of methods for interacting with the ActiveX Scripting Host. Below is a detailed guide on how to use these methods, including their parameters, return values, and error handling.

The following methods are supported

MethodDescription
CREATECreates an ActiveX scripting host instance and returns its handle.
DESTROYDestroys a script host instance.
GETPROPReturns the value of a specified property.
SETPROPSets the value of a specified property.
ADDCODEAdds a block of code to the scripting host.
EVALEvaluates a statement and returns the result.
EXECUTEExecutes one or more statements (no result is returned).
RUNExecutes a specific script function and returns the result (if any).
GETENGINESReturns a list of ActiveX script engines installed on the workstation.
ADDOBJECTAdds a global named object to the scripting host.
RUNEXExecutes a specific script function and returns the result (if any), allowing the script function parameters to be passed as separate arguments.

CREATE

Creates an ActiveX scripting host instance and returns it's handle.

The instance is initialised with the following information:

    <1> Script Language (required)
    <2> AllowUI (1/0)
    <3> hwndSite
    <4> Timeout in milliseconds (-1 == no Timeout, otherwise > 0)

 e.g.

    createParam = "JScript" : @fm : TRUE$ : @fm : @@window->handle
    hAXSH = rti_AXSH( REVAXSH_MTD_CREATE$, createParam )


InputcreateParam : an @fm delimited dynamic array as specified above
ReturnsThe handle of the new AXSH instance of successful, otherwise "0".
ErrorsAll errors are returned via Set_Status()

DESTROY

Destroys a script host instance. 

    call rti_AXSH( REVAXSH_MTD_DESTROY$, hAXSH )

InputhAXSH : Handle of the scripting host to destroy             [required]
ErrorsAll errors are returned via Set_Status()

GETPROP

Returns the value of a specified property

 e.g.

    propName = REVAXSH_PROP_LANGUAGE$
    language = rti_AXSH( REVAXSH_MTD_GETPROP$, hAXSH, propName  )

InputhAXSH     : Handle of the scripting host to access         [required]
InputpropName : Name of the property to access                   [required]
ReturnsThe property value
ErrorsAll errors are returned via Set_Status()

The supported properties are "Language", "Allow UI", "SiteHnd" and "Timeout"
ANY DEFAULTS CARL?

SETPROP


Sets the value of a specified property

 e.g.
 
    propName  = REVAXSH_PROP_ALLOWUI$
    propValue = FALSE$ 
    call rti_AXSH( REVAXSH_MTD_SETPROP$, hAXSH, propName, propValue )

InputhAXSH     : Handle of the scripting host to access           [required]
InputpropName : Name of the property to access                     [required]
InputpropValue : New property value to set.
ErrorsAll errors are returned via Set_Status()

ADDCODE


Adds a block of code to the scripting host (Any immediate code will be executed as normal)

 e.g.
 
    scriptCode = "function add( a, b ) { return a + b; }"
    call rti_AXSH( REVAXSH_MTD_ADDCODE$, hAXSH, scriptCode )

InputhAXSH     : Handle of the scripting host to access           [required]
InputscriptCode : Code to add.                                                  [required]
ErrorsAll errors are returned via Set_Status()
   

EVAL


Evaluates a statement and returns the result.

 e.g.
 
    statement = "3+10;"
    retVal = rti_AXSH( REVAXSH_MTD_EVAL$, hAXSH, statement )

InputhAXSH     : Handle of the scripting host to access           [required]
Inputstatement  : Code to evaluate.                                           [required]
ReturnsThe result of the evaluation.
ErrorsAll errors are returned via Set_Status()

EXECUTE


Executes one or more statements (no result is returned)

 e.g.
 
    scriptCode = "function add10( a, b ) { return a + 10; }; add10( 3 );"
    call rti_AXSH( REVAXSH_MTD_EXECUTE$, hAXSH, scriptCode )

InputhAXSH     : Handle of the scripting host to access           [required]
Inputstatement  : Code to execute.                                             [required]
ErrorsAll errors are returned via Set_Status()

RUN


Executes a specific script function and returns the result (if any).

 e.g.
 
    scriptCode = "function add( a, b ) { return a + b; }"
    call rti_AXSH( REVAXSH_MTD_ADDCODE$, hAXSH, scriptCode )

    method = "add"
    args   = 3 : @rm : 4
    retVal = rti_AXSH( REVAXSH_MTD_RUN$, hAXSH, method, args )

ReturnsReturn value from the function (if any)
ErrorsAll errors are returned via Set_Status()

GETENGINES


Returns a list of ActiveX script engines installed on the workstation. 

 e.g.
 
   engineList = rti_AXSH( REVAXSH_MTD_GETENGINES$ )

ReturnsAn @fm delimited list of installed engines.
ErrorsAll errors are returned via Set_Status()

ADDOBJECT


Adds a global named object to the scripting host

 e.g.
 
    objName = "MyXmlDoc"
    objDoc  = OleCreateInstance( "Msxml2.DOMDocument" )
    call rti_AXSH( REVAXSH_MTD_ADDOBJECT$, hAXSH, objName, objDoc )


InputhAXSH   : Handle of the scripting host to access               [required]
InputobjName : "Script name" of the object being added           [required]
InputoleVar     : OLE object being added                                    [required]
ErrorsAll errors are returned via Set_Status()

RUNEX

Executes a specific script function and returns the result (if any), allowing the script function parameters to be passed as separate arguments (This method is the only method that can pass OLE objects to a script function)

The maximum number of parameters that can be passed is 10.  Note that this method  stops looking for script function arguments when it encounters the first unassigned one.

 e.g.

    scriptCode = "function add( a, b ) { return a + b; }"
    call rti_AXSH( REVAXSH_MTD_ADDCODE$, hAXSH, scriptCode )

    method = "add"
    arg1   = 3
    arg2   = 4
    retVal = rti_AXSH( REVAXSH_MTD_RUN$, hAXSH, method, arg1, arg2 )


InputhAXSH   : Handle of the scripting host to access               [required]
Inputmethod    : name of the function to execute                        [required]
Inputarg1         : First parameter to pass to the function
Inputarg2         : Second parameter to pass to the function
Inputarg3         : Third parameter to pass to the function
Inputarg4         : Fourth parameter to pass to the function
Inputarg5         : Fifth parameter to pass to the function
Inputarg6         : Sixth parameter to pass to the function
Inputarg7         : Seventh parameter to pass to the function
Inputarg8         : Eighth parameter to pass to the function
Inputarg9         : Ninth parameter to pass to the function
Inputarg10       : Tenth parameter to pass to the function
ErrorsAll errors are returned via Set_Status()


A note relating to all scripting errors

 The Execute, AddCode, Eval and Run methods can all return parsing and execution errors from the scripting engine. In this case an @svm delimited list of error information is returned with the following structure:

    <0,0,1> Error Source
    <0,0,2> Error Description
    <0,0,3> LineText
    <0,0,4> LineNumber
    <0,0,5> CharPos
    <0,0,6> ContextID


Footnote: 

JSON (JavaScript Obect Notation) is a way of serializing JS variables/objects to a human-friendly string format so they can be saved and reloaded. It is not intended as a way to create JS objects and then call their methods and such... despite what the author thought


Pro Tip

CoPilot understands RegExp well. It suggests replacing the REGEXP we started with with "^[\w.%+-]+@[\w.-]+\\.\w{2,}$". 



 

Pixel
Pixel Footer R1 C1 Pixel
Pixel
Pixel
Pixel