Hey, Scripting Guy! How Can I Get an HTA to Remember Its Previous Position Onscreen?
Hey, Scripting Guy! Is there any way to get an HTA to remember its previous position onscreen? Some of my users are very particular about moving an HTA to a specific spot on the screen, then wanting it to reappear at that same spot the next time they run the application.
Hey, DF. You know, in the past few weeks there has been a spate of alleged memoirs that turned out to be hoaxes: the events described never really happened, at least not to the people who wrote the books. Sadly, we must confess that the Scripting Guys have been guilty of embellishing the account of our lives as well. With that in mind, and with the utmost contrition, we decided to set the record straight. Contrary to popular belief:
Scripting Guy Peter Costantini did not invent the light bulb, the sewing machine, or the personal computer. However, Peter insists that he did invent the wheel, the internal combustion engine, and the banana split.
Scripting Guy Dean Tsaltas was not sent to the guillotine during the French revolution; instead, he was burned at the stake during the Salem Witch trials of 1692. No one ever accused Dean of being a witch; the people of Salem just didn’t like him all that much.
Scripting Guy Greg Stemp did not bat cleanup for the 1927 New York Yankees; Lou Gehrig was the cleanup hitter that season. Greg instead hit seventh in the order, just ahead of catcher Joe Grabowski. In addition, Greg did not win the 1973 Miss Hawaii beauty pageant; the truth is, he was eliminated after the talent competition. (In retrospect, writing a Windows PowerShell script that retrieved events from an event log was not the crowd-pleasing act he assumed it would be. He should have stuck with tap-dancing.)
Interestingly enough, however, Scripting Guy Jean Ross really did crack her head open while attending the 2007 MMS conference in San Diego. Sometimes truth really is stranger than fiction.
We realize that it was a mistake to make up these stories and attempt to pass them off as true, and we promise never to do this again. You say you find that hard to believe? Well, then consider this: at lunch yesterday Scripting Guy Greg Stemp made a personal promise to the Queen of England that everything written in this column will be 100% true.
So does that mean we’re telling the truth when we say that it’s easy to get an HTML Application (HTA) to remember its previous position onscreen? Don’t just take our word for it; take a look at the following piece of code:
<SCRIPT Language="VBScript"> Dim intLeft Dim intTop Sub Window_onLoad window.offscreenBuffering = True Const ForReading = 1 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.OpenTextFile("HTA.ini", ForReading) strContents = objFile.ReadAll objFile.Close arrContents = Split(strContents, ",") window.moveTo arrContents(0), arrContents(1) End Sub Sub Window_onBeforeUnLoad intLeft = window.screenLeft intTop = window.screenTop Const ForWriting = 2 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.OpenTextFile("HTA.ini", ForWriting) objFile.WriteLine intLeft & "," & intTop objFile.Close End Sub </SCRIPT> <BODY> </BODY>
As you can see, there’s not a lot to this HTA; as a matter of fact, it’s totally blank. (Rather than create an HTA body from scratch we simply copied the Scripting Guys’ list of actual accomplishments for the past few years. In retrospect, that might have been a mistake, too.) What we do have is a pair of subroutines: Window_onLoad, a subroutine that automatically runs each time our HTA is loaded or refreshed, and Window_onBeforeUnload, a subroutine that automatically runs right before the HTA is closed. Why do we need these two “auto-run” subroutines? By astonishing coincidence, we were just about to tell you that.
Let’s start with the Window_onLoad subroutine. As you can see, we start off with this intriguing line of code:
window.offscreenBuffering = True
What we’re doing here is setting the offscreenBuffering property to True.
Oh, right; we suppose you probably could figure that out for yourselves, couldn’t you? OK, in that case, what we’re really doing is telling the HTA that we want it to draw the window (and any items to be contained in that window) off-screen; we don’t want to see the HTA onscreen until it’s ready to be seen. Why do we do that? Well, by default the HTA is going to load in the same spot every time; it’s only after it’s loaded – and visible – that the HTA window will move to its last-known coordinates. (We’ll explain all that in a minute.) If this happens on screen, you’ll see the HTA load up, then see it “jump” to a new location. If the HTA loads up off-screen, however, you won’t see the jump; instead, the HTA will simply appear, when ready, at the desired location.
That will all make more sense after you finish reading the column.
That’s right: we did promise to tell the truth from now on, didn’t we? Well, we hope that will all make more sense after you finish reading the column.
Note. Good point: we should also mention that we defined a pair of variables, intLeft and intTop. These are global variables; that is, variables available to any subroutine in the HTA. How do we know that they’re global variables? That’s easy: they were not defined within a subroutine. Instead, they simply exist as code inside the <SCRIPT> tag.
So what is exactly is happening off-screen? Well, for starters, we define a constant named ForReading and set the value to 1; we’ll use this constant when we open our .INI file for reading.
Note. Well, that’s another good point: yes, we are using a .INI file to tackle this problem. Each time our HTA loads it will read its starting coordinates from the .INI file; each time the HTA unloads it will save its current coordinates to that same .INI file. That’s how the HTA can re-start in the exact same screen position at which it ended.
After defining the constant, we create an instance of the Scripting.FileSystemObject object, then use the OpenTextFile method to open the file HTA.ini for reading (we’re assuming that the .INI file is in the same folder as the HTA itself):
Set objFile = objFSO.OpenTextFile("HTA.ini", ForReading)
Our .INI file consists of a single line, a line that looks something like this:
What’s the 0,0 for? Well, the first 0 represents the left-hand edge of our HTA window; in other words, we want the HTA to start out butted-up against the left side of the screen (indented 0 pixels from the left). The second 0 represents the top edge of the HTA window; that means that we want the HTA to start out 0 pixels from the top of the screen. The net effect? By default, the HTA starts off in the upper left-hand corner of the screen. But it doesn’t always have to start at 0,0, as you’ll soon see.
In order to set those starting coordinates we use the ReadAll method to read the contents of the .INI file, storing that single line in a variable named strContents:
strContents = objFile.ReadAll
After we close the file, we then use the Split function to split the contents on the comma; that gives us an array named arrContents, an array containing the following two elements:
And now comes the fun part: we use the moveTo method to move the HTA window to the specified coordinates, using the value of the first item in the array (index number 0) to represent the left side of the window, and the value of the second item in the array (index number 1) to represent the top of the window:
window.moveTo arrContents(0), arrContents(1)
We told you that would be fun, didn’t we?
So we’ve now solved half our problem: each time the HTA starts up it reads the .INI file, parses the contents, then positions the window according to the values saved in that file. That’s good. Like we said, though, that solves only half the problem. What happens if we move the HTA window? In that case, the left and top coordinates will change and the window will no longer be at 0,0. In turn that means we need to update the .INI file. But how? And, equally important, when?
Let’s start by discussing when the .INI file should be updated. The best time to do that is right before the HTA closes; that is, right about the time the Window_onUnload event is fired. Admittedly, we could update the .INI file each time the window gets moved, but there’s really no need to do that; after all, it might get moved again, and again, and again; each time we’d have to update the .INI file. Instead, we just wait until the window closes; that way we write the coordinates for the last-known position of the HTA to the .INI file.
Which, of course, is all we care about anyway.
To save those coordinates, we first grab the values of the screenLeft and screenTop properties and stash them in our two global variables:
intLeft = window.screenLeft intTop = window.screenTop
We then define a constant named ForWriting and set the value to 2; this constant enables us to write to the file HTA.ini. We next create a new instance of the Scripting.FileSystemObject object, then open the file HTA.ini for writing:
Set objFile = objFSO.OpenTextFile("HTA.ini", ForWriting)
All that’s left now is to replace the existing contents of the file with our left coordinate, a comma, and the top coordinate; that’s what this line of code is for:
objFile.WriteLine intLeft & "," & intTop
And then we close the file and simply wait for the HTA to be restarted. When that happens, the window will appear in the exact same spot where it stood the last time we closed the HTA.
Will that really work? Well, there’s only one way to find out: give it a try and see what happens.
Again, the Scripting Guys would like to apologize for our previous indiscretions. It was never our intention to deceive anyone; we just wanted people to believe something was true even when it obviously wasn’t.
Come on; everyone else is doing it these days.
Note. Really, we swear: Scripting Guy Jean Ross actually did cracker her head open while attending the MMS Conference. We know that sounds hard to believe. But, then again, you’ve never actually met Jean.