Quit Outlook object AFTER email is sent - vbscript

Here is the code I have so far:
Option Explicit
Call OpenOutlook()
Function OpenOutlook()
Dim ObjShell
Set ObjShell = CreateObject("WScript.Shell")
ObjShell.Run("Outlook.exe")
Call SendEmail()
'I tried closing from here but this didn't work either
'ObjShell.Quit
End Function
Function SendEmail()
'Declaring variables used through out this function
Dim ObjOutlook
Dim objMail
Set ObjOutlook = CreateObject("Outlook.Application")
'CreateItem(0) opens a New Email window...MailItem
set objMail = ObjOutlook.CreateItem(0)
objMail.Display
'MailItem Options
objMail.to = "test#mail.com.com"
'objMail.cc = "test2#mail.com"
objMail.Subject = "Did it work!?"
objMail.Body = "If you got this email, my VBs test worked!"
'objMail.Attachments.Add("C:\Attachment\abc.jpg")
objMail.Send
'This didn't work either
'If objMail.Sent = True Then
'ObjOutlook.Quit
'End If
'Quit closes Outlook like I want but it doesn't wait for the email to send
'ObjOutlook.Quit
End Function
What I'm trying to automate using VBScript:
Open Outlook
Send an email
Wait for email to send (Outbox to finish sending)
Close Outlook AFTER the email has been sent
Where I'm stuck:
First of all, I was having trouble opening Outlook. Below is the code that I used to create an Outlook Object:
Set ObjOutlook = CreateObject("Outlook.Application")
'CreateItem(0) opens a New Email window...MailItem
set objMail = ObjOutlook.CreateItem(0)
objMail.Display
What I did (Not even sure if this is the right way to do it):
Set ObjShell = CreateObject("WScript.Shell")
ObjShell.Run("Outlook.exe")
Why can't I just do ObjShell.Quit after I call the SendEmail() Function? Using .Quit gives me an error.
I just want to close the Outlook application once the email has been sent and I can't figure out how.

MailItem has a Sent property that indicates when the message has been sent. Try this:
...
objMail.Send
Do Until objMail.Sent
WScript.Sleep 500
Loop
' Safe to close...
ObjOutlook.Quit

Try this:
Option Explicit
Sub SendMail()
Dim outobj, mailobj
Set outobj = CreateObject("Outlook.Application")
Set mailobj = outobj.CreateItem(0)
With mailobj
.To = "user#test.com"
.Subject = "Testmail"
.Body = "If you got this email, my VBs test worked!"
.Send
End With
'Clear the memory
Set outobj = Nothing
Set mailobj = Nothing
End Sub
SendMail()
Msgbox("Done")

Firstly, what if it was the user who opened Outlook? I don't think any user will appreciate your code closing Outlook. You might want to check that both Application.Explorers.Count and Application.Inspectors.Count are zero before closing Outlook. Newest version of Outlook will automatically exist if there are no open explorers or inspectors even if you hold a reference to an Outlook object - that was done to guard ageist misbehaving apps that leaked references. To prevent Outlook from closing, hodl a reference to an Explorer object t(even if you do not show it) - call Namespace.getDefaultFolder(olFolderInbox), then hold a reference to the object returned by MAPIFolder.GetExplorer .
Secondly, after calling Send, use Namespace,SyncObjects collection to retrieve the very first SyncObject. Hook up the SyncObject.SyncEnd event and call SyncObject.Start. When SyncEnd event fires, you will be all done. But I don't think you can work with events in VB script...

Related

Open Outlook 2016 with a given non-default profile with VBScript

I have a VBScript which opens Outlook 2016 and sends a message.
The problem I have is that I have several Outlook profiles.
I would like to set the actual profile I wish to open from which to send the message.
My existing script is:
Dim objOutlook, objEmail
Dim strEmailReceiver, strEmailCc, strEmailBcc, strEmailSubject, strEmailBody, strEmailAttachments
Set objOutlook = CreateObject("Outlook.Application")
Set objEmail = objOutlook.CreateItem(0)
strEmailSubject=InPutBox("Input your message")
With objEmail
.To = "here#there.com"
' .Cc = strEmailCc
' .Bcc = strEmailBcc
.Subject = strEmailSubject
' .Body = strEmailBody
' If (strEmailAttachments <> "") Then
' .Attachments.Add strEmailAttachments
' End If
.Send
End With
'Clear the memory
Set objOutlook = Nothing
Set objEmail = Nothing
I wish to use the profile called CEO
If outlook is closed, I get a messagebox asking which profile to use, once selected the script works. This is the step I wish to avoid.
Immediately after creating an instance of the Outlook.Application object, add code like the following
Set objOutlook = CreateObject("Outlook.Application")
set objNS = objOutlook.GetNamespace("MAPI")
objNS.Logon("The Profile name")
Set objEmail = objOutlook.CreateItem(0)
keep in mind that if Outlook is already running, Namespace.Logon will do nothing and you will end up with the running instance of Outlook (since it is a singleton) using whatever profile it was using at the moment

