VBScript: Excel application object statusbar call rejected by callee - vba

I have a VBScript that errors when I try to get Excel's status bar string. See comments in code below. I tried putting objExcel.DisplayStatusBar = True in above the erroneous line, but then that line errors. That tells me something about objExcel is going wrong(?). If I put in a msgbox just prior to the erroneous line, it hangs the entire vbs (expected). This vbs runs in the morning so when I see the msgbox popup and click OK all systems have already completed except the vbs that is hanging because of the msgbox. I click OK on the msgbox and I get no error. The reason I am waiting with a timed loop is because macro CreateModel has some Application.OnTime calls in it back to CreateModel, which is necessary for reasons that are beyond this question. The VBScript doesn't 'know' that I have OnTime calls so if I don't 'wait' it will proceed with the rest of the vbs code and mess things up for other reasons. So I have to wait and I use the statusbar to know when all is finished. I can't do a purely timed wait because the processing time of CreateModel and its associated OnTime calls varies quite a bit.
It is a little confusing. Looking for debug suggestions and/or solutions if you have any.
EDIT: if someone knows how to create the error "call rejected by callee" for line sStatus = objExcel.StatusBar, that would help me debug this.
EDIT2: Here is a picture of the error. The script is a .vbs file. I had to grey out the path for my clients protection:
VBScript:
Dim objExcel, wMn, r, wT
Set objExcel = CreateObject("Excel.Application")
Set wMn = objExcel.Workbooks.Open("Z:\path\Model_*.xlsm")
objExcel.Application.Visible = True
objExcel.Run "'Z:\path\" & wMn.Name & "'!CreateModel"
'wait until model is finished
'have to do this because Application.OnTime is called in CreateModel and vbs doesn't wait
Dim sStatus
Dim dteWait
Do
dteWait = DateAdd("s", 600, Now()) '60 = 60 secs, 600 = 10 mins
Do Until (Now() > dteWait)
Loop
'objExcel.DisplayStatusBar = True '<-- if I include this line I get the same error, but for this line
'msgbox objExcel.StatusBar '<-- when I include this line no error occurs, see notes at top
sStatus = objExcel.StatusBar '<-- main error/issue
Loop While not sStatus = "Model Finished"
'more code below, but omitted for clarity

I would designate a cell, give it a named range value ("macroDoneCheck", for example), and have the macro load a "done" value that you can check for in your VBS. The loop for waiting/checking this cell value in your VBS is something like:
Do
WScript.Sleep(30000) 'wait 30 seconds
isDone = objExcel.Range("macroDoneCheck")
Loop While not isDone = "Complete"
Or something like that. You may need to specify the sheet as well, like:
isDone = objExcel.Sheets("Sheet1").Range("macroDoneCheck")

I credited n8. with the best answer because it is a better approach, but this answer is less work and it worked fine.
From my original code/question, wrap sStatus = objExcel.StatusBar like this:
on error resume next
sStatus = objExcel.StatusBar
on error goto 0
Again, just an alternative to the answer n8. provided. It works because for some reason accessing the statusbar while CreateModel is still running produces and error, but in my case the while loop continues to loop because the while condition has not been met. I knew the while condition would eventually be met, even if accessing the statusbar produces an error some of the time, because when I hang the vbs with the msgbox (see notes in code in original question) everything works with no issues if I let it hang long enough. This may be a specific issue that others may not experience, so take it for what it is worth.

Related

VBA Exception Slipping Through Handler

