Hey, Scripting Guy! How Can I Get an HTA to Remember Its Previous Position Onscreen?

Hey, Scripting Guy! How Can I Get an HTA to Remember Its Previous Position Onscreen?

  • Comments 7
  • Likes

Hey, Scripting Guy! Question

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.

-- DF

SpacerHey, Scripting Guy! AnswerScript Center

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 there.

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:

0,0

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:

0

0

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.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi. I prefer to store this kind of settings in the registry. :)

    This way settings follow the user (if using roaming profiles) when changing computer.

    Otherwise. cool! :)

  • There seems to be a problem with this in that the moveto method is relative to the top left of the outer window, including the menu bar and window frame.  The screenLeft/Top properties are relative to the inner window, excluding the menu bar and window frame. This results in an offset of 4px,30px - if XP themes are turned on.  Or, an offset of 4px,23px if XP themes are off.  To be percise, you would then need to query for the theme state, then adjust your window offset accordingly (intTop = window.ScreenTop-23, etc)

  • cool thing - although offscreenBuffering isn't working :( tried a around but my hta still jumps to the new position instead of appearing.

    help appreciated ;)

  • tooooo bad window.offscreenBuffering = true is not working ... :( would be really good to have that. never the less

    <pre>

    Sub Window_onLoad

    window.offscreenBuffering = true

    Dim OuterWidth ' initial outer Size width

    Dim OuterHeight ' initial outer Size height

    Dim intLeft ' initial Left distance

    Dim intTop ' initial Top distance

    Dim intHlafW ' middle pos x

    Dim intHalfH ' middle pos y

    Dim BorderWidth ' calc border w (both sides in one)

    Dim BorderHeight ' calc border h (both sides in one ! Bottom -> like BorderWidth/2)

    OuterWidth = 400

    OuterHeight = 250

    window.ResizeTo OuterWidth,OuterHeight

    window.MoveTo 0,0

    BorderWidth = OuterWidth-document.body.clientWidth

    BorderHeight = OuterHeight-document.body.clientHeight

    intHlafW = Round((OuterWidth+BorderWidth)/2)

    intHalfH = Round((OuterHeight+BorderHeight)/2)

    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)-intHlafW), (arrContents(1)-intHalfH)

    window.ResizeTo OuterWidth+BorderWidth,OuterHeight+BorderHeight

    End Sub

    Sub Window_onBeforeUnLoad

    intLeft = window.screenLeft+intHlafW-(BorderWidth/2)

    intTop = window.screenTop+intHalfH-(BorderHeight-BorderWidth/2)

    Const ForWriting = 2

    Set objFSO = CreateObject("Scripting.FileSystemObject")

    Set objFile = objFSO.OpenTextFile("HTA.ini", ForWriting)

    objFile.WriteLine intLeft & "," & intTop

    objFile.Close

    End Sub

    </pre>

  • To get the REAL top left corner of your HTA window just run it at the beginig:

    var xoffset = screenLeft;

    var yoffset = screenTop;

    window.moveTo(xoffset,yoffset);

    xoffset -= screenLeft;

    yoffset -= screenTop;

    xoffset,yoffset - is your "theme offset", the difference between window and client screen coordinates. To get real top left coords do:

    winLeft = screenLeft + xoffset;

    winTop = screenTop + yoffset;

  • Well, I must say, none of these scripts work.

    Unfortunate because this would be an interesting solution for an HTA

  • As Sarkie says, none of these scripts work.

    Unfortunately, Microsoft did not provide this for an HTA,

    why is this.