How Can I Pass Command-Line Variables to an HTA When It Starts?

How Can I Pass Command-Line Variables to an HTA When It Starts?

  • Comments 6
  • Likes
Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I pass command-line variables to an HTA when it starts?

-- DM

SpacerHey, Scripting Guy! AnswerScript Center

Hey, DM. Ok, we admit it: you almost had us on this one. (Not that being able to stump the Scripting Guys is particularly hard, mind you.) We had no idea whether you could pass command-line variables to an HTA, and even less idea how we could capture those command-line variables. But then we stumbled across the commandLine property and, after a bit of hacking around, came up with a solution.

Note. If you aren’t familiar with the acronym HTA, HTA is short for HTML Application. An HTML Application provides a way for you to wrap up your scripts in a graphical user interface, complete with radio buttons, check boxes, dropdown lists and what-have-you. For more information about HTAs, check out our new HTA Developers Center.

The commandLine property simply reports back the command used to start an HTA. Because HTAs are GUI tools, they are typically started simply by double-clicking the file icon in Windows Explorer or My Computer. Thus the commandLine is nothing more than the path to the HTA file, something like this (note that the double quotes are part of the value):

"C:\Scripts\Cmdline.hta"

Now suppose you type something like this at the command prompt:

c:\scripts\cmdline.hta arg1 arg2

Guess what the commandLine property returns then? That’s right; the entire command string, with the path surrounded by double quotes and the two arguments tacked onto the end:

"c:\scripts\cmdline.hta" arg1 arg2

In other words, we can capture the command-line arguments; as you can see, both arg1 and arg2 are in the value returned by the commandLine property. All we have to do now is figure out how to tease those values out of there.

Our first thought was to remove the double quotes and then use the VBScript Split function to carve the command-line into an array. Our intention was to split the string on each blank space, which would give us an array consisting of the following elements:

c:\scripts\cmdline.hta
arg1
arg2

That worked, but there was a problem. Suppose you had a blank space in the HTA path; for example, suppose our path was C:\Scripts\My HTAs\Cmdline.hta. That “extra” blank space was a problem; if we split the string on each blank space we’d get an array that looked like this:

c:\scripts\my
htas\cmdline.hta
arg1
arg2

Not so good. Therefore, to work around that problem we invented a new rule: we had to surround our command-line arguments with double quote marks. In other words, to start our HTA we had to type something like this from the command prompt:

c:\scripts\cmdline.hta "arg1" "arg2"

Why? Well, this returns a commandLine that looks like this:

"c:\scripts\cmdline.hta"  "arg1" "arg2"

Each argument is now enclosed within double quotes, just like the path is. If we now split on the double quote mark (chr(34)) we’ll get an array that looks like this:

<blank line>
c:\scripts\cmdline.hta
<blank space>
arg1
<blank space>
arg2
<blank line>

In other words, every other line in the array has data of interest to us. Why do the other lines consist of - for all intents and purposes - nothing at all? Well, to mimic the effect replace each " in this sentence with a carriage return-linefeed and see what you get:

"c:\scripts\cmdline.hta"  "arg1" "arg2"

That’s right:

c:\scripts\cmdline.hta
  
arg1
 
arg2

Believe it or not, that’s OK. Because we have an array, we can use a For Next loop to cycle through all the items. Furthermore, we can use the Step parameter to loop through every other line. For example, suppose we have the following array:

A
B
C
D

We can start with the second element (or item 1, because the first element in an array is item 0) and set the Step value to 2. That will read the second element (B) and then skip to the fourth element (D). And that’s exactly what we’ll do in our HTA. We’re going to start our For Next loop with item 3. Why item 3? Well, item 3 is the fourth element in the array. We don’t care about any of the first three elements: they consist of a blank line, the path to the HTA file, and a blank space. Element 4 is our first argument, so we’ll start there, then skip to item 6. (Remember, item 5 will just be a blank space.) That’s how we’ll extract just the command-line arguments for the HTA.

