none
WSH 5.7 WSC object behavior change

    Question

  • Hello Scripting Guy,

    I use a WSC (a COM server writting in JScript and run with scrobj.dll) that starts also other script files using shell.Run("TheOtherScript.js");

    The WSC server is instantiated with new ActiveXObject(PROGID). Until WSH 5.6 I could set the execution engine for shell.Run with the command 

    WScript //H:CScript

    or with //H:WScript to get the engine I need. For interactive mode I need WScript that pops up a message box on WScript.Echo; for non-interactive mode that is prohibited, since it holds the execution.

    After update to WSH 5.7 the WScript.Echo in TheOtherScript.js always causes a MessageBox, independend of the //H: parameter I passed before. This breaks all unattended script execution.

    I think this is a bug in WSH 5.7, when can it be fixed, how can I build a workaround?

    Thursday, July 22, 2010 8:54 AM

Answers

  • I've now had a chance to look at the actual component code. Correction to previous comments - there was no use of Eval() or ExecuteGlobal(), or WshShell.Exec().

    All you do is pass in the script host and maybe a script name. Then prepend to the script-host argument. (depending on your preferences) either

    "wscript //H:" , or

    "wscript " & <scriptname> & " //E:"

    and then use WshShell.Run(<command_string>);

    Admin credentials are required to successfully change the default script host. Script components always run with the same user credentials as the calling script.

    Saturday, August 07, 2010 7:23 PM

