excel attached email automatically saved in a folder - vba

I would like, thanks to a code, that as soon as I receive an email from a certain person that the attachment of this email is automatically saved in a folder "TEST" here, then that the email is marked as read and then filed. Here is what I could find online but it does not work. I have no error message but I have no record either.
Can you please help me
Sub extrait_PJ_vers_rep(strID As Outlook.MailItem)
Dim olNS As Outlook.namespace
Dim MyMail As Outlook.MailItem
Dim expediteur
Set olNS = Application.GetNamespace("MAPI")
Set MyMail = olNS.GetItemFromID(strID.EntryID)
If MyMail.Attachments.Count > 0 Then
expediteur = MyMail.SenderEmailAddress
Repertoire = "c:\TEST" & "\"
If Repertoire <> "" Then
If "" = Dir(Repertoire, vbDirectory) Then
MkDir Repertoire
End If
End If
Dim PJ, typeatt
For Each PJ In MyMail.Attachments
typeatt = Isembedded(strID, PJ.Index)
If typeatt = "" Then
If "" <> Dir(Repertoire & PJ.FileName, vbNormal) Then
MsgBox Repertoire & PJ.FileName & " Done before"
If "" = Dir(Repertoire & "old", vbDirectory) Then
MkDir Repertoire & "old"
End If
FileCopy Repertoire & PJ.FileName, Repertoire & "old\" & PJ.FileName
End If
PJ.SaveAsFile Repertoire & PJ.FileName
End If
Next PJ
MyMail.UnRead = False
MyMail.Save
Dim myDestFolder As Outlook.MAPIFolder
Set myDestFolder = MyMail.Parent.Folders("test")
MyMail.Move myDestFolder
End If
Set MyMail = Nothing
Set olNS = Nothing
End Sub
I would like, thanks to a code, that as soon as I receive an email from a certain person that the attachment of this email is automatically saved in a folder "TEST" here, then that the email is marked as read and then filed

The code does exactly what you need, you just need to hook up the NewMailEx event of the Outlook Application class which is fired when a new message arrives in the Inbox and before client rule processing occurs. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem. The EntryIDsCollection string contains the Entry ID that corresponds to that item. Use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item, like you do in the code.
Go to the VBA environment in Outlook, select the Application on the lef-hand side dropdown list like shown on the screenshot:
Then you can add the NewMailEx event handler:
Viola! The event handler will be added, you just need to paste your code here.

Related

Moving over 20,000 emails, based on email address, freezes Outlook

