VBA/Outlook extracting attachments from .eml files - vba

I'm trying to take a folder full of .eml messages with attachments and then extract/rename/save the attachments in another folder. My code :
Sub SaveAttachments()
Dim OlApp As Outlook.Application
Set OlApp = GetObject(, "Outlook.Application")
Dim MsgFilePath
Dim Eml As Outlook.MailItem
Dim att As Outlook.Attachments
Dim Path As String
Path = "C:\Users\richard\Desktop\Inbox\"
If OlApp Is Nothing Then
Err.Raise ERR_OUTLOOK_NOT_OPEN
End If
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")
Dim temp As Object
Set temp = fs.GetFolder(Path)
For Each MsgFilePath In temp.Files
Set Eml = OlApp.CreateItemFromTemplate(Path & MsgFilePath.Name)
Set att = Eml.Attachments
If att.Count > 0 Then
For i = 1 To att.Count
fn = "C:\Users\richard\Desktop\cmds\" & Eml.SenderEmailAddress
att(i).SaveAsFile fn
Next i
End If
Set Eml = Nothing
Next
Set OlApp = Nothing
End Sub
But I'm getting straightaway this error on the first file in the loop, ie the line
Set Eml = OlApp.CreateItemFromTemplate(Path & MsgFilePath.Name) :
-2147286960 (80030050) %1 already exists.
Any ideas on what is going on much appreciated !

Try this (TRIED AND TESTED)
Private Declare Function ShellExecute Lib "shell32.dll" Alias _
"ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As _
String, ByVal lpFile As String, ByVal lpParameters As String, _
ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Private Const SW_SHOWNORMAL As Long = 1
Private Const SW_SHOWMAXIMIZED As Long = 3
Private Const SW_SHOWMINIMIZED As Long = 2
Sub SaveAttachments()
Dim OlApp As Outlook.Application
Set OlApp = GetObject(, "Outlook.Application")
Dim MsgFilePath
Dim Eml As Outlook.MailItem
Dim att As Outlook.Attachments
Dim sPath As String
sPath = "C:\Users\richard\Desktop\Inbox\"
If OlApp Is Nothing Then
Err.Raise ERR_OUTLOOK_NOT_OPEN
End If
sFile = Dir(sPath & "*.eml")
Do Until sFile = ""
ShellExecute 0, "Open", sPath & sFile, "", sPath & sFile, SW_SHOWNORMAL
Wait 2
Set MyInspect = OlApp.ActiveInspector
Set Eml = MyInspect.CurrentItem
Set att = Eml.Attachments
If att.Count > 0 Then
For i = 1 To att.Count
fn = "C:\Users\richard\Desktop\cmds\" & i & "-" & Eml.SenderEmailAddress
att(i).SaveAsFile fn
Next i
End If
sFile = Dir$()
Loop
Set OlApp = Nothing
End Sub
Private Sub Wait(ByVal nSec As Long)
nSec = nSec + Timer
While nSec > Timer
DoEvents
Wend
End Sub

Related

Excel VBA, "Print" secured pdf to another pdf file using Shell

