Rob van der Woude's Scripting Pages

VBScript Scripting Techniques > HTAs

HTAs

HTAs (HTML Applications) are webpages (forms), with access to local resources.

Though the engine that executes HTAs (MSHTA.EXE) shares a lot of code with Internet Explorer, it doesn't have Internet Explorer's tight security restrictions.
In fact, if run with elevated privileges, HTAs have access to every resource that administrators have access to!

If you want to build a nice looking user interface for your VBScript scripts collection, try an HTA (or rewrite them in Visual Basic).
If you need to build a proof of concept for a new program, consider an HTA.

Visual Studio 2010 Express Installer screenshot

Microsoft's Visual Studio 2010 Express Edition installer is an example of a great looking HTA.

HTAEdit screenshot

HTAEdit (now seamlessly integrated in VBSEdit)

On this page, I intend to show some of the pitfalls I encountered in building HTAs, and some solutions or work-arounds.

My preferred tool for developing HTAs is VBSEdit. It used to come with a separate program HTAEdit in a single package, but recently these programs have been merged, and the new VBSEdit now handles HTAs too.

VBSEdit comes with a built-in debugger, but debugging an HTA is much harder then debugging VBScript code, so I usually write and test VBScript code in VBSEdit, and when ready, use it in an HTA's subroutines.

HTAs aren't restricted to HTML and VBScript, you can use JScript and JavaScript without the need to install anything, and PerlScript if you install Perl.

Note: When using multiple scripting languages in a single HTA, all event handlers (e.g. onclick) must always specify the language for the called commands, e.g.:
<input type="button" value="Click Me" onclick="vbscript:MsgBox 'Click Me (VBScript)'" />
<input type="button" value="No, Me" onclick="javascript:alert('No, Me (JavaScript)');" />

Back to the top of this page . . .

 

Building Your HTA

Each HTA is actually an HTML page with one or more HTA specific code blocks.

Code
Description Remarks
<!DOCTYPE HTML> Document Type Declaration Optional but recommended for IE versions > 6
<html lang="en"> HTML begin <html> required, language (lang property) optional but recommended
<head> HTML head begin Required
<title>My HTML application</title> HTML/HTA title bar caption Optional but recommended, will also show up in Windows' task bar, can be read or set with document.title
<HTA:APPLICATION HTA definition block begin Required; for a detailed list of all available properties see Microsoft's HTML Applications Reference
  APPLICATIONNAME="HTA Name" HTA name Required; can be read by script with HTAID.ApplicationName
  ID="HTAID" HTA unique ID Required
  VERSION="1.0" HTA version Required; can be read by script with HTAID.Version
  BORDER="none"
  SCROLL="auto"
  SINGLEINSTANCE="yes" Only one HTA with this HTA's ID can run at any time If omitted or "no", multiple instances of this HTA can run simultaneously
  WINDOWSTATE="maximize" Maximize the HTA window when opened Optional; not recommended if the HTA window must be resizable, as the window may resize spontaneously when dragged
  NAVIGABLE="yes"
/> End of HTA definition block
<meta http-equiv="x-ua-compatible" content="ie=9" /> Compatibility meta tag Optional; this will enable many CSS 3 and HTML 5 features, but you will need to adjust your event handlers
 
<style type="text/css"> Stylesheet begin Optional; external stylesheets are allowed too
  •
  •
  •
CSS style definitions
</style> End of stylesheet
</head> End of HTML head Required
<script language="VBScript"> WSH (VBScript) code begin Unlike JavaScript in web pages, which is located either inside the HTML head or inside the HTML body, the HTA's code (usually VBScript, but may also be JScript or PerlScript or any WSH supported scripting language) is positioned between the HTML head and body
Option Explicit
Dim global_variables
Global variables declaration Declare global variables here, set them in Window_OnLoad (or elsewhere)
 
Sub window_onload Use this code block to initialize objects and variables, parse the (optional) command line, etc.
  'This method will be called
  'when the application loads
End Sub
 


Place your other subroutines here
 
Sub window_onunload Use this code block to clean up objects, close files, etc.
  'This method will be called
  'when the application exits
End Sub
 
