|
Subject:
|
Problems using MSHTML
|
|
Posted By:
|
maccas
|
Post Date:
|
11/1/2004 9:40:37 AM
|
Hi all,
I'm currently working on an application based in Excel's VBA which uses the little-documented MSHTML COM tlb. The app logs onto a webiste for me, navigates through various pages and then downloads certain information from the website and uploads the information into my spreadsheet.
The program used to run absolutely fine, looping through all the buttons on the download page until it found the desired download button and then telling the website that the button had been clicked by using htmlButton.Click(). However Windows XP SP 2 detects this sort of behaviour as programatic downloading, which is now specifically blocked. For various reasons I'd rather not modify my security settings such that this behaviour is no longer blocked.
So I came up with the idea of using Win API calls to move the mouse over the button and force a click (Interet Explorer can't distinguish this behaviour from user action and so this is not blocked). I have worked a solution where I position and size the Internet Explorer window on my screen such that I know which pixel location need to move the mouse to in order to find the download button. The problem is that the layout of the website might change.
I've had a look through the object browser and found the TransformPoint method of the IDisplayServices interface which would appear to give me the ability to get a run-time co-ordinate location of any webpage element. However, I can't initialise the IDisplayServices object and consequently can't use its methods. Could anyone point me in the right direction or suggest a better way round this?
NB the MSDN website seems to suggest I need to use the QueryInterface method on IHTMLDocument2 but I get a compile error: Interface method restricted when I try to use it.
Any help would be appreciated, Maccas
|
|
Reply By:
|
maccas
|
Reply Date:
|
11/3/2004 7:04:48 AM
|
I'm not sure if anyone cares but for completenesses sake I thought I'd post the answer to my earlier query.
It looks like the IDisplayServices interface can't be accessed when scipting with VBA - you have to be using C / C++. There were sevral more obstacles to getting this done but in answer to my specific query I needed to use the getBoundingClientRect mthod on the HTMLInputElement.
Below is some sample code which demonstrates the desired method by opening an internet explorer window, navigating to Google, putting something in the search box, finding the location of the search button, moving the move over the button & simulating a mouse click.
To get the code running in a VBA project you'll need to add references to Microsoft HTML Object Library (MSHTML) & Microsoft Internet Controls (SHDocVW).
Anyway, enjoy ...
Option Explicit
Private Type myRECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Enum CoordType
Absolute
Pixels
End Enum
' User32 API functions used to determine screen resolution
Declare Function GetDesktopWindow Lib "User32" () As Long
Declare Function GetWindowRect Lib "User32" (ByVal hWnd As Long, rectangle As myRECT) As Long
' User32 API Mouse functions
Private Declare Sub mouse_event Lib "User32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
' Mouse Event Flags
Const MOUSEEVENTF_LEFTDOWN = &H2
Const MOUSEEVENTF_LEFTUP = &H4
Const MOUSEEVENTF_MOVE = &H1
Const MOUSEEVENTF_ABSOLUTE = &H8000
Sub Test()
Dim IExp As SHDocVw.InternetExplorer
Dim hDoc As MSHTML.HTMLDocument
Dim hCol As MSHTML.IHTMLElementCollection
Dim hInp As MSHTML.HTMLInputElement
Dim hPoint As MSHTML.tagPOINT
Set IExp = New SHDocVw.InternetExplorer
IExp.Visible = True
IExp.navigate "http://www.google.co.uk"
Do Until IExp.Busy = False
DoEvents
Loop
Set hDoc = IExp.document
' Find the "search for" input box on the page
Set hCol = hDoc.getElementsByTagName("input")
For Each hInp In hCol
If hInp.Name = "q" Then
hInp.Value = "Test" ' Put in something to look for
Exit For
End If
Next hInp
' Find the search button on the page
Set hCol = hDoc.getElementsByTagName("input")
For Each hInp In hCol
If hInp.DefaultValue = "Google Search" Then
' Scroll IE to top left hand corner
hDoc.parentWindow.scroll 0, 0
' Get coordinate of button
hPoint = GetCoord(hDoc, hInp, Absolute)
' Move mouse
mouse_event MOUSEEVENTF_ABSOLUTE + MOUSEEVENTF_MOVE, hPoint.X, hPoint.Y, 0, 0
' Simulate click
mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
Exit For
End If
Next hInp
End Sub
Private Function GetCoord(hDoc As MSHTML.HTMLDocument, hEle As MSHTML.HTMLInputElement, Output As CoordType) As MSHTML.tagPOINT
Dim ScreenRes As MSHTML.tagPOINT
Dim hIRectEle As MSHTML.IHTMLRect
Dim Point As MSHTML.tagPOINT
' Get the screen resolution
ScreenRes = GetScreenResolution
Set hIRectEle = hEle.getBoundingClientRect
' Find middle of input element
Point.X = (hIRectEle.Left + hIRectEle.Right) / 2
Point.Y = (hIRectEle.Top + hIRectEle.Bottom) / 2
' Add in offset for where the internet explorer window is located on screen
Point.X = Point.X + hDoc.parentWindow.screenLeft
Point.Y = Point.Y + hDoc.parentWindow.screenTop
' Convert to absolute coords, if necessary
If Output = Absolute Then
Point.X = (Point.X / ScreenRes.X) * 65000
Point.Y = (Point.Y / ScreenRes.Y) * 65000
End If
GetCoord = Point
End Function
Function GetScreenResolution() As MSHTML.tagPOINT
Dim R As myRECT
Dim hWnd As Long
Dim RetVal As Long
' Win API calls
hWnd = GetDesktopWindow()
RetVal = GetWindowRect(hWnd, R)
GetScreenResolution.X = (R.Right - R.Left)
GetScreenResolution.Y = (R.Bottom - R.Top)
End Function
|
|
Reply By:
|
savigans@yahoo.com
|
Reply Date:
|
2/15/2006 10:32:23 AM
|
Hello Maccas,
First of, that was a great article, very useful to me. Thanks!
I'm having some problem with it which I was wondering if you could help me out with. I got the following exception when trying to get the parent window of the current document. Any ideas why? Your help is greatly appreciated.
Thanks, -Savitha
Unhandled Exception: System.Runtime.InteropServices.COMException (0x80040154): Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)) at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) at mshtml.HTMLDocumentClass.get_parentWindow() at ConsoleApplication1.Module1.GetCoord(HTMLDocument hDoc, HTMLInputElement hEle, CoordType Output) in C:\New\ConsoleApplication1\ConsoleApplication1\Module1 .vb:line 95 at ConsoleApplication1.Module1.Test() in C:\New\ConsoleApplication1\ConsoleApplication1\Module1.vb:line 64 at ConsoleApplication1.Module1.Main() in C:\New\ConsoleApplication1\ConsoleApplication1\Module1.vb:line 110 Press any key to continue . . .
|