VBScript - check if mailItem is still viable - vbscript

I've ran into a problem with one of my scripts.
The script automates our shared mailbox (outlook). It auto-assigns mails to the correct person.
Everytime the script runs, it loops all the mails in the folder and checks if it has a category. If not, it assigns it to the right user.
Problem is, when a mail with no category gets dragged to another folder while running the script, it throws an error when trying to execute mail.Categories
Line: 222
Error: Could not complete the operation due to error 8004010f.
Is there a way to check if the mailItem is still viable?
I tried using IsEmpty, but the msgbox doesn't trigger.
Set outlook = CreateObject("Outlook.Application")
Set namespace = outlook.GetNameSpace("MAPI")
Set Account = namespace.Folders("accountName")
Set Inbox = argentaAccount.Folders("Inbox")
For Each mail in Inbox.Items
If IsEmpty(mail) Then
MsgBox("test")
End If
'check if item has a category'
If mail.Categories <> "" Then
'has a category'
Else
'Execute mailhandling code'
End If
Next
If anyone has any solution, I would be forever grateful.

With this line as a clue:
If IsEmpty(mail) Then
VBScript may work the same as VBA where For Each works like:
For i = 1 to Inbox.Items.Count
When item 1 is moved (or deleted), item 2 moves into position 1. On the next loop, position 2 with item 3 is processed. Every second item is skipped. In the second half of the loop there is nothing to process.
One way of dealing with this in VBA is to step in reverse order:
For i = Inbox.Items.Count to 1 step -1
Set mail = Inbox.Items(i)
If mail.Categories <> ""

Related

Do loop won't exit after conditions are met?

I've been trying to make a loop that repeats itself until an does not element exists on the page. However, when the condition is met, the loop does not exit. Any suggestions?
Dim userExt
userExt = 1
Do until Not Browser("Some_browser").Page("Some_page").WebElement("This username is already").exist
Browser("Some_browser").Page("Some_page").WebEdit("WebEdit").Set "registertester" & userExt+1
userExt = userExt+1
Browser("Some_browser").Page("Some_page").WebElement("Save & Continue").Click
Loop
I also tried changing the first line to
Do until Browser("Some_browser").Page("Some_page").WebElement("This username is already").exist=False
I tried adding an if exist then exit loop, which didn't work. It just kept looping. I know for sure the element does not exist on the page.
The element did not exist in the source code but for some reason UFT found it. I found that out by going on the page and click the "Highlight in Application" button in UFT. Although nothing was highlighted, UFT did not generate any error. I went on to check the properties of the element and found a property called visible.
I changed the Do Until statement to:
Do until Browser("Some_browser").Page("Some_page").WebElement("This username is already").GetROProperty("visible")="False"

VBS Message Box

First off I'm sorry that i'm very very new to VBS, but i'm trying to create a little script that will complete a series of key strokes then on a website for work then go to another website and complete another series of key strokes.
Whilst on the first website there I need to ask a question like did the action complete. Yes or No.
If No then the script needs to go back to the start of the script. If yes then the script needs to continue.
Later on, on the second website after a few keystrokes I need to ask another question weather to loop the whole process or to stop.
I've googled a few message box solutions but it doesn't seem to work. I have
The first question box.
intSerialNumber = _
Msgbox("Was there a problem?", _
vbYesNo, "Problem?")
If intSerialNumber = vbYes Then
LoopShip
Else
Continue
End If
Continue
The Second question box.
If msgbox("Continue?", vbYesNo) = vbNo Then
WScript.quit
End If
if LoopShip = True Then
LoopShip
End If
Wend
Try using nested loops with functions for readability like this. The user can rerun the code from the first website as many times as they want and can rerun the code from both websites as many times as they want.
Dim problemsOccurred
problemsOccurred = vbYes
Do
Call firstWebsite()
problemsOccurred = secondWebsite()
Loop Until (problemsOccurred = vbNo)
Sub firstWebsite()
Dim firstWebsiteProblem
firstWebsiteProblem = vbYes
Do
'First Website code Goes here
firstWebsiteProblem = Msgbox("Was there a problem?", vbYesNo, "Problem?")
Loop Until (firstWebsiteProblem = vbNo)
End Sub
Function secondWebsite()
'Second WebSite code goes here
secondWebsite = MsgBox("Continue Whole Process?", vbYesNo)
End Function