Handling COM event cancelation in VBScript

I would like to write a script, that sends an E-Mail via our companys SMTP-Server using CDO.
First I tried to write an HTA-application for that purpose, but it became rather fiddly to make it cormfortable enough so that other people may handle it well (because of proper recipient resolving).
So now I try to use the regular Outlook-Mail mask to prepare the mail first and then catch the send-item event via VBScript to give it's content to my CDO script.
Right now, my code looks like this:
Dim OutlookApplication
Dim MailItem
Const olDiscard = 1
Const olMailItem = 0
Set OutlookApplication = WScript.CreateObject("Outlook.Application", "Outlook_")
Set MailItem = OutlookApplication.CreateItem(olMailItem)
MailItem.Display
'(...) some code to add recipients, subject, text, etc... depending on the given WScript.Arguments
While Not MailItem Is Nothing
'keep the script alive
WScript.Sleep 1
WEnd
Function CDOSendMessage()
'some code to send the data to our smtp server, return true if successfull
CDOSendMessage = True
End Function
Sub Outlook_ItemSend(byVal Item, Cancel)
If Item.body = MailItem.body Then 'Any more fail proof suggestions on how to check if it's the correct mailitem I'm handling with this event? While the script is alive, it fires for EVERY mail I send via outlook
Cancel = True
If CDOSendMessage() then
Set MailItem = Nothing
MailItem.Close olDiscard
Else
Cancel = False
MsgBox "Sending message via CDO failed."
End If
End If
End Sub
The main problem is, that Cancel = True simply does not work. Outlook will send my mail using my regular mail adress no matter what. Can you tell me, what I'm doing wrong please?
Thank you very much in advance!
Guido
The Cancel parameter must be declared with the ByRef modifier.
Updated code as requested:
Dim OutlookApplication
Dim MailItem
Dim CDODone: CDODone = False
Const olDiscard = 1
Const olMailItem = 0
Set OutlookApplication = WScript.CreateObject("Outlook.Application", "Outlook_")
Set MailItem = OutlookApplication.CreateItem(olMailItem)
MailItem.UserProperties.Add "CDOFlag", 20, false, false
MailItem.Display
'(...) some code to add recipients, subject, text, etc... depending on the given WScript.Arguments
While Not CDODone Is Nothing
'keep the script alive
WScript.Sleep 1
WEnd
MailItem.Close olDiscard
Function CDOSendMessage()
'some code to send the data to our smtp server, return true if successfull
CDOSendMessage = True
End Function
Sub Outlook_ItemSend(byVal Item, byRef Cancel)
If Not Item.UserProperties.Find(CDOFlag) Is Nothing Then
Cancel = True
If CDOSendMessage() then
CDODOne = True
Else
Cancel = False
MsgBox "Sending message via CDO failed."
WScript.Quit
End If
End If
End Sub

Why does my VBA script not continue after debugging?

