Outlook VBA Print to PDF - vba

I would like to automatically print emails to PDF from outlook.
I haven't found a way to automate the print dialogue. There are a couple other threads dealing with this same issue in Outlook VBA, but no clear solution (I thought it would be simple!)
For example, I have a rule in outlook that automatically moves receipts to a specific folder. I'd like to automatically print these to PDF. I've tried to accomplish this by...
For Loop: Go through each unread item in the specified folder
Print: MailItem.Printout Method
Print Dialogue: Input path and filename and click OK. I haven't found any means of automating this process
Sub PrintReceipts()
'==============================================
'Declare variables, set namespace, define outlook folder (example names used below)
'==============================================
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Dim msg As Outlook.MailItem
Dim Path As String
Dim Name As String
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set olFldr = objNS.GetDefaultFolder(olFolderInbox).Folders("subfolder 1").Folders("subfolder 2")
'==============================================
'For each unread message save to Path with Name and mark as Read (path is just an example)
'==============================================
For Each msg In olFldr.Items
If msg.UnRead Then
Path = "C:\Users\User\Desktop\"
Name = msg.Subject & ".pdf"
msg.PrintOut
'=================================================
'Here is where I get lost.
'Print Dialogue opens. I have tried SendKeys but it does not work
'=================================================
msg.UnRead = False
End If
Next
End Sub
Alternative: I am wondering if I can do the following...
Save for Word: MailItem.SaveAs, to save the item as an .MHT
Open Word: Somehow open Word and apply ActiveDocument.ExportAsFixedFormat to export as PDF
Close Word and go back to Outlook
I hope someone may have an idea!

First of all, iterating over all items in the folder is not really a good idea in Outlook. Instead, you need to use the Find/FindNext or Restrict methods of the Items class. These methods allow getting items that correspond to your search criteria only. 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
To save the message body using the PDF file format there is no need to use the SaveAs method of the MailItem class. The WordEditor property of the Inspector class returns an instance of the Word Document class which represents the message body. You can call the ExportAsFixedFormat method of the Document class directly from Outlook avoiding any disk operations.
Dim objDoc As Object, objInspector As Object
Set objInspector = myItem.GetInspector
Set objDoc = objInspector.WordEditor
objDoc.ExportAsFixedFormat folderPath & fileName & ".pdf", 17
Set objInspector = Nothing
Set objDoc = Nothing
See Chapter 17: Working with Item Bodies for more information.

Related

how to read outlook email in a directory files in vb.net

I am new in vb.net development and I must read informations (subject, body, ...) in Outlook email files that are in a disk directory (D:\mails\to-read\message1.msg, D:\mails\to-read\message2.msg, ...).
Is it possible ?
Can you please explain me ? With example ?
Thanks for your help.
If the Outlook object model (Outlook automation) is a possible option you can use the NameSpace.OpenSharedItem method which opens a shared item from a specified path or URL. This method is used to open iCalendar appointment (.ics) files, vCard (.vcf) files, and Outlook message (.msg) files. So, in the code you will get a MailItem object where you could get all the required properties.
Public Sub TestOpenSharedItem()
Dim oNamespace As Outlook.NameSpace
Dim oSharedItem As Outlook.MailItem
Dim oFolder As Outlook.Folder
' Get a reference to a NameSpace object.
Set oNamespace = Application.GetNamespace("MAPI")'Open the Signed Message (.msg) file containing the shared item.
Set oSharedItem = oNamespace.OpenSharedItem("C:\Temp\RegularMessage.msg")
MsgBox oSharedItem.Subject
oSharedItem.Close (olDiscard)
Set oSharedItem = Nothing
Set oSharedItem = Nothing
Set oFSO = Nothing
Set oNamespace = Nothing
End Sub

Replace Text in body and save

