FAQ FAQ  Forum Search   Register Register  Login Login

HTAs stop working (repeatable)

 Post Reply Post Reply
Author
  Topic Search Topic Search  Topic Options Topic Options
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Topic: HTAs stop working (repeatable)
    Posted: 30 Jan 2010 at 22:47

Hi folks

I have a problem that causes all double-clicks of HTAs to be ignored, until you terminate MSHTA.EXE from Task Manager. The problem is 100% repeatable on my XP Pro SP2 laptop. I'd appreciate any input on how to solve this.

Start by creating HANG.HTA and HANG.VBS with the simple code provided for each.  Then proceed as follows.

1. Start the HTA.
2. Click the TEST button.
3. See the message from the VBS.
4. OK the message from the VBS.
5. See and OK the message from the HTA.

Those steps work fine. You can do them any number of times with the same (correct) results. Other HTAs continue to work as expected.

Now try this:

1. Start the HTA.
2. Click the TEST button.
3. See the message from the VBS.
4. Before OK'ing that message, close the HTA.
5. Now OK the message from the VBS.
6. See and OK the message from the HTA.

On my XP Pro SP2 laptop, *all* HTAs now stop working; in the sense that if you double click *any* HTA, nothing happens at all. This continues until you kill MSHTA.EXE manually from Task Manager.

Any ideas how to fix this?

Thanks in anticipation,
TC


HANG.HTA :

<!-- saved from url=(0014)about:internet -->
<html>
<HTA:APPLICATION
   APPLICATION="yes"
   ID="MYAPP1234"
   APPLICATIONNAME="My Application"
   maximizeButton="no"
   minimizeButton="yes"
   SHOWINTASKBAR="yes"
   SINGLEINSTANCE="yes"
   SYSMENU="yes">
<HEAD>
<script type=text/vbscript>
SUB TEST
dim sh, rv
set sh = CreateObject("WScript.Shell")
rv = sh.run ("HANG.VBS",, true)
set sh = nothing
MSGBOX "DONE RV=" & rv & ", ERR=" & ERR.NUMBER
on error goto 0
END SUB
</script>
</HEAD>
<BODY>
<input type=button value="TEST" onclick="TEST()">
</body>
</html>

 

HANG.VBS:

OPTION EXPLICIT
MSGBOX "HANG.VBS"
WSCRIPT.QUIT

Back to Top
jvierra View Drop Down
MVP
MVP


Joined: 31 Aug 2006
Location: United States
Online Status: Offline
Posts: 6846
  Quote jvierra Quote  Post ReplyReply bullet Posted: 30 Jan 2010 at 23:28
YOu have two thr4eads deadlocked.  HTA (MSHTA) is still in memory although it cannot be seen.  Look inTask Manager to see.
 
This is an old trick.  It is not legitimate code although it can be written.,  YOu should never design something that works like this.
 
Change this line will eliminate problem:
rv = sh.run ("HANG.VBS",, true)
To
rv = sh.run ("HANG.VBS",, false)
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 30 Jan 2010 at 23:46
Originally posted by jvierra

YOu have two thr4eads deadlocked.  HTA (MSHTA) is still in memory although it cannot be seen.  Look inTask Manager to see.
 
What two threads? The HTA waits for the VBS to complete. The VBS does complete. Then the HTA resumes - as shown by the fact that it displays its message. I can't see any deadlock there ...
 
Originally posted by jvierra

This is an old trick.  It is not legitimate code although it can be written.,  YOu should never design something that works like this. Change this line will eliminate problem:
rv = sh.run ("HANG.VBS",, true)
To
rv = sh.run ("HANG.VBS",, false)
 
Sorry, I don't understand your point. I explicitly need the HTA to wait until the VBS has finished. That's why I said 'true', instead of 'false'. There's nothing illegitimate about that, as far as I can see ...
 
Any more thoughts?
 
TIA,
TC
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 30 Jan 2010 at 23:47
PS. Thanks for the quick reply!
TC
Back to Top
jvierra View Drop Down
MVP
MVP


Joined: 31 Aug 2006
Location: United States
Online Status: Offline
Posts: 6846
  Quote jvierra Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 06:10
YOu are illegally closing teh HTA before the VBS completes.  THe HYA cannot close as it is waitong on teh VBS and the VBS cannot dismiss to it's parent process because the parent is hung inmemory attempting to shutdown.
 
