-
Feb 19th, 2014, 07:54 PM
#1
Thread Starter
Addicted Member
question about why one code works and the other does not
What is the difference between these two? Why is adding an event handler to an Internet explorer window causing the thread to wait for the UI thread?
Code A:
-Public WithEvents IE2 As SHDocVw.InternetExplorer to declare my internet explorer object
-Waits for the original navigate and readystate loop
-does not do any more navigates or click the login button until after the sub has finished or after the msgbox pops up
-is not logged in when i expect it to be.
Code B:
-Public IE2 As Object to declare my internet explorer object
-Waits for the original navigate and readystate loop
-waits for the second loop
-is logged in when I expect it to be
CODE A:
Code:
Public Module Module1
Public WithEvents IE2 As SHDocVw.InternetExplorer
Public Sub EOB()
IE2 = New SHDocVw.InternetExplorer
Dim element As mshtml.IHTMLElement
Dim doc As mshtml.HTMLDocument
IE2.visible = True
IE2.navigate("https://www.treasury.pncbank.com/idp/esec/login.ht")
Do : Loop Until IE2.readyState = 4
Do : Loop Until IE2.busy = False
doc = IE2.document
'here i plug in the login info
doc.getElementById("loginFormButton").click()
Do : Loop Until IE2.readyState = 4
Do : Loop Until IE2.Busy = False
MsgBox("at this point we should be at the login page but it seems like i'm waiting for the UI thread")
CODE B:
Code:
Public Module Module1
Public IE2 As Object
Public Sub EOB()
IE2 = CreateObject("internetexplorer.application")
Dim element As mshtml.IHTMLElement
Dim doc As mshtml.HTMLDocument
IE2.visible = True
IE2.navigate("https://www.treasury.pncbank.com/idp/esec/login.ht")
Do : Loop Until IE2.readyState = 4
Do : Loop Until IE2.busy = False
doc = IE2.document
'plug in the login info
doc.getElementById("loginFormButton").click()
Do : Loop Until IE2.readyState = 4
Do : Loop Until IE2.Busy = False
MsgBox("when this pops up i'm logged in")
End Sub
-
Feb 19th, 2014, 08:14 PM
#2
Re: question about why one code works and the other does not
You wonder why running a loop on the UI thread freezes? Don't ever loop like that, especially as there is a document completed event.
-
Feb 20th, 2014, 11:37 AM
#3
Thread Starter
Addicted Member
Re: question about why one code works and the other does not
Originally Posted by ident
You wonder why running a loop on the UI thread freezes? Don't ever loop like that, especially as there is a document completed event.
If you read the title of the post it says, and i quote, "question about why one code works and the other does not". I find that, consistantly on these forums, when I have a question the question gets totally ignored and people are suggesting alternate ways of doing something. I know there are alternate ways of waiting for a website to load. I want to know why, when I click the button on one code, the button click event fires instantly and on the other code (where i have public withevents IE) the clicking of the IE button does not happen until after the msgbox pops up or after the sub completes
-
Feb 20th, 2014, 12:38 PM
#4
Re: question about why one code works and the other does not
Doe sit happen if you omit WithEvents?
vb.net Code:
Public Module Module1 Public IE2 As SHDocVw.InternetExplorer Public Sub EOB() IE2 = New SHDocVw.InternetExplorer Dim element As mshtml.IHTMLElement Dim doc As mshtml.HTMLDocument IE2.visible = True IE2.navigate("https://www.treasury.pncbank.com/idp/esec/login.ht") Do : Loop Until IE2.readyState = 4 Do : Loop Until IE2.busy = False doc = IE2.document 'here i plug in the login info doc.getElementById("loginFormButton").click() Do : Loop Until IE2.readyState = 4 Do : Loop Until IE2.Busy = False MsgBox("at this point we should be at the login page but it seems like i'm waiting for the UI thread")
-
Feb 20th, 2014, 02:07 PM
#5
Thread Starter
Addicted Member
Re: question about why one code works and the other does not
Originally Posted by Merrion
Doe sit happen if you omit WithEvents?
[QUOTE=Merrion;4621085]Doe sit happen if you omit WithEvents?QUOTE]
if I change:
Code:
Public IE2 As SHDocVw.InternetExplorer
IE2 = New SHDocVw.InternetExplorer
to:
Code:
Public IE2 As object
IE2 = createobject("internetexplorer.application")
The .click of the login button fires when the code tells it to.
-
Feb 20th, 2014, 03:00 PM
#6
Re: question about why one code works and the other does not
It seems that the reason should be obvious, but since I don't do this type of thing I might be mistaken.
In the first case, you're instantiating the object on the UI thread, so any looping in the UI thread is going to delay other activity of the UI thread.
In the second case, you're creating a COM object, so it will run independently from the UI thread so will complete its tasks unimpeded.
-
Feb 20th, 2014, 03:31 PM
#7
Thread Starter
Addicted Member
Re: question about why one code works and the other does not
How can I add an event handler on a com object?
-
Feb 20th, 2014, 03:32 PM
#8
Thread Starter
Addicted Member
Re: question about why one code works and the other does not
Originally Posted by ident
You wonder why running a loop on the UI thread freezes? Don't ever loop like that, especially as there is a document completed event.
Oh and I tried using the document completed event and, it turns out, that the document is completed several times before i'm logged in so my readystate/busy loop will wait but the document completed event will misfire
-
Feb 20th, 2014, 04:24 PM
#9
Re: question about why one code works and the other does not
I'm not quite sure what to say about that. You are asking about intercepting events raised by the COM object to redirect them to your app. That may or may not be possible (not something I have ever looked into), but you are tying yourself to COM, which has it's own problems. There is a reason not to use that, but you already jumped on ident for saying that busy loops were bad. What's left to say? You have a pretty good answer to the question you asked. I believe that passel has it right. After all, busy loops don't just freeze the UI, that's nothing more than a symptom. The problem is that they prevent the UI from pumping messages, which makes it look like the UI is frozen, but it really isn't. The messages are queueing, they just don't process until you release the UI to process them. With that being the problem, the answer certainly isn't to move to COM. I've never had great success with that and avoid it where possible. Those snippets make for interesting tests in comparison, but neither one should be viable candidates for solving a real problem. So, is the question now how to solve a real problem, or is it still theoretical?
My usual boring signature: Nothing
-
Feb 20th, 2014, 04:55 PM
#10
Thread Starter
Addicted Member
Re: question about why one code works and the other does not
Originally Posted by Shaggy Hiker
So, is the question now how to solve a real problem, or is it still theoretical?
I could write this code on excel in VBA and it would work with the busy loops and application.doevents(). The problem is that I need to interact with a website's popup window. I log into website A and then click a link which opens up website B in a new window.
I've done this on excel by handling the newwindow event. When I try to handle the newwindow event then my wait for the website to load loops don't work. When I try to handle the newwindow event and use the document completed event the document completed event fires like 5 or 6 times before i'm on the website that I expect to be at.
-
Feb 20th, 2014, 05:32 PM
#11
Re: question about why one code works and the other does not
Busy loops most certainly work. That's why people reach for them so often, but over in the General Developer forum I have a thread where I measured the impact of a busy loop on the current draw of a computer. The fact that you can measure the cost of such a simple piece of code may make it unique. There may be no other three lines of code (other than ones that deal with external hardware devices), which create a measurable jump in power draw using a coarse instrument. Now, that makes no real difference on a multi-core system plugged into a wall. Your electric bill goes up by a thousandth of a penny...big deal. However, you don't want to be doing something like that on a battery powered device, and nobody who uses a battery powered device would want an app that does such a thing. It would be like poking a hole in your battery.
It's really annoying that the document completed event doesn't fire when the whole document is completed, but that's certainly the case....and getting worse. Back in the day, which isn't so long a go, a web document was just a document. These days, a document is composed of several different things, each of which can complete without the whole thing being complete. Nonetheless, it's the event you want. I feel that there will come a time when the web will be somewhat settled down and more reasonable events will be available, until then, life has to be lived as it is. The thing to do is to handle the DocumentCompleted event DESPITE the fact that it gets raised several times. In the event handler, that is where you check whether the part you want is available. In effect, each time DocumentCompleted is raised, you are asking, "yeah, but is the part I want ready?" The first few times the answer may be no, but eventually it will be yes. That will still be more effective than the busy loop, though it is certainly not all you could want.
My usual boring signature: Nothing
-
Feb 21st, 2014, 06:19 PM
#12
Thread Starter
Addicted Member
Re: question about why one code works and the other does not
Originally Posted by Shaggy Hiker
Busy loops most certainly work. That's why people reach for them so often, but over in the General Developer forum I have a thread where I measured the impact of a busy loop on the current draw of a computer. The fact that you can measure the cost of such a simple piece of code may make it unique. There may be no other three lines of code (other than ones that deal with external hardware devices), which create a measurable jump in power draw using a coarse instrument. Now, that makes no real difference on a multi-core system plugged into a wall. Your electric bill goes up by a thousandth of a penny...big deal. However, you don't want to be doing something like that on a battery powered device, and nobody who uses a battery powered device would want an app that does such a thing. It would be like poking a hole in your battery.
It's really annoying that the document completed event doesn't fire when the whole document is completed, but that's certainly the case....and getting worse. Back in the day, which isn't so long a go, a web document was just a document. These days, a document is composed of several different things, each of which can complete without the whole thing being complete. Nonetheless, it's the event you want. I feel that there will come a time when the web will be somewhat settled down and more reasonable events will be available, until then, life has to be lived as it is. The thing to do is to handle the DocumentCompleted event DESPITE the fact that it gets raised several times. In the event handler, that is where you check whether the part you want is available. In effect, each time DocumentCompleted is raised, you are asking, "yeah, but is the part I want ready?" The first few times the answer may be no, but eventually it will be yes. That will still be more effective than the busy loop, though it is certainly not all you could want.
My jobs run on pc computers so I perfer the readystate loops. The problem is that the readystate loop stops functioning as intenended when I want to handle the IE newwindow event. How can I handle that event outside of my UI thread?
-
Feb 21st, 2014, 09:04 PM
#13
Re: question about why one code works and the other does not
Actually, you prefer the busy loops (I assume that's what readystate means), and you happen to be running on a PC, so the cost to the system doesn't matter, but that's just a detail.
The UI thread is where all the messages are handled because that is where the message loop exists. I believe you could make other message loops, but you'd find that FAR harder than getting rid of the busy loops, if it is even possible. Therefore, one alternative would be to perform the busy wait in a background thread. There may be a different way if you are targeting framework 4.5, though I have never tried it. It's kind of the same thing, though, as it would perform the busy wait in a task, which may or may not run on a different thread at the discretion of the OS.
My usual boring signature: Nothing
-
Feb 25th, 2014, 10:38 AM
#14
Re: question about why one code works and the other does not
I might not understand your needs, and I'm not familiar with having to deal with web pages, but generally you don't want to hang up the GUI thread, so you need to process application state information in some kind of event.
It seems like the DocumentCompleted event is the place it should be done.
It would seem like since you have two conditions that you are waiting on (readyState = 4 and busy=false), you could check for these conditions in the DocumentCompleted event.
If the conditions aren't met, then perhaps this is not the final DocumentCompleted event, so you wait for the next one to check again.
If for some reason, the final DocumentCompleted even comes before those two conditions are satisfied, then another convenient event for periodically checking for some condition (polling), would be the timer tick event.
Set up a "state" machine in the timer (or called from the timer) to test that expected conditions are met, possibly do some processing and then look for the next state (step) in the process.
Moving "Loop" logic code into periodic event of the timer is the way to poll for conditions over time without hanging the code and preventing events from being processed.
A possible example, based on what you've posted:
Code:
Public WithEvents IE2 As SHDocVw.InternetExplorer
Dim WaitState As Integer
Dim element As mshtml.IHTMLElement
Dim doc As mshtml.HTMLDocument
Public Sub EOB()
IE2 = New SHDocVw.InternetExplorer
IE2.visible = True
IE2.navigate("https://www.treasury.pncbank.com/idp/esec/login.ht")
WaitState = 1
Timer1.Interval = 1
Timer1.Start() 'Start the state machine for EOB login
End Sub
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
Select Case WaitState
Case 1
If IE2.readyState = 4 Then
WaitState = 2
End If
Case 2
If IE2.busy = False Then
doc = IE2.document
'here i plug in the login info
doc.getElementById("loginFormButton").click()
WaitState = 3
End If
Case 3
If IE2.readyState = 4 Then
WaitState = 4
End If
Case 4
If IE2.busy = False Then
MsgBox("when this pops up i'm logged in")
'Perhaps at this point clean up by disposing of doc, or setting it to nothing if it is no longer needed
WaitState = 0 'no longer processing EOB
Timer1.Stop() 'stop the timer from firing
End If
End Select
End Sub
End Class
-
Feb 25th, 2014, 02:10 PM
#15
Re: question about why one code works and the other does not
Ok let's think about this for a little bit because it's getting silly. You would not add a timer to handle such things. You don't want to use SHDocVw.InternetExplorer at any rate, COM based browser control is what you should focus on. Plenty of examples on MSDN.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|