
HTAs stop working (repeatable) |
Post Reply
|
| Author | ||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
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. 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. 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,
<!-- saved from url=(0014)about:internet -->
HANG.VBS: OPTION EXPLICIT |
||
![]() |
||
jvierra
MVP
Joined: 31 Aug 2006 Location: United States Online Status: Offline Posts: 6846 |
Quote Reply
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)
|
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
Posted: 30 Jan 2010 at 23:46 |
|
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 ...
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 |
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
Posted: 30 Jan 2010 at 23:47 |
|
|
PS. Thanks for the quick reply!
TC
|
||
![]() |
||
jvierra
MVP
Joined: 31 Aug 2006 Location: United States Online Status: Offline Posts: 6846 |
Quote Reply
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.
|
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
Posted: 31 Jan 2010 at 17:33 |
|
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
|
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
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
|
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
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
|
||
![]() |
||
jvierra
MVP
Joined: 31 Aug 2006 Location: United States Online Status: Offline Posts: 6846 |
Quote Reply
Posted: 31 Jan 2010 at 19:27 |
|
|
Have you tried this simple way to execute an external script?
|
||
![]() |
||
jvierra
MVP
Joined: 31 Aug 2006 Location: United States Online Status: Offline Posts: 6846 |
Quote Reply
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.
|
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
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
|
||
![]() |
||
jvierra
MVP
Joined: 31 Aug 2006 Location: United States Online Status: Offline Posts: 6846 |
Quote Reply
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.
|
||
![]() |
||
rasimmer
Frequent Contributor
Joined: 30 Jan 2009 Location: United States Online Status: Offline Posts: 344 |
Quote Reply
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)
|
||
![]() |
||
jvierra
MVP
Joined: 31 Aug 2006 Location: United States Online Status: Offline Posts: 6846 |
Quote Reply
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.
|
||
![]() |
||
tc
I'm new here
Joined: 30 Jan 2010 Online Status: Offline Posts: 10 |
Quote Reply
Posted: 01 Feb 2010 at 19:38 |
|
|
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!
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 Instead, do it like this: dim sh, obj 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.
TC |
||
![]() |
||
Post Reply
|
| Forum Jump | Forum Permissions ![]() You cannot post new topics in this forum You cannot reply to topics in this forum You cannot delete your posts in this forum You cannot edit your posts in this forum You cannot create polls in this forum You cannot vote in polls in this forum |