I am trying to move over 20,000 emails, based on email address, into desired folders.
The code I found freezes Outlook. The code does work before the freeze.
Using first code from the answer to this post
Option Explicit
Public Sub Move_Items()
' // Declare your Variables
Dim Inbox As Outlook.MAPIFolder
Dim SubFolder As Outlook.MAPIFolder
Dim olNs As Outlook.NameSpace
Dim Item As Object
Dim Items As Outlook.Items
Dim lngCount As Long
On Error GoTo MsgErr
' Set Inbox Reference
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
' // Loop through the Items in the folder backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items(lngCount)
If Item.Class = olMail Then
Select Case Item.SenderEmailAddress
' // Email_One
Case "Email_One#email.com"
' // Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder One")
Set Item = Items.Find("[SenderEmailAddress] = 'Email_One#email.com'")
If TypeName(Item) <> "Nothing" Then
' // Mark As Read
Item.UnRead = False
' // Move Mail Item to sub Folder
Item.Move SubFolder
End If
' // Email_Two
Case "Email_Two#email.com"
' // Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder Two")
Set Item = Items.Find("[SenderEmailAddress] = 'Email_Two#email.com'")
If TypeName(Item) <> "Nothing" Then
' // Mark As Read
Item.UnRead = False
' // Move Mail Item to sub Folder
Item.Move SubFolder
End If
End Select
End If
Next lngCount
MsgErr_Exit:
Set Inbox = Nothing
Set SubFolder = Nothing
Set olNs = Nothing
Set Item = Nothing
Set Items = Nothing
Exit Sub
'// Error information
MsgErr:
MsgBox "An unexpected Error has occurred." _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume MsgErr_Exit
End Sub
Also is it possible to filter not a specific email address e.g. dave#test.com but *#test.com?
I think at least your first problem might be the line 'Set Inbox = olNs.GetDefaultFolder(olFolderInbox)'
I have the similar line 'Set Items = objNS.GetDefaultFolder(olFolderInbox).Items' in my start-up routine Private Sub Application_Startup() . This worked fine ever since we switched to 365, but then circa February 2021 it started to crash on start-up. I got here by searching on this problem. Presumably they have changed something about the object model.
I also suppose it could be where olNs is set in the first place ' Set objNS = olApp.GetNamespace("MAPI"), if you mail doesn't use MAPI?
I've chucked the problem at out IT support, and I'll let you know if they come back with anything other than a mildly panicked 'what the hell you doing using VBA?'
The delay is caused by running a time-consuming task/code in Outlook. So, you need to optimize what and how is run in Outlook.
The problem is in the source code. I've noticed that you are iterating over all items in the folder:
// Loop through the Items in the folder backwards
For lngCount = Items.Count To 1 Step -1
That is completely a bad idea!
Instead, you need to use the Find/FindNext or Restrict methods to process all items that correspond to the specified search criteria. The Find method returns a single and first entry from the list of items. To get the second (if any) you need to use the FindNext method in the loop.
Read more about these methods in the following articles:
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
Also you may consider using the AdvancedSearch method of the Application class. The key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
See Advanced search in Outlook programmatically: C#, VB.NET for more information.
If processing every item there is no need for a Find. Find replaces the For loop item. It is more likely to run to completion when there are fewer items.
The simplest change is to remove the Find. This should fix any array out of bounds errors. Still it is inefficient.
// Email_One
Case "Email_One#email.com"
'// Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder One")
'// Mark As Read
Item.UnRead = False
'// Move Mail Item to sub Folder
Item.Move SubFolder
One way to limit processing to the applicable items.
Option Explicit
Public Sub Move_Items_Restrict()
'// Declare your Variables
Dim myInbox As Folder
Dim subFolder As Folder
Dim myItem As Object
Dim myItems As Items
Dim resItems As Items
Dim strfilter As String
Dim i As Long
' Not while developing
'On Error GoTo MsgErr
' Set Inbox Reference
Set myInbox = Session.GetDefaultFolder(olFolderInbox)
'// Email_One
Set myItems = myInbox.Items
strfilter = "[SenderEmailAddress] = 'Email_One#email.com'"
Debug.Print strfilter
' some of these work, fromemail does
' https://learn.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2007/aa579702(v=exchg.80)
'strfilter = "#SQL=urn:schemas:httpmail:fromemail LIKE '%#test.com'"
'Debug.Print strfilter
Set resItems = myItems.Restrict(strfilter)
Debug.Print resItems.count
If resItems.count > 0 Then
'// Set SubFolder of Inbox
Set subFolder = myInbox.folders("Folder One")
For i = resItems.count To 1 Step -1
Set myItem = resItems(i)
With myItem
'// Mark As Read
.UnRead = False
'// Move Mail Item to sub Folder
.Move subFolder
End With
' If there is a memory error,
' release item when no longer necessary,
'Set myItem = Nothing
Next
End If
'// Email_Two
Set myItems = myInbox.Items
strfilter = "[SenderEmailAddress] = 'Email_Two#email.com'"
Debug.Print strfilter
Set resItems = myItems.Restrict(strfilter)
Debug.Print resItems.count
If resItems.count > 0 Then
'// Set SubFolder of Inbox
Set subFolder = myInbox.folders("Folder Two")
For i = resItems.count To 1 Step -1
Set myItem = resItems(i)
With myItem
' // Mark As Read
.UnRead = False
' // Move Mail Item to sub Folder
.Move subFolder
End With
' If there is a memory error,
' release item when no longer necessary,
'Set myItem = Nothing
Next
End If
MsgErr_Exit:
Exit Sub
'// Error information for users to advise the developer
MsgErr:
MsgBox "An unexpected Error has occurred." _
& vbCrLf & "Error Number: " & err.Number _
& vbCrLf & "Error Description: " & err.description _
, vbCritical, "Error!"
Resume MsgErr_Exit
End Sub

Generated reply email is catching the oldest email that includes the subject text

The code below will generate a reply email based on the text that is input in the cell Worksheets("Checklist Form").Range("B5"))
This reply email has all the recipients, a customized body, subject and everything works perfectly. EXCEPT I realized through testing that it will grab the oldest email with the text subject or the oldest email that contains that text in it's subject. The thing is, the code seems to copy the recipients from the oldest email of the oldest thread that is in your inbox but then replies to the most recent email in that same thread.
For example if the worksheet(Checklist Form B5) contains the phrase "Kawhi Leonard" the reply email generated will reply to the oldest email thread, but the newest email it seems in that thread. What's weird is it will catch the recipients of the oldest email of the oldest thread in your inbox that contains that subject.
This is a problem since I get many emails with some of the same key words or subjects. Is there a solution to have the code grab the most recent text in a subject of an email. Or a better solution would have a choice of catching the most recent or another one. Or also grab the email with the exact subject not the oldest one that contains the text in it's subject.
Sub Display()
Dim olApp As Outlook.Application
Dim olNs As Namespace
Dim Fldr As MAPIFolder
Dim olMail As Variant
Dim i As Integer
Dim IsExecuted As Boolean
Signature = Environ("appdata") & "\Microsoft\Signatures\"
If Dir(Signature, vbDirectory) <> vbNullString Then
Signature = Signature & Dir$(Signature & "*.htm")
Else
Signature = ""
End If
Signature = CreateObject("Scripting.FileSystemObject").GetFile(Signature).OpenAsTextStream(1, -2).ReadAll
Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderInbox)
IsExecuted = False
For Each olMail In Fldr.Items
If InStr(olMail.Subject, Worksheets("Checklist Form").Range("B5")) <> 0 Then
If Not IsExecuted Then
With olMail.ReplyAll
.HTMLBody = "<p style='font-family:calibri;font-size:14.5'>" & _
"Hi Everyone," & "<p style='font-family:calibri;font-size:14.5'>" & _
Worksheets("Checklist Form").Range("B4") & "Regards," & "</p><br>" & _
Signature & .HTMLBody
.Display
End With
IsExecuted = True
End If
End If
Next olMail
End Sub
Firstly, never loop through all items in a folder - use Items.Find/FindNext or Items.Restrict. In your particular case, call Items.Sort, then Items.Find - you only need a ingle item.
Secondly, you need to sort the collection first - call Items.Sort on ReceivedTime.
Thirdly, you are invoking Worksheets("Checklist Form").Range("B5") on each step of the loop. This is extremely inefficient.
Off the top of my head:
set items = Fldr.Items
items.Sort "ReceivedTime", true
strSubject = Worksheets("Checklist Form").Range("B5")
set olMail = items.Find(" #SQL=Subject LIKE '" & strSubject & "'")

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

