I inserted the below VBA I found somewhere on the web into my outlook to catch e-mails which I have forgotten to attach the attachment to before they get sent.
It worked fine on initial application of the VBA however today I managed to forget to attach the attachment to an e-mail and sure enough it has stopped working for whatever reason. Can anybody help me rectify this please?
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim m As Variant
Dim strBody As String
Dim intIn As Long
Dim intAttachCount As Integer, intStandardAttachCount As Integer
On Error GoTo handleError
'Edit the following line if you have a signature on your email that includes images or other files. Make intStandardAttachCount equal the number of files in your signature.
intStandardAttachCount = 1
strBody = LCase(Item.Body)
intIn = InStr(1, strBody, "original message")
If intIn = 0 Then intIn = Len(strBody)
intIn = InStr(1, Left(strBody, intIn), "attach")
intAttachCount = Item.Attachments.Count
If intIn > 0 And intAttachCount <= intStandardAttachCount Then
m = MsgBox("You forgot to attach your file didn't you?" & vbCrLf & " ...idiot" & vbCrLf & vbCrLf & " Send it without?", vbQuestion + vbYesNo + vbMsgBoxSetForeground)
If m = vbNo Then Cancel = True
End If
handleError:
If Err.Number <> 0 Then
MsgBox "Outlook Attachment Reminder Error: " & Err.Description, vbExclamation, "Outlook Attachment Reminder Error"
End If
End Sub
I inserted the below VBA I found somewhere on the web into my excel to catch e-mails which I have forgotten to attach the attachment to before they get sent.
Your code isn't going to work in Excel as is.
The event Application_ItemSend is an Outlook specific event handler. It is not present in Excel. It is tied to the Outlook action "Send." See here for a description.
Setup event handler for Excel to handle Outlook events
If you want to have this work for emails generated in Excel you are going to have to do something different.
In your situation, create a separate class module and name it OutlookEventHandler and paste the following:
Private WithEvents ol As Outlook.Application
Private Sub ol_ItemSend(ByVal Item As Object, Cancel As Boolean)
'your code here
End Sub
Public Sub init()
Set ol = GetObject(, "Outlook.Application")
End Sub
Then, in your ThisWorkbook module add the following:
Private olEH As OutlookEventHandler
Private Sub Workbook_Activate()
Set olEH = New OutlookEventHandler
olEH.init
End Sub
This will result in your event handler being called for emails sent by both Excel and Outlook.
Related
I have been tasked to create an automated report system where an report from Google Data Studios are uploaded to specific projects (On a site called Basecamp). The reports always include both a report within the body of the e-mail and an attached PDF file. The are sent to a Gmail account (data studios refuse to schedule towards a non-Google account). The filters within Gmail doesnt really work well with the Basecamp system so I use filters to re-route them towards a Outlook account. There I use rules to send each e-mail towards the correct client within Basecamp.
Here comes the problem, Basecamp shows both the body of the e-mail AND the attached PDF version which makes us show duplicates.
Is there a way to create a macro that first deletes all attachments (or body of an e-mail) and THEN forward the e-mail.
It cant be done manually it have to be a rule that does it automaticaly. Keep in mind that I am not a coder and have never done anything like this so please keep it simple for my dumb brain!
Thank you in advance!
Marcus
PS: I found a code that seems to be what I am after.
Public WithEvents ReceivedItems As Outlook.Items
Private Sub Application_Startup()
Set ReceivedItems = Outlook.Application.Session.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub ReceivedItems_ItemAdd(ByVal Item As Object)
Dim xForwardMail As Outlook.MailItem
Dim xEmail As MailItem
On Error Resume Next
If Item.Class <> olMail Then Exit Sub
Set xEmail = Item
If InStrRev(UCase(xEmail.Subject), UCase("kto feature")) = 0 Then Exit Sub 'change subject text to your need
If xEmail.Attachments.Count = 0 Then Exit Sub
Set xForwardMail = xEmail.Forward
With xForwardMail
.HTMLBody = ""
With .Recipients
.Add "skyyang#addin88.com" 'change address to your own
.ResolveAll
End With
.Send
End With
End Sub
I am trying to get that code to work, and changes the subject to a specific word and then route it to a final e-mail account that then filters out to correct clients. However the code doesnt seem to work, it DOES forward the e-mail but the attachment is still there. The code was found at https://www.extendoffice.com/documents/outlook/5359-outlook-forward-attachment-only.html#a1
It seems you need to modify the code slightly:
Public WithEvents ReceivedItems As Outlook.Items
Private Sub Application_Startup()
Set ReceivedItems = Outlook.Application.Session.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub ReceivedItems_ItemAdd(ByVal Item As Object)
Dim xForwardMail As Outlook.MailItem
Dim xEmail As MailItem
Dim myattachments as Outlook.Attachments
On Error Resume Next
If Item.Class <> olMail Then Exit Sub
Set xEmail = Item
If InStrRev(UCase(xEmail.Subject), UCase("kto feature")) = 0 Then Exit Sub 'change subject text to your need
If xEmail.Attachments.Count = 0 Then Exit Sub
Set xForwardMail = xEmail.Forward
Set myattachments = xForwardMail.Attachments
While myattachments.Count > 0
myattachments.Remove 1
Wend
With xForwardMail
.HTMLBody = ""
With .Recipients
.Add "skyyang#addin88.com" 'change address to your own
.ResolveAll
End With
.Send
End With
End Sub
The Remove method of the Attachments class removes an object from the collection.
I'm working within MS Access 2013 and MS Outlook 2013 on Windows 10 and I have a Access DB with a "Navigation Subforms" paradigm that allows sending a single e-mail on two separate occasions.
I'm trying to write code to do the following:
when a new e-mail is sent,
I want to save it as a .msg file on disk automatically.
From what I can tell, it seems the way to do this is via trapping the .ItemAdd event that fires on Outlook Sent Folder within Access, and in there executing the .SaveAs method.
I was trying to implement a solution based on these two answers:
How to Trap Outlook Events from Excel Application
Utilizing Outlook Events From Excel
but I just can't seem to combine the two and make the event fire.
My feeling is that either I'm not referencing/setting things correctly or the execution reaches an end before the e-mail is moved from the Outbox Folder to the Sent Folder, but I'm not sure.
How can I do this?
Thanks for reading, code follows:
My current class module - "cSentFolderItem"
Option Explicit
Public WithEvents myOlItems As Outlook.items
Private Sub Class_Initialize()
Dim oNS As NameSpace
Dim myOL As Outlook.Application
Set myOL = New Outlook.Application
Set oNS = myOL.GetNamespace("MAPI")
Set myOlItems = oNS.GetDefaultFolder(olFolderSentMail).items
End Sub
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
Debug.Print "I got a new item on Sent box!"
Dim myOlMItem As Outlook.MailItem
Set myItem = myOlItems.items(email_subject)
myItem.Display
myItem.SaveAs "C:\Users\XXXXXX\Desktop\mail_test.msg", olMSGUnicode
End Sub
"Regular" code:
Public Function GetApplication(Class As String) As Object
'Handles creating/getting the instance of an application class
Dim ret As Object
On Error Resume Next
Set ret = GetObject(, Class)
If Err.Number <> 0 Then
Set ret = CreateObject(Class)
End If
Set GetApplication = ret
On Error GoTo 0
End Function
Sub Test()
email_subject = "Mail test match string - [aaaa-mm-dd]"
Set myOlItems = New cSentFolderItem 'declare class module object
Dim MyOutlook As Outlook.Application
Set MyOutlook = GetApplication("Outlook.Application") 'trying to get correct application object
'The following code is a dummy e-mail creation, after which I press SEND:
Dim MyMail As Outlook.MailItem
varTo = "target_email#address.com"
varSubject = email_subject
varbody = "test line 1" & vbCrLf & "test line 2" & vbCrLf & "test line 2"
varSubject = Replace(varSubject, "[aaaa-mm-dd]", NOW())
Dim linhas() As String
linhas = Split(varbody, vbCrLf)
bodyHTMLtext = "<body>"
For i = 0 To UBound(linhas) - 1
bodyHTMLtext = bodyHTMLtext & linhas(i) & "<br>"
Next
bodyHTMLtext = bodyHTMLtext & linhas(UBound(linhas))
bodyHTMLtext = bodyHTMLtext & "</body>"
Set MyMail = MyOutlook.CreateItem(OLMAILITEM)
MyMail.To = varTo
MyMail.Subject = varSubject
MyMail.Display
MyMail.HTMLBody = bodyHTMLtext & MyMail.HTMLBody
AppActivate varSubject
'trying to leave Outlook object open:
''Cleanup after ourselves
'Set MyMail = Nothing
''MyOutlook.Quit
'Set MyOutlook = Nothing
End Sub
Ok, after some long hours, I figured it out, and got to the following solution.
My class module "MyOutlook" is:
Option Explicit
Public myOutlookApp As Outlook.Application
Public mySentFolder As Outlook.Folder
Public WithEvents myItems As Outlook.items
Private Sub Class_Initialize()
Set myOutlookApp = GetApplication("Outlook.Application")
Dim oNS As NameSpace
Set oNS = myOutlookApp.GetNamespace("MAPI")
Set mySentFolder = oNS.GetDefaultFolder(olFolderSentMail)
Set myItems = mySentFolder.items
End Sub
Private Sub myItems_ItemAdd(ByVal Item As Object)
Debug.Print "Got_EMAIL!!! Looking for subject = " & email_subject
'"e-mail_subject" is Public a string, assigned in another part of the program
If Item.Subject = email_subject Then
Item.SaveAs "C:\Users\640344\Desktop\mail_test.msg", olMSGUnicode
End If
End Sub
Where GetApplication is:
Function GetApplication(Class As String) As Object
'Handles creating/getting the instance of an application class
'If there exists one already (in my case, Outlook already open),
'it gets its name, else it creates one
Dim ret As Object
On Error Resume Next
Set ret = GetObject(, Class)
If Err.Number <> 0 Then
Set ret = CreateObject(Class)
If Class = "Outlook.Application" Then
'Outlook wasn't opened, so open it
ret.Session.GetDefaultFolder(olFolderInbox).Display
ret.ActiveExplorer.WindowState = olMaximized
ret.ActiveExplorer.WindowState = olMinimized
End If
End If
Set GetApplication = ret
On Error GoTo 0
End Function
Note that I added the 3 lines of code after 'Outlook wasn't opened, so open it because otherwise I would get an error. It's not a bad idea for my users that the program opens Outlook, anyway.
On the "regular" code part of my project, outside any procedure, I declare:
Public myOutlook As myOutlook
Then, on my project's "main" sub:
Set myOutlook = New myOutlook
'[...]
'Code where entire program runs
'[...]
Set myOutlook = Nothing
This way, myOutlook object (and its variables) "lives" the entire time the program (with its Navigation Forms) is running, and is waiting to trap _ItemAdd events on the default Sent Folder of Outlook.
Note that I look only for e-mails with subject equal to the email_subject string, because I don't want to save all sent e-mails, just the one sent by using the program, and I have code to assign my desired subject to that string.
Im creating an outlook Macro to validate an Email attachment and recipient name before sending the mail.
The recipient name can be easily validated through the ItemSend Function on the Outlook session.
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim Recipients As Outlook.Recipients
Dim recip As Outlook.Recipient
Dim i
Dim prompt As String
Set Recipients = Item.Recipients
For i = Recipients.Count To 1 Step -1
Set recip = Recipients.Item(i)
If InStr(LCase(recip), "bad#address.com") Then
prompt$ = "You sending this to this to " & Item.To & ". Are you sure you want to send it?"
If MsgBox(prompt$, vbYesNo + vbQuestion + vbMsgBoxSetForeground, "Check Address") = vbNo Then
Cancel = True
End If
End If
Next i
End Sub
While this helps with recipients, it does not allow to validate the attachment name before sending the mail. i.e Validate the Mail Draft. The code below helps to check for attachments present on the draft but does not help validate it.
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
If InStr(1, Item.Body, "attach", vbTextCompare) > 0 Then
If Item.Attachments.Count = 0 Then
answer = MsgBox("There's no attachment, send anyway?", vbYesNo)
If answer = vbNo Then Cancel = True
End If
So i tried to add item.Attachment. Name \ item.attachment.FileName but this works only if i attribute it to a outlook MailItem instead of a normal object.
Is it possible to create code to validate the attachment name for certain criteria ( name should conform to certain naming constraints ). The code has already been created and works as a normal macro and not as a session Macro.
Function Segregate_Function(Attach_Name_Pass1 As String)
Dim FullName As String
Dim Recepients As String
Region_Ext = Right(Attach_Name_Pass1, 7)
region = Left(Region_Ext, 3)
'MsgBox region
If region = "ENG" Then
Recepients = "ABC#gmail.com;XYZ#gmail.com"
Call Send_Function(Attach_Name_Pass1, Recepients)
Else
MsgBox " Not an Acceptable Attachment. Mail Could not be Generated "
End If
End Function
I would like the above code to execute when clicking on send to validate an attachment name directly, instead of having a procedural Macro running.
Do advice.
Try testing within ItemSend.
Something like this:
Option Explicit
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim att As attachment
Dim Attach_Name_Pass1 As String
Dim Region_Ext As String
Dim Region As String
Cancel = False
If Item.Attachments.count = 0 Then
If MsgBox("There's no attachment, send anyway?", vbYesNo) = vbNo Then Cancel = True
Else
Debug.Print Item.To
If InStr(Item.To, "ABC#gmail.com") > 0 Or InStr(Item.To, "XYZ#gmail.com") > 0 Then
For Each att In Item.Attachments
Attach_Name_Pass1 = att.DisplayName
Region_Ext = Right(Attach_Name_Pass1, 7)
Region = Left(Region_Ext, 3)
'MsgBox region
Debug.Print Region
If Region <> "ENG" Then
Cancel = True
MsgBox " Not an Acceptable Attachment. Send cancelled."
Exit For
End If
Next
End If
End If
End Sub
I have a macro coded to a rule that autoforwards all incoming and sent emails to a private email address in the BCC field (any auto BCC rule is disabled at the server level.) With the help of the board here, the macro works flawlessly, and for all intents and purposes is invisible.
However, if you open the SENT message in the SENT FOLDER, the BCC field is visible to all for the world to see. I have learned this is a "feature" in Outlook, apparently since 2003.
Is there a way to suppress the visibility of the BCC field when viewing the SENT email?
Or is there a way one can set the display options of an individual folder NOT to display a BCC - EVER?
Thank you for any assistance.
My code:
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim objRecip As Recipient
Dim strMsg As String
Dim res As Integer
Dim strBcc As String
Dim answer
Dim oAtt
Dim strProc1 As String
On Error GoTo Application_ItemSend_Error
strBcc = "myprivateemail#gmail.com"
Set objRecip = Item.Recipients.Add(strBcc)
objRecip.Type = olBCC
If Not objRecip.Resolve Then
strMsg = "Could not resolve the Bcc recipient. " & _
"Do you want still to send the message?"
res = MsgBox(strMsg, vbYesNo + vbDefaultButton1, _
"Could Not Resolve Bcc Recipient")
If res = vbNo Then
Cancel = True
End If
End If
Set objRecip = Nothing
On Error GoTo 0
Exit Sub
Application_ItemSend_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") " & "Error on
Line " & Erl & " in procedure Application_ItemSend of VBA Document
ThisOutlookSession"
End Sub
If you want to remove BCC recipients in the Sent Items folder, listen for the Items.ItemAdd event on the Sent Items folder, loop through all recipients in the MailItem.Recipients collection and delete recipients with Recipient.Type = olBCC.
"the BCC field is visible to all for the world to see"
Well, if anyone in the world can view your own sent folder, then this is the case. Otherwise the BCC field is not part of the email, recipients do not receive it. The goal of the feature is to have the ability to recall your own BCC messages, so you do not forget that you have sent them.
Try the following...
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim olRec As Outlook.Recipient
Dim Address$
Address = "Om3r#blala.com"
Set olRec = Item.Recipients.Add(Address)
olRec.Type = olBCC
olRec.Resolve
End Sub
I have tried the codes below and it was working exactly as I wanted it to be.
http://www.mrexcel.com/forum/excel-questions/361751-visual-basic-applications-saving-email-only-after-send-pushed.html
However, as I was creating this for other colleagues who are using different Excel version, some of my colleagues are having library error. After read around, I believe I need to use late binding instead of early binding so people with different excel version can access the function.
How to I change the code below (to late binding) so that all other version of excel users can also use the function? Copy of the working codes below.
Class Code
Option Explicit
Public WithEvents obj_OL As Outlook.Application
'
Private Sub obj_OL_ItemSend(ByVal Item As Object, Cancel As Boolean)
'// For example, change pathway to suit, such as: //
'// "\\Dacsrv02\rtddata\Advice\TechQueries\TRIM Emails\" & emailname
OutMail.SaveAs ThisWorkbook.Path & _
Application.PathSeparator & _
emailname
'// AFTER the event has been called, explicitly release Outlook. //
Set obj_OL = Nothing
Set OutMail = Nothing
End Sub
Sub Codes
Option Explicit
'// Connect the declared object w/the class module //
Dim cls_OL As New clsOutlook
'// Declare the email msg (mail item) and emailname string as Public, so //
'// that they can be "seen" from any procedures in the class mod. //
Public OutMail As Outlook.MailItem
Public emailname As String
Sub Email_Response()
Dim strbody As String
'// Create our new mail item //
Set OutMail = cls_OL.obj_OL.CreateItem(0)
'// Example substitute //
strbody = "This is just example text"
On Error Resume Next
emailname = "something.msg"
With OutMail
.To = ""
.CC = ""
.BCC = ""
.Subject = "Specific Subject"
.body = strbody
.Display
End With
On Error GoTo 0
End Sub
You cannot (easily) support events using late binding, not in VB script. Whoever is using your code, will need to add the right version of Outlook to the project references.