VBA EXCEL error 91 object variable or with block variable not set - excel

Hi I have this weird problem where I get error 91 ("object variable or with block variable not set"). I do not always get the error when I run the script, only now and then (random). I have tried to do as much as I can without success.
I am scraping a site to get the most recent prices of some pc components.
In order to do that I made a script and call the script sequentially multiple times for different parts. The error is not related to a specific part. I get the error randomly on all the parts. Sometimes I can do all the calls without any errors.
The script (not the full script, only what you need):
Sub ImportUrlData(url As String)
Dim ie As InternetExplorer
Dim html As HTMLDocument
Dim varListingIDElement As IHTMLElement
Dim varShopName As String
varShopName = ""
Set ie = New InternetExplorer
ie.Visible = False
ie.navigate url
Do While ie.readyState <> READYSTATE_COMPLETE
DoEvents
Loop
Set html = ie.document
Set varListingIDElement = html.getElementById("listing")
varShopName = getXPathElement("/table/tbody/tr[1]/td[1]/p/a", varListingIDElement).innerText
I always get the error on this last line:
varShopName = getXPathElement("/table/tbody/tr[" & count & "]/td[1]/p/a", varListingIDElement).innerText
(The error is not occurring inside the 'getXPathElement' function, so no need to show that too. If you do want to take a look at it just ask and I'll post it here in an edit.)
When I debug from start to end with F8 I never get the error. Is it possible that the next call already starts before the previous one isn't finished yet?
Thanks in advance!
(Sorry if there are some dutch words I have overseen.)

There are some cases where you can encounter an issue like this (where F8 always works, but a normal run doesn't) if the object return, in whatever way, isn't fully connected. It is difficult to say for certain if this is the case, and I have only encountered this when I am setting an object to something that has it's own process for initializing, and when this process is outside of the control of the VBA routine.
For your problem, specifically, there are a couple of approaches you can take:
Set varListingIDElement = html.getElementById("listing")
If Not varListingIDEElement is Nothing Then
varShopName = getXPathElement("/table/tbody/tr[1]/td[1]/p/a", varListingIDElement).innerText
End If
The above approach should prevent the error entirely, but it also means varShopName may not be set.
On the other hand:
Set varListingIDElement = html.getElementById("listing")
Application.Wait(Now + TimeValue("00:00:03"))
varShopName = getXPathElement("/table/tbody/tr[1]/td[1]/p/a", varListingIDElement).innerText
This will force the application to wait three seconds between the initial setting of the variable, and the final calling of the variable. You could use this approach if it is indeed an issue of the prior process not finishing before the new one begins. You could also combine this approach with the first to get the best of both.
Good luck!

Related

Word gets stuck during export via Excel VBA

My Excel VBA script contains an export function which results in one Word document of about 12 pages.
On my private computer, this works.
On my company's laptop this seems to take too long and probably some kind of Word watchdog is triggered. Word gets stuck after about 75% of the code and the message "Word has stopped working" pops up.
I determined the point where it gets too much for Word to handle. As the export code is quite extensive and also contains several company and project details that I don't want to reveal, I prefer to not post it here.
Here is the snippet at which Word gets stuck:
ThisWorkbook.Worksheets("Backup - Do not change").Shapes("companyLogo").Copy
With wdFrontHeaderTable
.rows.Height=30
With .Cell(1,1).Range
.Paste
.ParagraphFormat.Alignment=0
End With
With .Cell(1,1).Range.InlineShapes(.Cell(1,1).Range.InlineShapes.Count)
.LockAspectRatio=msoTrue
.Width=120 'this one still works
End With 'here, the error occurs
End With
If I set one before that point and then run the two halves of code, one after another, it works.
If I uncomment the whole block, the error occurs in the next block.
The code does what it is supposed to do, even on the company laptop. I guess it just takes too long and the watchdog "thinks" Word was stuck.
Is there a way to disable the watchdog or another way to solve this?
I tried Application.Wait (1000).
Word is initialized by
Dim wdApp As Object
Dim wdDoc As Object
Set wdApp = CreateObject("Word.Application")
Set wdDoc = wdApp.Documents.Add

My vba code closes a document before the print command can be executed

The code I am trying to run is in excel vba and its supposed to open a word document print it and then close the document. For some reason it seems that the code doesn't finish sending the document to the printer and yet it still closes. So the code runs to completion and doesn't generate an error message but nothing manages to print.
When I run the code step by step the document does manage to print. I tried adding: Application.Wait(Now + TimeValue("0:00:05")) to give it time to work. I tried a another form of that line in case the program was telling word to wait instead of excel: Excel.Application.Wait(Now + TimeValue("0:00:05")). I've also tried playing around with the time making it wait 10 second instead of 5.
Any help would be great
If ENG28 = "" Then
Else
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Open(ENG28)
objWord.Visible = True
objDoc.PrintOut
Application.Wait(Now + TimeValue("0:00:05"))
objWord.Quit 0
End If
I see that you made use of the .Wait method however there is a better way as you can never guarantee that your document is in a printed state within the 5-10 seconds threshold.
The PrintOut method basically has an argument you may add named Background. The default value of this argument is True which would mean that printing occurs in the background and the code continues to run which is causing your file to close before completing the print function. In this case, if you set the Background argument value to False, the macro will not take any more instructions until the printing is done.
try changing the PrinOut line to the following, see if that helps:
objDoc.PrintOut Background:=False

Unable to find the right excel file library reference error

Every time I open this Excel file I get this error which is in screenshot 1 and screenshot 2.
When I click OK it takes me to code windows and asks me to select library.
I tried everything to make it work but no luck.
Screenshot one and screenshot two:
Make sure that these references are in this Priority order.
HTML and Internet Controls need to be above OLE Automation.
If you are still having this issue after this, run a Repair on the Office install and it refreshes the .DLL files that may have been corrupted.
Update: 7/6/2017
The pervious answer above will re-associate the references and allow the script to compile.
However, there is a better way.
This issue occurs when the VBA script is shared to other computers that are not identical platforms(OS and MS Office versions) and use early binding. Early binding reduces latency and is the correct way when only intended for one computer.
The answer is to use late binding format in the script and not select any references. Change any data type objects other than object back to object and use the following format:
Sub Late_Binding()
Dim IE_App_obj As Object
Dim MyShell_obj As Object
Dim IE_Window_obj As Object
Dim Windows_cnt As Long
Dim x_cnt As Variant
Dim HTML_Element_obj As Object
Set IE_App_obj = CreateObject("InternetExplorer.Application")
'Use IE_App_obj to Navigate to webpage and control it.'
Set MyShell_obj = CreateObject("Shell.Application")
'Use MyShell_obj to find an existing webpage and control it.'
Let Windows_cnt = MyShell.Windows.Count
For x_cnt = Windows_cnt - 1 To 0 Step -1
On Error Resume Next
If Instr(MyShell_obj.Windows(x_cnt).Document.Title,"WebPage_Title") > 0 Then
Set IE_Window_obj = MyShell_obj.Windows(x_cnt)
Exit For
End If
Next
Set HTML_Element_obj = IE_Window_obj.Document.getElementByID("ID_text")
End Sub
Latency will increase but so will stability.

Selenium Webdriver (VBA): Explicit Wait

I am navigating a web application that will often throw an error if there is an attempt to click an element before it can be interacted with.
When using Selenium WebDriver (java), I can easily work around the problem:
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.elementToBeClickable(By.id("element")));
However, I am trying to write the script in VBA utilizing the Selenium type library, and, despite trying numerous different ways, the only success I am having is:
webdriver.wait
which I have been told should be avoided if at all possible. If someone can advise how to translate my java into VBA, or provide any other solution, I would be extremely grateful.
You might try looping until the element has been set correctly with a time out to ensure you can't go into an infinite loop. The danger with the accepted answer is there is no way to escape the loop if not found.
Dim t As Date, ele As Object
t = Timer
Do
DoEvents
On Error Resume Next
Set ele = .FindElementById("element")
On Error GoTo 0
If Timer - t = 10 Then Exit Do '<==To avoid infinite loop
Loop While ele Is Nothing
Note: User #florentbr wrote a js wait clickable function to be executed via Selenium Basic. Example in this SO answer.
The selenium plugin for VBA is unofficial and doesn't support this feature.
You can work around this by using onError to retry the action that is producing an error until it succeeds or times out:
Sub test
OnError GoTo Retry
webDriver.findElementById("element")
Exit Sub
Dim i as integer
:Retry
webDriver.Wait(500)
i = i + 1
if i = 20 then onerror go to 0
Resume
end sub
In vba you can use Implicit wait "driver.Timeouts.ImplicitWait = 10 'Timeunits 'seconds" it will wait maximum limit if the element is found before the set time it will process further.
You can use a variation in the old wait system used with Internet Explorer.
Do While SeDriver.FindElementById("Id").IsDisplayed = False
DoEvents
Loop
Just replace "IsDisplayed" with "IsPresent", as needed. This method introduces an artificial implicit wait, perfect if what you need is to wait for an element generated by an AJAX request.
Often there are elements on-site that can be immediately found by selenium but cannot be interacted with and usually when we debug at this stage and run the code from there, everything works just fine. The solution I used for that was adding wait.
Sub Pause_for_2_seconds()
Application.Wait (Now + TimeValue("00:00:02"))
End Sub
And Where ever you feel that the element require some time just add
Call Pause_for_2_seconds
in your actual Sub
For Example see below
chr.SendKeys (Keys.Tab)
Call Pause_for_2_seconds
chr.SendKeys (Keys.Tab)
chr.SendKeys (Date + 52)
chr.SendKeys (Keys.Tab)
Call Pause_for_2_seconds
chr.SendKeys (Keys.Tab)
chr.SendKeys (Keys.Enter)

Wait for CSV file to open?

I am trying to download and open a CSV file using VBA in Excel. When I step through using the debugger my code works fine but when I try to run it normally it won't work, it is trying to copy the info from the newly opened CSV file into the existing .xlsm file but it isn't finding anything. I have found ways of checking that the file is open but I need it to keep checking and once it does exist to continue with the code.
Here is how Im getting the file to download:
Sub getFile(address)
On Error Resume Next
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = False
ie.navigate address
ie.Quit
Set ie = Nothing
On Error GoTo 0
End Sub
And then after this in the sub that calls getFile,
Do While Workbooks(Workbooks.Count).Name <> "file.csv"
Loop
Ive tried a few variations that all work fine when taking it slow in the debugger but crashes otherwise. Does anyone know a way to have it keep checking and wait until the file is open? It requires the user to do something so what it to make sure that file.csv is open. Should I put my check in sub getFile or keep it after it is called?
SOULTION:
Taking hnk's advice below I tried this and it worked perfectly:
Sub getFile(address)
On Error Resume Next
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = False
ie.navigate address
Do While Workbooks(Workbooks.Count).Name <> "file.csv"
DoEvents
Loop
ie.Quit
Set ie = Nothing
On Error GoTo 0
End Sub
Moved the check to before closing IE and added DoEvents to my loop.
Usually such behavior happens not because of the code logic but because of interrupts. Depending on the nature of your environment it could be one of two things
some background refreshing process is getting executed sometime in between your code execution time causing some issues. e.g. in a financial services environment, your Bloomberg API could be doing its timely BlpUpdate event
your code blocks up a large chunk of time and causes unexpected behavior because event which was triggered during your loop was blocked too long waiting for your code to complete on the main thread.
So you need to try the following two solutions, one of which should work
For the second problem, inside your
Do While Condition
' ... your code ...
Loop
add a line of code that checks for and clears any pending Event queues, making it...
Do While Condition
DoEvents ' either put it at the start or at the end of your code
' ... your existing code ...
Loop
This will give the system some 'breathing space' to wrap up pending events. You'll need to experiment with the location of your DoEvents code.
For the first problem, you can try enclosing your entire loop within an Event Protected area of code, to ensure it finishes running before anything disturbs it.
Application.EnableEvents = False
'your loop and other code comes here
Application.EnableEvents = True
Once again, you might need a bit of trial and error to see how much of the code needs to be thrown into the Event-free zone before it works as expected.

Resources