Save email attachment based on email subject

Ever day at 12 am there is an automatic email with an excel attachment from a vendor service with a specific subject. I am using rules and code to attempt to save the attachment and insert the information into a database I have created upon being received in the inbox.
I have tried code that I have found online however I don't know if doesn't work because of some network/ security setting my company has or if its he code it self.
Rule:
CODE:
Public Sub CribMaster2Database(itm As Outlook.MailItem)
Dim objAtt As Outlook.Attachment
Dim saveFolder As String
saveFolder = "c:\temp\"
If olItem.Subject = "Test" Then
For Each objAtt In itm.Attachments
objAtt.SaveAsFile saveFolder & "\" & objAtt.DisplayName
Set objAtt = Nothing
Next
End If
End Sub
Add code to the ThisOutlookSession to watch your folder for arrivals.
CribMaster_ItemAdd fires whenever something arrives in your watched folder.
At the very top of the module:
Dim WithEvents CribMaster As Items
Const SAVE_PATH As String = "c:\temp\"
Private Sub Application_Startup()
Dim ns As Outlook.NameSpace
Set ns = GetNamespace("MAPI")
'Change `holi4683` to the name of your account
'(should be visible just above your inbox).
Set CribMaster = ns.Folders.Item("holi4683") _
.Folders.Item("Inbox").Items
End Sub
Sub CribMaster_ItemAdd(ByVal Item As Object)
Dim olAtt As Attachment
Dim i As Integer
With Item
For i = 1 To .Attachments.Count
Set olAtt = .Attachments(i)
olAtt.SaveAsFile SAVE_PATH & olAtt.DisplayName
.UnRead = False
DoEvents
Next i
End With
Set olAtt = Nothing
End Sub
I'd usually use a rule to move the emails to a subfolder and watch that folder - means I don't have to worry about meeting invites, etc.
To do this you'd change your watched folder like this:
Set CribMaster = ns.Folders.Item("holi4683") _
.Folders.Item("Inbox") _
.Folders.Item("SubFolder").Items
Restart Outlook for the code to work, or manually run the Application_Startup() procedure.

Outlook Rule Save email to text

I'm having trouble with automatically exporting the body of an email into a text file using a script.
I've managed a script that will save the text into a file on a macro but that won't work on a rule which is what I need.
My current code is as follows:
Sub SaveAsTXT()
Dim myItem As Outlook.Inspector
Dim objItem As Object
Dim myFolder As Folder
Set myItem = Application.ActiveInspector
If Not TypeName(myItem) = "Nothing" Then
Set myNamespace = Application.GetNamespace("MAPI")
Set myFolder = myNamespace.GetDefaultFolder(olFolderInbox)
Set objItem = myItem.CurrentItem
strname = objItem.Subject
strdate = Format(objItem.ReceivedTime, " yyyy mm dd")
objItem.SaveAs "c:\users\philip\documents\" & strname & strdate & ".txt", olTXT
End If
End Sub
Apologies if it looks a bit messy, I've edited it countless times trying to get it to work.
That's the code that will correctly run when I'm in the open email and run it as a macro but it won't work correctly when run as a rule
I have tried amending to Sub SaveAsTXT(Item as Outlook.Mailitem) but this also doesn't seem to work
So basically the question is how to I ensure the code will select the email (which will always be entitled "Rotas" without quotes) when it is run as a rule?
Info: Using office 2010 and I'm not a very good coder to start with.
Actually I managed to sort it out myself.
I didn't consider that the item as Outlook.Mailitem element was actually the thing that was selected by the rule. So I applied item as the object rather than objItem
Find the successful (and cleaned up) code below:
Sub SaveAsTXT(myMail As Outlook.MailItem)
Dim objItem As Object
Dim myFolder As Folder
If Not TypeName(myitem) = "Nothing" Then
If myMail.Subject = "Rotas" Then
strname = myMail.Subject
strdate = Format(myMail.ReceivedTime, " yyyy mm dd")
myMail.SaveAs "c:\users\philip\documents\" & strname & ".txt", olTXT
End If
End If
End Sub