The ABCs of HTAs: Scripting HTML Applications

HTAs

AutoRefresh an HTA Using a Timer

A lot of people now busy writing HTAs got their start writing “plain vanilla” system administration scripts. (Remember? Scripts that ran from the command line and – gasp! – couldn’t display output in tables or in color, scripts that didn’t allow you to select items from check boxes or radio buttons.) Having mastered the intricacies of Windows Script Host (WSH) and VBScript, these script writers set their sights on creating scripts that could run in the graphical environment of HTAs.

Of course, that necessitated a few changes. For example, most script writers use Wscript.Echo to report information back to the user. However, Wscript.Echo won’t work in an HTA. (Neither will any other WSH methods. That’s because WSH methods must execute under Windows Script Host; HTAs do not run in the WSH environment.) But that was easy enough to overcome: after all, you can either write information directly to the HTA itself or you can use VBScript’s Msgbox function to return information to the user. Similar alternatives exist for pretty much anything else you can do in WSH.

However, there’s always been one sticking point: Wscript.Sleep, which enables you to pause a script for a specified amount of time. The Sleep method is especially useful for creating monitoring scripts: scripts designed to check the value of a resource, pause for a set amount of time, and then check the value again. This is very easy to do in a WSH script: you simply check the resource, use Wscript.Sleep to pause the script, then loop around and check the resource again.

Unfortunately, though, Sleep is a WSH method; that means it won’t work in an HTA. So how can you insert a similar pause in your HTA? Nobody knows.

No, wait, that’s not right: somebody does know. Turns out that there’s an HTML method called setInterval that enables you to “schedule” a block of code to run periodically. For example, here’s a line of code that instructs an HTA to run a subroutine named RefreshList every 30 seconds (30,000 milliseconds):

iTimerID = window.setInterval("RefreshList", 30000)

Pretty easy, huh? Suppose you had a subroutine named GetFreeDiskSpace and you wanted to run it every 2 minutes (120,000 milliseconds). Here’s your code:

iTimerID = window.setInterval("GetFreeDiskSpace", 120000)

Note that an ID number for this timer is stored in a variable named iTimerID. That will be useful to us later on.

To make use of this code include it in a Window_OnLoad subroutine. Any code found in the Window_OnLoad subroutine automatically executes whenever a window is loaded (that is, any time your HTA is started or reloaded). To create an HTA that automatically runs the RefreshList subroutine every 30 seconds, just include this block of code in your HTA:

Sub Window_OnLoad
    iTimerID = window.setInterval("RefreshList", 30000)
End Sub

Let’s put this all in context by showing you a more practical example. Following is code for an HTA that displays the processes currently running on a computer. Process names are retrieved and written to the HTA using a subroutine named – hey, our old pal RefreshList! We’ve included a timer (as part of the Window_OnLoad subroutine) that runs RefreshList every 30 seconds.

Here’s what the HTA code looks like:

<html>
<head>
<title>Auto Refresh</title>
<HTA:APPLICATION 
     ID="objAutoRefresh"
     APPLICATIONNAME="Auto Refresh"
     SCROLL="auto"
     SINGLEINSTANCE="yes"
>
</head>

<SCRIPT LANGUAGE="VBScript">

    Sub Window_OnLoad
        iTimerID = window.setInterval("RefreshList", 30000)
    End Sub

    Sub RefreshList
       strHTML = ""
       strComputer = "."
       Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
       Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process")
       
       For Each objProcess in colProcesses
           strHTML = strHTML & objProcess.Name & "<BR>"
       Next
       
       ProcessList.InnerHTML = strHTML
    End Sub

</SCRIPT>

<body><span id = "ProcessList"></span>

</body>
</html>

And here’s what the actual HTA looks like, in all its glory:

List Box

Hey, we never said we were trying to win a beauty contest here.

Note that once the timer has been set you never have to bother with it again; it will run forever and ever. (Or at least until the HTA terminates.) But what if you wanted to be able to stop the timer? Well, in that case you could create another subroutine (perhaps activated by a button click) that uses the clearInterval method to stop the timer. Here’s a sample subroutine that stops the timer iTimerID:

Sub StopTimer
    window.clearInterval(iTimerID)
End Sub

For this to work, iTimerId will have to be declared as a global variable. You can declare a global variable by “dimming” iTimerID in the <SCRIPT> tag, but not inside a subroutine:

<SCRIPT LANGUAGE="VBScript">

    Dim iTimerID

    Sub Window_OnLoad
        iTimerID = window.setInterval("RefreshList", 30000)
    End Sub

There’s one other thing we can do to spiff up this HTA. When you start the HTA it displays a blank screen; it’s only 30 seconds later that the list of processes first shows up. That’s because, in our Window_OnLoad subroutine, we set a timer that says, “Wait 30 seconds, then run the RefreshList subroutine.” If we want the list of processes to show up right away we just need to call RefreshList in Window_OnLoad. That way we get both the initial list and a timer that refreshes the list every 30 seconds.

Here’s what our new Window_OnLoad subroutine looks like:

Sub Window_OnLoad
    RefreshList 
    iTimerID = window.setInterval("RefreshList", 30000)
End Sub

And here’s what the completed HTA looks like:

<html>
<head>
<title>Auto Refresh</title>
<HTA:APPLICATION 
     ID="objAutoRefresh"
     APPLICATIONNAME="Auto Refresh"
     SCROLL="auto"
     SINGLEINSTANCE="yes"
>
</head>

<SCRIPT LANGUAGE="VBScript">

    Sub Window_OnLoad
        RefreshList 
        iTimerID = window.setInterval("RefreshList", 30000)
    End Sub

    Sub RefreshList
       strHTML = ""
       strComputer = "."
       Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
       Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process")
       
       For Each objProcess in colProcesses
           strHTML = strHTML & objProcess.Name & "<BR>"
       Next
       
       ProcessList.InnerHTML = strHTML
    End Sub

</SCRIPT>

<body><span id = "ProcessList"></span>

</body>
</html>