I'm hoping someone can help. I'm trying to speed up the process of filling a webform that must be completed dozens or hundreds of times with information stored in excel.
To do this, I need one button to open an IE window and navigate to a certain website's login page (I've figured this bit out). The user can then log in and navigate to the form that needs to be filled. Then, I'd like the user to be able to return to the excel page, click another button, which will automatically fill several drop downs and text boxes.
Within Excel, I already have some code to allow the user to search for the particular set of information that needs to go to the form, so all they should have to do is click the button to transfer it over. The first bit of the code is just this:
Public IE As Object
Public Sub OpenIE()
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
IE.Navigate "loginpage url"
End Sub
Where I'm having trouble, however, is having a different function access the same IE window once the user has logged in and navigated to the form. Right now I've got this:
Sub FillMacro()
Dim sh As Object, oWin As Object
Set sh = CreateObject("Shell.Application")
For Each oWin In sh.Windows
If TypeName(oWin.document) = "HTMLDocument" Then
Set IE = oWin
Exit For
End If
Next
IE.Visible = True
Application.Wait (Now + TimeValue("00:00:01"))
IE.document.getElementById("idec").Value = "John"
Application.Wait (Now + TimeValue("00:00:01"))
IE.document.getElementById("idee").Value = "Smith"
End Sub
Most of that I've gotten from other posts on this forum, and while I'm a bit of a novice at this, the problem seems to be that for some reason VBA can't find the text boxes with the id of LastName or FirstName. What's more, the IE.Visible = True doesn't bring the IE window back to the foreground, so I'm trying to find the proper line to do that. When I try to run the code, I get an "Object Required" error at:
IE.document.getElementById("idec").Value = "John"
I've tried searching this site and will continue to look, but any help in the meantime would be greatly appreciated!
On the Internet Explorer page, here is the line for the first text box I'm trying to fill:
<input name="componentListPanel:componentListView:1:component:patientLastNameContainer:patientLastName" class="input300" id="idec" type="text" maxlength="60" value="">
Why not automate logging process as well? Login could be stored in Excel and its value read by macro from cell.
As Tim Williams suggests, if there is Iframe on website, use (for me it works only with contentwindow included):
IE.document.getElementById("iFrameIdHere").contentwindow.document.getElementById("idec").Value = "John"
Instead of Application.Wait use:
Do Until IE.ReadyState = 4 And IE.Busy = False
DoEvents
Loop
It will save you a lot of time when page loads fast and prevent errors when loading exceeds wait time. Use it ONLY after page reloads (meaning after navigating or anything what causes page reloads, especially .click on HTML elements.
Use early binding, it's a bit faster than creating objects. It can increase performance by a few percent based on page loading speed, the faster pages load, the bigger increase.
Set IE = New InternetExplorer
Finally, you can toggle loading pictures, depending on whether you need to download images from website.
Public Sub ShowPictures(ByVal EnabledStatus As Boolean)
Public ScrapingCancelled as Boolean
Dim obj_Shell
Dim v_Result As Variant
Set obj_Shell = CreateObject("WScript.Shell")
'Reads the registry key that determines whether 'Show pictures' Internet Explorer advanced setting is enabled
v_Result = obj_Shell.RegRead("HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Display Inline Images")
Select Case v_Result
Case "yes" 'Pictures are displayed
If EnabledStatus = False Then _
obj_Shell.RegWrite "HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Display Inline Images", "no", "REG_SZ"
Case "no" 'Pictures are not displayed
If EnabledStatus = True Then _
obj_Shell.RegWrite "HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Display Inline Images", "yes", "REG_SZ"
Case Else
ScrapingCancelled = True
End Select
End Sub
No images loaded:
ShowPictures (0)
Images loaded:
ShowPictures (1)
A good practice is to set value to 1 in the end of macro.
Related
So I've probably spent the last week researching/trying to fix this annoying error but to no avail. Here's what I'm trying to do and the error I'm getting:
I have a userform that contains 12 multipage pages and to access each one I have coded previous/next buttons. Now, One of these pages is disabled through properties to hide from the user, that is until a specific checkbox is clicked. When it's clicked, the page is enabled, and the user is now able to view this page as well as all the others. when the checkbox is clicked again (making it false and no longer true), the page hides from the user's sight. This is basically what I'm trying to accomplish. The pages are like so:
page1, page2, page3, page4, "page5", page6, etc.
As you can see, page5 is what is being disabled/enabled. Now also, something important to note, I've made the userform to appear before the workbook is actually visible using this:
Private Sub Workbook_Open()
Application.ScreenUpdating = False
ThisWorkbook.Application.Visible = False
OpeningWindow.Show
Windows(ThisWorkbook.Name).Visible = True
Application.Visible = True
Application.ScreenUpdating = True
End Sub
This I would like to avoid changing because it's vital that the userform appear first before the workbook because the workbook is acting as the storage/database for everything that is being typed in the userform using this multipage system. Now when I run the userform through VBA and test out this function "checkbox_click Enables/Disables multipage page", it works no problem. When I attempt to open it (as if you were starting up excel without anything opened) everything works fine, until I click the checkbox in question to enable the multipage to make it visible. This is where I'm getting the error "The object invoked has disconnected from it's clients". Now I've tried some other things out to see if I could get the same result:
Made the multipage page visible = False instead of Enabled = false.
Result: This somewhat works, however when attempting to click the previous button to go back a page (Page6 to Page5) when Page5 is visible = False, The previous button doesn't respond, as if it knows that Page5 is there even though it's invisible.
Anyway, to wrap things up, I would like to ask the community here if anyone knows exactly why, from the code I've provided below that is responsible for this "page enable/disable feature", I'm getting this object invoked has disconnected error and if there's a way to fix it.
Private Sub CheckBox119_Click()
If CheckBox119.Value = True Then
Me.MultiPage1.Pages(5).Enabled = True
CheckBox138.Value = True
Label309.Visible = True
Else
Me.MultiPage1.Pages(5).Enabled = False
CheckBox138.Value = False
Label309.Visible = False
End If
End Sub
CheckBox138 btw is located on Page5 and is there if the user wishes to click it to disable page5 and Jump to Page4, which is this code:
Private Sub CheckBox138_Click()
If CheckBox138.Value = False Then
MultiPage1.Value = 4
CheckBox119.Value = False
Label309.Visible = False
End If
End Sub
Also, I'm relatively new to coding in VBA, but I'm always ready to learn.
After some playing around, I believe I figured out what the problem was. The checkbox138 in the disabled page was the culprit. By deleting the code Private sub checkbox138_click(), it works now. I'm not entirely sure why this is the case (so someone with more knowledge may be able to explain) But When checkbox119 is clicked, checkbox138 is suppose to turn true being that's what the initial code expressed. However, even though making checkbox119 true is suppose to enable the disabled page followed by making checkbox138 true, there seems to be a hiccup. It seems checkbox138 is thinking the disabled page is still disabled (even though checkbox119 is suppose to enable it) therefore making the checkbox138 hidden.
My goal is to create a VB application that reads and writes information to various webpages loaded in Internet explorer.
I have created a program that works exactly as I intend in VBA. I am now trying to re-implement the same programming in VB.
I have a function that looks for and returns an Internet Explorer Object where the input matches the LocationName.
Assuming the target page is loaded, I can work with it. Methods such as getElementByID() work perfectly. If the browser window is closed and reopened, and the code is run again, the results are very inconsistent. The getIE function seems to work, but when trying to use methods like document.getElementByID() a NullReferenceException is thrown.
Does anyone know if there is anything I am missing that I need to include to get the document property to update?
EDIT: I have looked over the NullReferenceException article. It hasn't helped me unfortunately. In case I was unclear in my wording, I would like to reiterate that the same bit of code yields a different result when executed the 2nd time under the same conditions (same webpage open in Internet Explorer).
On the second execution IE.locationName is retrievable but IE.Document.title is not. The problem is definitely with the Document property as far as I can tell. I am truly stumped.
Many thanks
Public Function getIE(targetTitle As String) As SHDocVw.InternetExplorer
' Create the shell application and Collection of open windows
Dim shellObj As Object = CreateObject("Shell.Application")
Dim shellWindows As SHDocVw.ShellWindows = shellObj.Windows()
getIE = Nothing
' Scan through the Collection
For I = (shellObj.Windows.Count - 1) To 0 Step -1
' If found, assign this to the function output and exit early
If InStr(shellWindows(I).LocationName, targetTitle) Then
getIE = shellWindows(I)
Debug.Print("Found: " & shellWindows(I).LocationName)
Exit For
End If
Next I
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim IE As SHDocVw.InternetExplorer
IE = getIE("my site title")
If IE Is Nothing Then
Debug.Print("Site not open")
Exit Sub
End If
' The code always gets this far, and despite the page being found, starts throwing exceptions from here on if the window has been closed and reopened whilst my application has stayed running.
' Sample form data insertion code
IE.Document.getElementById("textbox1").value = "my value"
' Click the submit button
IE.Document.getElementById("submit").click()
' Wait for page to load
While IE.Busy
End While
IE.Document.getElementById("textbox2").value = "my 2nd value"
' done
IE = Nothing
End sub
I was searching a lot on the web and found your website very helpfull - that is why I have not posted anything yet, just reading.
However, I need your advice in the following step of my project. The porject is to automate the creation/placing of ad for selling goods.
The website is http://olx.bg/adding/
What I am failing to do through VBA is to press the blue square called "ДОБАВИ СНИМКА" which stands for "ADD PHOTO" in the form.
Once pressed a default BROWSE window opens to select the file.
Thanks in advance!
Add two references to your VBA project:
Microsoft Internet Controls
Microsoft HTML Object Library
The HTML behind the button has an id = add-files which makes it pretty easy to locate using getElementById
Here is a working example:
Sub PushAddPhotoButton()
Dim IEexp As InternetExplorer
Set IEexp = New InternetExplorer
IEexp.Visible = True
IEexp.navigate "http://olx.bg/adding/"
Do While IEexp.readyState <> 4: DoEvents: Loop
Dim photoButton As HTMLInputElement
Set photoButton = IEexp.Document.getElementById("add-files")
If (Not photoButton Is Nothing) Then
photoButton.Click
Else
MsgBox "Button not found on web page."
End If
IEexp.Quit
Set IEexp = Nothing
End Sub
If your IE security settings are on 'High' for Internet then you won't get a dialog or an error. The IE window will simply flash and then be gone.
Change to:
I am really struggling to get my VBA code to wait until a website I am accessing, finishes an internal search it is performing.
So far I have tried:
Do While ie.Busy: DoEvents: Loop
Set doc = ie.document
Do While doc.readyState <> "complete": DoEvents: Loop
but it does not work. I could post the html 'refresh' function if that would help?
Any ideas would be greatly appreciated.
EDIT
It is using the following function which I have gathered from the source code:
var limit="5";
function beginrefresh()
{
if (!document.images) return;
if (limit==1)
document.getElementsByTagName("Form")[0].submit();
else
{
limit-=1;
window.status="Page will refresh in " + limit + " seconds.";
setTimeout("beginrefresh()",1000);
}
}
window.onload = function () { beginrefresh(); }
EDIT
I have cracked it and it was really simple in the end. I used the following to search for a known id on the page that was being loaded. So when it found this id it knew that the code could continue. A very useful technique IF you know an the name of an id on the page loading.
Do
Do While ie.Busy: DoEvents: Loop
Set doc = ie.document
Do While doc.readyState <> "complete": DoEvents: Loop
Set testobject = ie.document.getElementById("changeFilterCriteria")
If Not testobject Is Nothing Then Exit Do
Loop
EDIT
Or maybe not - it does work for about 5 or 6 times in a row but then crashes on
Set testobject = ie.document.getElementById("changeFilterCriteria")
IE comes up with 'internet explorer cannot display the webpage',
If I then click back and then refresh the page is still performing its internal search.
Any help would be gratefully received!
Jim
This is a tricky situation that I have come across numerous times as well. The traditional "Do...Until" loop with IE.ReadyState doesn't work after the first time the page is accessed, mostly because IE and Excel are out of synch at that point. The best approach I've found is to use a Boolean flag in a "Do...If" loop, where the flag is triggered by a reference to an element in the page you're refreshing. Until the page refreshes, the flag will be in one state. As long as the flag is in that state, the Do loop continues. So, somewhere after you've instructed the page to refresh, insert the following code (be sure to adapt for your specific circumstance):
Dim flag as Boolean: flag = False
Dim lng_cnt as long: long_cnt = 0
Dim elem_temp as IHTMLElement: Set elem_temp = IE.document.getElementById("*element_name*")
Do Unitl flag = True or lng_cnt = 30
If elem_temp Is Nothing Then
flag = False
Else
flag = True
Exit Do
End If
lng_cnt = lng_cnt +1
Application.Wait (Now() + TimeValue("00:00:01"))
Loop
Note: I added the counter (lng_cnt) to prevent infinite looping. It instructs the program to wait 1 second before continuing if the page hasn't loaded yet, and will discontinue after 30 seconds; because you never know if the page's server is down or whatever. Hope this helps.
I'm trying to create a toolbar in Internet Explorer 8 & 9. The main functionality of this is to launch a in-house product when clicked on the icon and convert document accordingly. However, I'm having some difficulty using mshtml.HTMLDocument. Please take a look at my code and advise.
Thanks for helping out
' Get the hWnd value of the IE window that the macro button was clicked within
myhWnd = GetForegroundWindow()
MsgBox(myhWnd)
' for all IE window within the shell window, by the way this
' includes Windows Explorer as well as IE windows!
Dim IE As New SHDocVw.InternetExplorer
Dim SWs As New SHDocVw.ShellWindows
For Each IE In SWs
Doc = IE.Document ' Set the active document
Functions.OutputDebugString("setting active document for printing using set Doc in main")
If TypeOf IE.Document Is mshtml.HTMLDocument Then ' if the document is IE then proceed because EXPLORER window returns in the same group - (This is where I'm having trouble. It keep saying 'Document is not an HTML)
MsgBox("I'm here")
If (IE.HWND = myhWnd) Then ' ENABLE IT AFTER TESTING
Functions.OutputDebugString("JOB SUBMITTED TO QUEUE NOW. PLEASE WAIT.DO NOT CLICK BUTTON AGAIN TO INCREASE SPEED OF OPERATION...")
IE.StatusText = "JOB SUBMITTED TO QUEUE NOW. PLEASE WAIT.DO NOT CLICK BUTTON AGAIN TO INCREASE SPEED OF OPERATION..."
Functions.OutputDebugString("Verifying if the page is still downloading or not.. Do not process if its still downloading, wait in loop for 500 mili seconds")
If Not (IE.Busy = True) Then ' if IE is not currently downloading page then proceed
loopBackfromBusy:
Functions.OutputDebugString("Check in registry about exclusing access to printer")
processFlag = saveFileName(IE.LocationName, Doc.url) ' pass the document URL to give user more informative error messages.
Functions.OutputDebugString("Form_Load() - process flag = ")
If processFlag = True Then ' if no other window is printing then proceed
Functions.OutputDebugString("FILE SUBMITTED FOR PRINTING in form load")
IE.StatusText = "FILE SUBMITTED TO PRINTER FOR PRINTING. DO NOT CLICK BUTTON AGAIN AND WAIT FOR OPERATION FINISH..."
Functions.OutputDebugString("Initializing printer settings using initializeValues()")
intilizeValues(Doc.title) ' initialize printer settings and set everything ready for printer
DefaultPrinter = Printer.PrinterName ' store current default printr name
MsgBox(PrinterName)
Functions.OutputDebugString("Changing current default printer to Print Driver.. Note that this name is case sensitive and must be available on user machine or this software will not work correctly...")
SetDefaultPrinterMyWinNT(PrinterName) ' set the default printer to KC
Functions.OutputDebugString("Fired printing using default IE command")
IE.ExecWB(17, 0) '.... print the document silently
Else
MsgBox("Document is not an HTML document")
End If
The issue is here : If TypeOf IE.Document Is mshtml.HTMLDocument . It keep saying document is not an HTML document. I'll appreciate any suggestion or recommendation.
UPDATE :
Have changed the If condition to
If IE.Name = "Windows Internet Explorer" Then
If (IE.HWND = myhWnd) Then
MsgBox(IE.locationURL)
The above works fine however its not executing the code after which is
IE.StatusText = "JOB SUBMITTED..."
I decided not to check via this If TypeOf IE.Document Is mshtml.HTMLDocument. This worked If IE.Name = "Windows Internet Explorer"