Download ScriptDemo
Demo Project - 31 Kb
ScreenShots
Introduction
I am always
amazed to see how the
script control (msscript.ocx)
is fun to use and at the same time how
VC++
developers reacted when it's time to use.
Maybe the extension (.ocx) make them feel,
it's visual basic!
In this article,
I would like to remove those frontiers and
give some new reasons for
VC++
developers to use it.
Description
To use either
VBScript
or JScript
is fairly simple in a
VB and
VC++ Application thanks to
Microsoft's development efforts to create
Windows Scripting technology. A developer
only needs to know how to use the Microsoft
Scripting ActiveX
control (msscript.ocx)
and how to pass value to a script method.
For this reason, the first wrapper class
that I want to identify is the
CScriptObject
.
This wrapper is very simple to use and it
provides most of the functionality that you
will want to use in your application. It has
a function to load
script (text data) from a file or
resource, get a list of methods name,
selecting script
language and to the execute function and
statement. This class has no dependencies on
MFC and can also be used in a console
application.
First of all
to call a script
it is important to know that
VBScript
and JScript
deal only with
VARIANT
parameters. This is the
reason I created the
CSafeArrayHelper
class. The
CSafeArray
helper wrapper
class allows you to create parameters that
you will pass to your script function.
class CSafeArrayHelper
{
public:
CSafeArrayHelper();
~CSafeArrayHelper();
bool Create(VARTYPE vt, UINT cDims, UINT lBound, UINT cCount);
bool Destroy();
UINT GetDimension();
bool Attach(LPSAFEARRAY psa);
bool AttachFromVariant(VARIANT* pVariant);
LPSAFEARRAY Detach();
LPSAFEARRAY GetArray();
bool AccessData(void FAR* FAR* pvData);
bool UnaccessData();
bool Lock();
bool Unlock();
bool PutElement(long lIndices, void FAR* vData);
bool GetElement(long lIndices, void FAR* vData);
VARIANT GetAsVariant();
protected:
LPSAFEARRAY m_pSA;
private:
};
It provides the
exact same features that you will want to
use with
SAFEARRAY
object but its usage may be simpler for some
of us (like me!). The function
GetAsVariant
may be useful in case when you want to view
the type of data that was encapsulated in
your
SAFEARRAY
.
This function could not provide ways to read
all data types since the
SAFEARRAY
Data type (fFeatures
)
didn't implement it. Nonetheless to say,
this function do a guess on the data types.
How to use
First to use
this control,
I will recommend you to take a look at the
documentation for
VBScript and
JScript
to know all you can do within your
script
function.
Writing a
Script function
Let's say we
want to create a simple function to convert
temperature from Fahrenheit to Celsius.
In
VBScript
write:
Function Celsius(fDegrees)
Celsius = (fDegrees - 32) * 5 / 9
End Function
or in
JScript
write:
function Celsius(fDegres)
{
return (fDegres-32)*5/9;
}
To call this
function, one only needs to store each
parameter into
VARIANT
.
Since your function (method) can have more
than one parameter, a
SAFEARRAY
is needed to encapsulated them. In that
latter case, you may want to view the
parameter count for the array passed to your
function by checking the
.length
property for string function or by some
other means.
function CountParam(aParam)
{
var strPresent = "Parameter is : " + (aParam.length>0 ? "Present": "Not present");
return strPresent;
}
The same
technique may be used in
VBScript.
This allows you to detect variable length
argument at run time. To call a function
without argument, a
SAFERRAY
is created but without parameter.
Calling a
Script function
Your code
can be as easy as this:
Collapse
void CScriptDemoDlg::OnBtnExecute()
{
CString strParam, strProc;
m_ctlParameter.GetWindowText( strParam );
m_ctlFunctions.GetWindowText( strProc );
CSafeArrayHelper sfHelper;
try{
_variant_t var;
if (strProc.IsEmpty())
sfHelper.Create(VT_VARIANT, 1, 0, 0); else
{
sfHelper.Create(VT_VARIANT, 1, 0, 1); var = _bstr_t(strParam);
}
sfHelper.PutElement(0, (void*)&var); LPSAFEARRAY sa = sfHelper.GetArray();
_variant_t varRet;
if (m_ScriptObj.RunProcedure(strProc, &sa, &varRet))
m_ctlResult.SetWindowText( (LPCTSTR)(_bstr_t(varRet)) );
else
{
CString strError = m_ScriptObj.GetErrorString();
m_ctlResult.SetWindowText( strError );
}
}
catch(...)
{
CString strError = m_ScriptObj.GetErrorString();
m_ctlResult.SetWindowText( strError );
}
}
Some Ideas
Some of the
ideas that you may want to try.
- You may
want to have your
script acts like a plugin,
one suggestion is to have a resource
script
into a DLL and loads it at runtime (you
may also have it part of your
application). In that case, you will
want to have specific module-related
function, like:
InitModule
,
ReleaseModule
,
btnOK_Click
,
btnCancel_Click
,
LoadUserData(strUsername)
,
SaveUserData(strUserData)
,
etc... and each of your DLL will have to
implement them.
- You may
have your
script to do a complete tucancode.net
and you will load the script file based
on the tucancode.net (the
CScriptObject
class can load a script file for you!).
Example:
This script
starts the "Calculator" program.
function StartCalc()
{
var WshShell = new ActiveXObject("WScript.Shell");
var oExec = WshShell.Exec("calc");
WshShell = null;
}
- You may
want to create ActiveX object that lives
longer than for a function call:
var XML_Obj;
function StartModule()
{
XML_Obj = new ActiveXObject("Msxml.DOMDocument");
XML_Obj.async = false;
}
function StopModule()
{
XML_Obj = null;
}
function LoadSettings(strFilename)
{
XML_Obj.load(strFilename);
}
- There
are cases that you may want to execute
the script
code directly, just add the code, do not
create a function...try it for fun!
References
Microsoft Windows
Script Control
VBScript
Documentation
JScript
Documentation