Email generator called from Application_NewMail is generating duplicate emails - vba

I have this macro which picks the file (pdf) out of a specified folder and then emails it as an attachment.
It is running via Application_NewMail and then links to an Excel file where the pdf is created and saved.
It works but the attachment is sent twice in two separate emails - I thought about writing another macro to remove duplicate emails but I am afraid this might delete emails I do not want to delete.
Folder path and email address have been replaced by placeholders.
Sub Any_help_appreciated()
Dim objMail As Outlook.MailItem
Dim fso As Object 'Scripting.FileSystemObject
Dim strFile As String
Dim fsoFile 'As Scripting.File
Dim fsoFldr 'As Scripting.Folder
Dim dtNew As Date, sNew As String
Set fso = CreateObject("Scripting.FileSystemObject")
strFile = "FOLDER PATH" 'path to pdf folder
Set fsoFldr = fso.GetFolder(strFile)
dtNew = Now - TimeValue("00:00:30") 'select pdf if created in last 30 secs
For Each fsoFile In fsoFldr.Files
If fsoFile.DateCreated > dtNew Then
sNew = fsoFile.Path
Set objMail = Application.CreateItem(olMailItem)
With objMail
.To = "email.address#email.com"
.Subject = "Subject"
.BodyFormat = olFormatPlain
.Attachments.Add sNew
.Send ' .send
End With
End If
Next fsoFile
End Sub

You get two emails because Application_NewMail event handler gets executed twice and two emails are sent consecutively (only two because it takes some time before server processes the e-mail). I guess it is because .To value of email.address#email.com is your email account for testing.
Try to add some check if the code for objMail.Send does get executed twice like writing something to a text file, log etc.
Or move the PDF file to a different folder after processing it.

Related

Outlook not saving attachment via VBA

I have some VBA code that actually works fine on my machine, but not my clients. Where it gets hung up is the opening of an email attachment and saving it to a location on his computer.
For Each nm in file_names 'file_names is just an array of strings
found_file=False
curr_date=CDate("1-1-9999")
For Each olItem in olItems
If olItem.ReceivedTime < curr_date and olItem.SenderEmailAddress=email and TypeName(olItem)="MailItem" then
Set olAttach=olItem.attachments.Item(1)
If not olAttach is Nothing then
If olAttach.Filename Like nm & ".*" then
found_file=True
curr_date=olItem.ReceivedTime
end if
end if
end if
Next
If found_file then
olAttach.SaveAsFile pth & olAttach.Filename 'errors out here
...
The error message is Cannot save the attachment and does not specify a reason.
I have tried to have him enable all macros, switch off protected view options, restart excel and outlook, try different file locations to save to, there are no double \ that occur when file path is concatenated with the file name, and I made sure he wasn't using a Mac. Apparently one of the attachment files does open but it just refuses to save.
Looks like the file path/name string passed to the SaveAsFile method is not a well-formed path. For example, the FileName may contains forbidden symbols and etc. Try to use the following code as a test:
Sub SaveAttachment()
Dim myInspector As Outlook.Inspector
Dim myItem As Outlook.MailItem
Dim myAttachments As Outlook.Attachments
Set myInspector = Application.ActiveInspector
If Not TypeName(myInspector) = "Nothing" Then
If TypeName(myInspector.CurrentItem) = "MailItem" Then
Set myItem = myInspector.CurrentItem
Set myAttachments = myItem.Attachments
'Prompt the user for confirmation
Dim strPrompt As String
strPrompt = "Are you sure you want to save the first attachment in the current item to the Documents folder? If a file with the same name already exists in the destination folder, it will be overwritten with this copy of the file."
If MsgBox(strPrompt, vbYesNo + vbQuestion) = vbYes Then
myAttachments.Item(1).SaveAsFile Environ("HOMEPATH") & "\My Documents\" & _
myAttachments.Item(1).DisplayName
End If
Else
MsgBox "The item is of the wrong type."
End If
End If
End Sub

How to send a Word document as body of an email with VBA