Whenever I hit an error with my script, the focus turns to the VBA code and the offending line. I fix it, and hit save. Then I notice that the script is no longer running, even after I make sure that it's not paused.
For example, right now I'm using a Form_Timer() event to do some testing (interval set to 1000ms). To test the script again, I just set it to a minute in the future (e.g. if the current time is 8:54:00 AM I set it to fire at 8:55:00 AM). But this stops working after an error. Does anyone know why this is? I don't want to have to tell my users to close and re-open their copies of the Access DB just to make the script work again.
Code:
Private Sub Form_Timer()
On Error GoTo ErrorHandler
current_date_time = Now
If current_date_time = #6/28/2016 8:52:00 AM# Then
MsgBox ("the current_date_time variable holds: " & current_date_time)
'Declare objects
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim qdf As DAO.QueryDef
Dim oApp As Outlook.Application
Dim oMail As Outlook.MailItem
Dim mail_body As String
'Set objects
Set dbs = CurrentDb
Set qdf = dbs.QueryDefs("qry_BMBFLoc")
Set rst = qdf.OpenRecordset
Set oApp = New Outlook.Application
Set oMail = oApp.CreateItem(olMailItem)
mail_body = "The following jobs do not have the special BF location set in Job Orders: " & vbCrLf
If Not (rst.EOF And rst.BOF) Then
rst.MoveFirst
Do Until rst.EOF = True
mail_body = mail_body & rst!job & "-" & rst!suffix & vbCrLf
rst.MoveNext
Loop
'Email contents
oMail.Body = mail_body
oMail.Subject = "Blah"
oMail.To = "someone#something.com"
oMail.Send
'Close stuff
rst.Close
dbs.Close
Set rst = Nothing
Set oMail = Nothing
Set oApp = Nothing
End If
End If
Exit Sub
ErrorHandler:
Dim msg As String
If Err.Number <> 0 Then
msg = "email Form Timer Error #" & Str(Err.Number) & " error Line: " & Erl & Chr(13) & Err.Description
MsgBox msg, , "Error", Err.HelpFile, Err.HelpContext
End If
Exit Sub
End Sub
In order to reactivate the code, you could close the form when the error is triggered. The user would then have to reload the form to complete the action.
However, without any intervention the error is likely to occur again.
Edit: Or you could write a Function to automatically close, and re-open the offending form. Calling it in the on error command.
When there is an error in access form, the timer will stop working, you don't need to close and reopen the whole database, only the form to start the timer again. Otherwise you can add a button called "refresh" and bind macro to it which will turn the timer on again.
Yeah this sucks. I am writing a vba script for outlook and so the only way to debug is to close and reopen outlook after every error.

how to drag mails automatically in outlook?

our team has a shared mailbox in which we receive hundred of mails daily and we have to drag them manually from inbox into its separate sub-inboxes. So is there any rule or macro to drag them automatically.
VBS sample
Set objOutlook = GetObject (, "Outlook.Application")
Set objNamespace = objOutlook.GetNameSpace ("MAPI")
Set objFolderRoot = objNamespace.Folders ("Y2013")
Set objFolderSource = objFolderRoot.Folders ("Inbox")
Set objFolderDistance = objFolderRoot.Folders ("test")
MsgBox "Total emails is Inbox folder: " & objFolderSource.Items.Count
Set objEmail = objFolderSource.Items (1)
MsgBox "Subject of first email: " & objEmail.Subject
objEmail.Move objFolderDistance
Set objEmail = Nothing
Set objFolderDistance = Nothing
Set objFolderSource = Nothing
Set objFolderRoot = Nothing
Set objNamespace = Nothing
Set objOutlook = Nothing
Of course you MUST add object validation in this code.
You could also take a look to VBA (not VBS which you are using) sample on http://msdn.microsoft.com/en-us/library/office/ff860683.aspx

Handle the incoming emails using vbscript

Problem: Handle the incoming emails using vbscript.
Outlook Version: Outlook 2000
Description: I cannot use VBA for this as I believe Outlook 2000 doesn't let you run a VBA script from the rules wizard and hence I have to use the Run a Program | VBScript method.
What I know: I know how to handle email from VBA like this
Sub Sample(MyMail As MailItem)
Dim strID As String, olNS As Outlook.NameSpace
Dim olMail As Outlook.MailItem
strID = MyMail.EntryID
Set olNS = Application.GetNamespace("MAPI")
Set olMail = olNS.GetItemFromID(strID)
'~~> Rest of the code
Set olMail = Nothing
Set olNS = Nothing
End Sub
I also know how to run the vbscript on the email which is already in the inbox. To run a vbscript in OL2000, you have to use Run A Program and point it to the vbs file. The Run A Script is not available in OL2000.
What I do not know: And this is where I need help. How to get the mail object which has not hit the mail inbox in VBS. Once I get the object then I can do the rest of the necessary operations.
You are correct that OL2000 cannot run a VBA macro from a rule, if this article is to be believed.
Here's how I handle incoming emails. It does use VBA but as far as I know, there isn't a way to do so in VBScript.
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set Items = objNS.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub Items_ItemAdd(ByVal item As Object)
On Error Goto ErrorHandler
Dim Msg As Outlook.MailItem
If TypeName(item) = "MailItem" Then
Set Msg = item
'~~> do something with the new message here
End If
ProgramExit:
Exit Sub
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
Resume ProgramExit
End Sub
This code should be pasted into the ThisOutlookSession module, then Outlook should be restarted.

Resources