18

What is the stackoverflow approved (and hence correct) method to force a VBS to run using cscript instead of wscript - irrespective of what the user tries?

A quick Google search shows plenty of examples, but some of them simply don't work and those which do often don't handle the fact that it may have been run with arguments so I'm keen to know what the best way is.

Here is one example which doesn't handle arguments:

sExecutable = LCase(Mid(Wscript.FullName, InstrRev(Wscript.FullName,"\")+1))
If sExecutable <> "cscript.exe" Then
  Set oShell = CreateObject("wscript.shell")
  oShell.Run "cscript.exe """ & Wscript.ScriptFullName & """"
  Wscript.Quit
End If

I appreciate that this could probably be easily modified to handle arguments, but realise that this may not be the best way to approach the problem.

Background: I'm writing a script which can run by double clicking or (most likely) from either a DOS batch file or as a scheduled task. It can contain one or more optional command line arguments.

Richard
  • 1,371
  • 7
  • 21
  • 46

5 Answers5

22

My Lord, what unadulterated rubbish. It makes me cry to see such cruddy coding (no offense to anybody, lol). Seriously, though, here's my 2 pence:

Sub forceCScriptExecution
    Dim Arg, Str
    If Not LCase( Right( WScript.FullName, 12 ) ) = "\cscript.exe" Then
        For Each Arg In WScript.Arguments
            If InStr( Arg, " " ) Then Arg = """" & Arg & """"
            Str = Str & " " & Arg
        Next
        CreateObject( "WScript.Shell" ).Run _
            "cscript //nologo """ & _
            WScript.ScriptFullName & _
            """ " & Str
        WScript.Quit
    End If
End Sub
forceCScriptExecution

It handles arguments, AND checks for spaces in said arguments -- so that in the case of a filename passed to the original script instance that contained spaces, it wouldn't get "tokenized" when passed to cscript.exe.

Only thing it doesn't do is test for StdIn (e.g., in the case where someone piped something to the script via the command line, but forgot to use "cscript script.vbs") -- but if it was executed by WScript.exe, WScript.StdIn's methods all return Invalid Handle errors, so there's no way to test that anyway.

Feel free to let me know if there's a way to "break" this; I'm willing to improve it if necessary.

JohnLBevan
  • 18,988
  • 5
  • 75
  • 151
  • 1
    Nice use of `//nologo` and well remembered on the test of space (and then adding the quotes) for each argument. – Richard Mar 07 '11 at 15:15
  • 1
    The CreateObject line is missing a space between the script name and the arguments. As such, the run command fails. To fix, change `WScript.ScriptFullName & """" & Str` to `WScript.ScriptFullName & """ " & Str`. – Richard May 22 '11 at 18:10
  • 1
    FYI: I edited the script to include the space / add line breaks to display the code better on site. However I just realised the space isn't missing - during the for loop spaces are prefixed on all arguments. As well as separating arguments from one another, the first of these separates them from the script name. – JohnLBevan Feb 04 '13 at 16:12
4

Two small additions to forceCScriptExecution let me see its Window after termination and handle its return code.

Sub forceCScriptExecution
    Dim Arg, Str
    If Not LCase( Right( WScript.FullName, 12 ) ) = "\cscript.exe" Then
        For Each Arg In WScript.Arguments
            If InStr( Arg, " " ) Then Arg = """" & Arg & """"
            Str = Str & " " & Arg
        Next
        **ret =** CreateObject( "WScript.Shell" ).Run **("cmd /k** cscript //nologo """ & WScript.ScriptFullName & """ " & Str**,1,true)**
        WScript.Quit **ret**
    End If
End Sub

Notes: "cmd /k" let the windows stay after execution. Parameter "1" activates the window. Parameter "true" waits for termination, so variable "ret" can return the error code.

clv
  • 41
  • 2
3

Here's a similar one in JScript for making .js files run in CScript:

(function(ws) {
  if (ws.fullName.slice(-12).toLowerCase() !== '\\cscript.exe') {
    var cmd = 'cscript.exe //nologo "' + ws.scriptFullName + '"';
    var args = ws.arguments;
    for (var i = 0, len = args.length; i < len; i++) {
      var arg = args(i);
      cmd += ' ' + (~arg.indexOf(' ') ? '"' + arg + '"' : arg);
    }
    new ActiveXObject('WScript.Shell').run(cmd);
    ws.quit();
  }
})(WScript);

WScript.echo('We are now in CScript. Press Enter to Quit...');
WScript.stdIn.readLine();

https://gist.github.com/4482361

methodMan
  • 617
  • 2
  • 11
  • 27
sstur
  • 1,589
  • 14
  • 20
1

One approach might be to give it another extension instead of .vbs. Say .cvbs for example. Associate .cvbs with cscript.exe not wscript.exe, that way executing or double clicking a .cvbs file will never invoke the wscript.exe.

AnthonyWJones
  • 178,910
  • 32
  • 227
  • 302
  • Sadly this won't work as I don't have control over the computer where this script will be run. I need to be able to give them mycode.vbs and know that however they try to run it, it will always be run with cscript rather than wscript. – Richard Jan 15 '11 at 16:09
0

Here is my code snippet i use for some of my scripts. It handles Arguments as well. All you have to do is replace the {EnterWorC} with either a "w" or "c" WITH quotes

Dim WorC, Command, Arguments, I

WorC={EnterWOrC}  'Make sure you replace "{EnterWOrC}" with a "w" or a "c" and BE SURE TO PUT QUOTES AROUND THE LETTER.

WorC=LCase (WorC)
If lcase (WorC)="w" Or lcase (WorC)="c" Then

If LCase (Right (WScript.FullName,11))<> WorC & "script.exe" Then
    command=WScript.ScriptFullName
    Arguments=""
    For I=0 To UBound (WScript.Arguments)
        Arguments=Arguments & Chr (34) & WScript.Arguments(I) & Chr (34) & Space (1)
    Next
    CreateObject("Wscript.Shell").Run WorC & "script.exe " & Chr (34) & command & Chr (34) & Space (1) & Arguments, 1
    WScript.Quit
End If
    WorC=Empty
    Command=Empty
    I=Empty
    Arguments=Empty
End If

Here you will have to replace the 2nd line (2nd NON-blank line)

WorC={EnterWOrC}  'Make sure you replace "{EnterWOrC}" with a "w" or a "c" and BE SURE TO PUT QUOTES AROUND THE LETTER.

For Wscript: WorC="W"

For CScript: WorC="C"

It is NOT case Sensitive.

Ronnie Matthews
  • 423
  • 4
  • 10