I have inherited a hideous Excel VBA application, written by a guy who apparently didn't like functions and comments. Opening the source code is like staring into an abyss of madness, with 9 levels of hell indentation awaiting to trap the unwary.
First, the setup.
In the process of debugging an intermittent issue, I have identified a particular line in the primary function as its location. Since the exception is not easy for me to fix, I gave this line, and only this line, an error handler that displays a custom message.
' Lots of stuff cut out
On Error GoTo TechReleaseBuildError
lb_Err1 = BuildDirectories(gj_TechBld.mt_TechRlsOutDir)
On Error GoTo 0
' Lots of stuff cut out
Exit Sub
TechReleaseBuildError:
MsgBox "Error building tech release. Pause the synchronization software and try again."
Stop
Resume
End Sub
The Stop and Resume are only there to help debugging, since the normal error messages that pop up don't have an option to debug the error.
Within the BuildDirectories function, I have identified one line that was causing the error that was being kicked up to primary function. I have placed error handlers in this function as well.
' Lots of stuff cut out
Exit Function
TEST:
Stop
Resume
SynchroRetry:
Dim CurrentError As ErrObject
CurrentError = Information.err
SynchroRetryCounter = SynchroRetryCounter + 1
Debug.Print "Synchro Error, retrying #" + SynchroRetryCounter
Stop
If SynchroRetryCounter > 5 Then
Stop
Information.err.Raise CurrentError.Number, CurrentError.Source, CurrentError.Description, CurrentError.HelpFile, CurrentError.HelpContext
Else
Application.Wait Now + #12:00:03 AM#
Resume
End If
End Function
At the very beginning of BuildDirectories I have placed an On Error GoTo TEST statement as a catch-all error handler so I can identify what lines they are coming from, and also used the SynchroRetry handler for the one line that I already know causes an issue, which I immediately reset back to the TEST handler. The SynchroRetry handler just waits for 3 seconds and retries, failing after the 5th retry.
' For some reason, this line (and only this line so far) causes problems when synchronization software is running.
' The error handler waits for a few seconds then tries again a few times.
Dim SynchroRetryCounter As Integer
SynchroRetryCounter = 0
On Error GoTo SynchroRetry
lj_FSO.CopyFile Source:=lt_RPOFilePath & lt_RPOFileName, Destination:=lt_VinRposDir & "\" & lt_RPOFileName, OverWriteFiles:=True ' Trouble line
On Error GoTo TEST
I have verified with Find that these are the only 3 On Error statements in the function (the only ones in the module, even). The top-level function also has no On Errors other than the two shown.
In brief summary, the top-level function has an error handler on only one line, and the function it encloses also has two error handlers inside it, one for one of its known troublesome lines and a general one that should be encompassing the entire function.
The problem is that some error is occurring that is causing my "Error building tech release." message to display, but does not seem to be triggering the TEST or SynchroRetry error handlers. I cannot imagine how this is possible. I'm relatively new to VBA's monstrosity of an error handling model, but I don't believe I did anything wrong. Did I miss some subtlety here? How is an exception able to bypass my handlers and bubble up the stack like this? I do not believe the assignment or passing of the function value could cause the problem, since their just a Boolean and String value. The problem is also intermittent, and only appears when we have file synchronization software active on the directory this program is copying files to.
So a couple hours after I posted this, I managed to figure it out.
The reason that the Debug button wasn't on the error boxes is because in Options the Error Trapping was set to "Break on Unhandled Error". Changing it to "Break in Class Module" brought the button back.
The reason the exceptions were bypassing my error handlers is because they were happening in my SynchroRetry error handler. In particular, I had to change the first few lines from this:
Dim CurrentError As ErrObject
CurrentError = Information.err
SynchroRetryCounter = SynchroRetryCounter + 1
Debug.Print "Synchro Error, retrying #" + SynchroRetryCounter
to this:
Dim CurrentError As New ErrObject
Set CurrentError = Information.err
SynchroRetryCounter = SynchroRetryCounter + 1
Debug.Print "Synchro Error, retrying #" + CStr(SynchroRetryCounter)
Note the additions of New, Set, and CStr.
Problem solved.

Excel Visual Basic produces The object invoked as disconnected from its clients

