VBScript Scripting Techniques > 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.
Microsoft's Visual Studio 2010 Express Edition installer is an example of a great looking HTA.
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)'" /> |
Back to the top of this page . . .
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 . . .
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 . . .
Using local variables inside subroutines is a piece of cake.
Global variables in HTAs, on the other hand, require some extra attention.
Dim
statements) and all constants (Const
statements).strVar
for strings, objVar
for objects, arrVar
for arrays...gv
for global variables, e.g. gvsVar
for strings, gvoVar
for objects, gvaVar
for arrays...Dim gvoFSO Set gvoFSO = CreateObject( "Scripting.FileSystemObject" )
window_onload
(or any other) subroutine.window_onload
subroutine, which is executed after the entire HTA window is loaded.window_onload
subroutine.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). |
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.
body {
background-color: #fdfeff;
color: darkblue;
font-family: Calibri;
font-size: 12pt;
margin: 4em 3em;
}
Option Explicit
Sub CheckIfPrime( )
Dim i, intInput
intInput = document.getElementById( "InputNumber" ).value
If intInput < 3 Then
document.getElementById( "OutputResult" ).innerHTML = "Yes, " & intInput & " is a prime number."
Else
For i = 2 To intInput - 1
If intInput Mod i = 0 Then
document.getElementById( "OutputResult" ).innerHTML = "No, " & intInput & " is not a prime number."
Exit Sub
End If
Next
document.getElementById( "OutputResult" ).innerHTML = "Yes, " & intInput & " is a prime number."
End If
End Sub
Sub ValidateInput( )
Dim objRE, strInput
strInput = document.getElementById( "InputNumber" ).value
Set objRE = New RegExp
objRE.Global = True
objRE.Pattern = "[^\d]+"
If objRE.Test( strInput ) Then
strInput = objRE.Replace( strInput, "" )
document.getElementById( "InputNumber" ).value = strInput
document.getElementById( "OutputResult" ).innerHTML = "Enter a number, and click the ""Check"" button to check if it is a prime number."
End If
If strInput = "" Then
document.getElementById( "OutputResult" ).innerHTML = "Enter a number, and click the ""Check"" button to check if it is a prime number."
End If
Set objRE = Nothing
End Sub
Sub Window_OnLoad
window.resizeTo 640, 480
document.title = document.title & ", Version " & MyFirstHTA.Version
End Sub
</script>
<body>
<p><input type="text" id="InputNumber" onchange="ValidateInput" onkeyup="ValidateInput" />
<input type="button" value="Check" onclick="CheckIfPrime" /></p>
<p> </p>
<p id="OutputResult">Enter a number, and click the "Check" button to find out if it is a prime number.</p>
</body>
</html>
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:
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.CheckIfPrime
subroutine when clicked.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 . . .
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 . . .
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 . . .
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 . . .
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 . . .
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 . . .
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 . . .
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 . . .
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 . . .
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:
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 . . .
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 . . .
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 . . .
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:
window_onload
is not exetuted: with content="ie=11"
or content="ie=edge"
, VBScript code no longer seems to be recognized; either revert to content="ie=9"
or convert all code to JavaScriptObject doesn't support this property or method
for HTAID.version
: revert to content="ie=9"
'Window_OnLoad' is not defined
: rename your subroutines Window_OnLoad
and Window_OnUnload
to window_onload
and window_onunload
respectively (lower case)'Self' is not defined
: when called directly in event handlers (e.g. onclick
) make sure to use self
in lower case (e.g. onclick="self.close( )"
)not defined
errors in subroutines called by event handlers: make sure the case of the subroutine's name matches, and that the language is defined, and add parentheses (e.g. onclick="vbscript:MySubroutine( )"
)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 . . .
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)
Sub TestDebugging( )
document.getElementById( "DoesNotExist" ).value = "Error"
End Sub
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)
Sub TestDebugging( )
document.getElementById( "DoesNotExist" ).value = "Error"
End Sub
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 . . .
Editors like VBSEdit make debugging HTAs easier, but not quite as easy as "normal" VBScript code.
Some tips:
window_onload
subroutine, and clear objects in the window_onunload
subroutine.window_onload
subroutine just below the global declarations, the window_onunload
subroutine at the end of the code block, and all other subroutines in between.Set object = Nothing
.Back to the top of this page . . .
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.0136 seconds