I've created a macro that works with outlook and excel that will use a list of email addresses (in excel) and send all those addresses an email (in outlook). I want to take a word document (from microsoft word) and use it as the body of the email. The problem is, I will have images in the word document and I need the word document to keep it's formatting. Right now, my VBA takes the content of my word document but the formatting is gone and images aren't included. This is my code:
Sub spamEmail()
'Setting up the Excel variables.
Dim olApp As Object
Dim oMail As Outlook.MailItem
Dim iCounter As Integer
Dim Dest As Variant
Dim SDest As String
Dim Excel As Object
Dim Name As String
Dim Word As Object
Dim oAccount As Outlook.Account
Dim doc As Word.Document
Dim itm As Object
Dim MsgTxt As String
'Set the outlook account to send.
Set oAccount = Application.Session.Accounts.Item(2)
'Create excel object.
Set Excel = CreateObject("excel.application")
Excel.Visible = True
Excel.Workbooks.Open ("C:\Users\Deryl Lam\Documents\Book1.xlsx")
Excel.Workbooks("Book1.xlsx").Activate
'Create a word object.
Set Word = CreateObject("word.application")
Set doc = Word.Documents.Open _
(FileName:="C:\Users\Deryl Lam\Documents\emailBody.docx", ReadOnly:=True)
'Pulls text from file for message body
MsgTxt = doc.Range(Start:=doc.Paragraphs(1).Range.Start, _
End:=doc.Paragraphs(doc.Paragraphs.Count).Range.End)
'Loop through the excel worksheet.
For iCounter = 1 To WorksheetFunction.CountA(Workbooks("Book1.xlsx").Sheets(1).Columns(1))
'Create an email for each entry in the worksheet.
Set oMail = Application.CreateItem(olMailItem)
With oMail
SDest = Cells(iCounter, 1).Value
If SDest = "" Then
'Dont do anything if the entry is blank.
Else
'Do additional formatting on the BCC and Subject lines, add the body text from the spreadsheet, and send.
Name = Cells(iCounter, 2).Value
.BCC = SDest
.Subject = "FYI"
.Body = "Dear " & Name & "," & vbCrLf & MsgTxt
'SendUsingAccount is new in Office 2007
'Change Item(1)to the account number that you want to use
.SendUsingAccount = oAccount
.Send
End If
End With
Next iCounter
'Clean up the Outlook application.
Set olMailItm = Nothing
Set olApp = Nothing
End Sub
I've searched all over google for a solution but I haven't found one. How can I send a word document as the body of the email with it's formatting in tact and the images included?
You are getting the contents of your template document as a string, which by definition will not contain any formatting or images. You should instead copy the contents to the clipboard and then paste them into the new email.
Something like this:
Sub emailFromDoc()
Dim wd As Object, editor As Object
Dim doc As Object
Dim oMail As MailItem
Set wd = CreateObject("Word.Application")
Set doc = wd.documents.Open(...path to your doc...)
doc.Content.Copy
doc.Close
set wd = Nothing
Set oMail = Application.CreateItem(olMailItem)
With oMail
.BodyFormat = olFormatRichText
Set editor = .GetInspector.WordEditor
editor.Content.Paste
.Display
End With
End Sub
If not for the images, you could save the document as an HTML file, read its contents, then set the MailItem.HTMLBody property.
Images are more involved. I don't see a straightforward way to bring them into a message body.

Getting attachment from Outlook and placing in the body of a new Email after Formatting

This is essentially what I am trying to do...
search for a specific email by subject name
get the attachment to that email ( the attachment is an excel sheet of raw data)
run a formatting subroutine from another module on the excel attachment
place the newly formatted attachment to the body of a new email
Send the new email out to the client
I need help with steps 3 & 4.
Option Explicit
Sub sendEmail()
Dim olApp As Outlook.Application
Dim olEmail As Outlook.MailItem
Dim olNs As Namespace
Dim Fldr As MAPIFolder
Dim olMi As MailItem
Dim olAtt As Attachment
Dim MyPath As String
Dim i As Long
Set olApp = New Outlook.Application
Set olEmail = olApp.CreateItem(olMailItem)
Set olNs = olApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderInbox)
MyPath = "C:\Users\(Me)\Desktop\"
For i = Fldr.Items.count To 1 Step -1
Set olMi = Fldr.Items(i)
If InStr(1, olMi.Subject, "[The email I'm looking for by subject]") > 0 Then
For Each olAtt In olMi.Attachments
olAtt.Module2.Format '<--- this is where i try to do step 3
olAtt.SaveAsFile MyPath & "NewSheet" & ".xls"
With olEmail
.BodyFormat = olFormatHTML
.Body = olAtt.Range '<----this is where i try to do step 4
.To = "someone#something.com"
.Subject = "Tester"
.send
End With
Next olAtt
olMi.Save
End If
Next i
Set olAtt = Nothing
Set olMi = Nothing
Set Fldr = Nothing
Set MoveToFldr = Nothing
Set olNs = Nothing
Set olApp = Nothing
End Sub
See Getting Started with VBA in Outlook 2010.
I have noticed the following code:
For i = Fldr.Items.count To 1 Step -1
Set olMi = Fldr.Items(i)
If InStr(1, olMi.Subject, "[The email I'm looking for by subject]") > 0 Then
Do not iterate over all items in the folder. Instead, you need to use the Find/FindNext or Restrict methods of the Items class to get items which corresponds to your conditions. You can read more about them in the following articles in MSDN. Also you may find the following articles helpful:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
.Body = olAtt.Range '<----this is where i try to do step 4
The Outlook object model provides three main ways for working with item bodies:
Body.
HTMLBody.
The Word editor. The WordEditor property of the Inspector class returns an instance of the Word Document which represents the message body. So, you can use the Word object model do whatever you need with the message body.
See Chapter 17: Working with Item Bodies for more information.

