HTML Applications (HTAs) provide a way for you to wrap your scripts up in a graphical user interface, an interface replete with check boxes, radio buttons, drop-down lists and all those other Windows elements you’ve grown to love. In this article, the first of a multi-part series, I’ll introduce the basics behind creating your very own HTA.
Creating Your Own HTAs
You might have noticed that you can’t buy MS-DOS any more, and for good reason: after people got a look at Windows, well, typing in long, cryptic commands from the C:\ prompt just didn’t seem like nearly as much fun. The command prompt still has its place, but the success of Windows (and the Macintosh, and all the new Windows-like Unix shells) suggests that most people prefer a graphical user interface.
Because of that you might expect the Microsoft scripting technologies – such as Windows Script Host (WSH) and VBScript – to have some of the coolest and fanciest graphical user widgets around. Oddly enough, though, that isn’t the case. Outside of a message box here and an input box there, neither WSH nor VBScript offers much in the way of GUI elements. Using WSH and/or VBScript, can you create a form that enables you to select computer names from a list box? No. Can you create a form that allows people to select options from a series of check boxes? No. Can you – sorry; no. If you want a graphical user interface, you’re simply going to have to give up scripting and turn to full-fledged, full-time programming.
Or are you? Let’s think about this for a second: why do people like scripting in the first place? Well, scripting enables you to perform useful tasks quickly and easily, without having to compile anything and without having to use anything more powerful (or expensive) than Notepad. Suppose I told you that you could create a useful utility, like the following, quickly and easily, without having to compile anything and without having to use anything more powerful (or expensive) than Notepad:
Would that satisfy your craving for a graphical user interface? If so, then welcome to the world of HTAs.
What is HTA?
The term HTA is actually an acronym: it’s short for HTML Application. For my purposes an HTA is nothing more than a way to provide a graphical user interface for your scripts. As I’ve already noted, neither WSH nor VBScript provide much in the way of graphical user interface elements: no check boxes, no list boxes, no nothing. Internet Explorer, however, makes use of all of these elements – and more. Because an HTA leverages Internet Explorer, you can take advantage of all these graphical user interface elements when writing system administration scripts. (And, yes, I am going to show you how to do just that.)
How closely related are HTML files and HTAs? Well, take any HTML file and change the file extension from .htm (or .html) to .hta. Congratulations: you’ve just created your first HTA.
So why not just use HTML?
There’s a very simple answer to that: security. There are a lot of security restrictions placed on Internet Explorer, and for good reason: if you visit a Web site you’d probably prefer that this Web site not use a client-side script that starts reconfiguring your settings or rooting around through your file system. Consequently many system administration scripts – including those that use WMI or ADSI – either will fail when run from Internet Explorer or, at best, will present you with a dialog box similar to this one:
Any time you run your script from an HTML file you’ll be presented with a dialog box like this. That might be OK, but it’s definitely not the best possible user experience.
HTAs, by contrast, are not bound by the same security restrictions as Internet Explorer; that’s because HTAs run in a different process than Internet Explorer. (HTAs run in the Mshta.exe process rather than the Iexplore.exe process.) Unlike HTML pages, HTAs can run client-side scripts and they have access to the file system. Among other things, this means that HTAs can run your system administration scripts, including those that use WMI and ADSI. Your scripts will run just fine, and you won’t receive any warnings about items that might be unsafe.
Of course, this doesn’t mean that HTAs somehow bypass Windows security. For example, if you don’t have the right to change a user’s password then you can’t use a script to change a user’s password. Placing that script in an HTA won’t make a difference: you still won’t be able to change the user’s password. And HTAs do have some security restrictions of their own. I don’t want to get too bogged down in the details today, so let’s leave it at this:
- Although HTAs use Internet Explorer and the Internet Explorer object model, they run in a different process than Internet Explorer. Consequently, they can run scripts and perform other tasks that aren’t allowed in Internet Explorer.
- If you’d like more information about HTAs and security, take a look at the HTML Applications SDK on MSDN.
How do you create one of these HTA things?
Boy, is this a coincidence or what: this article just happens to be the first in a series of tutorials that will teach you – that’s right – how to create your own HTA! (What do you suppose the odds were of that happening?) This month I’ll introduce the basics of HTA construction. In subsequent articles I’ll begin delving into more advanced topics. Should be fun, huh?
Note. What do you mean, “A series of monthly articles takes too long; I want to master HTAs right now!” Well, that might be a bit of a problem. The truth is, there aren’t very many good resources out there for the beginning HTA writer. However, you might check out the HTA Helpomatic tool, which includes a number of code snippets that you can paste into your HTA. |
Speaking of basics, I should probably mention right off the bat that HTAs can be created using Notepad or any other text editor. However, because of the heavy-duty use of HTML you might consider creating your HTAs in a Web page editor such as Microsoft FrontPage. Regardless, you create a plain text file and then save it with a .HTA file extension. A very rudimentary – but complete – HTA might look something like this:
<head> <title>HTA Test</title> <HTA:APPLICATION APPLICATIONNAME="HTA Test" SCROLL="yes" SINGLEINSTANCE="yes" WINDOWSTATE="maximize" > </head> <script language="VBScript"> Sub TestSub Msgbox "Testing 1-2-3." End Sub </script> <body> <input type="button" value="Run Script" name="run_button" onClick="TestSub"><p> </body>
Don’t worry; it’s nowhere near as complicated as it might first appear. In fact, it’s possible to break an HTA down into three basic parts, each of which I’ll explain in some detail:
- The <HTA:Application> tag
- The </script> tag
- The <body> tag
The <HTA:Application> Tag
You can actually create an HTA without using the <HTA:Application> tag; your HTA will run just fine and there will be no change in functionality or performance.
So if you don’t have to use the <HTA:Application> tag then why would you ever want to use it? Well, the <HTA:Application> tag provides you with the ability to control the appearance of your HTA window; this is done by configuring the various tag properties. Here’s the <HTA:Application> tag used in my sample script:
<head> <title>HTA Test</title> <HTA:APPLICATION APPLICATIONNAME="HTA Test" SCROLL="yes" SINGLEINSTANCE="yes" WINDOWSTATE="maximize" > </head>
Note. The <title> tag is actually separate from the <HTA:Application> tag; it simply refers to text that appears in the title bar of the HTA window. I show it here simply because the title is usually found inside the <head> tag, the same place I find <HTA:Application>. |
What does this tag do? Well, the ApplicationName property sets the name of the HTA as shown in Windows Task Manager. If you don’t specify an application name, Task Manager displays the path to the HTA file. Setting the Scroll property to Yes causes scroll bars to automatically appear in the HTA if returned data is bigger than the window size; if set to No then only the data that actually fits in the window will be displayed. The SingleInstance property ensures that only one instance of your HTA will run at a time; clicking the HTA file icon will not start a second instance of your little application.
And let’s not forget WindowState: setting the WindowState to maximize causes the HTA to display in a full-sized window each time the HTA starts up.
Some of the more useful HTA Application properties are shown in the following table. For a complete list of properties see the HTML Applications SDK on MSDN.
Property | Description |
ApplicationName | Sets the name of the HTA. This name is used to enforce the SingleInstance property, and is also the name that appears in Windows Task Manager. |
Border | Sets the type of border used for the HTA window. Values include thick (for a resizable window) and thin (for a non-resizable window). |
Caption | Yes/No value specifying whether the HTA displays a caption in the title bar. The default value is Yes. |
MaximizeButton | Yes/No value specifying whether the HTA displays a Maximize button in the title bar. The default value is Yes. |
MinimizeButton | Yes/No value specifying whether the HTA displays a Minimize button in the title bar. The default value is Yes. |
Scroll | Yes/No value specifying whether the HTA will scroll if returned data is larger than the window size. The default value is Yes. |
ShowInTaskbar | Yes/No value specifying whether the HTA is shown in the Windows taskbar. The default value is Yes. |
SingleInstance | Yes/No value specifying whether more than one instance of this HTA can be active at a time. For this property to take effect, you must also specify an ApplicationName. The default value is Yes. |
SysMenu | Yes/No value specifying whether the HTA displays the System menu in the title bar. The default value is Yes. |
WindowState | Sets the initial size of the HTA window. Values are Normal, Minimize, and Maximize. By default the WindowState is set to Normal. |
The following illustration maps some of these properties to an actual HTA window:
Example
The <script> Tag
Although there are exceptions, many VBScript scripts (that is, .vbs files) do their thing and then get out of there; probably 99% of the scripts found in the Script Center Script Repository do just that. What do I mean when I say these scripts “do their thing and then get out of there?” Well, when you start most VBScript scripts the script host executes line 1, then – without missing a beat – executes line 2, then line 3, and so on. After the last line in the script has been executed, the script ends, the script host process terminates, and the party’s over.
By contrast, HTAs usually don’t work that way; that’s because HTAs are “event driven” applications. In general, when you start an HTA nothing happens; instead the HTA just sits there. In fact, the HTA will sit there forever until an event occurs that the HTA has been programmed to respond to. Typically that means someone clicked a button, selected an item from a drop-down list, or did something similar.
Note. Could you create an HTA that automatically does something the moment you start it? Sure: just include your code inside a subroutine named Window_onLoad. As the name implies, that subroutine automatically runs any time the HTA window is loaded or refreshed. For example, this code automatically calls the TestSub subroutine each time you start your HTA:
Sub Window_onLoad TestSub End Sub |
In other words, instead of being one big, long, continuous script designed to run from start to finish, an HTA is often composed of separate and distinct smaller scripts: there’s one script that runs any time a user clicks Button A, another script that runs any time a user clicks Button B, and so on. Does this have implications for the scripts I want to use in my HTA? You bet it does.
In fact, there are at least two implications. To begin with, all these separate and distinct scripts must be housed inside a single <script> tag; this, by the way, is standard HTML practice. Second, each “script” is actually a subroutine, one that needs to have a distinct name. (You can name a subroutine anything you want, as long as there are no spaces or other illegal characters in the name. For more information, see the Procedures section of the Microsoft Windows 2000 Scripting Guide.) From now on I’ll use the term subroutine to refer to these blocks of code; that should help differentiate the code I want to run from the <script> tag.
Here’s an example of a <script> tag that includes a single subroutine: TestSub. In a little bit I’ll explain how to add additional subroutines to this tag.
<script language="VBScript"> Sub TestSub Msgbox "Test" End Sub </script>
There’s nothing too complicated here: just remember that you need to place all your code in subroutines and all your subroutines must be inside the <script> tag.
OK, I Give Up. So if HTAs don’t run and then automatically terminate, how do you exit an HTA? Well, the easiest way is probably just to click the window’s Close button. Of course, it’s possible to create an HTA that doesn’t have a Close button; for an example, see this article in the ABCs of HTAs. In a case like that you can use a subroutine like this to exit an HTA:
Sub ExitHTA self.close() End Sub |
The <body> Tag
If you’ve ever put together a Web page you know that the body of the document is where most of your “stuff” goes: your text, your tables, your buttons, your check boxes, your horizontal rules, etc. The same thing is true with an HTA. When you build an HTA you typically put the <HTA:Application> tag at the top followed by the <script> tag. Last – but definitely not least – comes the <body> tag. Inside the body tag will be the elements that you want to appear in your HTA’s user interface. In my sample HTA I have a very simple user interface; it consists of a single button labeled Run Script. As you can see, the HTML tagging for the button is included between the <body> and </body> tags:
<body> <input type="button" value="Run Script" name="run_button" onClick="TestSub"><p> </body>
To be honest, the <body> tag is kind of boring; what’s interesting are the elements that go inside that tag. In this first article I’ll focus on two of these elements: buttons and spans. In subsequent articles I’ll cover additional things like check boxes, radio buttons, and list boxes.
Your very first HTA
Here’s a sample HTA that will tell you the version of the operating system installed on the local computer. To try this HTA out, copy the code, paste it into Notepad, and the save the thing with a .hta file extension (for example, MyFirstHTA.hta). Then just double-click the file icon in My Computer or Windows Explorer and get ready for some excitement:
<head> <title>Operating System Version</title> <HTA:APPLICATION APPLICATIONNAME="Operating System Version" SCROLL="yes" SINGLEINSTANCE="yes" > </head> <script language="VBScript"> Sub GetOSVersion strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems Msgbox objOperatingSystem.Caption & " " & _ objOperatingSystem.Version Next End Sub </script> <body> <input type="button" value="Operating System" name="run_button" onClick="GetOSVersion"> </body>
So what happens when you start the HTA and click the Operating System button? You should see a message box similar to this:
Yes, I know; but you have to start somewhere, right? I’ll add a few additional features as time goes on. And don’t worry if you don’t understand what I did here. In just a moment, I’ll start walking you through all the relevant lines of code. But first, a quick note about HTA aesthetics.
Making HTA Better
I admit it: at the moment, my HTA has an appearance that only a mother could love. Can I make it a little nicer, but without having to work too terribly hard at that? Sure I can; after all, the body of the HTA is – at heart – just another HTML page. Therefore I can try making my HTA better by beefing up the <body> tag. For example, how about a nice cyan background:
That’s right: eat your heart out, Paris Hilton. I can change the background color of my HTA just by adding a bgcolor attribute to the <body> tag. For example, this sets the background color to that breathtaking shade of cyan shown above:
<body bgcolor="cyan">
There are plenty of other colors you can use besides cyan; for a list, take a look at the Internet Explorer documentation.
If you have a picture you find suitably attractive you can alternatively set the background attribute to the name of that picture:
<body background = "keyboard.jpg">
Depending on what Keyboard.jpg looks like you’ll end up with something like this:
Cool, huh? Keep in mind that depending on the color of your background you might also need to change the color of your fonts, something I’ll deal with later. And as long as I am on the subject you might want to check out the gradient background explained in the ABCs of HTAs. That’s way cool, and has the advantage of not requiring a .JPG or other picture file.
Adding a Button
One feature almost every HTA ends up having is a good old-fashioned button. Your HTA might have fancy check boxes and list boxes and other items for configuring options and setting preferences, but at some point you’ll need to do something to tell the HTA to get to work. More often than not that will involve clicking a button.
Adding a button to an HTA is no different than adding a button to an HTML page. For example, this code adds a button labeled Operating System to my HTA:
<input type="button" value="Operating System" name="run_button" onClick="GetOSVersion">
As you can see, I use the <input> tag along with a number of attributes. Let’s take a quick look at those attributes and what they do:
Attribute | Description |
type | This specifies the type of <input> element to be placed on the page; as I’ll see next month the <input> tag can take on forms other than a button. |
value | “Value” is a misleading term; what I am really talking about here is the label that appears on the button. In other words, in this example I am creating a button that has the label Operating System. |
name | As you might expect, this is a unique name given to the button. Why give your button a name? Well, you don’t have to; you can leave this attribute out if you wish. However, if your buttons do have names then they can be configured and modified using scripts. For example, you can enable or disable buttons programmatically.I am getting a bit ahead of myself here, but this code disables a button named Run_Button:
Sub DisableButton Run_Button.Disabled = TRUE End Sub |
onClick | This is the name of the subroutine I want to run any time the button is clicked. This is optional; you can create buttons that don’t actually do anything when clicked. |
Incidentally, these are not the only attributes that can be configured for a button. For more information see the HTML and DHTML Reference on MSDN.
Try It Yourself. At the moment I have an HTA with a single button; click that button and the HTA will report back the version of Windows installed on the local machine. That’s probably not going to revolutionize the software industry, but it’s still a useful little utility. Now, suppose I also want to retrieve the version of the latest service pack installed on the computer. Furthermore, suppose I want this to be a separate script; that is, I want to be able to get the operating system version or the service pack version. How can I do that?
Well, I could create a second HTA, one that had a single button and performed a single task (retrieving the service pack version). Alternatively, I could simply add a second button to my existing HTA, a button with the name service_pack and the label (value) Service Pack, which runs a subroutine named GetServicePack any time the button is clicked. (And, yes, I know that this subroutine doesn’t exist. But I’ll take care of that soon enough.) So here’s your assignment: see if you can add a second button to your HTA, and end up with something that looks like this:
Adding a Subroutine
Early in this article I showed you a very simplistic subroutine; now let’s take a look at a more practical example, the subroutine I used in my Operating System HTA. Note that the code inside the subroutine is standard WMI code; with a few exceptions involving Windows Script Host (something I’ll discuss in a moment) almost any code that runs in a standalone .vbs script can run in an HTA. You might want to (or have to) make some changes to ensure that the output displays properly, but the basic code functions – connecting to the WMI service, running the ExecQuery method, looping through a collection of returned data – can be left as-is.
Here’s the sample subroutine. Note that – like all subroutines – it’s housed between the <script> and </script> tags:
<script language="VBScript"> Sub GetOSVersion strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems Msgbox objOperatingSystem.Caption & " " & _ objOperatingSystem.Version Next End Sub </script>
What if I wanted to have a second subroutine, one that runs when I click a different button? That’s no problem; you can include as many subroutines as you want inside the <script> tag. Just make sure that you give each subroutine a unique name, and be sure and use the End Sub statement to mark the end of each subroutine. For example, here’s a very simple subroutine (named MySecondRoutine) that I’ve added to the <script> tag:
<script language="VBScript"> Sub GetOSVersion strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems Msgbox objOperatingSystem.Caption & " " & _ objOperatingSystem.Version Next End Sub Sub MySecondRoutine Msgbox "This is my second subroutine." End Sub </script>
Try It Yourself. If you did the previous Try It Yourself exercise you have a second button in your HTA interface, one that – in theory – retrieves the version of the latest service pack installed on the computer. Why do I say “in theory?” Because, at the moment, I have a button, but that button isn’t tied to a script that can retrieve the service pack version. So let’s see what I can do about that. Here’s a script that reports back the version of the latest service pack installed on the computer:
strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems Msgbox objOperatingSystem.ServicePackMajorVersion _ & "." & objOperatingSystem.ServicePackMinorVersion Next
See if you can take this code and add a subroutine named GetServicePack to your HTA. After you’ve added the subroutine, start the HTA, click the Service Pack button and verify that your new subroutine works.
Why Can’t I Use Wscript.Echo?
You might have noticed that when it came time to report back the operating system version I used the VBScript Msgbox function rather than the more common Wscript.Echo. Why didn’t I use Wscript.Echo? Here’s why:
As it turns out the various Wscript methods – Wscript.Echo, Wscript.Sleep, Wscript.Quit, etc. – are designed solely to run under the Windows Script Host environment. When I am working in an HTA I am not running under WSH; instead I am running under the MSHTA process. Because of that the Wscript methods are not available to us (nor can I create them). Consequently I need to find workarounds for each method, and Msgbox is a perfectly adequate replacement for Wscript.Echo. (I’ll talk about workarounds for other methods – such as Wscript.Sleep – when I get to them.)
The moral of the story: Don’t bother with Wscript.Echo; it won’t work.
Using a <span> Tag
So far my HTA works great: you click a button and you get back a message box with information about the operating system or the latest service pack installed on a computer. Very cool. Now, suppose I want to add the ability to retrieve a list of all the processes running on a computer. That’s easy enough, except for one thing: right now the computer being used to type this tutorial has 49 processes running on it. That’s a problem; who wants to respond to 49 message boxes, one for each process?
I am not silly enough to say “nobody,” but I’ll go out on a limb and say that most people would rather not click OK 49 times. (47 or 48 maybe, but not 49.) So let’s see. I need to display data somewhere other than in a message box, and I have an HTA that has plenty of white space just sitting there doing nothing. Hmmm, that gives us an idea….
Did someone say, “Why don’t I just display the data in the HTA itself?” Good idea; in fact, way better than the idea we came up with. This is one of the real benefits of using an HTA: not only can you display data in your application itself but – as I’ll see – you can also display formatted data in that HTA. But how exactly do I do that?
Actually there are several different ways. The one I find the easiest to code involves the use of the <span> tag. The <span> tag enables you to name an area within your HTA and then programmatically manipulate all the HTML elements within that tag. What does that mean? Well, save the following code as a .HTA file, run the thing, and then click the Operating System button:
<head> <title>HTA Test</title> <HTA:APPLICATION APPLICATIONNAME="HTA Test" SCROLL="yes" SINGLEINSTANCE="yes" > </head> <script language="VBScript"> Sub GetOSVersion strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems DataArea.InnerHTML = objOperatingSystem.Caption & " " & _ objOperatingSystem.Version Next End Sub </script> <body> <input type="button" value="Operating System" name="run_button" onClick="GetOSVersion"> <p> <span id = "DataArea"></span> </body>
Your screen should look something like this:
See? No message box; instead my data is written to the HTA itself.
To accomplish this astonishing feat I didn’t have to sell my souls, I only had to do two things. First I modified the <body> area to include a <span> (and specified an id of DataArea). my revised <body> area looks like this:
<body> <input type="button" value="Operating System" name="run_button" onClick="GetOSVersion"> <p> <span id = "DataArea"></span> </body>
I still have my button, but now I’ve added two more items to the body. First, I added a <p> tag; this is just a paragraph tag, something I added to put a little bit of space between my button and the data to be displayed. Second I added a <span> tag, setting the id to DataArea; that’s what I do here:
<span id = "DataArea"></span>
Notice that my <span> is empty; there’s nothing between the starting and closing tags. If I wanted to, I could put something there to start with. For example, this revised tag has some instructions to the user:
<span id = "DataArea">Click the button to return information about the operating system.</span>
When I click the button this instructional message will disappear and be replaced by the information returned from the GetOSVersion subroutine.
So how exactly do I accomplish all this? If you recall, in the first incarnation of my HTA I retrieved the operating system version and then displayed that information using the Msgbox function. The code for doing that looked like this:
Msgbox objOperatingSystem.Caption & " " & _ objOperatingSystem.Version
Now take a look at my revised code:
DataArea.InnerHTML = objOperatingSystem.Caption & " " & _ objOperatingSystem.Version
What I’ve done here is replace Msgbox with this:
DataArea.InnerHTML =
InnerHTML is a property of the <span> tag; it simply refers to everything contained inside the <span> and </span> tags, including text, HTML formatting, and other HTML tags. All I do here is set the InnerHTML property of the span named DataArea to the operating system version. What if, for some unknown reason, I wanted to set the value of InnerHTML to “No information available.”? Then we’d use code like this:
DataArea.InnerHTML = "No information available."
Each time I change the value of the InnerHTML property the old value is deleted and the new value takes its place.
Try It Yourself. If you’ve been doing the Try It Yourself exercises then you should have an HTA that includes a button named Service Pack; when that button is clicked it runs a subroutine named GetServicePack. At the moment, GetServicePack displays the returned information in a message box. See if you can modify the body of the HTA and the GetServicePack subroutine to write the returned information to the HTA itself (like, say, maybe in a <span>….).
Did Someone Say Formatting?
Ah, yes. Those of you used to writing scripts that run in a command window might have forgotten that fonts can actually come in different sizes, styles, and colors. I won’t spend a lot of time talking about formatting today; suffice to say that any formatting that can be applied using HTML tags can be included within the InnerHTML property. For example, suppose you were creating a regular Web page and wanted some of the text displayed in red Arial. How would you do that? Well, you’d likely do something like this:
<font color='red' face='Arial'>No information available.</font>
As you can see, I enclosed the text itself inside the <font> and </font> tags. As part of the <font> tag, I assigned values to the color and face attributes. That’s it; it’s that easy.
So how would I specify this same formatting when assigning a value to the InnerHTML property? Why we’d just use the exact same syntax:
DataArea.InnerHTML = "<font color='red' face='Arial'>No information available.</font>"
Still not satisfied? Would you like this text to be boldface as well? Then just add the <b> and </b> tags:
DataArea.InnerHTML = "<b><font color='red' face='Arial'>No information available.</font></b>"
If you want to change the size or make the text blink (um, not recommended) or do any of the other things you can do with text then just add the appropriate HTML tags. For more information about the formats that can be applied to HTML elements take a look at the HTML and DHTML Reference on MSDN.
Displaying Multi-Line Data
This isn’t particularly hard, but it is a little bit tricky. Why? Well, suppose I use a subroutine similar to this:
Sub GetProcesses strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process") For Each objProcess in colProcesses DataArea.InnerHTML = objProcess.Name Next End Sub
OK, that looks pretty good, doesn’t it? Yes, it does; unfortunately, it doesn’t work. If you run this subroutine you’ll have only one process displayed in your HTA. What happened to the other 48 processes?
What happened is that I goofed when I set up the For Each loop that cycles through the collection of processes. Take a close look at what happens inside that loop:
For Each objProcess in colProcesses DataArea.InnerHTML = objProcess.Name Next
Each time I encounter a new process I set the InnerHTML to the name of that process. Remember what I said about InnerHTML: each time I assign InnerHTML a new value the old value – that is, whatever used to be between the <span> and </span> tags – disappears. That’s exactly what’s happening here. I write the name of the first process to InnerHTML, then loop around, get the name of the second process, and write that value to InnerHTML. That’s fine, except that writing a new value erases the previous value. This keeps going until I write the name of the last process in the collection, which – because I don’t overwrite it – ends up being the only item displayed.
No, not good at all. But that’s OK; this is easy enough to fix. The trick here is not to write to the InnerHTML property each time I get the name of a process. Instead what I want to do is save up the names of all those processes and – as soon as I have the entire collection – write the whole kit-and-kaboodle to the InnerHTML property. Let’s show you a revised subroutine, then explain what I did different. (Notice, if you’re cutting and pasting as I go, the name of this new subroutine. You’ll need to change your onClick process to match. It would also make sense to change the name of the button to something like “Processes.”)
Sub GetProcesses 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 DataArea.InnerHTML = strHTML End Sub
There are two major differences here. To begin with, I’ve radically revised my For Each loop:
For Each objProcess in colProcesses strHTML = strHTML & objProcess.Name & "<br>" Next
Notice that I no longer set the value of the InnerHTML property. Instead each time I loop through the collection of processes I assign a variable named strHTML the following:
- The existing value of strHTML
- The name of the process
- The HTML tag <br>
Why? Well, suppose the first time I run through the loop I encounter a process named winword.exe; consequently strHTML will equal this:
winword.exe<br>
How did I get that? This is just the value of strHTML (which, the first time through the loop, is equal to nothing) plus the name of the process (winword.exe) plus the <br> tag. I now loop through a second time and encounter a process named outlook.exe. my variable strHTML now equals this:
winword.exe<br>outlook.exe<br>
Again, I simply took the existing value of strHTML – winword.exe<br> – and tacked on the name of the next process – outlook.exe – and another <br> tag. I keep repeating this process until I have looped through the entire collection.
Only then do I actually assign a value to the InnerHTML property. And this time around I assign InnerHTML the value of the variable strHTML, which just happens to contain a list of all the process names. Here’s the code that takes care of that:
DataArea.InnerHTML = strHTML
When I run the HTA and click the Processes button I get back something like this:
That’s more like it.
Why All the <br> Tags?
Good question. Remember that I am dealing with HTML here and in HTML the <br> tag represents a carriage return-linefeed: it’s like being in Notepad and pressing the ENTER key. If I want an item (such as a process name) to start on the next line I need to include a <br> tag; it’s as simple as that. Suppose I left out the <br> tag, and had a subroutine similar to this:
Sub GetProcesses 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 Next DataArea.InnerHTML = strHTML End Sub
Here’s what we’d get when I ran the subroutine:
Instead of having a nice list of processes – one process per line – I have a huge single line of processes, all glommed together (there isn’t even a space to separate the individual process names). That’s because I need to manually enter line breaks, which is why I need all those <br> tags.
Yeah, that was a pretty dramatic demonstration, wasn’t it?
Try It Yourself. Thus far I’ve simply echoed one property value – Name – for each process running on a computer. See if you can modify the subroutine so that it echoes an additional property value – ProcessID – on the same line. Your finished HTA should look something like this:
Displaying Data in a Table
Our preceding HTA (from the last Try It Yourself exercise) displays the value of two different properties. And, to be honest, my output looks fine; with the two dashes it’s not too hard to figure out where one property value ends and another begins. Suppose, however, that I were displaying 4 or 5 values on a single line? Output like this is a bit harder to decipher:
How can I make this output easier to deal with? Well, one way is to display the information as a table. I don’t have time to talk about tables this month; I’ll have to save that for another time. Just to whet your appetite a little, however, here’s a variation on my last Try It Yourself exercise. This subroutine displays both the process name and the process ID; the difference is that it displays this information in a two-column table:
Sub GetProcesses strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & _ "\root\cimv2") Set colProcesses = objWMIService.ExecQuery("Select * From Win32_Process") strHTML = "<table border='1' style='border-collapse: collapse' " & _ "bordercolor='#111111' width='100%' id='Table1' >" For Each objProcess in colProcesses strHTML = strHTML & "<tr>" strHTML = strHTML & "<td width='50%'>" & objProcess.Name & "</td>" strHTML = strHTML & "<td width='50%'>" & objProcess.ProcessID & _ "</td>" Next strHTML = strHTML & "</table>" DataArea.InnerHTML = strHTML End Sub
And here’s what the actual HTA looks like after the subroutine runs:
HTAs for Beginners: Part 2
Hello everyone, and welcome back to HTAs for Beginners. (Thank you, but, please, hold your applause until the end.) In the last episode, I introduced you to HTAs (short for HTML Applications), a way to wrap your scripts in a graphical user interface. In that first article I showed you how to create a very simple HTA, one that – when you clicked a button – went out, grabbed some information about, say, the services installed on a computer, and then displayed that information right in the HTA itself. For many of you, that was a life-changing experience.
Well, OK. But it was an experience.
Our first article gave you enough information to create a rudimentary HTA, and no doubt some of you thought, “Well, now that I’ve learned how to add a button to an HTA I’ve probably learned all there is to know.” On the contrary. Buttons are nice, but they are hardly the end-all and the be-all of HTA development: there are scores of other user interface elements that you can incorporate into an HTA, including list boxes, check boxes, and everyone’s favorite, the radio button. And by astonishing coincidence, in this month’s article (Part 2 in the series) I am going to show you how to add many of those controls to an HTA.
Please, no need to thank us; I am just doing my job.
If you’re looking ahead, this month I’ll go through the basics of adding various HTML controls to an HTA. Having mastered the use of the various controls, next month I’ll start working on practical implementations, and begin building an HTA that does something cool and something useful. (No, I don’t exactly know what that will be. But, hey, I still have a month to figure that out!) After that, well—I’ll just have to see what happens after that.
At any rate, thank you all for coming today. I hope you enjoy the show, and please drive home safely.
A Note About This Month’s Try-It-Yourself Exercises
Throughout this month’s tutorial I’ll periodically stop and give you a chance to test your new-found knowledge by asking you to write an HTA incorporating the current topic (for example, adding a drop-down list box to an HTA). Just to prove that I am not totally heartless, I’ve provided this basic template that can be used in all of the exercises:
<html> <head> <title>HTA Test</title> <HTA:APPLICATION ID="objTest" APPLICATIONNAME="HTA Test" SCROLL="yes" SINGLEINSTANCE="yes" > </head> <SCRIPT LANGUAGE="VBScript"> ' PUT YOUR SUBROUTINES HERE </SCRIPT> <body> ' PUT THE HTML TAGS HERE </body>
To use the template, copy the preceding code and paste it into Notepad (or some other text editor). Replace the line ‘ PLACE YOUR SUBROUTINES HERE with the script code and the line ‘ PUT THE HTML TAGS HERE with your HTML code. Save the whole thing with a .hta file extension, and you’ll have yourself a (keep your fingers crossed) fully-functional HTA.
Adding a Drop-down List Box
There’s definitely a coolness factor to HTAs: after all, an HTA looks way cooler than a script that runs in a command window. On the other hand, looks aren’t everything. In fact, there’s no point in creating an HTA unless the HTA does something a regular script can’t do.
So what can an HTA do that a plain-old .vbs file can’t? Well, suppose you have a number of file servers and you periodically need to run a script to retrieve information from one of those computers. How can you target a single script to just one of your many file servers? Well, one tried-and-true way is to use command-line arguments; thus:
cscript myscript.vbs /server:atl-fs-01
What’s wrong with using command-line arguments? Nothing, absolutely nothing.
Well, OK, you do have to type in the computer name each time. And you do have to type that name in correctly each time. And you do have to memorize all the names of all your file servers. None of that is particularly inviting, but what choice do you have?
Well, how about this for a choice: you take an HTA, add a drop-down list box pre-populated with the names of all your file servers, and then simply select a name from the list any time you want to run a script. No typing (or typing errors) and no memorization of server names required.
In other words, a drop-down list box (like the one shown below) is not only aesthetically-pleasing, but can be a time- and frustration-saver as well.
Yes, I do make a pretty convincing case, don’t we? Of course, I did leave out one minor point: how exactly do you add a drop-down list box to an HTA? Relax; I wouldn’t build your hopes up like that and then let you down. As it turns out, the code for creating a drop-down list box (just like the code for creating all the other controls I’ll show you in this article) is pretty simple and straightforward:
<select size="1" name="OptionChooser" onChange="TestSub"> <option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option> </select>
See, that’s not so scary, is it? This standard HTML code starts off – as all drop-down lists and list boxes do – with a <select> tag. I simply type in a <select> tag along with the following parameters:
- size. This is the number of options that appear onscreen at any one time. Setting the size to 1 gives us a drop-down list. What if I set the size to something other than 1? Well, I don’t want to spoil the surprise, but I’ll talk about that when I talk about list boxes. For now, just remember this: if you want a drop-down list, set the size to 1.
- name. This is the name given to the control, and the name I’ll use in my scripts to refer to that control. For example, to echo the value of my drop-down list (that is, to determine which option was selected) I use this single line of code:
Msgbox OptionChooser.Value
- onChange. This is the event handler for the control. What I am saying here is pretty simple: any time the value of this control changes (that is, any time someone selects a different option) run this subroutine (in this example, the subroutine named TestSub).
I should point out that including an event handler (onChange) as part of the control is purely optional; I don’t have to run a script any time someone makes a selection in the drop-down list. Alternatively, I could have the user make a selection and then click a button in order to run a script; it’s up to you to decide which approach you like the best. I should note, however, that there’s actually a problem with the control as currently configured, something I’ll discuss in more detail – and resolve – at the end of this section.
After the <select> tag, I stuff in three more lines of code, then add a closing </select> tag to indicate that I am done configuring this particular control. There you go: your very first drop-down list box.
Oh, good point: what about that stuff between <select> and </select>?
<option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option>
As you probably figured out (the tag name <option> is a dead giveaway, isn’t it?) these are the list options, the actual items that appear when you click the drop-down list. Each individual item in the list consists of the following pieces:
- The <option> tag, with a unique value parameter. The value parameter can be anything you want, though the values should be unique. And while the values can exactly match the displayed text, they don’t have to; in my preceding example, the first option has a value of 1 but the displayed text is Option 1. When I use a subroutine to determine which option was selected, I will typically work with the value of the option.
- The displayed text. Anything that lies between the <option> tag and the </option> tag will be displayed as-is in the drop-down list. In the example my first option shows up as Option 1 in the drop-down list. What if I wanted it to show up as Ping remote computer? Then we’d configure the option like this:
<option value="1">Ping remote computer</option>
Note that I don’t need to change the value; as I said earlier, values can be anything I want them to be.
- The closing </option> tag. Simply so I know where one option leaves off and the next one begins.
You might have noticed that I don’t give the individual options their own names. Why? Because I have no reason to name individual options. Instead, when you select an option the value of the selected option gets passed to the <select> object. This allows us to determine which option was selected by examining the value of the <select> object. (In case you’re wondering, that’s why option values need to be unique.) For example, here’s a simple little subroutine that reports back the value of the selected option:
Sub TestSub Msgbox "You selected Option " & OptionChooser.Value & "." End Sub
See? All I do is report back the value of the drop-down list itself (OptionChooser), which will just happen to be exactly the same as the value of the selected option. (Keep in mind that the value is going to be 1, 2, or 3, because those are the values I assigned the individual options.)
So What’s Wrong with the Control as Configured?
I showed you a simplistic version of the drop-down list because I wanted you to see how easy it is to implement HTML controls in an HTA; consequently, I wanted to keep the code as short and concise as I could. However, there is a problem with the control and the onChange event. When you load the HTA, Option 1 will automatically be selected. That’s fine, but suppose you want to run the script against Option 1; how do you do that? Believe it or not, it’s tougher than you think. After all, you can’t select the option; it already is selected. Because of that, clicking the option won’t trigger the onChange event; onChange won’t get triggered because nothing has changed. In fact, as configured, the only way to select Option 1 is to first select, say, Option 2 (which causes the script to run against that option) and then select Option 1.
But don’t worry, there’s an easy way to fix this. All I need to do is add a blank option at the top, using code similar to this:
<select size="1" name="OptionChooser" onChange="TestSub"> <option value="0"></option> <option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option> </select>
Notice that my first option – with the value 0 – has no displayed text; it will appear blank in the drop-down list. I then need to modify my subroutine to ensure that the script runs only if the value of the selected option does not equal 0. In other words, the script will not run if you select the blank option:
Sub TestSub If OptionChooser.Value <> 0 Then Msgbox "You selected Option " & OptionChooser.Value & "." End If End Sub
Make sense? If not, try this complete HTA and you’ll see exactly what I mean:
<html> <head> <title>HTA Test</title> <HTA:APPLICATION ID="objTest" APPLICATIONNAME="HTA Test" SCROLL="no" SINGLEINSTANCE="yes" > </head> <SCRIPT LANGUAGE="VBScript"> Sub TestSub If OptionChooser.Value <> 0 Then Msgbox "You selected Option " & OptionChooser.Value & "." End If End Sub </SCRIPT> <body> <select size="1" name="OptionChooser" onChange="TestSub"> <option value="0"></option> <option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option> </select> </body>
Click Option 1, 2, or 3 and you’ll see a message box reporting the option you selected. Click the blank option and nothing will happen. Problem solved.
Try It Yourself
OK, let’s see what you’ve learned so far. For my first try-it-yourself exercise, see if you can create an HTA that includes a drop-down list box with the following computer names as options:
- atl-dc-01
- atl-dc-02
- atl-dc-03
When you select one of the options, a subroutine should report back the name of the selected computer. (Hint: To do that, you might want to make the value of each option identical to the displayed text.)
Adding a List Box
By definition a drop-down list box shows only one item at a time; if you want to see additional items you need to click the control and drop down a list of additional items. There’s nothing wrong with that; however, there might be times when you’d find it easier/more effective to see multiple options at the same time. In other words, there might be times when you want to use a list box rather than a drop-down list:
Hey, why not; after all, variety is the spice of life. So let’s talk about adding a list box to an HTA.
One of the best things about list boxes is this: after you know how to create and use a drop-down list you’ll know how to create and use a list box as well. For example, here’s the code that creates a list box (shown above) with three options:
<select size="3" name="OptionChooser" onChange="TestSub"> <option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option> </select>
What’s the difference between this code and the code that created the drop-down list? Just one thing: in this case I’ve set the value of the size parameter to 3. That means I want to show three options onscreen at the same time. If I set the size to 12 we’d show 12 options onscreen at the same time. And if I set the size to 1 we’d have a drop-down list. It’s that simple.
Incidentally, you don’t have to make a list box the same size as the number of options you have. What if you have 100 options but only have room onscreen to show 10 at a time? That’s fine: just set the size to 10 and a scroll bar will automatically be added to the list box, enabling you to scroll up and down and access all 100 options. Play around with this a bit and see for yourself.
Try It Yourself
This is basically the same exercise as the first; this time, however, I want you to use a list box instead of a drop-down list. See if you can create an HTA that includes a list box (one that shows three items at a time) with the following computer names as options:
- atl-dc-01
- atl-dc-02
- atl-dc-03
- atl-dc-04
- atl-dc-05
- atl-dc-06
When you select one of the options, a subroutine should report back the name of the selected computer.
After adding the list box be sure and include a <BR> tag or a <P> tag; that will ensure that your controls don’t line up horizontally across the screen. |
Adding a Multi-Select List Box
I have one more list box variation to show you. In HTML you can create something known as a multi-select list box. With a multi-select list box you can hold down the Ctrl key and select as many options as you want. For example, do you need to select options 2, 4, and 5, all at the same time? Then go ahead and do it:
I understand your concerns: no doubt creating a multi-select list box takes a lot of very sophisticated, highly-technical skill. Just how complicated is creating a multi-select list box? This complicated:
<select size="3" name="MultiListbox" multiple> <option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option> </select>
No, I didn’t paste the wrong code in by mistake. The truth is that creating a multi-select list box is pretty much like creating a regular old list box. In fact, there are only two differences:
- I added the multiple attribute to the <select> tag. That’s what turns a plain-old list box into a way-cool multi-select list box.
- I removed the onChange event from the <select> tag. Why? Well, I don’t want to run a script each time the list box changes in some way; if that happened we’d select the first option and then the script would run, without waiting for us to select additional options. Instead I want the user to be able to select all the desired options and then click a separate button to kick off a script. Thus I need to remove the onChange event handler and then add a button that will run the TestSub subroutine.
In fact, the only tricky part about adding a multi-select list box crops up when you need to determine which options – if any – were selected. You can’t just echo back the Value when dealing with a multi-select list box; the singular notion “Value” is meaningless when you can have multiple items with multiple values. Instead, you’ll have to loop through all the options and determine which ones have a Selected property equal to True. That’s how you can identify which items have been selected and which ones haven’t.
No, it’s not too hard. In fact, all it takes are a few lines of code:
Sub TestSub For Each objItem in MultiListbox.Options If objItem.Selected Then strSelected = strSelected & objItem.Value & vbCrLf End If Next Msgbox "You selected the following items: " & vbCrLf & strSelected End Sub
See? All I do is create a For Each loop to cycle through all the options (items) in the list box; that’s what this line of code does:
For Each objItem in MultiListbox.Options
For each option (item) I check the value of the Selected property. If Selected is True that means the item was selected; I then store the value of that particular option in a variable named strSelected. If Selected is False I don’t do anything except move on to the next item in the collection. After looping through the entire set of options I then echo back a message similar to this, detailing the options that were selected:
That’s all there is to it.
Incidentally, if you never did figure out how to add a button to your HTA, here’s a hint:
<input id=runbutton type="button" value="Run" name="run_button" onClick="TestSub">
Try It Yourself
Similar to the previous two exercises, see if you can create an HTA that includes a multi-select list with the following computer names as options:
- atl-dc-01
- atl-dc-02
- atl-dc-03
- atl-dc-04
- atl-dc-05
- alt-dc-06
In addition, your HTA should include a button that, when clicked, calls a subroutine that reports back all the options that were selected. (Remember, with a multi-select list box you can hold down the Ctrl key and select more than one option at a time.)
Adding a Check Box
Picture an HTA that lists a number of different scripts you can run against a computer; maybe there’s a script that performs a hardware inventory, one that performs a software inventory, another that retrieves performance statistics. (Yes, it’s exactly the way medieval mystics pictured Paradise.) When you load that HTA, you’d like to be able to pick and choose which scripts to run and which scripts not to run. Maybe this time out you want to run the two inventory scripts but not the performance script; the next time out maybe you want to do the hardware inventory and get the performance statistics.
In other words, you want options that can be toggled on and off independently (that is, choosing the hardware script doesn’t automatically cause you to run the software script). If you’re thinking to yourself, “Hmmm, this sounds like a job for a check box,” you’re absolutely right:
The check box is just another variation of the <input> control (a control I learned about last month), this one with a type of, well, checkbox:
<input type="checkbox" name="Checkbox1"> Checkbox 1
Basically all you have to do is add an <input> tag along with the type and name parameters. (Note: No closing tag – that is, no </input> tag – is required.) After adding the tag simply type the label that appears next to the tag. In my preceding example, I gave the check box the label Checkbox 1. (Yes, they do pay us to think up stuff like that. Why do you ask?) If I wanted a check box with the label Run disk cleanup script we’d use this code:
<input type="checkbox" name="Checkbox1"> Run disk cleanup script
Tip. What if you’d like to have the check box checked by default? In that case, just set the value of the checked parameter to True:
<input type="checkbox" name="Checkbox1" value="Checkbox1" checked=True> Checkbox 1 |
To determine whether or not a check box has been checked all I do is look at the value of the Checked property. If True then the box is checked; if False, then the box is not checked. Here’s some sample code that does that checking for us:
Sub TestSub If Checkbox1.Checked Then Msgbox "You checked the check box." Else Msgbox "You did not check the check box." End If End Sub
What if I have more than one check box in the HTA? In that case, you just need to write separate If-Then blocks that examine the Checked property of each of those check boxes. In other words:
Sub TestSub If Checkbox1.Checked Then Msgbox "You checked check box 1." Else Msgbox "You did not check check box 1." End If If Checkbox2.Checked Then Msgbox "You checked check box 2." Else Msgbox "You did not check check box 2." End If End Sub
Note that I do two things in this subroutine: first I look at the value of the Checked property for Checkbox1, then I do the same thing for Checkbox2.
Try It Yourself
Create an HTA that includes two check boxes, one labeled atl-dc-01 and one labeled atl-dc-02. The HTA should also include a button that, when clicked, tells you whether either (or both) of the two check boxes has been checked.
Adding Radio Buttons
Radio buttons are designed for items that are mutually-exclusive: that is, you can be one of those things, but you can’t be more than one of those things. For example, you can choose to save a file or you can choose not to save a file, but you can’t choose to simultaneously save and not save a file. (That sounds like one of those clever human tricks Captain Kirk would use on the old Star Trek show to send a computer into an endless loop and once again defeat cold-blooded logic. We’d like to see Captain Picard do that.)
In other words, radio buttons are different from check boxes. Suppose you had 10 check boxes on screen. By definition you could select any 2 or any 5 or even all 10 of those check boxes. With radio buttons (shown below) things are very different: you can select one – and only one – radio button within a group:
Yes, hardly the control for the Me Generation; after all, nowadays I want it all. But with a radio button, you only get one.
I should mention from the outset that the radio button is not the most intuitive HTML control you’ll find yourself using. Up to this point all the controls I’ve created have had unique names. That makes sense; how else would you distinguish one control from another? With radio buttons, however, things are different. Say I want to create a group of three radio buttons, like the group I just showed you. In that case, I need to create three separate radio buttons, each with the exact same name.
No, I am totally serious: all the radio buttons in a group must have the same name (although they should have different values). That’s how my script knows that these buttons belong together. (In other words, that’s how the script knows that these buttons are mutually-exclusive.) If that doesn’t make sense, don’t worry; I’ll show you exactly how it works.
Here’s some HTML code that creates radio buttons labeled Option 1, Option 2, and Option 3:
<input type="radio" name="RadioOption" value="1">Option 1<BR> <input type="radio" name="RadioOption" value="2">Option 2<BR> <input type="radio" name="RadioOption" value="3">Option 3<P>
As you can see, all three of these buttons have the same name: RadioOption. That leads to the obvious question: if all the buttons have the same name, how can you tell them apart? That is, how can you tell if a user selected Option 1 rather than Option 2 or Option 3?
Good question. When it comes to radio buttons, all the controls with the same name are put into an array that has the same name as the control (in my preceding example, that means an array named RadioOption). To determine which button was selected all I have to do is set up a For Each loop to loop through that array, taking a peek at each button and seeing whether or not the Checked property is True. If Checked is True, then that button was selected. Your code for looping through an array of radio buttons might look something like this:
Sub TestSub For Each objButton in RadioOption If objButton.Checked Then Msgbox "You selected Option " & objButton.Value & "." End If Next End Sub
As you can see, all I am doing is looking to see whether or not the Checked property is True; if it is, I then echo back the button Value (this, by the way, is why button Values need to be unique). If the Checked property isn’t True, I don’t do anything at all. Like I said, not the most intuitive thing in the world, but it works.
Incidentally, it’s possible to have more than one group of radio buttons within the same HTA; just make sure that each group is given a different name. If you have a set of radio buttons, all with the name RadioOption, then you’ll want to give your next set of radio buttons a name like SecondRadioOption. As you’ve probably figured out by now, all the names you give to controls are arbitrary; name your controls whatever you want. (Just make sure you avoid using blank spaces anywhere in the name.)
Try It Yourself
For this exercise, let’s see if I can create an HTA that includes a group of radio buttons; the individual radio buttons should have the following computer names as labels:
- atl-dc-01
- atl-dc-02
- atl-dc-03
The HTA should also have a button that, when clicked, reports back the name of the selected computer.
Adding a Text Box
Drop-down lists, radio buttons and other fancy user interface controls can be a great timesaver for your users: it’s much faster (and much less error-prone) to have users select from a predetermined set of computers than it is to have them type in the desired computer name. However, you can’t always provide your users with predetermined options. For example, suppose you have an HTA for creating user accounts. There’s no way to provide drop-down lists pre-populated with all the possible first names and all the possible last names for a new user; it just won’t work. Instead, you have to provide a mechanism that allows people to enter any information. And the best mechanism for that is the humble – yet highly useful – text box:
Humble, useful, and very easy to implement: to add a text box to your HTA just use an <input> tag, specifying the type (text), a name, and, optionally, the size (that is, the number of characters wide):
<input type="text" name="BasicTextbox" size="30">
If you don’t include a size, by default the text box displays 20 characters.
Handy hint. The size of a text box is just the width of the text box onscreen; in no way does the size determine the number of characters that can be typed into a text box. If you do want to limit the number of characters a user can type into a text box then add the maxLength parameter. For example, this code displays a text box 30 characters wide, but only allows you to type a maximum of 25 characters:
<input type="text" name="BasicTextbox" size="30" maxLength="25"> |
So how can I retrieve the information that’s been typed into a text box? That’s easy: I just have to retrieve the Value property. For example, this simple little subroutine echoes back whatever’s been typed into a text box named BasicTextbox:
Sub TestSub Msgbox BasicTextbox.Value End Sub
By the way, it’s also very easy to programmatically “type” something into a text box. All you have to do is set the Value of the text box. For example, this subroutine “types” Fabrikam, Inc. into a text box named BasicTextbox:
Sub TestSub BasicTextbox.Value = "Fabrikam, Inc." End Sub
Would you ever want to do something like this? Sure. Suppose you have a tool for creating a new user account. The person creating the account selects the department from a drop-down list. If, say, all members of the Accounting department work in the same building in the same city, then you could have a subroutine that automatically fills in the building and city. Yet another reason why HTAs are so handy.
Try It Yourself
OK, this is an easy one: create an HTA that includes a text box and a button; when the button is clicked, it should report back whatever was typed into the text box.
Adding a Multi-Line Text Box
The standard text box is fine if you’re entering nothing more than a first name or last name; because the text box is limited to a single line, however, it’s not as useful for entering anything more substantial. If you need more room to enter (or to display) text, then you can use the <textarea> control, and get something that looks like this:
Now that’s more like it. And don’t worry; configuring a multi-line text box is very easy:
<textarea name="ScriptArea" rows=5 cols=70></textarea>
As you can see, all I need to do is toss in a <textarea> tag (and the accompanying </textarea> tag) and set the following parameters:
- name. The name of the control.
- rows. The number of lines that appear onscreen. A scrollbar will automatically be added to the control to enable you to type additional lines.
- cols. The width of the control, in characters. If you set cols to 70 then you should be able to type 70 characters, using Courier 12-point, on a single line.
You retrieve the value of a multi-line text box the same way you retrieve the value of a regular text box:
Sub TestSub Msgbox ScriptArea.Value End Sub
That’s true. But you don’t have to tell anyone how easy that is. Let them think you’re doing something only a programming genius could do!
Try It Yourself
This is a variation on the previous exercise. Instead of creating an HTA that includes a text box and a button, create an HTA that includes a multi-line text box and a button. The multi-line text box (textarea) should be sized for 40 columns and 5 rows; the button, when clicked, should display whatever was typed into the text box.
Adding a Password Box
One final control I want to introduce is the password box. The password box is just like the text box, but with one important exception: everything you type into a password box is “masked” so that the characters are not visible onscreen. In other words:
The code for adding a password box is almost identical to the code for adding a text box. To add a password box you use the <input> tag and you specify a name and size; the only difference is that you set the type to “password” rather than “text”:
<input type="password" name="PasswordArea" size="30">
And, yes, you’re absolutely right: retrieving the information typed into a password box is as simple as getting back the value of that control:
Sub TestSub Msgbox PasswordArea.Value End Sub