Monitoring IE for (navigational) changes

C

Csaba Gabor

I'd like to be able to monitor my instances of IE and be alerted every
time there's a page change/update. Effectively, I'm asking for how to
make a very simple customization of IE. I'm using IE 6 on Win XP Pro,
SP2.

The first sentence is nebulous so let me refine it. I've got a
VBscript (.vbs file) running in the background (along with an attendant
..ocx that I wrote for it using VB5CCE). If nothing else, to notify the
script could simply mean to call it (since it can detect that it's
already running and figure out why it was called). Now the script (or
its .ocx) would be happy if any ONE of the following problems could be
solved:

1. Be notified (of the app / destination IP) whenever any outgoing
internet communication is initiated.
2. Be notified (of the instance) whenever any instance of IE
initiates any outgoing communication to a server (submitting
a form, navigation to another page, setting .src, fetching a .css
or .js file, etc.)
3. Be notified whenever any instance of IE navigates to another page
(e.g. form is submitted, link is clicked, location is set)
4. Be notified whenever any (NEW) instance of IE is started up.
Because then I can have the .vbs set an onBeforeUnload (at least
I think I can make this work).

What it's really after is option 3, because the script would like to
inspect the resulting page. Now 4 could be solved by, once a minute,
having my .vbs loop through all the instances of IE to see what there
is, but this is really awful and wasteful with a potential one minute
delay. There should be a direct way to accomplish what I want.

Thanks for any tips,
Csaba Gabor from Vienna
 
C

Csaba Gabor

The following THREE(!) posts describe one way to achieve what I want: a
way to detect whenever a specific web site is active and to alter its
behaviour. This one describes what's going on and how to do it, while
the next two have the supporting files for the OCX that I create. The
following example will detect whenever an instance of IE has a specific
web page (google) and then modify that web page (we'll override the
behaviour of the main search button).


The basic idea is that we're going to create a TSR (well, it doesn't
really terminate) which is alerted to any IE/Explorer window
creation/deletion (note, however, that window.showModalDialog does not
result in such an alert). Each time we get such a change notification,
we'll loop through and place a hook on any unhooked instance so that
we'll be notified whenever any instance of IE has completed an act of
navigation (actually, we'll use DownloadComplete since
NavigateComplete2 does not fire on refresh). At that point we are free
to modify the page.


Enjoy,
Csaba Gabor from Vienna


Here are the detailed instructions:
Step 1. Create Launcher.vbs, inserting the code from below
This is the launching script (If I had VB6, I'd expect to combine
Launcher.vbs/GoogHook.ocx into GoogHook.exe)

Step 2. Bring up VB5CCE (or some other VB development environment to
create an ActiveX project (OCX))

Step 3. Configure VB5CCE:
a. Under Project \ Properties \ Component \ Version Compatibility
select No Compatibility, then click OK
b. Under Project \ References
select the following projects, then click OK to add them
1. Microsoft Internet Controls
(ShDocVw.dll) for shell functions
2. Microsoft Scripting Runtime
(ScrRun.dll) for Dictionary object

Step 4. Select the Project1 and change its name to GoogHook
Step 5. Select the UserControl1 and change its name to hook
Insert the code from the next post (User ctl: hook)
Keep in mind that VB5CCE does not automatically save files and you
have to look carefully where they are being saved, cause it
doesn't remember directory settings so well.

Step 6. Select Project \ Add Class Module
a. Name this class clsIeEvents
b. Add the code from the following post (class: clsIeEvents)

Step 7. Select File \ Make... to create your GoogHook.ocx

Step 8. To run, double click on Launcher.vbs
Then open a new browser and navigate to google.com
Put in something to search on, and click the Search button
You should get a confirm dialog

Step 9. End this demo by closing the IE page that says:
Close page to terminate app



'------------ Launcher.vbs ------------
'Loads an OCX that modifies the google Search button
'behaviour every time a google search page is loaded
'Runs till the control IE window is closed

Set myHook = WScript.CreateObject("GoogHook.hook", "hookEvent_")
If myHook.duplicateP Then _
msgBox "Exit on account of Duplicate": _
Wscript.Quit 'Single instance OCX

set myHook.m_oCallback = GetRef("hookEnd")
Sub hookEnd (msg): myHook.keepLooping = False: MsgBox msg: End sub
Sub hookEvent_NewWin(lCookie, oIE)
MsgBox "New Window " & oIE.Document.parentWindow.nCtr & _
vbCrLf & "cookie: " & lCookie
End Sub

Do While myHook.keepLooping
myHook.WaitForMsg: myHook.DoEvent: Loop
'---------- end Launcher.vbs ----------


Notes: This builds on and refines the example at:
http://groups-beta.google.com/group....vbscript/browse_frm/thread/fb37977720411d57/
Although that example showed how to detect whenever instances of IE
launched, it was incomplete for the purpose of monitoring web pages
because it did not show how to monitor those instances of IE.

