If you’ve been a Scripting Pro VIP subscriber for a while, you probably know that I like to use HTML Applications (HTAs) as a GUI for many of my VBScript administration applications. I’ve been using HTAs for quite some time now and really enjoy developing them. However, I've run into a couple of annoyances:
- The elements (e.g., input boxes, buttons, text areas) in an HTA window don't render immediately if you perform a time-consuming process during the startup (i.e., onload) process. The time-consuming process has to complete before any HTA form elements are displayed. In other words, you see a blank HTA window until the time-consuming process finishes, which can be confusing, especially for first-time users who might think the HTA isn't working correctly.
- The HTA window is pretty much a blob on the desktop while your HTA performs the time-consuming process. The HTA window is inaccessible, which means you can’t minimize or move it until that process completes.
I've been experimenting with various techniques to work around these annoyances. I've found a way to work around the first annoyance, which I'll share with you now. I'm still experimenting with working around the second annoyance and will let you know if and when I find a suitable solution.
Cause a Pause
As it turns out, the simplest workaround for avoiding a blank window is to cause a pause periodically when the time-consuming process is running so that you can check to see whether the process has finished. As long as that process is still loading, you display a message stating that fact. For example, if the time-consuming process is loading data into the HTA, you can display a message such as: Loading...Depending on the size of your data, loading the data could take a while. Although you could use VBScript's MsgBox function to cause a pause and display such a message, you'd have to click an OK button after you launch the HTA, so it's not an ideal solution. A better solution is to use a timer-like HTML method called setInterval and its counterpart clearInterval to cause the pause and some HTML code to display the message.
I created two simple HTAs—WithoutTimeInterval.hta and UsingTimeInterval.hta—to demonstrate the annoyance and how the setInterval method works around it. These HTAs use the DoLongProc subroutine and Delay function to simulate a time-consuming process. WithoutTimeInterval.hta (Listing A) shows the code before the workaround. As you can see in Figure A, when this HTA starts running, the entire inner area of the HTA display is empty. UsingTimeInterval.hta (Listing B) shows the code after the workaround. As Figure B shows, instead of seeing a blank inner display, you see the message that the data is loading.
The setInterval method runs a block of code, pauses for a specified number of milliseconds, then runs the code again. This continues until you cancel the setInterval timer with the clearInterval method. The syntax that I followed for setInterval is
iTimerID = window.setInterval(vCode, iMilliSeconds)
where vCode is the code to execute (or a pointer to it) when the specified interval has elapsed and iMilliSeconds is the interval in milliseconds. For example, the code
TimerInterval = window.setInterval("DoLongProc",100)
runs the DoLongProc subroutine every 100 milliseconds (100ms).
The setInterval method returns an integer that you use with the clearInterval method to cancel the timer. The syntax for clearInterval is
where iIntervalID is the value returned by the setInterval method. For example, the code
cancels the interval previously set.
An Easy-to-Implement Workaround
The setInterval workaround is easy to implement. As Listing B illustrates, you have to perform four steps:
- Declare the Public variable that will contain the setInterval iTimerID return value (like TimerInterval in the example just given), as callout A in Listing B shows. Declaring it as a Public variable ensures that your setInterval return value is available to all subroutines.
- Use the clearInterval method immediately within your subroutine process to cancel the timer, as callout C in Listing B shows.
- Add the HTML code that displays the message that the data is loading, as callout D in Listing B shows. Note that if you were to add this code to WithoutTimeInterval.hta in the same spot without adding the setInterval and clearInterval code, this loading message wouldn't appear—therein lies the annoyance.
Note that the combined effect of step 2 and step 3 is that you're using a timer object to have the time-consuming process (in this case, DoLongProc) run in the background as a separate thread every 100ms.
I tested the setInterval workaround with HTAs that call two subroutines within the Window_Onload subroutine and have found that I can use the same iTimerID (TimerInterval) in both subroutine calls without any problems. I just declared
TimerInterval = window.setInterval("DoLongProc",100)TimerInterval = window.setInterval("DoLongProc2",100)
in step 2. However, you might want to set up an iTimerID variable for each setInterval method that you use in your Window_Onload subroutine to make it easier to understand and debug the HTA later on. Declaring separate iTimerID variables require more upfront work, though, because you have to declare another Public variable, use the clearInterval method in both processing subroutines (making sure you use the appropriate iTimerID), and insert an additional HTML-coded message.
That’s all it takes to avoiding blank windows during an HTA's startup. To try out the demo HTAs, click the Download the Code Here button to download WithoutTimeInterval.hta and UsingTimeInterval.hta.