THis is one reasonwhy we chould not use MsgBox for generalized output from an HTA or a VBS.  It canbe used but may also haven unsuspecting side effects.
 
YOu may also have other issues with you OS setup that canbe adding to this problem. 
 
Just consider that it makes littel sense to try to shut down an HTA that is waiting on an external process.  Yes it looks like a bug but it is one that has been induced by an unintended situation.
 
 
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 17:33
Originally posted by jvierra

Just consider that it makes littel sense to try to shut down an HTA that is waiting on an external process.  Yes it looks like a bug but it is one that has been induced by an unintended situation. 
 
j, I agree completely. It does make little sense to shut down an HTA that is waiting on an external process. By it's my *user* who is doing that! The HTA is front-ending a VBS. The VBS takes time to complete. I'm simulating that (in this simpole test case) with a message box. Before the process has completed *the user* closes the HTA. "Why did you do that?" - "Because I could!"
 
My point is that this series of trivial events causes all HTAs to stop working on that box. It is unacceptable (to my mind) that a simple user action could cause such a problem - illogical though his action may be. A user should never be able to take a simple, everyday action (like closing a window) which causes all HTAs to malfunction from then onwards!
 
Anyway, I've managed to fix it using Windows Management Instrumentation (WMI). I'll post that seerately to keep things neat & clean.
 
Thanks again for your quick replies.
 
TC
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 17:42

This code fixes the problem described above. Put it after the return from the VBS call. In the example given, a suitable place would be, immediately before the END SUB statement.

I've included voluminous comments to explain it.
 
NOTE: THIS CODE WORKS FINE ON MY PC FOR MY PURPOSES. IT'S THE READER'S RESPONSBILITY TO CHECK IT WORKLS CORRECTLY FOR HIM. USE THIS CODE AT YOUR OWN RISK.
 
Code follows:
 
 
'  ------------------------------------------------------------------------
'                              HTA BUGFIX
'  ------------------------------------------------------------------------
 
'  If the user closes the HTA window BEFORE the synchronous VBS returns, this HTA's copy of MSHTA.EXE is left in the mix, and ALL HTAs from now on are ignored when they are double-clicked! To fix this, we have to explicitly kill that copy (and only that copy) of MSHTA.EXE. Luckily we can do that using Windows Management Instrumentation (WMI).

'  We need to know our own filename. Can we get this at runtime instead of coding it in?
CONST MYNAME = "HANG.HTA"

'  First, we have to see if the user did close the window before the VBS returned. Surprisingly, WINDOW.CLOSED will still be False - a bug in itself! But accessing a window property (eg. SCREENLEFT) will return error 0x80004005, "Unspecified error", so lets' use that.
 