Here’s the complete HTA code (remember to save this as a .HTA file and not a .VBS file):

<html>
<head>
<title>Command Line Agruments</title>

<HTA:APPLICATION 
     ID="objTestHTA"
     APPLICATIONNAME="Command Line Agruments"
     SINGLEINSTANCE="yes"
>
</head>

<SCRIPT Language="VBScript">

Sub Window_onLoad
    arrCommands = Split(objTestHTA.commandLine, chr(34))
    For i = 3 to (Ubound(arrCommands) - 1) Step 2
        Msgbox arrCommands(i)
    Next
End Sub

</SCRIPT>

<body>
</body>
</html>

The part we really care about is the Window_onLoad subroutine (a subroutine which automatically runs any time the HTA is started). The first line in our subroutine uses the Split function to create an array from the value returned by the commandLine property, using the double quote mark - chr(34) - as the split character. We then use a For Next loop starting with item 3 and continuing through the last item in the array (the upper bound of the array minus 1). In this script we simply echo back the value of each argument, though obviously once you have that value in hand you can do anything you want with it.

Just remember to surround each argument with double quotes (something you’d have to do anyway if any of those arguments included spaces). If you do that, the script should work as expected. If you don’t… well, if you’re crazy enough to try to include command-line arguments that aren’t surrounded by double quotes all we can do is wish you the best of luck (and tell you that it’s been nice knowing you).


Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • When handling command line parameters in HTAs, surely it would be better to make them work in the same way as other command line utils?

    "Yes it would. And don't call me Shirley!" :-)

    To avoid all the quotes business, simpley select the part of the command line after the second quote.

    (""""  is a method of describing a single quote as a literal string.)

    code

    ------

    <html>

    <head>

    <HTA:APPLICATION

     APPLICATIONNAME="Show Command Line"

     ID="commandlineparams"

     VERSION="1.0"/>

    </head>

    <script language="VBScript">

    Dim strCommandLine,strParams,strParam,arrParams

    Sub Window_OnLoad

    strCommandLine=commandlineparams.commandline

           ' start at character 2 to avoid the first quote

    strParams=Right(strCommandLine,Len(strCommandLine)-InStr(2,strCommandLine,"""",0))

    arrParams=Split(strParams," ")

    For Each strParam In arrParams

    If strParam ="" Then

                       'empty string, don't do anything!

    Else

       results.value=results.value & strParam & vbcrlf

    End If

    Next

     'This method will be called when the application loads

     'Add your code here

    End Sub

    </script>

    <body bgcolor="white">

    <!--Add your controls here-->

    <textarea name="results" id="results" rows="5" cols="100"></textarea>

    <!--{{InsertControlsHere}}-Do not remove this line-->

    </body>

    </html>

  • What if one of the parameters is a filepath+filename and has a space in it?

  • @Steve

    That's not too difficult: TardisRepairMan's solution removes the first quoted string ONLY (which is the app startup path\name). After that you can parse the remaining arguments in any manner you wish, including checking for quotes paths if these are expected.

  • Thanks for the post, I have a personal thing about using quotes in command lines so here is my solution for double quotes on the parameters delema.  Use a multi charater split. My choice was the " /:" similar to the vb script paramaters we all know and love.  These work for me because they can't be contained in file names as a group either.

    So I offer up the following

    If the command line is: cmdlinetest.hta /:ARG1 /:This is arg 2

     Or

    even this if you must:   "cmd line test.hta" /:ARG1 /:This is arg 2

    The following HTA will work nicely

    <html>

    <head>

    <HTA:APPLICATION

        ID="objTestHTA"

        APPLICATIONNAME="Command Line Agruments"

        SINGLEINSTANCE="yes"

    >

    </head>

    <SCRIPT Language="VBScript">

    Sub Window_onLoad

       arrCommands = Split(objTestHTA.commandLine, " /:")

       For i = 1 to (Ubound(arrCommands))

           document.write(arrCommands(i) & "<br>")

       Next

    End Sub

    </SCRIPT>

    <body>

    </body>

    </html>

    I started the FOR loop at 1 because I don't care about the name of the HTA command and dropped the STEP as it is no longer needed.  

    The result is an HTA that displays the following

        ARG1

        This is arg 2

    <and its good knowing you all too>  :)

  • Full code to parse double-quote arguments  :

    // ------------------------------------------------------------------------------

    // In "\"p 1\" p2  p3 \"p4 b\" p 5"

    // Out {"p 1", "p2", "p3", "p4 b", "p", "5"}

    // ------------------------------------------------------------------------------

    function /*string[] */ParseArguments(commandLine)

    {

    var raw = commandLine

    , args = []

    , arg = ""

    , idx = 0

    , c = null

    , prevC = null

    , withinQuote = false

    , withinQuoteCount = 0

    ;

    for (var i = 0; i < raw.length; i++)

    {

    c = raw.charAt(i);

    if (c == "\"")

    {

    // double-quote character

    withinQuote = !withinQuote;

    c = "";

    withinQuoteCount = 0; // reset

    }

    else if (c == " " || c == "\t")

    {

    // space character

    if (withinQuote)

    {

    // between quotes

    if (withinQuoteCount > 0)

    {

    // the space char is not the first char between quote : so include it

    withinQuoteCount++;

    // concatenates to the current item

    args[idx] = (args[idx] || "") + c;

    }

    }

    else if (!(prevC == " " || prevC == "\t" ? true : false))

    {

    // not between quotes AND the previous character is not a space: creates a new item

    args[idx] = (args[idx] || "").trim();

    // check for empty string (the argument can be a double quotes)

    if (args[idx])

    args[++idx] = ""; // add a new item

    }

    }

    else

    {

    // it's not a space character

    if (withinQuote)

    withinQuoteCount++;

    args[idx] = (args[idx] || "") + c;

    }

    prevC = c;

    }

    args[idx] = (args[idx] || "").trim();

    return args;

    }

    // ------------------------------------------------------------------------------

    function /*object */GetHta()

    {

    // 'HTML Applications Reference' (msdn.microsoft.com/.../ms536473(v=vs.85).aspx)

    var apps = document.getElementsByTagName("hta:application"); // not working in IE/HTA

    if (apps.length)

    return apps[0];

    apps = document.getElementsByTagName("application");

    for (var i = 0; i < apps.length; i++)

    if ("hta" === apps[i].scopeName.toString().toLowerCase())

    return apps[i];

    return null;

    }

    // ------------------------------------------------------------------------------

    function /*string[] */GetHtaArguments()

    {

    var app = GetHta();

    if (!app)

    return null;

    // commandLine Property (msdn.microsoft.com/.../ms536479%28v=vs.85%29.aspx)

    return ParseArguments(app.commandLine);

    }

    // ------------------------------------------------------------------------------

    if (!String.prototype.trim)

    {

    String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); }

    String.prototype.ltrim = function() { return this.replace(/^\s+/, ""); }

    String.prototype.rtrim = function() { return this.replace(/\s+$/, ""); }

    }

  • For me, using JScript the following code returns "undefined" as the value. function ShowArguments() { ("commandLine = " + objVCAuto.commandLine); /* var args = objVCAuto.commandLine; alert(args); var argStr = ""; for (var i = 0; i < args.length;="" i++)="" {="" argstr="" +="Argument[" +="" i="" +="" "]=" + args[i] + " \n";="" }="" alert(argstr);*/="" }="" where="" i'm="" using="" a="" button="" to="" call="" the="" function.="" the="" id="" of="" my="" hta:application="" is="" "objvcauto".="" so,="" why="" doesn't="" this="" work?="" i'm="" executing="" the="" .hta="" from="" cmd.exe.="">