I receive emails that contain a link. That link does not work since I am not on that company's network. I can change part of the link for external use to get it to work.
For example the email has this link:
https://ipdms.web.companyname.com/ipdms/itemlocation
I change it to:
https://companyVPN.companyname.com/ipdms/itemlocation
I was able to create a script but I need to open the email, run the macro, and then hit save on the email.
Sub Change2VPN()
Application.ActiveInspector.CurrentItem.body = _
Replace(Application.ActiveInspector.CurrentItem.body, "ipdms.web", "companyVPN")
End Sub
I searched but have not been able to get anything to work. Is there a way I can either accomplish this on all items in a folder and save the email where it is or at least do it from the reading pane?
I can add the macro button to the ribbon.
I cannot run scripts as a rule on incoming emails due to corporate policies.
Basically you need to get the currently selected folder where a ribbon button was clicked and iterate over all items in the folder to get the job done:
Sub Change2VPN()
Dim olFolder As Outlook.Folder
Dim Item As Object
Dim explorer as Outlook.Explorer
Set explorer = Application.ActiveExplorer()
Set olFolder = explorer.CurrentFolder
For Each Item In olFolder.Items
If TypeOf Item Is Outlook.MailItem Then
Dim oMail As Outlook.MailItem: Set oMail = Item
oMail.HTMLBody = Replace(oMail.HTMLBody, "ipdms.web", "companyVPN")
oMail.Save()
End If
Next
End Sub

Print mail item as pdf

I am attempting to save all of the mail items within a folder in Outlook as PDF.
Sub pdfConversion()
Dim outApp As Object, objOutlook As Object, objFolder As Object, myItems As Object, myItem As Object
Dim psName As String, pdfName As String
Set outApp = CreateObject("Outlook.Application")
Set objOutlook = outApp.GetNamespace("MAPI")
Set objFolder = objOutlook.GetDefaultFolder(olFolderInbox).Folders("PDF Conversion")
Set myItems = objFolder.Items
For Each myItem In myItems
myItem.PrintOut copies:=1, preview:=False, ActivePrinter:="Adobe PDF", printtofile:=True, _
collate:=True, prtofilename:="C:\Users\lturner\Documents\" & myItem.Subject & ".pdf"
Next myItem
End Sub
I am using Outlook 2007, which doesn't have the option to save mails as PDF, hence I'm attempting to use the .PrintOut method.
Using the above I am currently receiving a "Named argument not found" error. I've looked elsewhere on the internet, but cannot seem to find a solution.
I used a combination of the answers posted by Krishna and Eugene Astafiev to produce the below code, which will now produce a PDF document out of the myItem.
Dim objDoc As Object, objInspector As Object
For Each myItem In myItems
fileName = Replace(myItem.Subject, ":", "")
Set objInspector = myItem.GetInspector
Set objDoc = objInspector.WordEditor
objDoc.ExportAsFixedFormat folderPath & fileName & ".pdf", 17
Set objInspector = Nothing
Set objDoc = Nothing
Next myItem
Posting this so anyone in the future who stumbles across the question can see the working code, which uses the WordEditor property.
There is no need to use the SaveAs method of the MailItem class. The WordEditor property of the Inspector class returns an instance of the Word Document class which represents the message body. You can call the ExportAsFixedFormat method of the Document class directly from Outlook avoiding any disk operations. See Chapter 17: Working with Item Bodies for more information.
MailItem.Printout doesnt take any paramters. it uses application level default settings.
The workaround i can think here is 2 step,
Step 1
use MailItem.SaveAs "somefile.doc", olDoc to convert to a RTF or Document
Step 2 using word automation, (reference word objects 14.0)
convert this to pdf using fixedformat.
Document.ExportAsFixedFormat "somefile.pdf", 17
This will only work if the original email is formatted as document that supports word conversion.
For instance if you are trying to print a Custom Form to PDF then this will fail because the Inspector will always pass back NOTHING for the word editor.

Accessing Text Body of Outlook MailItem Object - HTML and Plaintext