How can I use OpenArgs to print multiple reports in a loop situation?

I am slimming down my databases, eliminating duplicate reports where I can and creating better code. I have one database that involves our welders and foremen. In this code, I can print a report for an individual foreman, which sends the string "strActive" through openargs The report looks at strActive and on the OpenForm action, sets the filter for active, inactive, or all welders, based on the string value passed through.
It works perfectly for the single page at a time code. There, the user chooses a foreman from a list or enters the foreman's clock number. The query the report is based on uses the global string "ForemanCLK" to only get results for that welder.
Formerly, I had three reports; one for all welders, one for active welders, and one for inactive welders.
Previously, I would set a variable based on strActive and open the appropriate report using another variable in the code in place of the report name. The loop worked fine and opened the report 39 times, each time with a new foreman name and data.
I'm baffled as to why opening the report now, with an openarg, doesn't work. I only get the first foreman name, 39 times. I've verified that I get the foremanCLK variable of the different foremen by commenting out the docmd line and debug.print(ing) the various values needed by the form. The report simply doesn't load it correctly.
Set rec = CurrentDb.OpenRecordset(sql)
rec.MoveFirst
For ctr = 0 To rec.RecordCount - 1
ForemanCLK = rec(0).Value
DoCmd.OpenReport "rptForeman", acViewNormal, , , , strActive
'DoCmd.OpenReport "rptForeman", acViewNormal
rec.MoveNext
Next
The above code gets me many copies of the same foreman's report filtered by strActive
For ctr = 0 To rec.RecordCount - 1
ForemanCLK = rec(0).Value
'DoCmd.OpenReport "rptForeman", acViewNormal, , , , strActive
DoCmd.OpenReport "rptForeman", acViewNormal
rec.MoveNext
But this gets me all the different foremen, except it is not filtered.
I've tried passing in a Where clause.
acViewNormal, ,"[active]=" & True
and
acViewNormal, ,"[active]=" & False
and
acViewNormal, ,"[active]=" & True & " OR [active]=" & False
I did the same verification with the single report, and it filters correctly, but in the loop, it does not filter at all. It does, however give me the different foremen.
The big question here is...
WHY? Does access not have enough time between reports to close it and therefore it doesn't perform the operations on the open event?
Any other ideas?
You must close the report explicitly inside your loop: DoCmd.Close acReport, "rptForeman". If OpenReport is called a second time on an already-open report, the object gets the focus in access, but it doesn't re-run the Open event.
Okay, I must hang my head in shame.
I error checked the hell out of that code above using every variable except strActive.
When I added that to my debug.print line, it came up with nothing. How can that be!?!!??
I simply forgot to set that at the beginning of the sub, like I did with the other action that opens a single report. strActive is a global variable, but was not set at any other point in the code up until this piece.
Once this was added to the beginning of the sub, all worked fine.

VB6 ADODB Record Set Update

So I'm pretty new to Visual Basic and inherited this VB6 code that i need to work on now. Right now, I'm trying to update a SQL database using an ADODB.RecordSet. I have a Select SQL statement that pulls the right data from the database into the ADODB.RecordSet, but I'm having problems updating all the rows. What I am trying to do is to update the same column for each row with the same value. Right now, its updating a few of the records, but I'm getting an error pop up. The error I get is:
Run Time error 3021: Either BOF or EOF
is True or the current record has been
deleted. Requested operation requires
a current record.
When I click to debug, it takes me to rsUpdate.fields(TargetFieldName) = value
The project itself is huge and too large to post, but the part of the code that I'm working on now is this:
If rsUpdate.State = adStateOpen Then
If rsUpdate.EOF Then
rsUpdate.Close
Exit Function
End If
rsUpdate.MoveFirst
Dim i as Integer
For i = 0 To rsUpdate.recordCount
rsUpdate.fields(TargetFieldName) = value
rsUpdate.MoveNext
Next i
On Error GoTo canupdaterecord
rsUpdate.Update
On Error GoTo 0
rsUpdate.Close
End If
Exit function
So any help you guys can give me would be greatly appreciated. Like I said, I'm pretty new to VB and am kind of learning this all as I go.
I would guess the problem is an off-by-one error:
For i = 0 To rsUpdate.recordCount
rsUpdate.fields(TargetFieldName) = value
rsUpdate.MoveNext
Next i
If recordcount returns 5, this loop will make 6 runs: 0,1,2,3,4,5.
I'd write it like this:
while not rsUpdate.EOF do
rsUpdate.fields(TargetFieldName) = value
rsUpdate.MoveNext
wend
Try this also:
If reUpdate.EOF Then
Exit Sub
End If