My macro uses Internet Explorer to open PeopleSoft Financials and then run a public query. It has been working for a long time until recently. I'm aware of an update to Internet Explorer on our machines in early July and am wondering if that has caused this issue.
The macro is throwing an "Object has disconnected from its clients" error message.
Here is how the variable for Internet Explorer is defined:
Public ExpApp As InternetExplorer
Here is the code where the error is thown:
Set ExpApp = CreateObject("InternetExplorer.Application")
ExpApp.Visible = True
ExpApp.navigate vLogin
Do Until ExpApp.readyState = READYSTATE_COMPLETE ' ERROR IS THROWN HERE
MyTimer
Loop
Do Until ExpApp.document.Title <> "PeopleSoft 8 Sign-in"
MyTimer
Loop
Do Until ExpApp.readyState = READYSTATE_COMPLETE
MyTimer
Loop
vLogin is a string with the URL to the PeopleSoft login screen
MyTimer is a little function that waits 1 second each time it is kicked off
This macro has been around a while so I imagine that this is pretty old code. I'm hoping there are a few minor changes I could make instead of re-writing this macro.
Any suggestions would be greatly appreciated. Thanks for the help......

Publish an excel worksheet as an HTML file using a command button

So I have tons of data in my workbook that I need to pass on to my users in a way that allows them to interact with the workbook..., but also limits what they can actually do. So I have made it so they can access certain pages to Add needed data, then I've given access to a menu page so they can run a report.
This report I have found is best if it's an html page.
To that end, I have tried several different ways, save as...and publish. I like the publish version, but I can not for the life of me get this working. All the samples I see, appear to be the same. Here is the line of code in question:
ActiveWorkbook.PublishObjects.Add(xlSourceSheet, ActiveWorkbook.Path & ".htm, "Sheet1", "", xlHtmlStatic, "", "Report Title").Publish True
Every time I get a run time error '1004':
Method 'Publish' of object 'PublishObject' failed
I have the above line of code in a sub, am I missing something? Do I need to set up the publish differently? Thanks for any ideas.
I have had a similar mysterious problem that sometimes it (.Publish) works and sometimes it doesn't, when I try publish.
However, I think the problem might be, in cases when it doesn't work right off the bat, to first save the relevant region as a webpage so that it exists already on the server. After that region has been saved at least once (manually or else maybe with .SaveAs .. see below), then the publish might work.
Here is an example of how I get this to work more consistently, something to convey the structure that works for me:
wkb=ActiveWorkbook.Name 'save wb for later access
url="http://a.com/b.htm" 'save to Internet
sheetname="c"
Sheets(sheetname).Range("d1:e2").Select 'activating sheet may be necessary
On Error Resume Next 'anticipate error to handle it
'Now comes the publish line the first time... which may work.. or not
ActiveWorkbook.PublishObjects.Add(xlSourceRange,url,sheetname,"d1:e2",xlHtmlStatic,"blah","moreblah").Publish (True) 'may fail to create page on website if page didn't already exist
theerr=Err.Number
On Error GoTo 0 'back to default error handling (none)
If theerr <> 0 Then 'if here perhaps because page nonexistent
'msgbox "maybe add warning here [FYI]"
Sheets("dummysheetwithlittleornodata").Copy 'will create workbook
On Error Resume Next
ActiveWorkbook.SaveAs url 'may fail to create webpage first time
ActiveWorkbook.saveAs url 'twice needed for some reason sometimes. [works for me]
On Error GoTo 0
'with fresh dummy html page created, now publish should hopefully work.. let's try again
ActiveWorkbook.Close savechanges:=False 'clean up and avoid popup
Workbooks(wkb).Activate 'get back to correct wkb
Sheets(sheetname).Range("d1:e2").Select
ActiveWorkbook.PublishObjects.Add(xlSourceRange,url,sheetname,"d1:e2",xlHtmlStatic,"blah","moreblah").Publish (True) 'hopefully if failed first time, now it succeeded ! good luck.
End If
The above code structure has allowed me to solve several publish problems I was having. All the techniques together have been enough so far to allow me to save a range from a sheet as a webpage (to server I have write access to) without having to do anything manually (like a manual save or even click on popup). Good Luck.
[The different non-obvious techniques include activating a range before trying a range publish, using the error handling approach and anticipating failure at certain points, the 2x saveas to compensate for mysterious inconsistent failures when using one saveas only, closing and getting back to original workbook cleanly, and using saveas to "guarantee" page exists in form that will make publish to succeed more consistently.]

Method 'range' of object _global failed when doing nothing

I have a big macro which basically processes some columns and spits out results based on some cross-checking with an access 2003 database. It works absolutely fine - no hitches at all.
However, I recently had to make a modification to it. It was literally changing an '8' to a '9' in one line of the code. But next time I ran it, it threw the 1004: Method 'Range' of object '_Global' failed error. Excel 2003 is a funny thing - I once scratched my head for hours over this, trying to find offending lines of code that could be causing the error, but alas to no avail. I did something that I didn't expect to trigger anything:
Starting with the original macro (100% confirmed working), if I just open the code up, and then save it so the 'last updated' metadata will update to reflect the save, though absolutely nothing has changed, it will throw that error again on opening.
It's as if it's so fragile that saving the macro as is will break it. Any ideas?
Update: here's what I changed that initially brought about the issue
iOutputCols = 9 'this was changed to 9 from 8
ReDim Preserve sOutputCols(iOutputCols)
sOutputCols(0) = "Policy No"
sOutputCols(1) = "Client"
sOutputCols(2) = "Trans"
sOutputCols(3) = "Effective Date"
sOutputCols(4) = "ClosingRef"
sOutputCols(5) = "Gross"
sOutputCols(6) = "Comm"
sOutputCols(7) = "Net Due"
sOutputCols(8) = "Risk" 'this line was added
Making the change here, while originally causing the error, doesn't seem special - I did small changes like the above elsewhere in the code and in other modules, one time I even did something as testval = "test" and even that redundant line will produce the error. The most minimalistic way to cause it? Simply open it up, save it without changing anything, and on next use the error occurs.
The error occurs at this line, in a completely different code section which is part of a form:
If strErr <> "" Then MsgBox strErr, vbInformation + vbOKOnly, "Action Error"
Application.ScreenUpdating = True 'error occurs here, message box which shows me the error right above
End Sub
Update 2
Removing the error handling throws the error on this line
Case "> Send Next Period Reminder" 'error on line below
Call ReplaceText(wordApp, "[office_address]", Range("Address_" & Worksheets("UserParms").Range("F6").Value).Value) 'error this line
Call ReplaceText(wordApp, "[office_address2]", Range("Address2_" & Worksheets("UserParms").Range("F6").Value).Value)
'more of the same replacetexts below
For context, this is when an option is selected for "Send Next Period Reminder", which pulls a word .dot template from a static folder and populates it based on the data selected within the sheet (Hence the replace texts). This is in a different module and has never been touched before.
Try properly qualifying your Range method calls. You have lines like this:
Call ReplaceText(wordApp, "[office_address]", Range("Address_" & Worksheets("UserParms").Range("F6").Value).Value) 'error this line
Call ReplaceText(wordApp, "[office_address2]", Range("Address2_" & Worksheets("UserParms").Range("F6").Value).Value)
While it may not be obvious, there are cases, both environmental and code-based, where these unqualified uses of Range could fail. Change the references like Range("Address... to something like yourTargetWS.Range("Address...

Automation Errors: 800706B5, 80004005, 80010108 appear for internal SAP site scrape

I am writing a macro that will scrape my company's internal SAP site for vendor information. For several reasons I have to use VBA to do so. However, I cannot figure out why I keep getting these three errors when I attempt to scrape the page. Is it possible that this has something to do with the UAC integrity model? Or is there something wrong with my code? Is it possible for a webpage using http can be handled differently in internet explorer? I am able to go to any webpage, even other internal webpages, and can scrape each of those just fine. But when i attempt to scrape the SAP page, i get these errors. The error descriptions and when they occur are:
800706B5 - The interface is unknown (occurs when I place breakpoints before running the offending code)
80004005 - Unspecified error (occurs when I don't place any errors and just let the macro run)
80010108 - The Object invoked has disconnected from its clients. (I can't seem to get a consistent occurrence of this error, it seems to happen around the time that something in excel is so corrupted that no page will load and i have to reinstall excel)
I have absolutely no idea what is going on. The Integrity page didn't make much sense to me, and all the research I found on this talked about connecting to databases and using ADO and COM references. However I am doing everything through Internet Explorer. Here is my relevant code below:
Private Sub runTest_Click()
ie.visible = True
doScrape
End Sub
'The code to run the module
Private Sub doTest()
Dim result As String
result = PageScraper.scrapeSAPPage("<some num>")
End Sub
PageScraper Module
Public Function scrapeSAPPage(num As Long) As String
'Predefined URL that appends num onto end to navigate to specific record in SAP
Dim url As String: url = "<url here>"
Dim ie as InternetExplorer
set ie = CreateObject("internetexplorer.application")
Dim doc as HTMLDocument
ie.navigate url 'Will always sucessfully open page, regardless of SAP or other
'pauses the exection of the code until the webpage has loaded
Do
'Will always fail on next line when attempting SAP site with error
If Not ie.Busy And ie.ReadyState = 4 Then
Application.Wait (Now + TimeValue("00:00:01"))
If Not ie.Busy And ie.ReadyState = 4 Then
Exit Do
End If
End If
DoEvents
Loop
Set doc = ie.document 'After implementation of Tim Williams changes, breaks here
'Scraping code here, not relevant
End Function
I am using IE9 and Excel 2010 on a Windows 7 machine. Any help or insight you can provide would be greatly appreciated. Thank you.
I do this type of scraping frequently and have found it very difficult to make IE automation work 100% reliably with errors like those you have found. As they are often timing issues it can be very frustrating to debug as they don't appear when you step through, only during live runs To minimize the errors I do the following:
Introduce more delays; ie.busy and ie.ReadyState don't necessarily give valid answers IMMEDIATELY after an ie.navigate, so introduce a short delay after ie.navigate. For things I'm loading 1 to 2 seconds normally but anything over 500ms seems to work.
Make sure IE is in a clean state by going ie.navigate "about:blank" before going to the target url.
After that you should have a valid IE object and you'll have to look at it to see what you've got inside. Generally I avoid trying to access the entire ie.document and instead use IE.document.all.tags("x") where 'x' is a suitable thing I'm looking for such as td or a.
However after all these improvements although they have increased my success rate I still have errors at random.
My real solution has been to abandon IE and instead do my work using xmlhttp.
If you are parsing out your data using text operations on the document then it will be a no-brainer to swap over. The xmlhttp object is MUCH more reliable. and you just get the "responsetext" to access the entire html of the document.
Here is a simplified version of what I'm using in production now for scraping, it's so reliable it runs overnight generating millions of rows without error.
Public Sub Main()
Dim obj As MSXML2.ServerXMLHTTP
Dim strData As String
Dim errCount As Integer
' create an xmlhttp object - you will need to reference to the MS XML HTTP library, any version will do
' but I'm using Microsoft XML, v6.0 (c:\windows\system32\msxml6.dll)
Set obj = New MSXML2.ServerXMLHTTP
' Get the url - I set the last param to Async=true so that it returns right away then lets me wait in
' code rather than trust it, but on an internal network "false" might be better for you.
obj.Open "GET", "http://www.google.com", True
obj.send ' this line actually does the HTTP GET
' Wait for a completion up to 10 seconds
errCount = 0
While obj.readyState < 4 And errCount < 10
DoEvents
obj.waitForResponse 1 ' this is an up-to-one-second delay
errCount = errCount + 1
Wend
If obj.readyState = 4 Then ' I do these on two
If obj.Status = 200 Then ' different lines to avoid certain error cases
strData = obj.responseText
End If
End If
obj.abort ' in real code I use some on error resume next, so at this point it is possible I have a failed
' get and so best to abort it before I try again
Debug.Print strData
End Sub
Hope that helps.