</script> End of WSH (VBScript) code
<script type="text/javascript"> JavaScript code begin Optional; external javascripts are allowed too, inside the head or body;
unlike "true" web pages, in HTA's the JavaScript code may be located between the head and the body as well as inside.
  •
  •
  •
JavaScript code
</script> End of JavaScript code
<body> HTML body Use the HTML body to build the HTA's user interface
  •
  •
  •
</body>
</html> End of file

Back to the top of this page . . .

 

Adding Controls

In an HTA, you can add elements and controls just like you would in a web form.

You can reference an element directly by its ID, e.g. if you have a textbox with ID "MyTextBox" you can read its value with the following code:

strText = MyTextBox.value

Or:

strText = document.getElementById( "MyTextBox" ).value

The latter is much easier to debug in VBSEdit.

Back to the top of this page . . .

 

Global Variables

Using local variables inside subroutines is a piece of cake.
Global variables in HTAs, on the other hand, require some extra attention.

Summarizing: For easier maintenance, declare your global variables at the top of the VBScript code block, and set them in the window_onload subroutine (or in other subroutines).

 

Demo Project: My First HTML Application

MyFirstHTA embedded screenshot

For this demo, we will create an HTA that accepts a number as input, and then checks if it is a prime number.

Yes, if you are new to HTAs, the following code might look rather overwhelming.
Don't worry, I will explain most of it.
And you don't have to retype the code, it can be downloaded here.

 

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title>My First HTML Application</title>
  5.  
  6. <HTA:APPLICATION
  7.   APPLICATIONNAME="My First HTML Application"
  8.   ID="MyFirstHTA"
  9.   VERSION="1.0"
  10.   SCROLL="no"/>
  11.  
  12. <style type="text/css">
  1. body {
  2. 	background-color: #fdfeff;
  3. 	color: darkblue;
  4. 	font-family: Calibri;
  5. 	font-size: 12pt;
  6. 	margin: 4em 3em;
  7. }
  1. </style>
  2. </head>
  3.  
  4. <script language="VBScript">
  1. Option Explicit
  2.  
  3. Sub CheckIfPrime( )
  4. 	Dim i, intInput
  5. 	intInput = document.getElementById( "InputNumber" ).value
  6. 	If intInput < 3 Then
  7. 		document.getElementById( "OutputResult" ).innerHTML = "Yes, " & intInput & " is a prime number."
  8. 	Else
  9. 		For i = 2 To intInput - 1
  10. 			If intInput Mod i = 0 Then
  11. 				document.getElementById( "OutputResult" ).innerHTML = "No, " & intInput & " is not a prime number."
  12. 				Exit Sub
  13. 			End If
  14. 		Next
  15. 		document.getElementById( "OutputResult" ).innerHTML = "Yes, " & intInput & " is a prime number."
  16. 	End If
  17. End Sub
  18.  
  19. Sub ValidateInput( )
  20. 	Dim objRE, strInput
  21. 	strInput = document.getElementById( "InputNumber" ).value
  22. 	Set objRE = New RegExp
  23. 	objRE.Global  = True
  24. 	objRE.Pattern = "[^\d]+"
  25. 	If objRE.Test( strInput ) Then
  26. 		strInput = objRE.Replace( strInput, "" )
  27. 		document.getElementById( "InputNumber" ).value      = strInput
  28. 		document.getElementById( "OutputResult" ).innerHTML = "Enter a number, and click the ""Check"" button to check if it is a prime number."
  29. 	End If
  30. 	If strInput = "" Then
  31. 		document.getElementById( "OutputResult" ).innerHTML = "Enter a number, and click the ""Check"" button to check if it is a prime number."
  32. 	End If
  33. 	Set objRE = Nothing
  34. End Sub
  35.  
  36. Sub Window_OnLoad
  37. 	window.resizeTo 640, 480
  38. 	document.title = document.title & ",  Version " & MyFirstHTA.Version
  39. End Sub
  1. </script>
  2.  
  3. <body>
  4.  
  5. <p><input type="text" id="InputNumber" onchange="ValidateInput" onkeyup="ValidateInput" />
  6. &nbsp;
  7. <input type="button" value="Check" onclick="CheckIfPrime" /></p>
  8.  
  9. <p>&nbsp;</p>
  10.  
  11. <p id="OutputResult">Enter a number, and click the "Check" button to find out if it is a prime number.</p>
  12.  
  13. </body>
  14. </html>

 