I have searched inside a folder in outlook, found all emails with a defined title, and downloaded their attachments into a folder via Excel VBA.
I now need to print those to new pdfs via Adobe Reader XI through VBA - as they are password protected- to be able to convert to RFT (I use VBA to get data from the PDF converted to RFT).
Somehow the correct RF layout is only created if the already saved pdf file is printed to a secondary pdf- Saving doesn't work - whether by explorer pdf viewer, Nitro or Adobe makes no difference.
I have tried Attachment.Printout but get error that the object does not support, am not able to find the option within a Shellexecute that will allow printing to file, as the main advice online allows printing via:
Call apiShellExecute(Application.hwnd, "print", strPathAndFilename, vbNullString, vbNullString, 0)
with options /p and /h for printing. any help on how to accomplish this with or without shell (or directly convert secured pdf to rft is appreciated).
The code I use ( borrowed and edited from VBA to loop through email attachments and save based on given criteria) for automatically downloading the files is listed bellow:
Sub email234()
Application.ScreenUpdating = False
Dim sPSFileName As String
Dim sPDFFileName As String
Dim olApp As Object
Dim ns As Namespace
Set olApp = CreateObject("Outlook.Application")
Set ns = olApp.GetNamespace("MAPI")
Dim oItem As Object
Dim olMailItem As Outlook.MailItem
Dim olNameSpace As Object
Dim olFolder As Object
Dim olFolderName As String
Dim olAtt As Outlook.Attachments
Dim strName As String
Dim sPath As String
Dim i As Long
Dim j As Integer
Dim olSubject As String
Dim olSender As String
Dim sh As Worksheet
Dim LastRow As Integer
olFolderName = "\\Subscriptions\Inbox" 'ThisWorkbook.Worksheets("Control").Range("D10")
olSender = "Argus Alerts" 'ThisWorkbook.Worksheets("Control").Range("D16")
sPath = Application.FileDialog(msoFileDialogFolderPicker).Show
sPathstr = Application.FileDialog(msoFileDialogFolderPicker).SelectedItems(1)
Set olNameSpace = olApp.GetNamespace("MAPI")
'check if folder is subfolder or not and choose olFolder accordingly
Set olFolder = ns.Folders("Subscriptions").Folders("Inbox")
strName = "Argus Ammonia"
h = 2
For i = 1 To olFolder.Items.Count
If olFolder.Items(i).Class <> olMail Then
Else
Set olMailItem = olFolder.Items(i)
'check if the search name is in the email subject
'If (InStr(1, olMailItem.Subject, olSubject, vbTextCompare) <> 0) Then
If (InStr(1, olMailItem.Sender, olSender, vbTextCompare) <> 0) Then
With olMailItem
For j = 1 To .Attachments.Count
strName = .Attachments.Item(j).DisplayName
'check if file already exists
If Not Dir(sPathstr & "\" & strName) = vbNullString Then
strName = "(1)" & strName
Else
End If
If Err.Number <> 0 Then
Else
.Attachments(j).SaveAsFile sPathstr & "\" & strName
End If
Err.Clear
Set sh = Nothing
'wB.Close
On Error GoTo 0
h = h + 1
Next j
End With
End If
End If
Next i
Application.ScreenUpdating = True
MsgBox "Download complete!", vbInformation + vbOKOnly, "Done"
End Sub
You can hard code the path to your EXE, please refer to the below code:
Declare Function FindExecutable Lib "shell32.dll" Alias "FindExecutableA" _
(ByVal lpFile As String, ByVal lpDirectory As String, ByVal lpResult As String) As Long
Sub Test_Printpdf()
Dim fn$
fn = "C:\Users\Ken\Dropbox\Excel\pdf\p1.pdf"
PrintPDf fn
End Sub
Sub PrintPDf(fn$)
Dim pdfEXE$, q$
pdfEXE = ExePath(fn)
If pdfEXE = "" Then
MsgBox "No path found to pdf's associated EXE.", vbCritical, "Macro Ending"
Exit Sub
End If
q = """"
'http://help.adobe.com/livedocs/acrobat_sdk/10/Acrobat10_HTMLHelp/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Acrobat10_SDK_HTMLHelp&file=DevFAQ_UnderstandingSDK.22.31.html
'/s/o/h/p/t
Shell q & pdfEXE & q & " /s /o /h /t " & q & fn & q, vbHide
End Sub
Function ExePath(lpFile As String) As String
Dim lpDirectory As String, sExePath As String, rc As Long
lpDirectory = "\"
sExePath = Space(255)
rc = FindExecutable(lpFile, lpDirectory, sExePath)
sExePath = Left$(sExePath, InStr(sExePath, Chr$(0)) - 1)
ExePath = sExePath
End Function
Sub Test_ExePath()
MsgBox ExePath(ThisWorkbook.FullName)
End Sub
Added an API method to find the path, the command line parameters don't work as well with the newer Adobe Acrobat Reader DC.
For more information, please refer to these links:
Printing a file using VBA code
Print a PDF file using VBA

Save pdf attachment using a field from the pdf

The code below finds my Subfolder from my Inbox then opens the email in the active window.
I would like to "Open" the pdf form attached to this email so I could save the attachment using one of the text fields from the pdf form.
The only code I can find saves the attachment to the temp folder but does not get the content from the pdf form.
Sub OpenMailAttachment()
Dim ns As NameSpace
Dim Inbox As MAPIFolder
Dim openMsg As Outlook.MailItem
Dim mySubFolder As MAPIFolder
Dim myAttachment As Outlook.Attachment
Dim FileName As String
Dim myInspector As Outlook.Inspector
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
Set mySubFolder = Inbox.Folders("PdfTest")
mySubFolder.Display
Set openMsg = mySubFolder.Items(1)
openMsg.Display
mySubFolder.Application.ActiveExplorer.Close
openMsg.Application.ActiveWindow
For Each myAttachment in Item.Attachment
FileName = "C:\temp\" & myAttachment.FileName
myAttachment.SaveAsFile FileName
myAttachment = openMsg.Attachments.Item.DisplayName
'(I get Compile error: *.Item* argument not optional)
myAttachments.Application.ActiveInspector.Display
End Sub
This should it...
Option Explicit
' use Declare PtrSafe Function with 64-bit Outlook
Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" ( _
ByVal hwnd As Long, _
ByVal lpOperation As String, _
ByVal lpFile As String, _
ByVal lpParameters As String, _
ByVal lpDirectory As String, _
ByVal nShowCmd As Long _
) As Long
Sub OpenMailAttachment()
Dim ns As NameSpace
Dim Inbox As MAPIFolder
Dim openMsg As Outlook.MailItem
Dim mySubFolder As MAPIFolder
Dim Attachment As Outlook.Attachment
Dim myAttachments As Outlook.Attachments
Dim FileName As String
Dim myInspector As Outlook.Inspector
Dim Item As Object
Dim sFileType As String
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
Set mySubFolder = Inbox.Folders("PdfTest")
mySubFolder.Display
Set openMsg = mySubFolder.Items(1)
openMsg.Display
mySubFolder.Application.ActiveExplorer.Close
openMsg.Application.ActiveWindow
Set myAttachments = openMsg.Attachments
If myAttachments.Count Then
For Each Attachment In myAttachments
'Last 4 Characters in a Filename
sFileType = LCase$(Right$(Attachment.FileName, 4))
Select Case sFileType
' Add additional file types below
Case ".pdf" ', ".doc", "docx", ".xls"
FileName = "C:\temp\" & Attachment.FileName
Attachment.SaveAsFile FileName
ShellExecute 0, "open", FileName, vbNullString, vbNullString, 0
End Select
Next
End If
End Sub
Option Explicit Statement (Visual Basic)
Setting Option Explicit to Off is generally not a good practice. You could misspell a variable name in one or more locations, which would cause unexpected results when the program is run.

outlook vba select messages in sub-folder

Outlook 2007 is configured with two email accounts:
Account#1: Hotmail
Account#2: Gmail
I would like to create a macro named simulating a user doing the following:
Left click on a within either the hotmail or gmail account.
Highlight all messages within the folder previously selected.
display a messageBox with the number of emails selected from this folder
I have tried several methods to define the folder, but its not working. My suspicion is it would work on the default PST, but that isn't what I'm using. Even tried using the method below to identify the specific folder I want to use. It does print out a path, but I am not able to use that as a variable value directly.
Any suggestions?
=== Information ===
The following macro was used to obtain information about the account & folder locations:
http://www.gregthatcher.com/Scripts/VBA/Outlook/GetFolderInfo.aspx
Hotmail
Name: aaaaa
FolderPath: \#hotmail.com\aaaaa
-
Gmail
Name: bbbbb
FolderPath: \#gmail.com\bbbbb
' please add your values for Const emailAccount and Const folderToSelect
' To begin, launch: start_macro
'
' the macro will loop all folders and will check two things , folder name and account name,
' when both are matched , will make that folder the active one , then will select all emails
' from it and at final will issue number of selected items no other References are required
' than default ones
Option Explicit
#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems
#End If
' please provide proper values for email account and folder name
Const emailAccount = "username#hotmail.com"
Const folderToSelect = "folder"
' declare some public variables
Dim mySession As Outlook.NameSpace
Dim myExplorer As Outlook.Explorer
Dim mySelection As Outlook.Selection
Dim my_folder As Outlook.folder
Sub start_macro()
Dim some_folders As Outlook.Folders
Dim a_fld As Variant
Dim fld_10 As Outlook.folder
Set mySession = Application.Session
Set some_folders = mySession.Folders
For Each a_fld In some_folders
Set fld_10 = a_fld
Call loop_subfolders_2(fld_10)
Next a_fld
End Sub
Sub final_sub()
If Not (my_folder Is Nothing) Then
Set myExplorer = Application.ActiveExplorer
Set Application.ActiveExplorer.CurrentFolder = my_folder
Call select_all_items(my_folder)
Else
MsgBox "There is no folder available for specified account !!!"
End If
End 'end the macro now
End Sub
Sub loop_subfolders_2(a_folder As Outlook.folder)
Dim col_folders As Outlook.Folders
Dim fld_1 As Outlook.folder
Dim arr_1 As Variant
Set col_folders = a_folder.Folders
For Each fld_1 In col_folders
If Left(fld_1.FolderPath, 2) = "\\" Then
arr_1 = Split(fld_1.FolderPath, "\")
'Debug.Print fld_1.Name & vbTab & arr_1(2) & vbTab & fld_1.FolderPath
If InStr(LCase(emailAccount), "#gmail.com") > 0 Then
If LCase(folderToSelect) = LCase(fld_1.Name) Then
If LCase(emailAccount) = LCase(arr_1(2)) Or arr_1(2) = "Personal Folders" Then
Set my_folder = fld_1
Call final_sub
Else
Call loop_subfolders_2(fld_1)
End If
Else
Call loop_subfolders_2(fld_1)
End If
Else
If LCase(folderToSelect) = LCase(fld_1.Name) And LCase(emailAccount) = LCase(arr_1(2)) Then
Set my_folder = fld_1
Call final_sub
Else
Call loop_subfolders_2(fld_1)
End If
End If
End If
Next fld_1
End Sub
Sub select_all_items(my_folder As Outlook.folder)
Dim my_items As Outlook.Items
Dim an_item As MailItem
Dim a As Long, b As Long
Set my_items = my_folder.Items
b = my_items.Count
DoEvents
'sleep 2000
Set mySelection = myExplorer.Selection
If CLng(Left(Application.Version, 2)) >= 14 Then
On Error Resume Next ' there are other folders that do not contains mail items
For Each an_item In my_items
If myExplorer.IsItemSelectableInView(an_item) Then
myExplorer.AddToSelection an_item
Else
End If
Next an_item
On Error GoTo 0
Else
myExplorer.Activate
If b >= 2 Then
For a = 1 To b - 1
SendKeys "{DOWN}"
'Sleep 50
Next a
For a = 1 To b - 1
SendKeys "^+{UP}"
' 'Sleep 50
Next a
End If
DoEvents
'sleep 2000
End If
Set my_items = Nothing
Set mySelection = myExplorer.Selection
MsgBox mySelection.Count
End Sub
does this one work?
Function GetFolder(ByVal FolderPath As String) As Outlook.folder
Dim TestFolder As Outlook.folder
Dim FoldersArray As Variant
Dim i As Integer
On Error GoTo GetFolder_Error
If Left(FolderPath, 2) = "\\" Then
FolderPath = Right(FolderPath, Len(FolderPath) - 2)
End If
'Convert folderpath to array
FoldersArray = Split(FolderPath, "\")
Set TestFolder = Application.Session.Folders.item(FoldersArray(0))
If Not TestFolder Is Nothing Then
For i = 1 To UBound(FoldersArray, 1)
Dim SubFolders As Outlook.Folders
Set SubFolders = TestFolder.Folders
Set TestFolder = SubFolders.item(FoldersArray(i))
If TestFolder Is Nothing Then
Set GetFolder = Nothing
End If
Next
End If
'Return the TestFolder
Set GetFolder = TestFolder
Exit Function
GetFolder_Error:
'MsgBox ("Ordner für verschieben nicht gefunden")
Set GetFolder = Nothing
Exit Function
End Function
for me this works with all Folders, no matter if Primary or other box (but all of them being Exchange, but I do not think this maters)
e.g. These work:
Set mailitem.SaveSentMessageFolder = GetFolder(mailitem.SentOnBehalfOfName & "\inbox")
Dim Subfolder As Outlook.MAPIFolder
Set Subfolder = GetFolder(olfolder.FullFolderPath & "\erledigt")
Dim Subfolder As Outlook.MAPIFolder
Set Subfolder = GetFolder("someaccount\inbox")

Sending emails from Excel using Outlook without security warning

I am using code from Ron de Bruin's website to send emails through Excel using Outlook. I get this security warning "A program is trying to send e-mail message on your behalf" asking me to allow or deny.
How can I avoid this warning and send emails directly"
Note: I am using Excel 2007.
Here is the code:
Dim OutApp As Object
Dim OutMail As Object
Dim strbody As String
Dim cell As Range
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
Sheets("" & Sheet & "").Select
With Sheets("" & Sheet & "")
strbody = ""
End With
On Error Resume Next
With OutMail
.To = " email1#a.com"
.CC = ""
.BCC = ""
.Subject = ""
.Body = strbody
.From = ""
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
' restore default application behavior
Application.AlertBeforeOverwriting = True
Application.DisplayAlerts = True
ActiveWindow.SelectedSheets.PrintOut Copies:=3, Collate:=True
In addition to the methods described in the link from the comment, assuming you are the sender "...asking me to allow or deny", if you have Excel running you can have Outlook already running as well.
The simplest way would be:
Set OutApp = GetObject(, "Outlook.Application")
I found the code below somewhere on the internet a couple of years ago. It automatically answers 'Yes' for you.
Option Compare Database
' Declare Windows' API functions
Private Declare Function RegisterWindowMessage _
Lib "user32" Alias "RegisterWindowMessageA" _
(ByVal lpString As String) As Long
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As Any, _
ByVal lpWindowName As Any) As Long
Private Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Function TurnAutoYesOn()
Dim wnd As Long
Dim uClickYes As Long
Dim Res As Long
uClickYes = RegisterWindowMessage("CLICKYES_SUSPEND_RESUME")
wnd = FindWindow("EXCLICKYES_WND", 0&)
Res = SendMessage(wnd, uClickYes, 1, 0)
End Function
Function TurnOffAutoYes()
Dim wnd As Long
Dim uClickYes As Long
Dim Res As Long
uClickYes = RegisterWindowMessage("CLICKYES_SUSPEND_RESUME")
wnd = FindWindow("EXCLICKYES_WND", 0&)
Res = SendMessage(wnd, uClickYes, 0, 0)
End Function
Function fEmailTest()
TurnAutoYesOn '*** Add this before your email has been sent
Set appOutLook = CreateObject("Outlook.Application")
Set MailOutLook = appOutLook.CreateItem(olMailItem)
With MailOutLook
.To = " <Receipient1#domain.com>; <Receipient2#domain.com"
.Subject = "Your Subject Here"
.HTMLBody = "Your message body here"
.Send
End With
TurnOffAutoYes '*** Add this after your email has been sent
End Function

Moving files from one folder to another

I need to move a file from one folder to another folder using VBA.
For m = 1 To fnum
MsgBox " Please Select " & m & "files"
ffiles(m) = Application.GetOpenFilename
Next m
If Dir(outputfolder) = "" Then
fso.createfolder (outputfolder)
End If
fso.Movefile ffiles(m), outputfolder " getting error at this place "
I am getting an error message.
Error message id "Runtime error 438 . Object doesnt support this property "
My favorite way of doing it. Using the SHFileOperation API
Option Explicit
Private Declare Function SHFileOperation Lib "shell32.dll" _
Alias "SHFileOperationA" (lpFileOp As SHFILEOPSTRUCT) As Long
Private Const FO_MOVE As Long = &H1
Private Const FOF_SIMPLEPROGRESS As Long = &H100
Private Type SHFILEOPSTRUCT
hWnd As Long
wFunc As Long
pFrom As String
pTo As String
fFlags As Integer
fAnyOperationsAborted As Long
hNameMappings As Long
lpszProgressTitle As Long
End Type
Sub Sample()
Dim fileToOpen As Variant
Dim outputfolder As String
Dim i As Long
outputfolder = "C:\Temp\"
fileToOpen = Application.GetOpenFilename(MultiSelect:=True)
If IsArray(fileToOpen) Then
If Dir(outputfolder) = "" Then MkDir outputfolder
For i = LBound(fileToOpen) To UBound(fileToOpen)
Call VBCopyFolder(fileToOpen(i), outputfolder)
Next i
Else
MsgBox "No files were selected."
End If
End Sub
Private Sub VBCopyFolder(ByRef strSource, ByRef strTarget As String)
Dim op As SHFILEOPSTRUCT
With op
.wFunc = FO_MOVE
.pTo = strTarget
.pFrom = strSource
.fFlags = FOF_SIMPLEPROGRESS
End With
'~~> Perform operation
SHFileOperation op
End Sub
We can move files from one folder to another automatically using script
https://seleniumautomations.blogspot.com/2020/05/less-than-5-sec-to-clean-and-organise.html?view=magazine