The recommended way to hook into each IE is to use a BHO (Browser
Helper Object), but I haven't been able to get that working using
VB5CCE. If someone can adapt this example (with instructions, that
would be cool). Note that about 1/3 of the code is only used to ensure
that we have only a single instance of the OCX so we don't duplicate
the IE monitoring.

Unfortunately, while one may completely transform every part of the
underlying page, including layout, objects, and code, I have not found
a simple way to get data back from a window.showModalDialog (where I'm
the one creating the modal dialog). I find this unreasonable.
 
C

Csaba Gabor

'------------ start User ctl: hook ------------
'Csaba Gabor from Vienna
Const gUniqueTitle = "Global Hook Semaphore" 'for ogIE
'global semaphore for single instanceness
Dim ogIE As SHDocVw.InternetExplorer
Public duplicateP As Boolean 'So Launcher.vbs knows to abort
Public keepLooping As Boolean 'So Launcher.vbs knows when to stop

'collection of all IEs + creation/deletion events
Dim WithEvents ogSW As SHDocVw.ShellWindows
Dim oDictIE As New Dictionary 'hooked IEs

Private Declare Function WaitMessage Lib "user32" () As Long
'http://groups-beta.google.com/group....vbscript/browse_frm/thread/e39c85a45dc127d0/
Public m_oCallback As Object 'Launcher.vbs callback (on ogIE close)
'another callback mechanism follows
Public Event NewWin(lCookie As Long, oevtIE As Object)

Private Sub UserControl_Initialize()
Set ogIE = getSemaphoreIE 'semaphore / data object
If duplicateP Then Exit Sub
'allows notifications / iteration through IEs
Set ogSW = New SHDocVw.ShellWindows
hookIEs
keepLooping = True
End Sub

'Idea from
'http://groups-beta.google.com/group/comp.lang.basic.visual.misc/browse_frm/thread/c54dd092fa4590b0/
Public Sub WaitForMsg(): WaitMessage: End Sub
Public Sub DoEvent(): DoEvents: End Sub

Private Sub hookIeEvents(oDictIE As Dictionary, _
oIE As SHDocVw.InternetExplorer)
'allow us to be notified of events in this IE
Dim ieEvents As New clsIeEvents
Set ieEvents.ieEvent = oIE
Set oDictIE.Item(oIE.hWnd) = ieEvents
End Sub

Private Sub hookIEs()
'hook all unhooked IEs
Dim oIE As SHDocVw.InternetExplorer, foundP As Boolean, hWnd
Dim dctCompare As New Dictionary
For Each oIE In ogSW 'loop through all windows
'Ensure each IE is hooked, except the global semaphore (ogIE)
If ogIE.hWnd <> oIE.hWnd Then
If oDictIE.Exists(oIE.hWnd) Then
'Already hooked section. but 'Open link in
'new window' results in same HWND, which seems
'to be later killed then resurrected as new window
If Not dctCompare.Exists(oIE.hWnd) Then _
dctCompare.Add oIE.hWnd, True
Else
hookIeEvents oDictIE, oIE 'place the hook
dctCompare.Add oIE.hWnd, False 'dcumnt the new hook
End If
End If
Next
'Now (proper bookkeeping) we have to kill useless entries
For Each hWnd In oDictIE.Keys
If Not dctCompare.Exists(hWnd) Then
Set oDictIE.Item(hWnd) = Nothing
oDictIE.Remove hWnd
End If
Next
Set dctTmp = Nothing
End Sub

' IE/Explorer window creation/destruction events
Private Sub ogSW_WindowRevoked(ByVal lCookie As Long)
Dim oIE, oCall As Object, foundP As Boolean
For Each oIE In ogSW 'loop through all windows
If TypeName(oIE.Document) = "HTMLDocument" Then
foundP = (oIE.Document.title = gUniqueTitle)
If foundP Then Exit For
End If
Next
'If semaphore ogIE missing, callback to Launcher.vbs
If foundP = False Then
Set oCall = m_oCallback
oCall "We are done"
Set oDictIE = Nothing
Exit Sub 'cause we're terminating
End If

hookIEs 'cleanup
'Increment killed window cnt - shows way to store data on ogIE
With ogIE.Document.parentWindow: .kCtr = .kCtr + 1
MsgBox "Killed Window " & .kCtr & vbCrLf & lCookie: End With
End Sub

Private Sub ogSW_WindowRegistered(ByVal lCookie As Long)
'lCookie seems to be incremented each time a new window is created.
'Window has same upon closing, but otherwise lCookie inaccessible
With ogIE.Document.parentWindow: .nCtr = .nCtr + 1: End With
hookIEs
RaiseEvent NewWin(lCookie, ogIE) 'prove that we noticed
End Sub

