HTAs stop working (repeatable)

Batch, ASP, JScript, Kixtart, etc.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Locked
User avatar
tc
Posts: 6
Joined: Sat Jan 30, 2010 3:15 pm

HTAs stop working (repeatable)

Post by tc » Sat Jan 30, 2010 3:47 pm

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 TESTdim sh, rvset sh = CreateObject("WScript.Shell")rv = sh.run ("HANG.VBS",, true)set sh = nothingMSGBOX "DONE RV=" & rv & ", ERR=" & ERR.NUMBERon error goto 0END SUB</script></HEAD><BODY><input type=button value="TEST" onclick="TEST()"></body></html>

HANG.VBS:
OPTION EXPLICITMSGBOX "HANG.VBS"WSCRIPT.QUIT

User avatar
jvierra
Posts: 13717
Joined: Tue May 22, 2007 9:57 am
Contact:

HTAs stop working (repeatable)

Post by jvierra » Sat Jan 30, 2010 4:28 pm

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)

User avatar
tc
Posts: 6
Joined: Sat Jan 30, 2010 3:15 pm

HTAs stop working (repeatable)

Post by tc » Sat Jan 30, 2010 4:47 pm

PS. Thanks for the quick reply!
TC

User avatar
tc
Posts: 6
Joined: Sat Jan 30, 2010 3:15 pm

HTAs stop working (repeatable)

Post by tc » Sun Jan 31, 2010 10:42 am

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 non error resume nextn = window.screenleftn = err.numberon 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:WINDOWSsystem32mshta.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 ifon error goto 0
----------------------

Enjoy!

TC

User avatar
jvierra
Posts: 13717
Joined: Tue May 22, 2007 9:57 am
Contact:

HTAs stop working (repeatable)

Post by jvierra » Sun Jan 31, 2010 12:27 pm

Have you tried this simple way to execute an external script?

Code: Select all

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

User avatar
tc
Posts: 6
Joined: Sat Jan 30, 2010 3:15 pm

HTAs stop working (repeatable)

Post by tc » Sun Jan 31, 2010 12:41 pm

Thanks for the new code!

I'm off right now, but I'll try it tomorrow & get back to you then.

Cheers,
TC

User avatar
rasimmer
Posts: 182
Joined: Fri Jan 30, 2009 12:37 am

HTAs stop working (repeatable)

Post by rasimmer » Sun Jan 31, 2010 11:24 pm

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 TESTdim sh, rvbtn_Exit.Disabled = True
set sh = CreateObject("WScript.Shell")rv = sh.run ("HANG.VBS",, true)btn_Exit.Disabled = False
set sh = nothingMSGBOX "DONE RV=" & rv & ", ERR=" & ERR.NUMBERon error goto 0END 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)

User avatar
tc
Posts: 6
Joined: Sat Jan 30, 2010 3:15 pm

HTAs stop working (repeatable)

Post by tc » Mon Feb 01, 2010 12:38 pm

@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

Locked