I would like to load scripts from multiple files, some of which may use
VBScript and some of which may use JScript, and combine them together into a
single script so that each sub-script can call functions from any other
sub-script. This is equivalent to what can be achieved with a .wsf file with
WSH.
How is this possible? It seems I must create an coclass that implements
IActiveScript for each file, or at least for each engine. This would suggest
that each sub-script is separate.
Thanks,
Paul
This is a quick note to let you know that I am performing research on this
issue and will get back to you as soon as possible. I appreciate your
patience.
Regards,
Walter Wang (waw...@online.microsoft.com, remove 'online.')
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Scott Lloyd
Lloyd Software
"Paul Baker [MVP, Windows - SDK]" <paulrich...@community.nospam> wrote
in message news:eUQKceJn...@TK2MSFTNGP03.phx.gbl...
Paul
"Scott Lloyd" <sc...@lloydsoftware.com> wrote in message
news:16A64760-CA25-47A5...@microsoft.com...
Based on my research, your objective can be achieved by using
IActiveScript.AddNamedItem with flag SCRIPTITEM_GLOBALMEMBERS:
hr = pas->AddNamedItem(OLESTR("script1"),
SCRIPTITEM_ISVISIBLE|SCRIPTITEM_GLOBALMEMBERS);
hr = pas->AddNamedItem(OLESTR("script2"),
SCRIPTITEM_ISVISIBLE|SCRIPTITEM_GLOBALMEMBERS);
Say, you have three different script file loaded into three IActiveScript,
in the third one, you add two named items as above. In your
IActiveScriptSite.GetItemInfo, return first or second
IActiveScript.GetScriptDispatch() according to the name.
I can post a working test project if needed.
As an example, I have two VBScript IActiveScript's. The second depends on a
function in the first. I call IActiveScript.AddNamedItem for each script
with a name that identifies the other. When I run the second script,
IActiveScriptSite.GetItemInfo is being called with the name of the first
script. I call IActiveScript.GetScriptDispatch and it is returning S_OK.
However, there is still an error "Type mismatch: <function name>", even
though the function is a Sub with no parameters, which would suggest that
the function is just not in the namespace.
Also, the IActiveScript::SetScriptSite documentation says "The site must be
uniquely assigned to this scripting engine instance; it cannot be shared
with other scripting engines.". Does it really mean scripting engines or
does it mean IActiveScript interfaces? In any case, I *am* sharing the site,
don't I *have* to?
Paul
"Walter Wang [MSFT]" <waw...@online.microsoft.com> wrote in message
news:J2teL5sn...@TK2MSFTNGHUB02.phx.gbl...
Paul
"Paul Baker [MVP, Windows - SDK]" <paulrich...@community.nospam> wrote
in message news:%23Rdsryf...@TK2MSFTNGP05.phx.gbl...
Now my problem is that IActiveScript.AddTypeLib is always failing with
E_FAIL.
Thanks,
Paul
"Walter Wang [MSFT]" <waw...@online.microsoft.com> wrote in message
news:d9vXh7lo...@TK2MSFTNGHUB02.phx.gbl...
> Hi Paul,
>
> Yes it's required to call SetScriptState(SCRIPTSTATE_CONNECTED); please
> see
> Don Box's article below:
>
> #Say Goodbye to Macro Envy with Active Scripting
> http://www.microsoft.com/mind/0297/activescripting.asp
>
> My test project is based on the example code above. I'll attach the zipped
> project here (you may have to use Outlook Express to download it). Please
> change the script file path accordingly before test.
Yes the order will be important. This is also true in .wsf: script block
initialized first cannot call function in later script block.
When did you call AddTypeLib, based on my test, my following code works:
// lib id of stdole2.tlb in %windir%\system32
extern "C" const GUID __declspec(selectany) LIBID_StdOle2 =
{0x00020430,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
hr = pasp3->InitNew();
hr = pas3->SetScriptSite(pass);
hr = pas3->AddTypeLib(LIBID_StdOle2, 2, 0, 0);
and in the script, you can access the enum value in the type library
directly:
typedef [uuid({E6C8FA08-BD9F-11D0-985E-00C04FC29993})] enum
{
Default = 0,
Monochrome = 1,
VgaColor = 2,
Color = 4
} LoadPictureConstants;
3.js:
shell.writeln(Color);
This was my order of calls:
SetScriptSite
QueryInterface(IActiveScriptParse)
InitNew
ParseScriptText
AddTypeLib
In hindsight, it should at least be before ParseScriptText. I tried
inserting it after SetScriptSite, after QueryInterface(IActiveScriptParse)
and after InitNew and using your parameters (to load the standard type
library rather than my own). In all cases, it returned E_FAIL (not
E_UNEXPECTED).
If I step through it in the debugger, all I can see is assembler, but I see
it calling GetCurrentTheadId almost immediately and returning almost
immediately after that. It seems that my threading model may be incorrect
somehow. This in all happening in a thread that is not the primary thread
and has entered an MTA using CoInitializeEx with COINIT_MULTITHREADED.
The next thing I would like to do once all scripts are initialized properly
is call various procedures by name from various different threads.
Presumably I must call GetScriptDispatch and IDispatch.Invoke? Must I clone
the IActiveScript in order to do this or can I reuse the same IActiveScript
from multiple threads?
Bear in mind that I am using Delphi, not C++, so it is possible I have a bad
declaration somewhere (but I don't think so).
Thanks,
Paul
"Walter Wang [MSFT]" <waw...@online.microsoft.com> wrote in message
news:fPnhGD3o...@TK2MSFTNGHUB02.phx.gbl...
AddTypeLib works now, so never mind about that! The declaration *was* wrong.
Delphi declares a GUID as an unaligned structure and passes it was passing
it as four parameters on the stack (the "const" keyword corrected it to one
parameter). I used a declaration of IActiveScript that I found on the
Internet and corrected manually. I must have missed that. It's also possible
that it was correct for older versions of Delphi. I don't know why it is
calling GetCurrentThreadId, but it is, and then it calls LoadRegTypeLib,
something it wasn't doing when my declaration was wrong!
But please see my questions about calling procedures.
Paul
"Paul Baker [MVP, Windows - SDK]" <paulrich...@community.nospam> wrote
in message news:%23ULXHn4...@TK2MSFTNGP04.phx.gbl...
However, I have found that I can later call a procedure *in* any script
*from* any script if I call AddNamedItem. This is consistent with the
behaviour I have seen in Windows Scripting Host, actually.
So I do this, in summary:
When the program initializes, parse each script found in a "Scripts"
subfolder using SCRIPTTEXT_ISPERSISTENT, in order. The file extension
determines the engine CLSID.
When the program wants to execute script (in pseudo-code):
1. Clone script1; AddTypeLib; Connect script1;
2. Clone script2; AddTypeLib; script2.AddNamedItem(script1); Connect
script2; script1.AddNamedItem(script2);
3. Clone script3; AddTypeLib; script3.AddNamedItem(script1);
script3.AddNamedItem(script2); Connect script3.
script1.AddNamedItem(script3); script2.AddNamedItem(script3);
etc.
Then I loop through each script, looking for a prodedure by name, using
GetScriptDispatch, IDispatch.GetIDOfNames. Once I get a result that is not
DISP_E_UNKNOWNNAME, either there is an error or I can run the procedure
using IDispatch.Invoke. In that way, I can provide different named entry
points to my scripts while parsing the scripts only once.
I have to clone a script, because it is a multi-threaded application that
may be executing multiple copies of the same script at once from different
threads. In fact, GetScriptDispatch returns E_UNEXPECTED if I don't.
I do not call AddTypeLib when the program initializes, because it does not
appear to be persistent when cloned.
I do not call AddNamedItem when the program initializes, as in order to be
persistent, I have to use SCRIPTITEM_ISPERSISTENT and this results in calls
to IActiveScriptSite.GetItemInfo beyond my control that are not in the order
I would like.
Does this all sound okay? It seems to be working :)
Paul
"Paul Baker [MVP, Windows - SDK]" <paulrich...@community.nospam> wrote
in message news:%23jFWU%234oHH...@TK2MSFTNGP06.phx.gbl...
Thank you for the summary.
I also did some test with the .wsf and the result confirmed that each
script block will be added to other script blocks, it's only the global
code are executed in the order when they're added.
The GetItemInfo will be called immediately after you called AddNamedItem,
therefore AddNamedItem must be called when you have the actual IDispatch
ready.
Thanks again for sharing your solution here.
The only way to programatically trap and report detailed error information
is via a WshRemoteError object. This requires using a WshRemote object,
which has issues:
(a) There is overhead associated with it (if you wait for the script to
finish, you have to use a loop which may wait longer than it needs to; it
copies the script to a temporary file in newer versions).
(b) It fails to delete the temporary file in newer versions:
http://groups.google.com/group/microsoft.public.scripting.wsh/browse_thread/thread/17356ee4514a4b2f
This appears to be a side effect of the change mentioned in this article:
http://msdn.microsoft.com/library/en-us/script56/html/f9f0e5da-824d-4cde-a67b-02108178ee45.asp
(c) It is considered a security risk. It is disabled by default, and must be
enabled by setting this registry entry to 1:
HKLM\SOFTWARE\Microsoft\Windows Script Host\Settings\Remote
(d) Windows Scripting Host 2.5 fails to register the WSHRemote class. On new
machines, we have to run the command "wscript -regserver" to resolve this:
http://support.microsoft.com/kb/311269/en-us
(e) Sometimes the WshRemoteError.Description property is incorrect, for
example:
Unspecified error:
http://groups.google.com/group/microsoft.public.scripting.wsh/browse_thread/thread/a92d25ee7d0425b7
Catastrophic failure: If the script calls a COM server that returns E_FAIL
and calls SetErrorInfo, the message reported by WshRemoteError is
"Catastrophic failure" and the source is lost.
(e) Sometimes the WshRemoteError.SourceText property is empty for a
compilation error.
There is naturally an overhead associated with creating a WSH process.
In my case, I can improve performance by parsing scripts only once, assuming
that cloning dose not cause a full re-parse.
Paul
"Walter Wang [MSFT]" <waw...@online.microsoft.com> wrote in message
news:2Ru6L%23$oHHA...@TK2MSFTNGHUB02.phx.gbl...