[EDIT] This problem does not appear to exist in C#. See reworked code at the bottom.
This has baffled me for two days now and has finally led me to making my first post on here.
I am coding in the visual basic editor of Excel 2007.
I am using the Outlook 2007 object library, from Excel 2007, not Outlook. Not sure if this matters.
I'm writing a program that will run periodically on a mail folder and parse out important information from emails as they arrive. The emails in question look like plain text, but are identified by the VBA Locals window as being olFormatHTML! To test this, I click "reply" for one of the emails and then attempt to paste an Excel range into the body of the email, and Outlook gives me a popup (Compatibility Checker) that gives me the option to "switch to HTML". Looks like plaintext. Further, opening the message, clicking "Other Actions" --> Encoding yields Unicode (UTF-8). So why in the world, when I expand this MailItem object in the Locals window, does Excel think it is an HTML email?
This MailItem's .Body is empty, and this MailItem's .HTMLBody does not contain the actual contents of the email, which are nonempty when viewed through Outlook. Here's what the HTMLBody value is:
"<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta name=ProgId content=Word.Document><met"
Code to create Outlook application object, navigate to desired folder and pass MailItem object to the parser (skip if you're familiar with this part):
' Navigate to desired folder and pass information to text analyzer.
Sub AccessInbox(timeStamp As Date)
Dim olApp As Outlook.Application
Dim objNamespace As Outlook.Namespace
Dim objFolder As Outlook.MAPIFolder
Dim sharedFolder As Outlook.MAPIFolder
Set olApp = New Outlook.Application
Set objNamespace = olApp.GetNamespace("MAPI")
' Explicitly went through hierarchy since I'll be using with a shared Mailbox eventually.
Set objMailbox = objNamespace.Folders("Mailbox - My Name")
Set objFolder = objMailbox.Folders("Inbox")
Set sharedFolder = objFolder.Folders("Folder With Stuff")
'mostly irrelevant, see ParseEmailText code below this
Dim emailTimeStamp As Date
For Each Item In sharedFolder.Items
' Inbox can contain other kinds of objects than MailItem.
If TypeOf Item Is MailItem Then
Dim thisEmail As Object
Set thisEmail = olApp.CreateItem(MailItem)
thisEmail = Item
' Check to see if email has already been analyzed.
emailTimeStamp = thisEmail.ReceivedTime
If emailTimeStamp > timeStamp Then
' Send to email text analyzxer.
ParseEmailText thisEmail
Else
Exit For
End If
End If
Next
End Sub
Code to parse email body:
Sub ParseEmailText(email As Outlook.MailItem)
emBody = email.Body
' This is the part where I wish I could just access the email's body, but it is empty.
End Sub
[EDIT] I reworked this basic code in C# and the MailItem.Body is NOT blank anymore. In fact it works exactly as expected. Any ideas why VBA sucks so much?
class Parser
{
//Outlook variables
Microsoft.Office.Interop.Outlook.Application app = null;
Microsoft.Office.Interop.Outlook._NameSpace ns = null;
Microsoft.Office.Interop.Outlook.MailItem item = null;
Microsoft.Office.Interop.Outlook.MAPIFolder inboxFolder = null;
Microsoft.Office.Interop.Outlook.MAPIFolder atFolder = null;
public Parser()
{
}
public void ParseInbox()
{
//open outlook
//Access Outlook (only need to do this once)
app = new Microsoft.Office.Interop.Outlook.Application();
ns = app.GetNamespace("MAPI"); //Returns a NameSpace object of the specified type. The only supported name space type is "MAPI".
ns.Logon(null, null, false, false); //Namespace.Logon method: Logs the user on to MAPI, obtaining a MAPI session.
inboxFolder = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
atFolder = inboxFolder.Folders["Folder With Stuff"];
for (int i = atFolder.Items.Count; i > 0; i--)
{
item = (Microsoft.Office.Interop.Outlook.MailItem)atFolder.Items[i];
string emailText = item.Body;
}
}
}
You need to use the Set keyword when setting a reference to an object. This line in your code creates a new email object (which is using your default setting of HTML email):
Set thisEmail = olApp.CreateItem(MailItem)
And then this line of code isn't using the Set keyword:
thisEmail = Item
So your variable isn't referncing the object you think but the new email.
Try using:
Set thisEmail = Item
Or, instead, replace both of these lines:
Set thisEmail = olApp.CreateItem(MailItem)
thisEmail = Item
With this line:
Set thisEmail = Item

Call Outlook procedure using VBScript

I have a procedure in Outlook that sends all the saved messages in Drafts folder.
Below is the code:
Public Sub SendMail()
Dim olApp As Outlook.Application
Dim olNS As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Dim olDraft As Outlook.MAPIFolder
Dim strfoldername As String
Dim i As Integer
Set olApp = Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
Set olFolder = olNS.GetDefaultFolder(olFolderInbox)
strfoldername = olFolder.Parent
Set olDraft = olNS.Folders(strfoldername).Folders("Drafts")
If olDraft.Items.Count <> 0 Then
For i = olDraft.Items.Count To 1 Step -1
olDraft.Items.Item(i).Send
Next
End If
End Sub
Above code works fine.
Question:
I want to use Task Scheduler to fire this procedure as a specified time.
1. Where will I put the procedure in Outlook, Module or ThisOutlookSession?
2. I am not good in vbscript so I also don't know how to code it to call the Outlook Procedure. I've done calling Excel Procedure but Outlook doesn't support .Run property.
So this doesn't work:
Dim olApp
Set olApp = CreateObject("Outlook.Application")
olApp.Run "ProcedureName"
Set olApp = Nothing
I've also read about the Session.Logon like this:
Dim olApp
Set olApp = CreateObject("Outlook.Application")
olApp.Session.Logon
olApp.ProcedureName
Set olApp = Nothing
But it throws up error saying object ProcedureName is not supported.
Hope somebody can shed some light.
SOLUTION:
Ok, I've figured out 2 work around to Avoid or get pass this pop-up.
1st one: is as KazJaw Pointed out.
Assuming you have another program (eg. Excel, VBScript) which includes sending of mail via Outlook in the procedure.
Instead of using .Send, just .Save the mail.
It will be saved in the Outlook's Draft folder.
Then using below code, send the draft which fires using Outlook Task Reminder.
Option Explicit
Private WithEvents my_reminder As Outlook.Reminders
Private Sub Application_Reminder(ByVal Item As Object)
Dim myitem As TaskItem
If Item.Class = olTask Then 'This works the same as the next line but i prefer it since it automatically provides you the different item classes.
'If TypeName(Item) = "TaskItem" Then
Set my_reminder = Outlook.Reminders
Set myitem = Item
If myitem.Subject = "Send Draft" Then
Call SendMail
End If
End If
End Sub
Private Sub my_reminder_BeforeReminderShow(Cancel As Boolean)
Cancel = True
Set my_reminder = Nothing
End Sub
Above code fires when Task Reminder shows with a subject "Send Draft".
But, we don't want it showing since the whole point is just to call the SendMail procedure.
So we added a procedure that Cancels the display of reminder which is of olTask class or TaskItem Type.
This requires that Outlook is running of course.
You can keep it running 24 hours as i did or, create a VBscript that opens it to be scheduled via Task Scheduler.
2nd one: is to use API to programatically click on Allow button when the security pop-up appears.
Credits to SiddarthRout for the help.
Here is the LINK which will help you programmatically click on the Allow button.
Of course you have to tweak it a bit.
Tried & Tested!
Assuming that you have Outlook Application always running (according to comment below your question) you can do what you need in the following steps:
add a new task in Outlook, set subject to: "run macro YourMacroName" and set time (plus cycles) when your macro should start.
go to VBA Editor, open ThisOutlookSession module and add the following code inside (plus see the comments inside the code):
Private Sub Application_Reminder(ByVal Item As Object)
If TypeName(Item) = "TaskItem" Then
Dim myItem As TaskItem
Set myItem = Item
If myItem.Subject = "run macro YourMacroName" Then
Call YourMacroName '...your macro name here
End If
End If
End Sub
Where will I put the procedure in Outlook, Module or ThisOutlookSession?
Neither. Paste the below code in a Text File and save it as a .VBS file. Then call this VBS file from the Task Scheduler as shown HERE
Dim olApp, olNS, olFolder, olDraft, strfoldername, i
Set olApp = GetObject(, "Outlook.Application")
Set olNS = olApp.GetNamespace("MAPI")
Set olFolder = olNS.GetDefaultFolder(6)
strfoldername = olFolder.Parent
Set olDraft = olNS.Folders(strfoldername).Folders("Drafts")
If olDraft.Items.Count <> 0 Then
For i = olDraft.Items.Count To 1 Step -1
olDraft.Items.Item(i).Send
Next
End If
If you are using Outlook 2007 or newer I have found you can easily eliminate the security pop up you mentioned above when running your script by doing the following:
In Outlook 2007 Trust Center, go to Macro Security - Select "No security Check for macros"
In Outlook 2007 Trust Center, go to Programatic Access - Select "Never warn me abous suspicious activity.
Of course that technically leaves you open to the remote possibility for someone to email you some malicious email script or something of that nature I assume. I trust my company has that managed though and this works for me. I can use VBS scripts in Outlook, Access, Excel to send emails with no security pop up.
Another Option:
If you don't want to do that, another option that has worked well for me prior to this is here:
http://www.dimastr.com/redemption/objects.htm
Basically a dll redirect that does not include the popup. It leaves your other default security in place and you write \ call your VBA for it and send mail without the secutity pop-ups.