All replies

  • Don't you need to use the //E: switch rather than //H:  Also it's wscript not WScript when specifying the engine. :)

    Thursday, July 22, 2010 2:43 PM
  • My documentation, which predates WSH 5.6, states that you should not use Wscript.Echo statements in a *.wsc component because the Wscript object is not available. Perhaps this is why you have the component run another script, to bypass this limitation. I see a few options:

    1. Pass the Wscript object to the component method as an argument. I've never seen this, but my documentation says it is possible.

    2. In the Run method specify the host program to be used. Perhaps shell.Run(cscript TheOtherScript.js) or shell.Run(%comspec% /c cscript TheOtherScript.js).

    3. Have the method in the component return the information to be displayed, then have the Wscript.Echo statement in the calling program.

    Richard Mueller


    MVP ADSI
    Thursday, July 22, 2010 2:54 PM
  • With a .wsc component what is, or owns the global object?

    This is important to me as I was writing a global script that detects the script context (web browser, server, wsh etc.)

    Thursday, July 22, 2010 3:02 PM
  • The //H switch defines the default script host (wscript or cscript) and is NOT case sensitive AFAIK.  The //E switch defines which language engine to use (VBS or JScript or something else, if installed).  The OP wants to control the HOST, not the language engine.

    The only workaround I can think of is to explicitly invoke the desired script host as part of launching the script, as in ...

      WShell.Run "wscript " & scriptname, 1, true

    or

      WShell.Run "cscript " & scriptname, 1, true


    Tom Lavedas
    Thursday, July 22, 2010 6:21 PM
  • Running with admin credentials did accept //H:WScript but it continued using cscript engine anyway.

    Does this, as I suspect changes the script engine for the next script that follows, and not the current one invoked with the //H: switch?

     

    Thursday, July 22, 2010 7:24 PM
  • Tom,

    Thanks for reply. I found already that I could specify the script host explicitly. But the calling scripts that instantiate the WSC are called sometimes hosted by CScript and sometimes by WScript.

    In the automatic build environment they are started with CScript and the output is written to the log. But the same script should show the message boxes to indicate any problem the in the interactive environment. So how can I detect the calling host?

     

    Friday, July 23, 2010 9:08 AM
  • Richard,

    Thanks for reply. Unfortunately the options doesn't work for me.

    1. I need to run old scripts. Even if I would drop this requirement, there are hundreds of scripts that would have to adopt as well as the interface.

    2. I would like to use this approach, but I did not find a way to detect the host, in which the WSC is instantiated.

    3. The WScript.Echo is not used in the WSC, but in the scripts called with shell.Run(). But there are again the problems like in proposal (1). The scripts called by shell.Run() don't return any messages, and I even don't know, how new versions could do this.

    Since I have the option to adopt the WSC I would try to build a workaround here. I looks like the main question becomes, how to detect in the WSC the host. The WScript object is not available in the WSC, though the FullName property could be used to detect the host.

    I compared the registry changed WSH 5.6 and WSH 5.7 make on a //H: command line option. At HKCR\VBSFile\Shell ther are two keys (Open, Open2). While WSH 5.6 changes the values below the keys, WSH 5.7 sets a value at HKCR\VBSFile\Shell ("Open" or "Open2"). Somehow the scrobj.dll that executes the WSC does not get the new scheme of the //H: use.

    This is only an observation and I am reluctant to use it. Reading this registry values and guessing the WSH version looks error prone and may not work in the next WSH version.

    Friday, July 23, 2010 9:28 AM
  • The calling host information is available to the called script via the Wscript object's FullName property.  Of course the WSC object does not have intrinsic access to WScript's properties, but you could create a public property to receive the WScript object from the script that instantiates the object.  I've seen examples of this in posts in the old ms.public.script.vbscript newsgroup, if you need a reference.  There are archives out there that still have these posts available.

    Tom Lavedas
    Friday, July 23, 2010 12:27 PM
  • Tom,

    I could create a public property. But I need to run old scripts as stated in last port at (1). Is there no way to identify the WScript object in the WSC?

     

    Friday, July 23, 2010 4:06 PM
  • Script Component global object has three properties:

    getResource
    createScriptlet
    createComponent

    you could either use a For-In loop and match against one of those properties.
    or, test the property 'type' and you will get back "unknown" rather than "undefined" which is the typical value returned for non-component scripts.


    ***
    I'm not a huge VB fan, and the info given relates to JScript, as that language has the this operator, consequently access to the global object is much simpler (more obvious) than in VB.
    If it's possible to mix code of different languages, like we can in a .wsf file, then it should be easier to handle this issue for VBScript users.

    Hope that helps.

    Friday, July 23, 2010 11:48 PM
  • <?xml version="1.0"?>
    <component id="vbexample">

    <?component error="true" debug="true"?>

    <registration
        description="vb-example"
        progid="vbexample.wsc"
        version="1.00"
        classid="{24886a3d-d672-40f3-a312-de24b42873dd}"
    >
    </registration>
    <public>
        <property name="some_property">
          <get/>
          <put/>
        </property>
        <method> name="init">
        </method>
    </public>

    <script language="VBScript">
    <! [CDATA[

    dim some_property
    some_property = "True"

    function get_some_property()
          get_some_property = some_property
    end function

    function put_some_property(newValue)
          some_property = newValue
    end function

    function init()
          test = getContext() ' call JScript function (private func)
          ' Do something (based on script context)

          init = test
    end function

    ]]>
    </script>

    <script language="JScript">

    <![CDATA[

    function getContext() {
        var ctx;
        if(typeof(this.createComponent)=="unknown") {
            ctx = "com_component";
        } else {
            try{
                if(typeof(this.ScriptEngine) == "function") ctx = "wsh";
            } catch (e) {
            ctx = "undetermined";
            }
        }

        return ctx;
    }

    ]]> </script>

    Call the init method and get the script context from JScript code (a private function). In your own components, store the context in a global property, and use it for any code that attempts to write to the screen etc.

    The JScript function will work correctly for mixed languages in .wsf Jobs.

     

    Sunday, July 25, 2010 1:49 AM
  • technogeist,

     

    I'm, I failed to ask the question. Was i meant is not to check if a function is in a COM component, but to determine the scripting engine of the script, that instantiated the COM component.

    When I put your getContext in the WSC file, it returns (suprise!) "com_component". This doesn't help.

     

    Sunday, July 25, 2010 4:20 PM
  • My apologies for not comprehending your original problem. That's how I'm intending to reuse my existing code as components. By having the component not have external dependencies, such as requiring callee to tell it how to output data. But rather use self-reflection, like java-beans.

    Apart from one class which provides all script character output functions. My scripts no longer use WScript.Echo/Response.Write/document.write directly. They're output is decided by a global script_context variable which initialises the ioc class object. This allows me to use the same scripts verbatim in web pages or on the command-line.

    Sorry again. :)

    Sunday, July 25, 2010 6:27 PM
  • I was able to change the default scripting engine within the component, directed by the callee extracting the host name from WScript.FullName, and passing in wscript or cscript. So at the very least it is possible, but much more difficult trying to get that info, without help from the callee script.

     

     

     

    Monday, July 26, 2010 12:10 PM
  • How did you get the WScript object in the WSC?
    Saturday, August 07, 2010 8:28 AM
  • You can't access the just 'WScript' on its own. Only WScript.Shell

    I'm providing this info from memory, as I'm not able to access the script right now (but I'll post it later on - if you still need to see it).

    I can confirm that I used the following tag directly after the public section.

    <object id="WshShell" progid="WScript.Shell" />

    and used WshShell.Exec via eval(), as that's the only way to 'fill in the blanks' and execute it at runtime.

    VBS's Execute (or ExecuteGlobal) might have to be used rather than Eval().

    As I said, I'll post the actual code later, but I think you should be able to figure it out from the info above.

    :)

    Saturday, August 07, 2010 4:29 PM
  • I've now had a chance to look at the actual component code. Correction to previous comments - there was no use of Eval() or ExecuteGlobal(), or WshShell.Exec().

    All you do is pass in the script host and maybe a script name. Then prepend to the script-host argument. (depending on your preferences) either

    "wscript //H:" , or

    "wscript " & <scriptname> & " //E:"

    and then use WshShell.Run(<command_string>);

    Admin credentials are required to successfully change the default script host. Script components always run with the same user credentials as the calling script.

    Saturday, August 07, 2010 7:23 PM