|
How do I execute a script in a given language?
You need to create an instance of the engine you want (say VBScript) and then get its IActiveScript interface. Once that is done, you need to pass the script text to the engine and then move it to the CONNECTED state. This executes the script.
Here are the steps:
If the script engine needs you to create an instance of an object within the script, it will call IActiveScriptSite::GetItemInfo. You need to be able to create and return the appropriate object or type library.
Note: This is one such set of steps to execute a script. There are other possible flags that can be passed to IActiveScriptParse::ParseScriptText, and additional steps that need to be taken if you want to implement ActiveX Script Debugging support.
(Source: mark_baker@mindspring,com, Mark Baker, 11/13/99)
How can I call a specific function or subroutine within my script?
After executing the script, you can call IActiveScript:: GetScriptDispatch () to get an IDispatch interface for the script. Then you can call IDispatch::GetIDsOfNames() to translate a function/subroutine name into a DISPID. Then you can call IDispatch::Invoke with the DISPID to execute the function. With this technique you can also pass in parameters to the function via the Invoke() method.
(Source: Microsofts Knowledge Base article Q222966, 3/31/99)
How can I determine the correct scripting engine to create from a
scripts text?
There is no built-in support that will just select the "right" engine for the text. For example, if you pass VBScript text to a JScript engine, it will report syntax and other errors (obviously). You may be able to discern language by the file extension of the script file if it is stored as an external file. This is how the Microsoft Windows Scripting Host works. If it sees .vbs, it knows its VBScript. If it sees .js, it knows its JScript.
(Source: mark_baker@mindspring,com, Mark Baker, 11/13/99)
What is the easiest way to re-execute a script?
Don't use SetScriptState(SCRIPTSTATE_INITIALIZED), the global code won't be run again.
The easiest way is to Clone() the script engine, and then reconnect it. Clone()ing allows you to run the global code again. If you clone, make sure you use the SCRIPTITEM_ISPERSISTENT and SCRIPTTEXT_ISPERSISTENT flags so the named items and text you added the first time stick around in the cloned script engine.
(Source: John Crim, WebGecko Software, 7/29/99, microsoft.public.scripting.hosting)
Note that one of the flags to AddNamedItem and ParseScriptText is "ISPERSISTENT". When the script engine is partially reset -- taken back to UNINITIALIZED state and initialized again -- everything that you marked as PERSISTENT stays around. When the script engine is completely reset - that is, closed -- then EVERYTHING is destroyed, even persistent items. So yes, you probably want to close the engine and then call SetScriptSite again to move it back to INITIALIZED state if you really want to throw away everything. Now, if you want to add all the same named items and code that you added before, then it is faster to mark them as persistent and just do a partial reset -- that saves the time required to recompile the code, for instance. ASP uses both techniques -- it maintains a pool of "blank" script engines and also a pool of script engines with compiled source for a particular page. If that page request comes in, then it can just do a partial reset and not recompile. If a new page request comes in, it can use a blank script engine. Engines can be moved from the partially-reset pool to the fully-reset pool by closing them.
(Source: Eric Lippert, Microsoft Scripting Dev, 2/27/1999, microsoft.public.scripting.hosting (Submitted by Andrew Nosenko, 5/27/2000) )
When Im done with the scripting engine, how should I release
the engine?
IActiveScript::Close will release your interface pointers.
(Source: John Crim, WebGecko Software, 8/6/99, microsoft.public.scripting.hosting)
I did come across some logic a while back that indicated that you should ensure that the engine is _not_ in the initialized state when closing it due to problems with some scripting engines. I do the following as a matter of course in my code so I can't say whether it will fix your problem. Here's the code:
if ( state == SCRIPTSTATE_INITIALIZED )
m_engine->SetScriptState(SCRIPTSTATE_CONNECTED);
m_engine->SetScriptState(SCRIPTSTATE_DISCONNECTED); // possible overkill
m_engine->Close();
(Source: mark_baker@mindspring,com, Mark Baker, 9/8/99, microsoft.public.scripting.hosting)
How do I trap runtime errors in my script?
Like all programs, scripts running in an Host can throw two kinds of errors, compile-time and run-time. In earlier implementations, the Engines provided by Microsoft (VBScript and JScript), made no distinction between the two types of errors. Both were handled in IActiveScriptSite::OnScriptError(). With more recent versions of the script engines, a distinction was made between run-time and compile-time errors. Compile-time errors, such as syntax errors, are still reported to the Host using the IActiveScriptSite::OnScriptError() method. However, run-time errors, such as passing invalid arguments, are not directly reported to OnScriptError(). Instead, they are reported to a different method, IActiveScriptSiteDebug::OnScriptErrorDebug().
(Source: Knowledge Base, MSDN, Q232394, 6/8/98)
In the newest releases of the script engines, IActiveScriptSiteDebug::OnScriptErrorDebug is called when a run-time error occurs. The IActiveScriptSiteDebug interface gives the Script Host a chance to participate in debugging before the debugger is involved. In order for the Script Host to be notified when a run-time error occurs, a minimal implementation of IActiveScriptSiteDebug is required. When the IActiveScript::SetScriptSite method is called, the script engine will QueryInterface the Host's IActiveScriptSite pointer for the IActiveScriptSiteDebug interface. If this fails, the script engine will attempt to contact the script debugger on its own. However, if the QueryInterface is successful, the script engine will then call IActiveScriptSiteDebug::GetApplication() to establish the debugging facilities for the scripting session. If IActiveScriptSiteDebug::GetApplication() fails, the script engine will conclude that debugging is not available on the machine, and revert to IActiveScriptSite::OnScriptError() for all error handling.
(Source: Knowledge Base, MSDN website, Q23394, 2/4/2000)
Why does setting the boolean variable
"callOnScriptErrorWhenContinuing" in
IActiveScriptSiteDebug::OnScriptErrorDebug() not call
IActiveScriptSite::OnScriptError?
Probably a bug in the JScript/VBScript engines. Simplest fix is to just propagate the call to OnScriptError yourself within your implementation of OnScriptErrorDebug(). IActiveScriptErrorDebug derives from IActiveScriptError so you can just pass it in as-is
(Source: mark_baker@mindspring.com , Mark Baker, 11/29/99 & 12/9/99, microsoft.public.scripting.hosting)
How do I signal an error from my host that can be trapped by my
script?
How about a failed HRESULT.
(Source: Joe Graf, 11/24/99, microsoft.public.scripting.hosting)
I think you should throw an exception like AfxThrowOleException(YourHRESULT) (or AfxThrowOleDispatchException as noted by Larry Engholm) in msg::GetValueShort() in case you have nothing to return. This exception will be catched further by COleDispatchImpl::Invoke and SCODE will be returned to VB. As far as I know (I may be wrong) there are special operator On Error and object Err in VBScript so you can catch an error there and process it accordingly.
(Source: Alexandr Alexeev, 11/24/99, microsoft.public.scripting.hosting)
How do I handle script errors without using the script debugger?
To keep the debugger from popping up during an error, have you IActiveScriptSite object also implement IActiveScriptSiteDebug, and in IActiveScriptSiteDebug::OnScriptErrorDebug set *pfEnterDebugger = FALSE.
(Source: John Crim, WebGecko Software, 8/23/99, microsoft.public.scripting.hosting)
How do I disable the script debugger for my scripts?
The knowledge base article Q232394 - HOWTO: Catch Run-time Errors in an
ActiveX Script Host
(http://support.microsoft.com/support/kb/articles/q232/3/94.asp) describes how
to disable Active Debugging. Essentially, your host needs to implement the
IActiveScriptSiteDebug interface, and return E_FAIL from
IActiveScriptSiteDebug::GetApplication() .
(Source: Joel Alley, Microsoft Developer Support, 9/17/99, microsoft.public.scripting.hosting)
How do I trap syntax errors in a script within my host?
You need to implement support for trapping script errors in general. Then look at the EXCEPINFO.scode value. If its anything other than E_ABORT then its a script error. Currently, there is no documented error code specifically for syntax errors.
(Source: mark_baker@mindspring.com , Mark Baker, 11/26/99)
If the engine reports an error in a script, how do I know where the
error is located?
You need to implement support for trapping script errors in general. Then call IActiveScriptError::GetSourcePosition() to get the line number and character position (column) of the script text that is causing the problem.
(Source: mark_baker@mindspring.com , Mark Baker, 11/26/99)
If the engine reports an error in a script, how do I know what kind
of error it found?
Implement the IActiveScriptSite interface. Errors are reported by the engine by calling IActiveScriptSite::OnScriptError() and passing in a IActiveScriptError interface. Call IActiveScriptError::GetExceptionInfo() and pass in a zerod EXCEPINFO structure to hold the exception results.
Note: You must call SysFreeString() on any BSTRs within the EXCEPINFO structure returned by IActiveScriptError::GetExceptionInfo().
(Source: mark_baker@mindspring.com , Mark Baker, 11/26/99)
How do I know when a script is finished executing?
If I recall correctly, the call to SetScriptState(CONNECTED) runs the
script and blocks until the script is finished running (that is, running the
global script code and any functions it may call).
The same holds true
when you invoke functions in the script via the Script's IDispatch pointer. The
call should block until the function returns.
(Source: Chris Sousa, 9/1/99, microsoft.public.scripting.hosting)
If I call methods on the scripting engine from a thread other than
the one I used to create the engine, I get the HRESULT E_UNEXPECTED. What do I
need to do to be able to call the engine from any thread (or multiple
threads)?
#1 - Make your host free-threaded (vs apartment threaded). This will allow you to call any method on the engine from any thread. You have to call CoInitialize(COINIT_MULTITHREADED) to make this happen at the COM level. You also have to ensure that your code (ie IActiveScriptSite) is thread safe (ie use critical sections, semaphores, etc
#2 - If #1 is not an option, then you can only call the methods
IActiveScript::Clone and IActiveScript::InterruptScriptThread from any thread
other than the one you called the method IActiveScript::SetScriptSite from.
When the engine is created in apartment mode, it has affinity for the
thread that was running when this method was called.
#3 - If #2 is too restrictive, you can set up a message system between the 2 threads such that any calls that the 2nd thread wishes to make on the engine would be done by sending messages to the 1st thread and have it call the methods on behalf of the 2nd thread. This takes some more work on your part, but it will work just fine.
(Source: mark_baker@mindspring.com, Mark Baker, 9/17/99, microsoft.public.scripting.hosting)
After an engine has received a reference to its host by calling the IActiveScript::SetScriptSite method, the scripting engine can no longer accept calls from non-base threads. This happens because the scripting engine checks the thread that makes each call and rejects any calls from non-base threads.
NOTE: The base thread is the one that called IActiveScript::SetScriptSite.
(Source: Knowledge Base, MSDN, Q222837, 4/1/99)
The thread affinity is not based upon the thread that creates the engine, it is on the thread that sets the engine's site pointer. You can create the engine and pass it around to multiple threads as long as you do not set the site. Once the site is set, it is tied to that thread. Even though the engines are marked as free threaded, the components that are created by the engine are not guaranteed to be free threaded. Since this would cause all kinds of headaches, the engine developers took the stance of enforcing an apartment model approach, once the site is set. You can get around this by creating a thread that marshals all calls to the engine across thread boundaries. This is a non-trivial task. Chris Sousa and I did this for a project, but it took close to 12K lines of code to marshal all of the possible interface calls correctly.
(Source: Joe Graf, 12/7/99, microsoft.public.scripting.hosting)
In the IActiveScriptParse->ParseScriptText interface/method,
what is the definition of the "pszItemName" parameter?
In IActiveScriptParse::ParseScriptText, the pstrItemName parameter is used to give script blocks context. The name you pass in pstrItemName must be a name you've already passed at some point to IActiveScript::AddNamedItem. The pstrItemName parameter works a lot like the 'namespace' keyword in Visual C++. When you pass NULL, the script code
is evaluated in a global context, and when you pass a value, the script
code is evaluated with respect to the object you specified.
The most
concrete example of its use is probably during a call to
IActiveScript::GetScriptDispatch(). By passing the same name in the
pstrItemName parameter of GetScriptDispatch as you did to ParseScriptText (and
AddNamedItem), you can get an IDispatch pointer to just those methods added
with that context.
(Source: Joel Alley, Microsoft Developer Support, 8/11/99, microsoft.public.scripting.hosting)
In the IActiveScriptParse->AddScriptlet method what is the
difference between the pstrDefaultName, pstrItemName and pstrSubItemName
arguments?
The pstrDefaultName parameter of AddScriptlet gives the Script Host a
chance to name the event handler method. For example, if I wanted an event
handler called MyEventHandler that handled Command1_OnClick, this would be the
parameter I used to name it. pstrDefaultName differs from pstrEventName because
pstrEventName is the name of the actual event (OnClick in the example).
There are a few caveats to pstrDefaultName, of course. If the name is not
unique, then the script engine will make its own. If the pstrItemName parameter
is NULL, the script engine will make its own. If the script engine doesn't like
your name, it will make its own. The name that is actually assigned to the
event handler is passed back in the pbstrName parameter of AddScriptlet. If its
important to you to know what the name is, you should check this parameter.
The pstrItemName and pstrSubItemName parameters are less subtle. They
simply identify the object that will be the source of the event in question.
pstrItemName indicates the object, added with AddNamedItem and the
SCRIPTITEM_ISSOURCE flag, that will source the event. The engine will ask this
object for its IConnectionPointContainer interface to hook up to the event
interface. Since any subObjects of an object added with AddNamedItem can also
source events, the pstrSubItemName parameter gives the host a chance to
indicate that the subitem, and not the main item, will source the event. In
this case, the script engine will ask the subitem for its
IConnectionPointContainer interface.
(Source: Joel Alley, Microsoft Developer Support, 8/11/99, microsoft.public.scripting.hosting )
Can the IActiveScriptParse->AddScriptlet method be used to add
general functions to a script that are callable by the script passed to the
engine via IActiveScriptParse->ParseScriptText?
It's best to use ParseScriptText for general script methods. In the implementations I've seen, including SampleScript, the engine will get confused if the method being added isn't an event of either the pstrItemName or the pstrSubItemName.
(Source: Joel Alley, Microsoft Developer Support, 8/11/99, microsoft.public.scripting.hosting )
Is there an example of a host using
IActiveScriptParse->AddScriptlet?
I am a Script Engine writer and I can tell you the IE for example calls AddScriptlet() with the script to even code that you place in an HTML document. While normal scripts get passed to ParseScriptTExt(), the even scripts get passed to AddScriptlet(). I use this as the indication to my engine that this code is not to be executed immediately but instead added to the engine and only executed if the corresponding events get fired later.
(Source: Rod da Silva, 12/1/99, microsoft.public.scripting.hosting)
Why does my app crash inside OLEAUT32.DLL after I call
IActiveScript::InterruptScriptThread?
The documentation for the IActiveScript::InterruptScriptThread() method indicates that you should pass the address of an EXCEPINFO structure as the second parameter. However, it fails to mention that this structure must be initialized because InterruptScriptThread will try to use information stored in the EXCEPINFO structure.
(Source: Knowledge Base, MSDN, Q182946, 7/23/99)
What is the state of the script engine after calling
IActiveScript::InterruptScriptThread?
When IActiveScript::InterruptScriptThread is called, the script thread it's called on goes into a stopped state. The presumption is that if you interrupted the thread, there's something wrong with it and it shouldn't be run. You can reset this by calling SetScriptState() with the SCRIPTSTATE_STARTED or SCRIPTSTATE_CONNECTED flag again. Of course, this will not restart a script at the point it was interrupted. It will only make it runnable again.
(Source: Joel Alley, Microsoft Developer Support, 11/15/99, microsoft.public.scripting.hosting)
What are the semantics of IActiveScript::Clone?
It is used to keep from having to parse the script code every time you want to execute. It also lets different threads execute "copies" of an engine. It also has less overhead than creating a new engine each time.
(Source: Joe Graf, 11/14/99, microsoft.public.scripting.hosting )
How can I add 2 or more sets of script text to the engine via
IActiveScript->ParseScriptText and get back an Idispatch pointer for each
script?
The ActiveScript object can be QI'd for IDispatch.
To get two seperate IDispatch interfaces, each representing a seperate piece of text, you would need to call CoCreateObject twice to create two script engines, and call IActiveScriptParse once on each.
(Source: Chris Becke, 10/11/99, microsoft.public.scripting.hosting)
What is error code 0x80020101?
That HRESULT is "SCRIPT_E_REPORTED". It is what is returned by the
script engine when an error occurs which has already been reported to the
user.
Consider the scenario where VBScript calls JScript calls
VBScript and that returns, say, E_FAIL. The VBScript engine will report the
error to the user and return E_FAIL to JScript -- which will report the error
to the user and return E_FAIL to VBScript, which -- you guessed it -- reports
the error to the user and returns E_FAIL.
This is a terrible user
scenario. If the script engine successfully calls OnScriptError then it
propagates SCRIPT_E_REPORTED back as an indication that something failed but
that the user has already been informed, so just fail silently.
So,
something IS going wrong, but it should have been reported to the user
somehow.
(Source: Eric Lippert, Microsoft Scripting Dev, 11/22/99, microsoft.public.scripting.hosting)
When an ActiveX Script Engine, such as VBScript or JScript, encounters an error it attempts to report the error to its script host by calling the following methods: IActiveScriptSite::OnScriptError -or IActiveScriptSiteDebug::OnScriptErrorDebug.
After one of these methods is called, the script engine must also return an HRESULT from the method that was called when the error occurred. To warn the script host that the error has already been reported, and to ensure that a single error does not result in more than one notification, the script engine returns SCRIPT_E_REPORTED. This indicates that the error has already been handled and the host need not take further action.
(Source: Microsoft Knowledge Base, 2/4/2000)
How can I run a script using the security credentials of the
client?
Look at the CoSetSecurityBlanket() API.
(Source: Joe Graf, 11/22/99, microsoft.public.scripting.hosting)
I'm getting script error 0x8000FFFF when trying to move to the
CONNECTED state after my script was terminated by InterruptScriptThread. What
am I doing wrong?
Clone the engine using IActiveScript::Clone(). Then call
IActiveScript::SetScriptState(CONNECTED).
(Source: mark_baker@mindspring.com , Mark Baker, 12/16/99, microsoft.public.scripting.host )
Why would I get a 'Catastrophic Failure' HRESULT back from from
either ParseScriptText or from SetScriptState?
We have only encountered this error when a call was issued on the wrong thread.
(Source: Joe Graf, 12/17/99, microsoft.public.scripting.hosting)
I just wanted to mention that I got that error when I failed to call InitNew on IActiveScriptParse before calling ParseScriptText.
(Source: Kyle Palmer, 2/29/2000, FAQ Submission)
Why does the documentation state that I can call
IActiveScript::SetScriptState from a secondary thread, but I get a
Catastrophic Failure HRESULT back when I try to call it?
The doc is incorrect. MS released a PRB doc (Q222837) on their support
site (it's also in the MSDN) that contradicts what the doc says for the method
SetScriptState. Here is the pasted text from that PRB doc:
"After the
IActiveScript::SetScriptSite method has been called, you cannot call scripting
engine methods on non-base thread, however, there are two notable exceptions to
this rule:
- You can call the IActiveScript::InterruptScriptThread
method from any thread, thus giving the host a chance to stop a script that has
become stuck.
- You can call the IActiveScript::Clone method from any
thread, thus giving the host the ability to set up a standard scripting engine
and then replicate it for multiple job runs. "
(Source: mark_baker@mindspring.com , Mark Baker, 2/11/2000, microsoft.public.scripting.hosting )
Why would I get a Catastrophic Failure HRESULT
(0x8000FFFF) back from IActiveScript::GetScriptDispatch?
Three points:
1) The cause of this error is almost ALWAYS
calling on the wrong thread. Double check that.
2) You can also call
IAS::Clone on any thread.
3) Another possible cause of this error is
calling GetScriptDispatch when the script engine has not been initialized
CORRECTLY.
(Source: Eric Lippert, Microsoft Scripting Dev, 12/28/99, microsoft.public.scripting.hosting )
How can I implement "modules" functionality in my host similar to
what the Microsoft Script Control offers?
The idea of a "module" is taken from Visual
Basic. Other languages have a notion of a "class" or a "namespace". Some
languages may not have any such notion if they are more pure function based.
However, to create a "module" of related code you need 2 things: (1) the name
of the module (2) the functions/methods for the module. To create a module
programmatically, you call IActiveScript::AddNamedItem() with the name of the
module and pass through the flag SCRIPTITEM_CODEONLY. This indicates to the
engine that this named item is a "module" and _not_ a COM automation object.
Then to add the functions for the module, you call
IActiveScriptParse::ParseScriptText as many times as need be (to add the method
code) and pass in the name of the module in the 2nd parm (pstrItemName). This
will then add the functions under that module name. If you then want to execute
functions in a module, call IActiveScript::GetScriptDispatch() and pass in the
module name as the [in] parm. You will get back a normal IDispatch* and can
call GetIDsOfNames() on it to translate a specific function name to an id. Then
call IDispatch::Invoke() with that id and any parms you want to send through.
If you pass NULL through as the pstrItemName parm instead, then the code gets
added to the global namespace. This can then be accessed by passing NULL to
GetScriptDispatch.
(Source: mark_baker@mindspring.com , Mark Baker, 2/7/2000, microsoft.public.scripting.hosting)
What is the expected behavior of the flags that can be passed to
IActiveScript::InterruptScriptThread?
The flags for IActiveScript::InterruptScriptThread are currently ignored by both VBScript and JScript. The flags are included for possible additional functionality in the future, and to give other engines the opportunity for custom behavior.
Source: Joel Alley, Microsoft Developer Support, 2/24/2000, microsoft.public.scripting.hosting
How can I programatically access the global variables &
functions in a script?
Always watch your contexts. A script must be added with a pstrItemName of NULL to have its variables and functions added in the global context. When this is done the GetScriptDispatch() process of obtaining variable and function references works just fine. And the event function is part of the context and not part of the object sourcing the event.
Source: Curtis Clauson, 3/6/2000, microsoft.public.scripting.hosting
Why does IActiveScriptError::GetSourceLineText return E_FAIL when
called after a runtime error occurs?
I was under the impression that once the script code was compiled the script engine no longer reported the source line during errors. From what I have seen, the source is only reported upon a compilation error.
Source: Joe Graf, 3/8/2000, microsoft.public.scripting.hosting
Are there any special flags I need to pass to
IActiveScript::AddNamedItem() in order to parse a script in a named
context?
The SCRIPTITEM_NOCODE flag indicates that you don't want a script context for the named item. If you want to parse script text in a named context, don't pass the SCRIPTITEM_NOCODE flag.
Source: Joel Alley, Microsoft Developer Support, 3/13/2000, microsoft.public.scripting.hosting
I believe this was an answer to a question I recently posted. The answer tells you what flags not to set. In fact, I found that the only flag you need to set in this case is SCRIPTITEM_CODEONLY.
(Source: Larry Baer, FAQ Submission, 3/29/2000)
Named items can have code associated with them -- for instance, in IE a form might be a named item with some code (event handlers for the elements of the form) associated with it. If a named item is marked as CODEONLY then we do not attempt to actually get a pointer to the item -- the name represents "code only", not an object with some code associated with it. If a named item is marked as NOCODE then we do the opposite -- get a pointer to the object, but do not attempt to get code associated with it, because there is "no code" associated with this object. (If you then attempt to set that object as the default dispatch of a code block, that's an error.)
(Source: Eric Lippert, Microsoft Scripting Dev, 2/15/1999, microsoft.public.scripting.hosting (Submitted by Andrew Nosenko, 5/27/2000) )
Is there any way to save parsed code into a binary format to
eleviate having to re-parse the code at a later time?
You're not the first to ask for this feature. We wanted to serialize the pcode to a database....the feature doesn't work. The active scripting supports the correct interface but I think you get back E_NOTIMPL or E_FAIL I don't remember which.
(Source: Joe Graf, 3/31/2000, microsoft.public.scripting.hosting)
I tried: IPersist, IPersistFile, IPersistStorage, IPersistStreamInit, IPersistStream, IPersistPropertyBag
JScript supports none.
(Source: Pavel Gusak, 4/5/2000, microsoft.public.scripting.hosting)
How can I pause and then resume the execution of a script from
where it was paused?
IActiveScript::GetScriptThreadID(); SuspendThread(); ResumeThread();
(Source: Pavel Gusak, 4/12/2000, microsoft.public.scripting.hosting)
Note: you can't use IActiveScript::InterruptScriptThread to interrupt the script if you want to resume it from where it was interrupted.
(Source: Mark Baker, 5/12/2000)
Which IPersistXXX interface should I use to load source code
(script) into an engine?
We've (this newsgroup) found that the engines don't support any of them.
(Source: Joe Graf, 6/16/2000, microsoft.public.scripting.hosting)
What would be the proper way to terminate a script from an
automation object method that was added using AddNamedItem? I've tried
InterruptScriptThread, then SetScriptState(SCRIPTSTATE_DISCONNECTED), doing
this this fires an error on the next line of code following the method that
quits the script. Also tried calling OnScriptTerminate directly, somewhere in
this mixture has to be a clean way to exit.
I'm not sure that it is legal to call InterruptScriptThread from an automation object. It is usually called by the host in response to some condition. I can see 2 ways to solve the problem: (1) have the automation object use a Win32 event to signal the host to interrupt the script (2) have the automation object just return an error code of some type and process the error in the script using the VBScript On Error statemenet or Jscript try/catch statements. Since the MS doc is silent on whether InterruptScriptThread can be called from an automation object, it is probably better not to try to get this to work - you may run into problems with other non-MS engines such as Perl or Python that didn't implement the behavior since it wasn't clearly documented. The 2 solutions I mention should work fine in any engine, though.
(Source: Mark Baker, 5/24/2000, microsoft.public.scripting.hosting)
How can I support multiple scripting languages such that scripts
written in one language can call scripts written in another language (ie.
VBScript calls Jscript, etc)?
Have you tried passing the dispatch pointer of each engine to each other via AddNamedItem/GetItemInfo? This should work as long as they are on the same threads (otherwise you'll see E_CATASTROPHIC). I haven't tried this so don't hold me to it.
(Source: Joe Graf, 5/31/2000, microsoft.public.scripting.hosting)
Can the Dispatch pointer retrieved with GetScriptDispatch() be
passed between threads?
All calls to the scripting engine must come from the same thread (except InterruptScriptThread). This includes the script engine's dispatch pointer.
(Source: Joe Graf, 6/1/2000, microsoft.public.scripting.hosting)
How can I safely use a script interface (like an IDispatch pointer)
in a secondary thread if that thread doesn't own the interface?
You might try having thread B perform the call for thread A and then just pass the results to thread A via some internal data structure. You could also create a series of "services" that thread B could support on behalf of thread A - these services would be initiated using Win32 Events to signal thread B to do its stuff - thread B would then signal thread A when it is finished and the results are ready. This requires calling WaitOnSingleObject() (or one of its companions) to shift back and forth between the threads. But it gets around the limitation that the script engines only support a couple of specific methods on alternate threads.
(Source: Mark Baker, 6/1/2000, microsoft.public.scripting.hosting)
What is the best way to reset the scripting engine between
uses?
There's been some confusion as to how exactly one resets a script engine. Here's what I said on the subject the other day:
Note that one of the flags to AddNamedItem and ParseScriptText is "ISPERSISTENT". When the script engine is partially reset -- taken back to UNINITIALIZED state and initialized again -- everything that you marked as PERSISTENT stays around. When the script engine is completely reset -- that is, closed -- then EVERYTHING is destroyed, even persistent items.
This is correct, but I think it's worth describing _exactly_ what happens for the a few state transitions, just to clear up any confusion:
1) If the script engine is in CLOSED or UNINITIALIZED state, it is illegal to transition to anything but CLOSED or UNINITIALIZED. To get to INITIALIZED state from CLOSED or UNINITIALIZED, you must call SetScriptSite -- in CLOSED/UNINITIALIZED state, the script engine does not have a reference to the site, and hence cannot do anything.
2) If the script engine is STARTED, CONNECTED or DISCONNECTED and you move it to INITIALIZED, the following happens:
- any event sinks are disconnected and freed
- any non-persistent
named items are thrown away
- any non-persistent code blocks are thrown
away
- any persistent code blocks are marked as "has not yet run"
- the
existing script session (the runtime context) is thrown away
- a new script
session is created and the remaining named items are added to the global name
space -- we are ready to run code.
3) If the script engine is STARTED, CONNECTED, DISCONNECTED or INITIALIZED and you move it to UNINITIALIZED, the following happens:
- the connection to the host's site is thrown away
- any event sinks
are disconnected and freed
- any non-persistent named items are thrown away
- any non-persistent code blocks are thrown away, any persistent code
blocks are marked as "has not yet run"
- if in IE, the IE security manager
is reset
- if in the debugger, all debugger connections are reset, and all
actively debugged documents are destroyed - the existing script session is
thrown away
- no session is created -- without a connection to the host, we
cannot run code.
4) If you move the engine to CLOSED from any state:
- everything in (3) happens
- persistent named items are thrown away
- persistent code blocks are thrown away
(Source: Eric Lippert, Microsoft Scripting Dev, 2/4/1999, microsoft.public.scripting.hosting (Submitted by Andrew Nosenko, 5/27/2000))
What is the purpose of the IActiveScriptParseProcedure
interface?
The IActiveScriptParseProcedure interface is used by Internet Explorer to more easily handle button clicks and other events. Essentially, it the equivalent of combining a call to IActiveScriptParse::ParseScriptText and IActiveScript::GetScriptDispatch.
From the SamScrpt sample script engine, here's a brief write-up on ParseProcedureText, IActiveScriptParseProcedure's only method.
/******************************************************************************
* ParseProcedureText -- This method allows an Active Script Host to use
* IDispatch-style function pointers to fire methods instead of using the more
* difficult method of Connection Points. It parses a scriplet and wraps it in
* an anonymous IDispatch interface, which the host can use in lieu of
* Connection Points to handle events.
*
* Parameters: pstrCode -- Address of the script code to evaluate
* pstrFormalParams -- Address of any formal parameters to the
* scriptlet. (ignored)
* pstrProcedureName -- Name of the event
* pstrItemName -- The named item that gives this scriptlet its context.
* punkContext -- Address of the context object. This item is
* reserved for the debugger.
* pstrDelimiter -- Address of the delimiter the host used to detect
* the end of the scriptlet.
* dwSourceContextCookie -- Application defined value for debugging
* ulStartingLineNumber -- zero-based number defining where parsing
* began.
* dwFlags -- SCRIPTPROC_HOSTMANAGESSOURCE
* SCRIPTPROC_IMPLICIT_THIS
* SCRIPTPROC_IMPLICIT_PARENTS
* SCRIPTPROC_ALL_FLAGS
* ppdisp -- Address of the pointer that receives the IDispatch
* pointer the host uses to call this event.
* Returns: S_OK
* DISP_E_EXCEPTION
* E_INVALIDARG
* E_POINTER
* E_NOTIMPL
* E_UNEXPECTED
* OLESCRIPT_E_SYNTAX
******************************************************************************/
(Source: Joel Alley (MS), 6/10/1999, microsoft.public.scripting.hosting (Submitted by Andrew Nosenko, 5/27/2000))
What is the purpose of the IActiveScriptStats interface and
methods?
IActiveScriptStats is used by IE4 to handle script timeouts (prevent "denial of service"). Pass in one of the flags for the first param (stid). Currently, only SCRIPTSTAT_STATEMENT_COUNT is supported -- it returns the number of statements executed since the script started.
cpp_quote( "/* IActiveScriptStats::GetStat() values */")
cpp_quote( "")
cpp_quote( "#define SCRIPTSTAT_STATEMENT_COUNT 1")
cpp_quote( "#define SCRIPTSTAT_INSTRUCTION_COUNT 2")
cpp_quote( "#define SCRIPTSTAT_INTSTRUCTION_TIME 3")
cpp_quote( "#define SCRIPTSTAT_TOTAL_TIME 4")
[
object,
uuid(B8DA6310-E19B-11d0-933C-00A0C90DCAA9),
pointer_default(unique)
]
interface IActiveScriptStats : IUnknown
{
HRESULT GetStat(
[in] DWORD stid,
[out] ULONG *pluHi,
[out] ULONG *pluLo
);
HRESULT GetStatEx(
[in] REFGUID guid,
[out] ULONG *pluHi,
[out] ULONG *pluLo
);
HRESULT ResetStats(void);
}
(Source: Chris Weight, Microsoft Scripting Dev, 3/11/1998, microsoft.public.scripting.hosting (Submtted by Andrew Nosenko, 5/27/2000))
Is there a complete description of the multithreading support
within Active Scripting?
There are several threading models -- threading models are contracts between a caller and a callee regarding what the responsibilities of each are with respect to threading. The threading models in common usage are:
- Single threaded -- calls to an object must always be on the same thread. There are no synchronization issues because there is always only one thread no matter how many object instances there are.
- Free threaded -- calls to an object can be on any thread at any time, including multiple threads at the same time. The object is responsible for all synchronization issues.
- Apartment threaded -- calls to an object must always be on the same thread, but different instantiations of the object can be called on different threads at the same time. The object is responsible for synchronizing access to global (that is, not-per-instance) data.
- Rental threaded -- calls to an object can be on any thread, but the caller guarantees that only one thread is calling into the object at any time. That is, the caller is responsible for synchronizing calls to the object.
The active scripting engines provided by Microsoft (VBScript, JScript) are free threaded. However, ActiveX controls are typically apartment threaded. That means that the script engines must guarantee that they do not call into controls on a different thread than the thread that the control was created on.
Therefore, the script engines seriously restrict their callers - though technically the script engines are free threaded, for practical purposes you can treat them as apartment threaded.
Just to make sure I've described this model, let me re-iterate. Suppose you have an object of class Foo and you create three objects, Bar1, Bar2 and Bar3. Bar1 you create on ThreadA, Bar2 and Bar3 you create on ThreadB. Now, all calls to methods of Bar1 must be on ThreadA and all calls to Bar2 and Bar3 must be on ThreadB, to not break the apartment model. You can have multiple objects per thread -- Bar2 and Bar3 are both on ThreadB. If Bar1, Bar2 and Bar3 need access to some global data associated with class Foo then it is the responsibility of the object to synchronize thread access.
Anyway, the script engines enforce the apartment model in the following manner: Before the script engine is brought to INITIALIZED state, it is free threaded. Now suppose you call SetScriptSite on ThreadA -- this brings the script engine to INITIALIZED state. From this point on, until the script engine is reset to UNINITIALIZED state, all calls must be on ThreadA. Why? Because bringing the engine to INITIALIZED state does event sinks and other calls on ActiveX controls, and those controls must always be called on the same thread. Of course, there are a few exceptions -- InterruptScriptThread obviously cannot be called on ThreadA if ThreadA is the thread being interrupted!
(Source: Eric Lippert, Microsoft Scripting Dev, 2/19/1998, microsoft.public.scripting.hosting (Submitted by Andrew Nosenko, 5/27/2000))
Why would IActiveScript::InterruptScriptThread return E_INVALIDARG
if I know the parameters passed to InterruptScriptThread are valid?
IActiveScript::InterruptScriptThread will return E_INVALIDARG if an interrupt is already pending in the script thread (ie. waiting for the next statement to be executed), or if the script thread is not running.
(Source: Joel Alley, Microsoft Developer Support, 8/14/2000, microsoft.public.scripting.hosting)
Why does IActiveScript::InterruptScriptThread not terminate a
running script immediately when I'm trying to kill the script engine?
InterruptScriptThread only instructs the script engine to stop executing code at the next opportunity. The call sets a flag that the script engine only checks when executing code. If there isn't currently any code running (such as when the script engine is sitting idle waiting for events to fire), InterruptScriptThread will appear to not work.
To work around this, you might try adding a trivial method to your script, so that when you're ready to stop the engine, you could call InterruptScriptThread, and then call this method so the script engine would recognize the interrupt flag and stop.
(Source: Joel Alley, Microsoft Developer Support, 9/25/2000, microsoft.public.scripting.hosting)
How can I save/restore the values of script variables between
runs?
Get the LPDISPATCH from the script engine.
Call
GetIDsOfNames() with the name of the variable you are trying to read
Call
Invoke() with "property get" passed in.
The variable's contents will be
stored in the result variant
You have to convert JScript arrays to SAFEARRAYs and back. There is sample code out there (somewhere) to do this. I found it once..
(Source: Joe Graf, 9/28/2000-9/29/2000, microsoft.public.scripting.hosting)
What is the difference between the IActiveScript interface and the
IScriptControl interface?
The IActiveScript interface doesn't use the script control - it is there so that you can access the features of a scripting engine directly. The MS Script OCX is there to make accessing script engines more easy, e.g. for those people who don't want to implement COM interfaces directly or prefer to use OLE Automation (a much more familiar technique for many programmers, and the only one easily available in some programming languages).
(Source: Peter Jamieson, 9/29/2000, microsoft.public.scripting..hosting)
Is there a way I can access the parameters of an event within a
script added via IActiveScriptParse::AddScriptlet?
In VBScript, you could use 'auto-magic' events to declare the parameters for the event. Unfortunately, the only way to get events into JScript is through the AddScriptlet method. Since AddScriptlet creates a wrapper method around the script that takes no parameters and returns no values, there's no way to refer to the arguments that are passed in.
(Source: Joel Alley, Microsoft Developer Support, 2/12/2001, microsoft.public.scripting.hosting)
What are the correct HRESULT values to return when using the
interface IActiveScriptSiteInterruptPoll?
The script engine is calling IActiveScriptSiteInterruptPoll::QueryContinue() to make sure the host still wants the script to continue running. This might be useful in a long running script. As long as QueryContinue() returns S_OK, the script will continue to run to completion. If QueryContinue() returns any error code or S_FALSE, script execution will stop and the error handler will be called.
The S_CODE 16388 is the HRESULT 0x80004004 (E_ABORT).
(Source: Joel Alley, Microsoft Developer Support, 2/12/2001, microsoft.public.scripting.hosting)
Copyright © 1999-2001 Mark M. Baker