How do I close Word (or other app) if an error occurs in a VBScript?

I wrote a VBScript app to open Word and Excel documents and search and replace blocks of text and various sections, pulling the new text from a plain text file. I purposely avoided any error checking, primarily because I couldn't figure it out at the time (and the script ran reliably anyway). Now months later on my local machine, I am inexplicably getting error messages about Normal.dot being changed and a message box asking what I want to do about it (which requires three more dialogs to finally answer). Of course this kills my ability to run the script and simply walk away, as it causes the script to fail. Currently when this happens, I have to open the Task Manager, find Winword.exe (of which the GUI isn't running) and kill it then re-run my script.
What's a reasonable way of catching the error and successfully shutting down Word (or Excel). Based on this question I'm trying this:
Set objDoc = objWord.Documents.Open(curDir1 + "\docs\template_spec.dot")
If Err.Number <> 0 Then
WScript.Echo "Error in Word Open:" & Err.Description
objWord.Quit
Else
Set objSelection = objWord.Selection
'Do replacement activities'
ReplaceText(objSelection)
objDoc.SaveAs(curDir1 + "\docs\mynewdocument.doc")
objWord.Quit
End If
Set objShell = Nothing
Set objWord = Nothing
Set objExcel = Nothing
Of course, as fate would have it, I cannot replicate the problem, so it works like normal. Does this solution seem reasonable? And a side question: How the heck do I get Word to stop complaining about Normal.dot (or get the script to handle it)? It's as if Word leaves itself open in the background after I have closed the GUI in some cases.
have you considered wrapping everything into an 'On Error Resume Next' statement so that your script ignores all the errors and continues to run as much as possible before calling the objWord.quit regardless of success or fail.
if you want more information on the correct use of 'On Error Resume Next' then go over to the msdn article on it!
Hope this helps!
Paul
I'm afraid that
WScript.Echo "..."
if it ever fires, is going to stall your script. Other than that, everything looks right. I'll play with it when I get home.
Edit: Word does hang out in the background, quite frequently. For one thing, if you use Outlook, and use Word as your Outlook editor, Word won't go away until Outlook is gone.
I'd agree with the use of "on error resume next".
If you really need to forcefully terminate Word, you can use WMI and the Win32_Process class to find and kill the process. This should be a last resort if everything else fails.
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process Where Name = 'winword.exe'")
For Each objProcess in colProcess
objProcess.Terminate()
Next
This was a modified example from:
http://www.computerperformance.co.uk/vbscript/wmi_process_stop.htm
Also, make sure all your references to the Word automation object are closed and/or set to nothing before you terminate the process.
The most reliable way to terminate all ActiveX instances, clean up garbage, and release resources is to put the code for that purpose into Sub Class_Terminate() of a dummy class, created instance of the class allows to handle script quit event.
Option Explicit
Dim objBeforeQuitHandler, objWord
' create a dummy class instance
Set objBeforeQuitHandler = New clsBeforeQuitHandler
' create word app instance
Set objWord = CreateObject("Word.Application")
objWord.Visible = True
objWord.Documents.Add.ActiveWindow.Selection.TypeText "80040000 error was raised. About to terminate the script." & vbCrLf & "Word will be quitted without saving before script termination just you close popped up error message."
' your code here...
' raise an error
Err.Raise vbObjectError
Class clsBeforeQuitHandler
' dummy class for wrapping script quit event handler
Private Sub Class_Terminate()
Dim objDoc
On Error Resume Next ' to prevent errors in case of unexpected word app termination
If TypeName(objWord) <> "Object" Then ' word app has not been closed yet
objWord.DisplayAlerts = False
For Each objDoc In objWord.Documents
objDoc.Saved = True ' to prevent save as dialog popping up
objDoc.Close
Next
objWord.Quit
End If
End Sub
End Class

Resources