dim n
on error resume next
n = window.screenleft
n = err.number
on error goto 0
if n = &h80004005 then
 
   '  Now we have to find our own copy of MSHTA.EXE. *IF THE HTA IS SINGLE INSTANCE* - which this one is - we just look for the copy whose command line ends with out file name. If the HTA *IS NOT* single instance, there could be multiple copies that match that test, so you'll have to find some other way to find the right copy.
 
   dim process, s
   for each process in GetObject("winmgmts:").ExecQuery _
         ("select * from Win32_Process where name = 'mshta.exe'")
      s = trim (process.commandline)  ' can have trailing space!
      ' eg. "C:\WINDOWS\system32\mshta.exe" "C:\...\blah.hta"
      if right (s, 1) = """" then
         s = left (s, len (s) - 1)
      end if
      if right (s, 9) = "\" & MYNAME then
 
         '  Got it - kill it! This is like comitting suicide.
         '  The HTA will die completely at the process.terminate.
''MSGBOX "READY TO KILL " & process.commandline
         process.terminate 0
 
         ' never get here!
''MSGBOX "KILL FAILED!"
         exit sub '***
      end if
   next
 
   '  If we get here, it means we did not find right the process. So now, NO HTAs can be double clicked until someone kills the process manually. In testing, the code has always found the right process. It's not clear what (if anything) we could tell the user if this happened.
''MSGBOX "CAN'T FIND PROCESS!"
 
end if
on error goto 0
----------------------
 
Enjoy!
 
TC
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 17:54
Oops! I made a last minute change. We all knOw what happens with last minute changes!
 
Change this:
 if right (s, 9) = "\" & MYNAME then
 
to this:
 if right (s, 1 + len (MYNAME)) = "\" & MYNAME then
TC
Back to Top
jvierra View Drop Down
MVP
MVP


Joined: 31 Aug 2006
Location: United States
Online Status: Offline
Posts: 6846
  Quote jvierra Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 19:27
Have you tried this simple way to execute an external script?
 

<html>
<head>
<script language="vbscript">
    SUB TEST()
        dim sh, rv
        set sh = CreateObject("WScript.Shell")
        rv = sh.Exec("cscript HANG.VBS")
        While rv.Status = 0
           
        Wend
        MSGBOX "DONE RV=" & rv & ", ERR=" & ERR.NUMBER
    END Sub
   
    Sub test2()
        Set fso = CreateObject("Scripting.FileSystemObject")
        Set file = fso.OpenTextFile("hang.vbs")
        strScript = file.ReadAll()
        window.execScript strScript, "vbscript"
        file.Close
    End Sub
</script>
</head>
<body>
<input type=button value="I Hang!" onclick="TEST">
<input type=button value="I dont hang!" onclick="TEST2">
</body>
</html>

 
 
 
Back to Top
jvierra View Drop Down
MVP
MVP


Joined: 31 Aug 2006
Location: United States
Online Status: Offline
Posts: 6846
  Quote jvierra Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 19:32
I am tryi8jng to finf teh documentation that says you should not use Shell calls from an HTA.  If you spawn a secondary then is needs tobe async and you must poll for it's completion.  This can be done simnply withteh use of a timer.  YOu can also capture the mouse and keyboard - which is the normal DHTML method - until the external process is completed.
 
 
This issue also gets newcomers to programming in VB and other languages.   WIndows does not like to have you try and stall a thred that ispart of teh message loop.  It always causes unexpected results.  MIcrosoft has forever released system code that does things like this. 
 
The famous installer programs with a message box that is hidden behind the main window comes to mind.  The installer is stuck and can't be completed or terminated.
 
 
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 19:41
Thanks for the new code!
 
I'm off right now, but I'll try it tomorrow & get back to you then.
 
Cheers,
TC
Back to Top
jvierra View Drop Down
MVP
MVP


Joined: 31 Aug 2006
Location: United States
Online Status: Offline
Posts: 6846
  Quote jvierra Quote  Post ReplyReply bullet Posted: 31 Jan 2010 at 20:02
I'll try to find an example of not allowing the HTA to proceed while it is waiting.  I have one somewhere because i found this to be an issue quite a while ago.  I since have remembered not to do this kind of thing.
Back to Top
rasimmer View Drop Down
Frequent Contributor
Frequent Contributor


Joined: 30 Jan 2009
Location: United States
Online Status: Offline
Posts: 344
  Quote rasimmer Quote  Post ReplyReply bullet Posted: 01 Feb 2010 at 06:24
Another option is removing the sysmenu, not show in the taskbar and providing buttons to close the HTA (self.Close).  When you click the button to launch, you disabled the button for exit and the user can't exit the hta unless the do ALT+F4 (which could be disabled in the HTA if you capture the keys) or go into task manager and close it.  So, something like this:
 
<html>
<HTA:APPLICATION
   APPLICATION="yes"
   ID="MYAPP1234"
   APPLICATIONNAME="My Application"
   maximizeButton="no"
   minimizeButton="yes"
   SHOWINTASKBAR="no"
   SINGLEINSTANCE="yes"
   SYSMENU="no">
<HEAD>
<script type=text/vbscript>
SUB TEST
dim sh, rv
btn_Exit.Disabled = True
set sh = CreateObject("WScript.Shell")
rv = sh.run ("HANG.VBS",, true)
btn_Exit.Disabled = False
set sh = nothing
MSGBOX "DONE RV=" & rv & ", ERR=" & ERR.NUMBER
on error goto 0
END SUB
 
Sub btn_Exit_OnClick()
    self.close
End Sub
</script>
</HEAD>
<BODY>
<input type="button" id="btn_Test" value="TEST" onclick="TEST()">
<input type="button" id="btn_Exit" value="Exit">
</body>
</html>
 
Another solution in addition to this is to not call an external VBS and to just put the code in the HTA with the same options I've showed you above.  Be curious to hear if JVierra feels this is valid solution(s)
Back to Top
jvierra View Drop Down
MVP
MVP


Joined: 31 Aug 2006
Location: United States
Online Status: Offline
Posts: 6846
  Quote jvierra Quote  Post ReplyReply bullet Posted: 01 Feb 2010 at 06:48
rasimmer - good options.
 
Putting all of the code in the HTA is a the best method as it puts all code in one process and avoids many issues of external and hidden processes however, there are time when it is convenient to use external scripts.
Back to Top
tc View Drop Down
I'm new here
I'm new here


Joined: 30 Jan 2010
Online Status: Offline
Posts: 10
  Quote tc Quote  Post ReplyReply bullet Posted: 01 Feb 2010 at 19:38


@rasimmer: The custom close button is a great idea. Even if all the other methods failed, the custom button method would clearly work. Good lateral thinking!


@jvierra

I've tried both of your last suggestions (shell.exec and window.execscript). With two provisos, the shell.exec one works for me.

Let me give you some more background first. My HTA is a simple UI front end to a 4500 line VBScript which "screen scrapes" data from online financial registries. The script runs for about 10 minutes, then creates a new HTML document that the user can view on the screen, or save to disk.

I'm a retired professional software developer, so I can confidently say that the script is properly written and structured. None the less, there's no way I want 4500 lines of VBScript inside the HTA! Also, I run the script directly (without the HTA) for testing purposes. So I can't include the script directly in the HTA.

At present, the HTA already uses filesystemobject to read in the script - like your second suggestion - but then it uses EXECUTEGLOBAL (not WINDOW.EXECSCRIPT) to execute it. This works, but has two problems:

    1. The HTA window stops repainting until the script has finished. Eg. if some other window covers the HTA window, and then uncovers it, the HTA window does not repaint. This is only a cosmetic problem - but it looks really bad.

    2. The EXECUTEGLOBAL approach causes "active content" warnings in the generated HTML document in certain cases. This is a significant problem for my application.

Those two problems are why I tried SHELL.RUN instead.

So to your two last suggestions:

   o I tried your second suggestion (WINDOW.EXECSCRIPT) first, because it was only a one-line change to my existing code. It kept failing with an "unterminated string" error. After scratching my head for a while, I found that EXECSCRIPT does not handle comments! So that's really not a go-er for executing complex scripts, most of which will (hopefully) be commented. It would be possible to delete the comments from the string before passing it to EXECSCRIPT - but that would be quite tricky.

   o Then I tried your first suggestions (SHELL.EXEC). This worked, except that it displayed a command window while the script was running. That looked nasty! I changed CSCRIPT to WSCRIPT, and it still worked fine but the command window didn't appear. Perfecto!

However, when I tried this in the "real" application, it did start the script, but then the EXEC statement failed immediately with error 438, object does not have that property or method. After more head scratching, I realized that EXEC returns an object (not a value) - so it needs a SET statement (SET RV = ...).

Here's my bet. VBScript parses the RV = EXEC ... statement and sees that it's syntactically valid. Then it evaluates the right hand side: this starts the script (as a side effect), and returns a the exec object. Then VBScript says, "Oh, there's no SET keyword, so he wants me to assign the value of the default property of that object, to the variable RV". My guess is that the exec object does not have a default property - hence that error. With the SET keyword added, VBScript says, "Oh, he wants me to assing the object itself to the RV variable" - so all is good.

That's the only case that I can think of, where a statement half works, and half fails - instead of completely working or completely failing!


IN SUMMARY
----------

This is the note that I'll make for myself:

If your HTA wants to run a script and then wait for the script to complete, DO NOT do it like this:

      dim sh
      set sh = createobject ("wscript.shell")
      sh.run "BLAH.VBS",, true

Instead, do it like this:

      dim sh, obj
      set sh = createobject ("wscript.shell")
      set obj = sh.exec ("WSCRIPT BLAH.VBS")
      while obj.status = 0: wend

A side benefit of the recommended method, is that the HTA doesn't have to enter the loop immediately. It could do something else behind the scenes, then enter the loop when good and ready. And it could implement a custom timeout, and so on.


Thanks to all who replied!

TC

Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down