So I have this function that returns a value from a web page. The issue with this is that it works perfectly when I run it single step, but when I run it normally it returns another value and objIE.Quit is skipped. This is the code:
Private Function Mexico(partida As String) As String
partida = Left(partida, 8)
Set objIE = New InternetExplorer
objIE.Visible = True
objIE.navigate "http://www.siicexcaaarem.org.mx/Bases/TIGIE2007.nsf/4caa80bd19d9258006256b050078593c/$searchForm?SearchView"
Cargar
objIE.document.getElementsByName("Query")(0).Value = partida
For Each boton In objIE.document.getElementsByTagName("input")
If boton.Value = "Search" Then
boton.Click
Exit For
End If
Next
Cargar
Application.Wait Now + TimeValue("00:00:03")
Dim temp As String
Dim i As Integer
For Each t In objIE.document.getElementsByTagName("tr")
If t.className = "domino-viewentry" Then
temp = t.Children(8).innerText
End If
Next
If InStr(temp, "*") > 0 Then
temp = Left(temp, Len(temp) - 1)
End If
If InStr(temp, "%") = 0 Then
temp = temp & "%"
End If
Mexico = temp
objIE.Quit
End Function
And I am testing this with this sub:
Sub Mex()
MsgBox Mexico("33030001")
End Sub
When I run it single step, it returns "15%" with the parameter passed in the sub, while it returns just "%" when I run it normally with any given parameter. Any idea why is this happening? Any help will be appreciated.
Note: objIE is defined as a public variable, but this has not brought me any inconveniences so far, as I have other functions working properly for different websites. Also, Cargar is the usual "wait until page has loaded" instruction.
Private Sub Cargar()
Do Until objIE.Busy = False And objIE.readyState = 4
DoEvents
Loop
End Sub
You should ensure that your webpage is completely loaded before trying to grab objects off the webpage. Your line containing Application.Wait does not do this for you.
Add this sub into your module:
Sub ieBusy(ByVal ieObj As InternetExplorer)
Do While ieObj.Busy Or ieObj.readyState < 4
DoEvents
Loop
End Sub
then replace your line continaing the Application.Wait with: ieBusy objIE
So after messing with the code for hours and realizing that, sadly, the solution wasn't so simple as fixing the Cargar Load IE sub (as the page doesn't "load" as it's JavaScript driven), I found out that this was the solution:
For Each t In objIE.document.getElementsByTagName("tr")
DoEvents 'Holy fix
If t.className = "domino-viewentry" Then
temp = t.Children(8).innerText
End If
Next
I am quite unsure as why this fixed it, and came up with this while checking with msgboxes which parts of the code were not running correctly, and the msgbox inside the loop fixed it too. I'd appreciate your comments as to why this works.
Related
Some background, I had a VBA loop creating PPT slides with various filters/views on an Excel pivot table. It was working (after I added DoEvents). I have recently added functionality to create a new PPT file from scratch with multiple sides before they are populated with the data. It's not working anymore.
Two theories:
1) Somehow the memory got bogged down in the new PPT file creation loop and now the data population loop is erroring out.
2) Something about how the default chart is formatted is messed up. If I edit the charts manually, save, and populate, there is no error. However if I create and then automatically try to populate, there's an error.
Due to complexity of the scripts, the loop to create the slides is completely separate from the loop to reopen and populate the slides.
Here's the section that errors out:
'Paste the final temp dataset into PPT
Range("A1000").Activate
tempdata = Range(Selection, Selection.Offset(months, categories - 1)).Value
Set oChart = oPres.Slides(pages(b)).Shapes(metrics(a)).Chart
oChart.ChartData.Activate
Set wb = oChart.ChartData.Workbook
Set ws = wb.Worksheets(1)
ws.Range("A1:Z1000").ClearContents
ws.Range("A1", Range("A1").Offset(months, categories - 1)).Value = tempdata
'Let code catch up
Application.Wait (Now + TimeValue("00:00:02"))
DoEvents
'Redraw the selected dataset of the chart based on the # of categories and rows
oChart.SetSourceData Source:="='Sheet1'!$A$1:" & toChar(categories + 0) & months + 1, PlotBy:=xlColumns
Despite using both Application.Wait and DoEvents, it is still hanging up.
This is purely a timing issue because if I click Debug and continue running the code with no changes, it works fine. I am also using late binding (maybe?) through the Set Object statement and at the end of the loop I always Set oChart = Nothing.
Sometimes it works to write DoEvents multiple times, but as the process has gotten more complex, even this doesn't work. I'm all out of ideas. Any suggestions?
'Let code catch up
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
DoEvents
'Redraw the selected dataset of the chart based on the # of categories and rows
oChart.SetSourceData Source:="='Sheet1'!$A$1:" & toChar(categories + 0) & months + 1, PlotBy:=xlColumns
You may try:
Using Sleep, with this line at the top of your module (outside of your function):
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Then add this line in place of, or in addition to, DoEvents:
Sleep 1 ' Pause for 1 ms
See:
https://stackoverflow.com/a/3891017/2707864
See also:
https://www.myonlinetraininghub.com/pausing-or-delaying-vba-using-wait-sleep-or-a-loop
Using loops with DoEvents:
Dim PauseTime, Start, Finish, TotalTime
PauseTime = 4 ' Set duration.
Start = Timer ' Set start time.
Do While Timer < Start + PauseTime
DoEvents ' Yield to other processes.
Loop
Finish = Timer ' Set end time.
TotalTime = Finish - Start ' Calculate total time.
See:
https://www.mrexcel.com/forum/excel-questions/36052-when-how-use-doevents-solved-post166114.html#post166114
See also:
https://www.myonlinetraininghub.com/pausing-or-delaying-vba-using-wait-sleep-or-a-loop
Using combinations thereof, which can improve performance of your system depending on the wait time.
Public Sub WaitSeconds(intSeconds As Integer)
On Error GoTo PROC_ERR
Dim datTime As Date
datTime = DateAdd("s", intSeconds, Now)
Do
Sleep 100
DoEvents
Loop Until Now >= datTime
PROC_EXIT:
Exit Sub
PROC_ERR:
MsgBox "Error: " & Err.Number & ". " & Err.Description, , "modDateTime.WaitSeconds"
Resume PROC_EXIT
End Sub
See:
http://www.fmsinc.com/microsoftaccess/modules/examples/avoiddoevents.asp
#sancho.s, thanks for your help. So it turns out the error had nothing to do with DoEvents. I had been using that as a sloppy fix without understanding its functionality. Given that, none of the three options worked. I spent all day trying various combinations with no success. Instead, I had to brute force close the embedded PPT workbook, set oChart to Nothing, reinstantiate oChart, reopen the workbook, and close it again.
This made the process 2x slower (but no slower than forcing it to wait on a timer??), and it completely eliminated all errors. Apparently it just didn't like pasting the raw data and reselecting the data the first time the workbook was opened. No idea why.
Sub UpdateChart(ByVal a As Integer, ByVal b As Integer, ByVal months As Integer, ByVal categories As Integer, ByRef pages() As Integer, ByRef metrics() As String, ByVal oPres As Object, ByVal legend_flag As Boolean)
Dim tempdata As Variant
'Paste the final temp dataset into PPT
tempdata = Range(Worksheets("calc").Range("A1000"), Worksheets("calc").Range("A1000").Offset(months, categories - 1)).Value
If legend_flag Then
Set oChart = oPres.Slides(pages(b)).Shapes("legend").Chart
Else
Set oChart = oPres.Slides(pages(b)).Shapes(metrics(a)).Chart
End If
oChart.ChartData.Activate
Set wb = oChart.ChartData.Workbook
Set ws = wb.Worksheets(1)
ws.Range("A1:Z1000").ClearContents
ws.Range(ws.Range("A1"), ws.Range("A1").Offset(months, categories - 1)).Value = tempdata
'Close workbook
wb.Close
Set oChart = Nothing
If legend_flag Then
Set oChart = oPres.Slides(pages(b)).Shapes("legend").Chart
Else
Set oChart = oPres.Slides(pages(b)).Shapes(metrics(a)).Chart
End If
oChart.ChartData.Activate
'Redraw the selected dataset of the chart based on the # of categories and rows
oChart.SetSourceData Source:="='Sheet1'!$A$1:" & toChar(categories + 0) & months + 1, PlotBy:=xlColumns
'Close workbook
oChart.ChartData.Workbook.Close
Set oChart = Nothing
Exit Sub
End Sub
I also put the code snippet in a subroutine and added Exit Sub at the end to hard reset all parameters in an earlier attempt that didn't work. So all objects and parameters have definitely been cleared for good measure.
Does anyone have any ideas why the object definition/open workbook was tripping up like that? And why DoEvents doesn't actually work for this problem?
I have some workbooks stored in a document library on Sharepoint 2007. I want to check out a workbook, modify it, and check it back in.
Using the following code:
Option Explicit
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Sub test()
Dim bk As Workbook
Dim path As String
path = "http://sharepoint/sites/test/TEST_Relink.xlsm"
If Workbooks.CanCheckOut(path) Then
Application.DisplayAlerts = False
Workbooks.CheckOut path
DoEvents
Set bk = Workbooks.Open(path, False)
bk.Sheets("test").Range("h1").Value = "modified " & Now
DoEvents
Sleep 10000
bk.checkIn True
Application.DisplayAlerts = True
End If
End Sub
The bk.checkIn call always produces the following run-time error:
Method 'CheckIn' of object '_Workbook' failed
After I go into Debug, I press F5 to continue and the check-in always occurs successfully.
I added the 10-second delay with Sleep 10000 because I was thinking that maybe the check-out was taking a while to propagate to the server. But no matter how much time I set for Sleep, this same issue keeps occurring. Any thoughts?
EDIT:
I tried using a looped check of .CanCheckIn as follows:
While Not bk.CanCheckIn
DoEvents
Wend
bk.checkIn True
This gave the same error.
For those finding this like I did, I had
Workbooks(logFileName).CheckIn SaveChanges:=True, Comments:="New row added from " & mainFile
This produced the error message like yours, however on entering debug and pressing f5 would action. So here is my complex solution.....I just split out the code to the following
Workbooks(logFileName).Save
Workbooks(logFileName).CheckIn Comments:="New row added from " & mainFile
Hope this helps others.
Use this:
Dim xl As Excel.Application
Set xl = CreateObject("Excel.Application")
xl.AutomationSecurity = msoAutomationSecurityForceDisable
xl.EnableEvents = False
xl.DisplayAlerts = False
'code to checkin/checkout
xl.EnableEvents = True
xl.DisplayAlerts = True
You probably already figured it out but I thought I'd post it for anyone else who comes here looking for an answer.
If you are setting SaveChanges to True then you MUST also set Comments to be a String (a null value won't do)
So in your example you would need to do this:
bk.CheckIn True, ""
Am stuck and not able to proceed with this. Please find my code below. The code is basically to verify if an element is present in a webpage through VBA. I have created the below sub.
Sub ele_exist(val As String, ele As String)
Select Case val:
Case "byid":
Set verielement = doc.getElementById(ele)
If verielement Is Nothing Then
msgbox("something")
Else
msgbox("something")
End If
Case "byclass":
Set verielement = doc.getElementsByClassName(ele)
If verielement Is Nothing Then
msgbox("something")
Else
msgbox("something")
End If
Case "byname":
Set verielement = doc.getElementsByName(ele)
If verielement Is Nothing Then
msgbox("something")
Else
msgbox("something")
End If
End Select
End Sub
Now when i call this sub it gives syntax error
This is where i call the above sub
Sub start()
Set ie = New InternetExplorer
With ie
.navigate "http://www.google.com"
.Visible = True
While .Busy Or .readyState <> READYSTATE_COMPLETE
DoEvents
Wend
Set doc = .document
DoEvents
End With
***ele_exist ("byname","btnK")*** - THIS IS WHERE SYNTAX ERROR IS DISPLAYED AND THE CODE IS DISPLAYED IN RED
End Sub
I even tried converting it to a boolean FUnction rather than sub, but no luck.
Please help
As I mentione in comments, change
ele_exist ("byname","btnK")
to
ele_exist "byname","btnK"
or
Call ele_exist ("byname","btnK")
One more possible way is to use named parameters:
ele_exist val:="byname", ele:="btnK"
For additional explanation check my another post: What is the difference between entering parameters in these four different ways
I'm writing an Excel script to open a list of PDFs in Internet Explorer tabs. It works fine most of the time, but occasionally when I try to close my browser window, a few of the tabs will close, then it stops and all IE instances will freeze, so I have to kill them all in Task Manager. Note that I can avoid the problem by closing each tab individually.
I'm running IE8 and Excel 2007, for the record. Here's my code:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
ShowBrowserWarning
Dim TheHTML As String, PDFs, PDF, First, SerialValue, Test, k
If Target.Column = 1 And Target.Count = 1 Then
' Get the serial number from the adjacent column
SerialValue = Cells(Target.Row, Target.Column + 1)
TheHTML = ShowHTML("http://ucmwww.dnr.state.la.us/ucmsearch/findAllDocuments.aspx?brief=False&query=xwellserialnumber+LIKE+'" & SerialValue & "'+AND+xdocumenttype+LIKE+'WELL ENGINEERING/MECHANICAL'&format=HTML&sortfield=xdate")
Set PDFs = ExtractPDFs(TheHTML)
If PDFs Is Nothing Then
MsgBox "No associated well engineering/mechanical PDFs."
Else
First = True
Dim ie As Object
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = True
For Each PDF In PDFs
'While ie.Busy
' Dim testvar
' testvar = 1 + 1
'Wend
If First Then
' Open new IE window
ie.Navigate2 PDF.Value
First = False
Else
' Open tab in existing IE window
ie.Navigate2 PDF.Value, 2048
End If
Next
End If
End If
End Sub
What gives? Why does it freeze like that? Does it have anything to do this issue? (Please try not to laugh at my ignorance!) Any help is much appreciated.
Edit: see the italicized text above. I didn't quite describe the problem accurately!
And what about Browser-Busy check? Could it help to avoid the issue?
For Each PDF In PDFs
While ie.Busy
DoEvents
Wend
If First Then
' Open new IE window
ie.Navigate2 PDF.Value
First = False
Else
' Open tab in existing IE window
ie.Navigate2 PDF.Value, 2048
End If
Next
Or just wait between the browser.Navigate calls for a while to give the browser enough time to load one dokument before starting to load next one. Try different time-periods and watch if the freezing issue could be avoided this way.
For Each PDF In PDFs
DoEventsForTimePeriod timePeriodInSeconds:=15 ' try different time periods here
If First Then
' Open new IE window
ie.Navigate2 PDF.Value
First = False
Else
' Open tab in existing IE window
ie.Navigate2 PDF.Value, 2048
End If
Next
Private Sub DoEventsForTimePeriod(ByVal timePeriodInSeconds As Single)
' VBA.Timer: Returns a Single representing the number of seconds elapsed since midnight.
Dim pause As Single: pause = VBA.Timer + timePeriodInSeconds
Do While VBA.Timer < pause
DoEvents ' Yield to other processes.
Loop
End Sub
Well, I´m new too but, as far as can see, I would set ie = Nothing at the end of the sub to loose any relation between VBA and InternetExplorer Application
Good morning,
I am working on a macro to autorefresh excel links before slideshow, loop through slideshow and then restart over and over again. The issue I am having is the slideshow stops but doesnt exit so the wait timer doesnt have a chance to even kick in.
Can I get a suggestion of how I might fix this?
Sub LoopAllSlides()
Dim i As Integer
For i = 0 To 2000
Dim Endpoint As Single
Endpoint = Timer + 10
Do While Timer < Endpoint 'This loop works dont delete
DoEvents
Loop
ActivePresentation.UpdateLinks
With ActivePresentation.SlideShowSettings
.AdvanceMode = ppSlideShowUseSlideTimings
.LoopUntilStopped = msoFalse
.Run
End With
Next i
End Sub
You can always do this with the Application.Quit function, which will exit out of your current sub. So, something like this:
Sub LoopAllSlides()
Dim i As Integer
For i = 0 To 2000
Dim Endpoint As Single
Endpoint = Timer + 10
Do While Timer < Endpoint 'This loop works dont delete
DoEvents
Loop
ActivePresentation.UpdateLinks
With ActivePresentation.SlideShowSettings
.AdvanceMode = ppSlideShowUseSlideTimings
.LoopUntilStopped = msoFalse
.Run
End With
Next i
'Quit Application Function
IWishICouldQuitYou
End Sub
Function IWishICouldQuitYou()
With Application
For Each w In .Presentations
w.Save
Next w
.Quit
End With
End Function