Explanation:

As mentioned earlier, the HTA starts with an HTML like head.
Specifying a DocType (line 1) is optional, but it may help getting more predictable results when running the HTA in different Windows versions.
Unlike "true" web pages, you don't have to include a <title> tag here, you can set it later, on-the-fly, which is demonstrated in the Window_OnLoad subroutine, on line 61.

In the head we also find the mandatory <HTA:APPLICATION> tag (lines 6..10); Copy and paste it from this page or from any other HTA and use Microsoft's HTML Applications Reference to add or modify its properties.

Just for the sake of demonstration, I also included an optional stylesheet (lines 12..20).

The body (lines 65..75) contains 3 main elements:

  1. A textbox (line 67, ID InputNumber) to enter the number in; note that HTML 5's type="number" is ignored in HTAs, that is why we call the ValidateInput subroutine using the onchange and onkeyup event handlers.
  2. A button (line 69) that will call the CheckIfPrime subroutine when clicked.
  3. A text paragraph (line 73, ID OutputResult) that can be modified on-the-fly.

Between the head and the body we have a (VBScript) code block (lines 23..63).
Option Explicit (line 24) is optional, but you really should include this, if only for debugging purposes.

Subroutine CheckIfPrime( ) (lines 26..40) is the main program, checking the number and returning the result.
It reads the entered number using the code in line 28:

intInput = document.getElementById( "InputNumber" ).value

There is no need to check if the input actually is a positive integer, since validation is already handled by the ValidateInput( ) helper subroutine.
After checking if the number is a prime number, the result has to be presented (lines 30, 34 & 38):

document.getElementById( "OutputResult" ).innerHTML = "Yes, " & intInput & " is a prime number."

This will replace the text in the ouput text paragraph (line 73, ID OutputResult).

Subroutine ValidateInput( ) (lines 42..57) is a helper subroutine, filtering out everything from the input but numbers.
It is triggered when the content of the textbox (line 67, ID InputNumber) is changed (onchange event handler, i.e. on change of typed input, but also when new input is pasted from the clipboard) or when a key is pressed (onkeyup event handler).
The code in line 44 reads the input just like the CheckIfPrime( ) does.
The subroutine then uses a regular expression to remove everything but numbers (line 49).
The code in line 50 writes the corrected input back into the textbox.
The code in lines 51 and 54 changes the output text from result to description.

Subroutine Sub Window_OnLoad (lines 59..62) is executed when the HTA is loaded.
In our demo it resizes the HTA's window (line 60) and sets the HTA's title (line 61).

Investigate the demo script, play with it, e.g. change the presentation of the results, or try making it accept hexadecimal numbers, or trigger CheckIfPrime( ) when pressing the Enter key . . .
Whatever you like.

Back to the top of this page . . .

 

HTA's Path

Getting the full path to the HTA is easy:

strHTAPath = Self.location.pathname
Note: Keep in mind that, when running an HTA in VBSEdit, it runs a copy of the HTA in a different location!

Back to the top of this page . . .

 

Command Line Arguments

Handling command line arguments in HTAs is not as sophisticated as it is in "pure" VBScript, but it is "doable".

You can read the command line as a property of the HTA, so you need to use the HTA's ID:

strCommandLine = MY_HTA_ID.CommandLine

where MY_HTA_ID is the HTA's ID as defined in the HTA's head.

The string returned as command line starts with the HTA's full path, followed by the command line arguments, if any.
The HTA's path may be in doublequotes.

To get the command line arguments only you could use the following code:

If Left( MY_HTA_ID.CommandLine, 1 ) = """" Then
	strCommandLineArguments = Trim( Mid( MY_HTA_ID.CommandLine, Len( Self.location.pathname ) + 3 ) )
Else
	strCommandLineArguments = Trim( Mid( MY_HTA_ID.CommandLine, Len( Self.location.pathname ) + 1 ) )
End If

Back to the top of this page . . .

 

Close the HTA

Closing the HTA is simple, use either:

Self.close

or:

window.close True

I could not find documentation on the optional True argument of window.close.
I have once been told it forces the window to close, and my extemely limited testing showed that:

But, since VBSEdit only "recognizes" Self.close I would recommend using that.

In case you run the HTA in compatibility mode you may sometimes have to use window.close( ) (e.g. when using it in an event handler, <input type="button" value="Quit" onclick="window.close( )" /> otherwise you might get a "Self is not defined" error message)

Back to the top of this page . . .

 

Resize the HTA

To resize the HTA window, use the following code (e.g. in the Window_OnLoad subroutine):

window.resizeTo width, height

e.g.:

window.resizeTo 640, 480

Instead of placing the VBScript code in Window_OnLoad, you may also place it in the HTA's head as JavaScript:

<script type="text/javascript">window.resizeTo(640, 480);</script>
Important: Do not set the WINDOWSTATE in your HTA definition block when using code to resize the HTA's window.

Back to the top of this page . . .

 

Get the HTA Window's Dimensions

The current width of the HTA can be obtained using the following VBScript code:

intWidth = document.body.offsetWidth

Likewise, the current height of the HTA can be obtained using:

intHeight = document.body.offsetHeight

Add a CSS style to allow measuring the window dimensions even if all content fits in the window:

body, html { width: 100%; height: 100%; }

Back to the top of this page . . .

 

Center the HTA on Screen

To center the HTA on screen, use the following VBScript code:

posX = CInt( ( window.screen.width  - document.body.offsetWidth  ) / 2 )
posY = CInt( ( window.screen.height - document.body.offsetHeight ) / 2 )
If posX < 0 Then posX = 0
If posY < 0 Then posY = 0
window.moveTo posX, posY

Add a CSS style to allow measuring the window dimensions even if all content fits in the window:

body, html { width: 100%; height: 100%; }

Back to the top of this page . . .

 

Minimize or Maximize the HTA

To make the HTA minimize its own window, place the following code between the HTA's head and its VBScript section:

<!-- This "HHCtrlMinimizeWindowObject" works together with the JavaScript function "_jsMinWin()" and -->
<!-- the hidden input "MinimizeWindow" to minimize the HTA window (use "MinimizeWindow.click" in VBScript) -->
<object id="HHCtrlMinimizeWindowObject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
	<param name="command" value="minimize" />
</object>

<script type="text/javascript">
function _jsMinWin( ) {
	try {
		HHCtrlMinimizeWindowObject.Click( );
	}
	catch ( err ) {
		alert( err.message );
	}
}
</script>

Place the following code in the HTA's body:

<!-- This hidden input works together with the JavaScript function "_jsMinWin()" and the object -->
<!-- "HHCtrlMinimizeWindowObject" to minimize the HTA window (e.g. use "MinimizeWindow.click" in VBScript) -->
<input type="hidden" name="MinimizeWindow" id="MinimizeWindow" onclick="javascript:_jsMinWin();" />

You can now have the HTA minimize itself using the VBScript code MinimizeWindow.click or document.getElementById( "MinimizeWindow" ).click

 

The code required to maximize the HTA again is very similar; place the following code between the HTA's head and its VBScript section:

<!-- This "HHCtrlMaximizeWindowObject" works together with the JavaScript function "_jsMaxWin()" and -->
<!-- the hidden input "MaximizeWindow" to maximize the HTA window (use "MaximizeWindow.click" in VBScript) -->
<object id="HHCtrlMaximizeWindowObject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
	<param name="command" value="maximize" />
</object>

<script type="text/javascript">
function _jsMaxWin( ) {
	try {
		HHCtrlMaximizeWindowObject.Click( );
	}
	catch ( err ) {
		alert( err.message );
	}
};
</script>

Place the following code in the HTA's body:

<!-- This hidden input works together with the JavaScript function "_jsMaxWin()" and the object -->
<!-- "HHCtrlMaximizeWindowObject" to maximize the HTA window (e.g. use "MaximizeWindow.click" in VBScript) -->
<input type="hidden" name="MaximizeWindow" id="MaximizeWindow" onclick="javascript:_jsMaxWin();" />

You can now have the HTA maximize itself using the VBScript code MaximizeWindow.click or document.getElementById( "MaximizeWindow" ).click

Important: Do not set the WINDOWSTATE in your HTA definition block when using this code.
  Minimizing or maximizing the HTA window using this code will fail if the HTA lost focus.
So to keep on the safe side, insert a line window.focus just before the M**imizeWindow.click code.

Back to the top of this page . . .

 

Improve Loading Speed With MultiThreading

When an HTA loads, it won't show its interface untill all work has been done, i.e. untill the code in the Window_OnLoad subroutine, and all other code called from there, has been executed.
From the outside, it may look like the HTA loads an empty window "frame" and then waits for several seconds before showing its inteface elements.
You can speed up the loading of the interface by only executing the code to build the interface in the Window_OnLoad subroutine, and then start a new thread to initialize the "background" code.

Starting a new thread is not that complicated:

window.setTimeout "subroutine", 1000, "VBScript"

This will start subroutine after a 1 second (1000 milliseconds) delay, allowing the HTA enough time to render its interface before continuing.

The first argument for window.setTimeout, the command or subroutine to be started, may have some arguments of its own, replacing the doublequotes on subroutines's command line by singlequotes, e.g.:

window.setTimeout "MsgBox 'This MsgBox was started in a separate thread', vbOKOnly, 'Test'", 1000, "VBScript"

As you can see from the example, finding typos may be a bit harder than usual.

The third argument for window.setTimeout will be "VBScript" in most cases.
However, it also opens up a way to start a JavaScript code block, by using "JavaScript".

Back to the top of this page . . .

 

Embedding Images

As one might expect, it is possible to use images in HTAs with the <img src="..." /> tag.
Personally I prefer to distribute HTAs as a single file.
The good news: it is possible with inline-images.
The bad news: your HTA may become huge!

First compress your image as much as is acceptable (e.g. JPEG compression, decrease color depth, etc.).
To convert the image to a string that can be embedded in the HTA you can use base64 encoding.
Assuming you have the b64 utility from SourceFourge.net, use b64 -e image.gif image.b64 to encode the image, and embed the image like this (string marked red is the content of image.b64):

<img src="data:image/gif;base64,R0lGODlhIAAgAOYAAABbAIiwiER8RADMANbW1gCZAGaZZsXFxR3oHTrWOgCDAK60rgD0AA6dDmm+aRZmFlDBPubm5gDoACKN
Ir7mvlCeUACjAGb/ZqK0ojz4PAB1ACbUJgC1AESSRA7DDgDeAA+FD9jl2FbSVmmtab29vUS6PhqnGj62Poi2iACZAO7u7hPSE8zMzABmAN3d3VCsUHj4eH6/fqfMp1q+
WiL/In6ufh1xHXGccUzXTBqwAADuAAC9AAD/AADVAN7w3gtlC0mHSR3FHYjDiAqnAAWSBQB6AB6nHl62XpWmlQCLADfpN3WwdSJsIkSNRB/4H6fEp1DqUEH/QQicCDPM
M0mpSQDDAEmUSSKTIpWxlS1xLQBmAACwAMPqvlCkUFGyUZWwlQuiC3G/ahKlEg+MDz7JPhWvFafWp3u4ewtpCx+rH3a1dv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAUUAGsALAAAAAAgACAAAAf/gGuCg4SFhoeIiYqLjI2Oj5CRhFwQORSSiBQlQ0NhFjOYhBQnFqVn
B0YFXiqYPjMFYAUFSwcuDrIvrI+uBWIiaQ01tWtmBWQFuY0+RwUNODAmUljDgkkJCUnJictJRFMXUA1jXwcRhFRlURtJXbqFKmHdQRlRSkRXGCzmhEJENDQrFLQjpCLG
mCQenPxDQGTCAn2GQij4wIPHAAUVWBUEkaQKg4oWFTiEeGgCB5ADilRQA6LIjo8oi3R4uO/QiCQMcjKoUqSIBQk6c/LsQIKAu0MyNHzQwVRHj6VNmVrQ0KToUUQadkjY
yrWrhAIablhtZCXJh7No0xZocYNaIxRKYtOi7VGErdtGIVps6cG3L100SO46spFkgGHDVTT8COwCk4EWhwdwaPEACUlJTwBwqFJlSwsmNEMJAlBgh4XPoUWvAVKkAIAs
qVUHAABAQNGaqkMAADJWNaEIBIz6Hk68eKhAADs=" alt="Embedded icon" title="Embedded icon" />

And this is what our example looks like: Embedded icon

And if you aren't convinced yet, check the properties of the demo project screenshot above . . .

Or check the source code of my Attic Numerals Converter.

Back to the top of this page . . .

 

Embed an Icon

Assume you have an icon d:\iconsfolder\myicon.ico that you want to use for your HTA d:\scriptsfolder\myhta.hta.
If you don't mind having the icon in a separate file, add the following code to the HTA's HTA:APPLICATION block:

ICON="D:\iconsfolder\myicon.ico"

and you're done.
Instead of an absolute path, you can use a relative path, and the easiest relative path is the HTA's parent folder.
Move the icon to the HTA's parent folder and add this code to the HTA's HTA:APPLICATION block:

ICON="myicon.ico"

If you prefer to distribute your HTA as a single self-contained file, it is possible to embed an icon (or, unfortunately, any type of binary payload, including malware), by prepending it to the HTA.
I found this trick by Emeric Nasi to embed an icon in the HTA at Sevagas.com.
Add the following code to the HTA's HTA:APPLICATION block:

ICON="#"

Then embed the icon using the following command on a CMD prompt:

COPY /B d:\iconsfolder\myicon.ico + d:\scriptsfolder\myhta.hta d:\scriptsfolder\_myhta.hta

The new HTA d:\scriptsfolder\_myhta.hta will have the icon embedded.

If you want to edit the HTA later, use the original one or remove all binary code before the <html> opening tag, as most text editors will corrupt the embedded icon.
When you are finished editing the HTA, use the same command once more to embed an icon.

Warning: Because of the ability to embed malware, you may seriously want to consider not using this technique!
Most AntiVirus software will be triggered by the prepended binary payload.

Back to the top of this page . . .

 

Get the HTA's Process ID

Insert the following function in you HTA's VBScript block, and call MyPID( ) when you need the HTA's process ID:

Function MyPID( )
	Dim colItems, intCount, intPID, objItem, objWMIService, strQuery
	intCount =  0 ' Keeps track of the number of simultaneous instances of this HTA
	intPID   = -1
	' WMI query must be escaped, hence the replacement of single backslashes by double backslashes
	strQuery = "Select * From Win32_Process Where CommandLine Like '%" & Replace( self.location.pathname, "\", "\\" ) & "%'"
	Set objWMIService = GetObject( "winmgmts://./root/cimv2" )
	Set colItems      = objWMIService.ExecQuery( strQuery )
	For Each objItem in colItems
		intCount = intCount + 1
		intPID   = objItem.ProcessId
	Next
	Set colItems      = Nothing
	Set objWMIService = Nothing
	' Set result to -1 in case of zero or multiple simultaneous instances of this HTA
	If intCount <> 1 Then intPID = -1
	MyPID = intPID
End Function

Note that this function will return -1 if multiple instances of the same HTA are running simultaneously.

Back to the top of this page . . .

 

HTA Quirks and Restrictions

By default, HTAs display webpages in Compatibility View, which displays standards-mode content in IE7 Standards mode and quirks mode content in IE5 (Quirks) mode.
Rounded corners, rotated text, HTML 5's new input types, and other HTML 5 and CSS 3 features are ignored in this default mode.

The solution is to insert the following meta tag in the head of the HTA:

<meta http-equiv="x-ua-compatible" content="ie=9" />

This will enable HTML 5 and CSS 3 support in your HTA (though <input type="number" /> still seems to be ignored).

You may change content="ie=9" to content="ie=10" if you want, higher is not recommended because it introduces new serious challenges and quirks.
Some errors that may occur, and that are easy to prevent:

Warning: Some versions of VBSEdit may sometimes revert your case changes while editing (e.g. Self), so stay alert!

Make sure you also use the proper document type declaration at the top of the HTA:

<!DOCTYPE HTML>

Note, however, that in Windows 7 (and possibly in Windows 8 too) you now need to adjust all event handlers!

E.g. the "old" code:

<input type="button" value="Check" onclick="CheckIfPrime" />

must be changed into the "new" code:

<input type="button" value="Check" onclick="vbscript:CheckIfPrime( )" />

In Windows 10 this is not required (though it won't hurt), unless you use both VBScript and JavaScript.

Back to the top of this page . . .

 

VBSEdit Limitations

Testing your HTAs in VBSEdit is great, much faster than having to switch from the editor to a command line or Windows Explorer and vice versa.
Keep in mind, though, that when you click the "Run" button (or hit F5), VBSEdit will open and run a copy of the HTA in a different location!

Though the order of your HTA's subroutines is not critical to run it, VBSEdit's debugger treats them rather "linearly", e.g. if a global variable/object is set in line 100 in the Window_OnLoad subroutine, and referred to in another subroutine in line 50, VBSEdit's debugger will consider the variable/object not set.
So, for debugging purposes, you may want to change the order of the subroutines, as explained in the next paragraph.

Compatibility mode is great if you want to use rounded corners, rotated text and many other CSS 3 features, but it may make debugging a challenge in VBSEdit.
Compare the error messages when clicking the "Test" button in the following demo HTA, for quirks mode and for compatibility mode.

 

Quirks Mode (disabled meta tag in line 4)

  1. <!DOCTYPE HTML>
  2. <html lang="en">
  3. <head>
  4. <!--<meta http-equiv="x-ua-compatible" content="ie=9" />-->
  5. <title>My HTML application</title>
  6. <HTA:APPLICATION
  7.   APPLICATIONNAME="My HTML application"
  8.   ID="MyHTMLapplication"
  9.   VERSION="1.0"
  10. />
  11. </head>
  12. <script language="VBScript">
  1. Sub TestDebugging( )
  2. 	document.getElementById( "DoesNotExist" ).value = "Error"
  3. End Sub
  1. </script>
  2.  
  3. <body>
  4. <input type="button" value="Test" onclick="vbscript:TestDebugging( )" />
  5. </body>
  6. </html>

 

The following error message appears when the "Test" button is clicked:

The error message points out the exact line where the error occurred.

 

Compatibility Mode (enabled meta tag in line 4)

  1. <!DOCTYPE HTML>
  2. <html lang="en">
  3. <head>
  4. <meta http-equiv="x-ua-compatible" content="ie=9" />
  5. <title>My HTML application</title>
  6. <HTA:APPLICATION
  7.   APPLICATIONNAME="My HTML application"
  8.   ID="MyHTMLapplication"
  9.   VERSION="1.0"
  10. />
  11. </head>
  12. <script language="VBScript">
  1. Sub TestDebugging( )
  2. 	document.getElementById( "DoesNotExist" ).value = "Error"
  3. End Sub
  1. </script>
  2.  
  3. <body>
  4. <input type="button" value="Test" onclick="vbscript:TestDebugging( )" />
  5. </body>
  6. </html>

 

The following error message appears when the "Test" button is clicked:

Now the error message points to the line calling the subroutine that contains the error!
This gets even worse when nesting subroutines...

The work-around for this limitation is to temporarily disable compatibility mode until all VBScript code has been debugged.
Unfortunately, after enabling compatibility mode, you still have to debug all code once more, to test for compatibility issues...

Back to the top of this page . . .

 

Debugging

Editors like VBSEdit make debugging HTAs easier, but not quite as easy as "normal" VBScript code.

Some tips:

Back to the top of this page . . .

 

Clean Up Your Code

More, even, then "normal" VBScript code, HTA code can have some stray code left after making changes: undeclared new variables, unused variables, or even entire unused subroutines.
This will increase maintenance complexity of your HTA.

Using an editor like VBSEdit you will get warnings when trying to use undeclared variables, including typos in variable names (assuming Option Explicit is your first line of code in the VBScript block, which is highly recommended).

Use my CheckVarsVBS.exe to check for unused variables and subroutines.

Back to the top of this page . . .

 

Back to the top of this page . . .

 


page last modified: 2023-03-12; loaded in 0.0339 seconds