Function getSemaphoreIE()
'This creates/finds semaphore, ogIE, by unique title
'It also shows extant window stats
Dim oShApp, titl, Doc, oIE
Dim strRes 'stats for extant windows
Set oShApp = CreateObject("Shell.Application")
'determine whether ogIE exists; get extant window stats if not
For Each oIE In oShApp.windows
If TypeName(oIE.Document) = "HTMLDocument" Then
titl = oIE.Document.title
'If ogIE already exists, we abort
If titl = gUniqueTitle Then _
duplicateP = True: _
Set getSemaphoreIE = oIE: Exit Function
strRes = strRes & "IE: " & oIE.LocationURL
If titl <> "" Then strRes = strRes & " => " & titl
Else
'not (yet) an IE/HTML document
strRes = strRes & " " & oIE.LocationURL
End If
strRes = strRes & vbCrLf
Next 'oIE
MsgBox strRes, , "Current Windows - at end of getSemaphoreIE"
'all OK, so create semaphore ogIE
Set getSemaphoreIE = newIEinBackground(gUniqueTitle)
End Function

Function newIEinBackground(title)
'http://groups-beta.google.com/group....vbscript/browse_frm/thread/b5a4788bb2dacc09/
Dim ie, myPage As String
myPage = "Javascript:'<html><head><title>" & title & _
"</title></head>" & "<body><script " & _
"type=""text/javascript"">nCtr=0;kCtr=0" & _
"<\/script>Close page to terminate app</body></head>'"

Set ie = CreateObject("InternetExplorer.Application")

'next line for dev-makes it not in background and allows easy exit
ie.Visible = True
ie.Navigate ("about:blank") 'workaround for MS bug in next line
ie.Navigate (myPage) 'creates nCtr, kCtr: new/killed IEs
Do Until ie.ReadyState = 4: DoEvents: Loop 'allow to finish
ie.Document.parentWindow.opener = "me" 'allows self.close()
Set newIEinBackground = ie
End Function
'------------- end User ctl: hook -------------
 
C

Csaba Gabor

'------------ start class: clsIeEvents ------------
'Csaba Gabor from Vienna
'http://groups-beta.google.com/group...velopment/browse_frm/thread/54cc6e68ee0e9c55/
'http://groups-beta.google.com/group...velopment/browse_frm/thread/54cc6e68ee0e9c55/
'http://groups-beta.google.com/group...velopment/browse_frm/thread/54cc6e68ee0e9c55/
'http://groups-beta.google.com/group...velopment/browse_frm/thread/b1c0ad120c72fe59/
'http://groups-beta.google.com/group...velopment/browse_frm/thread/b1c0ad120c72fe59/

Option Base 0
Option Explicit
Public WithEvents ieEvent As SHDocVw.InternetExplorer

Private Sub ieEvent_DownloadComplete()
'http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp

Dim Matches, Match, DOMelem As Object, RE As Object
Set RE = CreateObject("VBscript.RegExp")
RE.Pattern = "http://[^/]*\.google\.(com|..)/"
RE.Global = True 'Is this necessary/useful?

If (Me.ieEvent.ReadyState >= 3) Then
Set Matches = RE.Execute(Me.ieEvent.LocationURL)
For Each Match In Matches
revisePage Me.ieEvent 'only revise google pages
Exit Sub
Next
End If
End Sub

Sub revisePage(ie As SHDocVw.InternetExplorer)
'When you click Search in Google, you'll be asked to reconfirm
'First part just ensures expected page structure
If TypeName(ie.Document) = "Nothing" Then Exit Sub
If TypeName(ie.Document.getElementById("myGoog")) <> "Nothing" _
Then Exit Sub
If TypeName(ie.Document.getElementsByName("btnG")) = "Nothing" _
Then Exit Sub
Dim aBtnG, i, oScript, fctn, modPage, uid
Set aBtnG = ie.Document.getElementsByName("btnG")
'Simple confirm dialog
fctn = "function btnOverride() {" & vbCrLf & _
" var okP = confirm('Are you sure?'); " & vbCrLf & _
" return(okP); }"
For i = 0 To aBtnG.length - 1
If aBtnG(i).tagName = "INPUT" Then
If aBtnG(i).Type = "submit" Then
'If we have a submit button named btnG
'We first create a new script element
Set oScript = ie.Document.createElement("SCRIPT")
oScript.Id = "myGoog"
oScript.Text = fctn
ie.Document.documentElement.appendChild (oScript)
'Now we attach the onclick behaviour to js func
uid = aBtnG(i).uniqueId
aBtnG(i).Id = uid
ie.Document.parentWindow.setTimeout _
"document.getElementById('" & uid & _
"').onclick=btnOverride;", 10
Exit Sub
End If
End If
Next
End Sub
'------------- end class: clsIeEvents -------------
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top