Extract attachement from Outlook Contacts

I'm wondering if anyone managed to build a code to extract attachements within Outlook contacts? I have a lot of contacts in my outlook 2010 with several attachements and would like to create a copy for backup. Also, if an automated way exist, is it possible to link the downloaded attachement to the contacts?
update
I have used several pieces of code to do what i want but getting a "User-defined type not defined". Anyone know hoe to avoid that error?
Option Explicit
Sub GetAttachments()
Dim ns As Outlook.NameSpace
Dim contactFolder As Outlook.MAPIFolder
Dim myItem As Outlook.Item
Dim ContactItem As Object
Dim Attmt As Outlook.Attachments
Dim FileName As String
Dim i As Integer
Set ns = Application.GetNamespace("MAPI")
Set contactFolder = ns.GetDefaultFolder(olFolderContacts)
Set myItem = contactFolder.Items
Set Attmt = myItem.Attachments
i = 0
' Check each contacts for attachments
For Each ContactItem In contactFolder.Items
' Save any attachments found
For Each Attmt In ContactItem.Attachments
' This path must exist! Change folder name as necessary.
FileName = "C:\Temp\" & Attmt.FileName
Attmt.SaveAsFile FileName
i = i + 1
Next Attmt
Next ContactItem
End Sub
Use ContactItem.Attachments collection. To save an attachment, call Attachment.SaveAsFile.
You can develop a VBA macro or add-in to get the job done. Be aware, VBA macros are not designed for distributing the solution on multiple PCs. See Getting Started with VBA in Outlook 2010 for more information about VBA macros in Outlook.
If you need to automate Outlook from another applications, see How to automate Outlook by using Visual Basic.
As Dmitry suggested, you can use the SaveAsFile method of the Attachment class to save the attached file on the disk.
Sub SaveAttachment()
Dim myInspector As Outlook.Inspector
Dim myItem As Outlook.ContactItem
Dim myAttachments As Outlook.Attachments
Set myInspector = Application.ActiveInspector
If Not TypeName(myInspector) = "Nothing" Then
If TypeName(myInspector.CurrentItem) = "ContactItem" Then
Set myItem = myInspector.CurrentItem
Set myAttachments = myItem.Attachments
'Prompt the user for confirmation
Dim strPrompt As String
strPrompt = "Are you sure you want to save the first attachment in the current item to the Documents folder? If a file with the same name already exists in the destination folder, it will be overwritten with this copy of the file."
If MsgBox(strPrompt, vbYesNo + vbQuestion) = vbYes Then
myAttachments.Item(1).SaveAsFile Environ("HOMEPATH") & "\My Documents\" & _
myAttachments.Item(1).DisplayName
End If
Else
MsgBox "The item is of the wrong type."
End If
End If
End Sub
To attach a file anew you can use the Add method of the Attachments class which creates a new attachment in the Attachments collection.
Sub AddAttachment()
Dim myItem As Outlook.MailItem
Dim myAttachments As Outlook.Attachments
Set myItem = Application.CreateItem(olMailItem)
Set myAttachments = myItem.Attachments
myAttachments.Add "C:\Test.doc", _
olByValue, 1, "Test"
myItem.Display
End Sub

How to find and download attachment from the latest mail in an Outlook folder?

I have a folder created where all the mails are deposited based on a rule. The mail in the folder keeps accumulating everyday. I want to download the attachment from the latest mail in that folder. Currently, I am able to parse through all the files and download attachment from all the mails. How do I download only from the latest mail? Below is my code.
Sub FebAttachment_Click()
Const AttachmentPath As String = "D:\Documents and Settings\rahul.baskaran\Desktop\"
Dim oApp As Object, ONS As Object, OInb As Object
Dim OItem, OAtch As Object
Dim OFind As Object
Dim OMail As Object
Dim strName As String
Dim strExt As String
Set oApp = GetObject(, "Outlook.application")
Set ONS = oApp.GetNamespace("MAPI")
Set OInb = ONS.Folders("Archive Folders").Folders("BIZOPS").Folders("2014.02")
Set OMail = OInb.Items
For Each OItem In OInb.Items
If OItem.Attachments.Count <> 0 Then
For Each OAtch In OItem.Attachments
strName = OAtch.Filename
strExt = Split(strName, ".z")(0)
OAtch.SaveAsFile AttachmentPath & OAtch.Filename
Exit For
Next
Else
MsgBox "The mail doesn't have an attachment"
End If
Next OItem
Sort the items by the creation date (Items.Sort) in the descending order, then retrieve the first item in the collection.
Make sure your code operates on the same Items collection (retrieve OInb.Items once and cache it in a variable).